Перейти к содержимому


Аналоги (похожие товары)


  • Вы не можете ответить в тему
Сообщений в теме: 59

#1 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 22 February 2012 - 02:43 PM

Версия: 1.1 Stable (от 03.04.2012)

На основе идеи dorian и eugene_wb отсюда: http://vsupport.club/t1027.html.
Вариант реализации сравнения: eugene_wb (не забываем жать кнопочку "Спасибо!").

Принцип работы:
  • Нужно чтобы в meta_keywords товаров были прописаны значения характеристик (теги, метки), по которым хотим сравнивать товары (разделитель по умолчанию - пробел). Пример: "мужские белый тапочки стразы хлопок";
  • Сравнивает meta_keywords товара со другими товарами (кроме тех, которых нет в наличии, цена=0 или они в Главной категории);
  • Отбирает найденные аналоги по проценту соответствия и выводит нужное кол-во в блок "Аналоги" на странице детального описания товара;
Используемые элементы кода:

1. В core\includes\product_detailed.php вставить перед последним (внизу) "} else {":

// New Code (Similar Products Module)
$percent = 90; // Минимальный процент совпадения
$num = 3; // Количество выводимых аналогов
$explode_char = " "; // Разделитель в строке meta_keywords
$counter = 0;
function sort_explode_callback($delimiter,$array_string) {
$arr = explode($delimiter,$array_string);
sort($arr);
return $arr;
}
function translit($str) {
$tr = array ("А"=>"a","Б"=>"b","В"=>"v","Г"=>"g","Д"=>"d","Е"=>"e","Ж"=>"j","З"=>"z","И"=>"i","Й"=>"y","К"=>"k","Л"=>"l","М"=>"m","Н"=>"n","О"=>"o","П"=>"p","Р"=>"r","С"=>"s","Т"=>"t","У"=>"u","Ф"=>"f","Х"=>"h","Ц"=>"ts","Ч"=>"ch","Ш"=>"sh","Щ"=>"sch","Ъ"=>"","Ы"=>"yi","Ь"=>"","Э"=>"e","Ю"=>"yu","Я"=>"ya","а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d","е"=>"e","ж"=>"j","з"=>"z","и"=>"i","й"=>"y","к"=>"k","л"=>"l","м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r","с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"h","ц"=>"ts","ч"=>"ch","ш"=>"sh","щ"=>"sch","ъ"=>"y","ы"=>"yi","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya"," "=>"_","."=>"",","=>"","/"=>"_","'"=>"","°"=>"","+"=>"_");
return strtr($str,$tr);
}
$cur_keywords = metaphone(translit(serialize(sort_explode_callback($explode_char,prdGetMetaKeywordTag($productID)))));
$similar_meta_tags = db_query("SELECT s.productID, s.name, s.Price, s.meta_keywords, t.filename FROM ".PRODUCTS_TABLE." AS s LEFT JOIN ".PRODUCT_PICTURES." AS t on (s.default_picture=t.photoID AND s.productID=t.productID) WHERE s.categoryID!=1 AND s.enabled=1 AND s.productID!=".$productID." AND s.in_stock>0");
while ($row = db_fetch_row($similar_meta_tags)) {
$artig_shuffled[] = $row;
}
shuffle($artig_shuffled);
foreach ($artig_shuffled as $row) {
similar_text($cur_keywords,metaphone(translit(serialize(sort_explode_callback($explode_char,$row["meta_keywords"])))),$sim);
if ($sim > $percent) {
$counter += 1;
if ($counter > $num) break;
if (strlen($row["filename"])>0 && file_exists( "data/small/".$row["filename"])) {
$row["filename"] = "small/".$row["filename"];
} else {
$row["filename"] = "empty.gif";
}
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;
}
}
$smarty->assign("sim_products", $result_similar);
// End of New Code


* Самостоятельно указываем значения переменных (см. начало кода):
  • $percent - минимальный процент совпадения Meta-Keywords;
  • $num - кол-во аналогов для вывода на страницу детального описания товара;
  • $explode_char - разделитель меток (тегов) в Meta-Keywords (пробел по-умолчанию).
2. В core\functions\product_functions.php найти функцию function prdGetMetaKeywordTag и поменять в ней meta_description на meta_keywords - в ДВУХ местах! ВНИМАНИЕ! Поищите сначала глобальным поиском, может эта функция где-то у вас используется, у меня например она вообще оказалась незадействована...

3. Сделать дубликат файла core\tpl\user\ваш_шаблон\blocks\popularstatic.tpl.html и обозвать его например similar.tpl.html. Поменять в нем автозаменой (все вхождения) это:

$popular_products


на это:

$sim_products


4. Подключить в Админке similar.tpl.html как новый блок из файла и настроить его отображение только в Подробном описании товара.

P.S. Есть еще добавочный код, для того чтобы в списке аналогов выводились рекомендуемые товары, заданные в Админке - они выводятся впереди списка аналогов, ну это кому нужно будет - скажу как сделать...
  • 2

#2 badisoft

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 5075 сообщений
Репутация: 786
Мастер

Отправлено 23 February 2012 - 02:15 AM

Эта... заленился я вдумчиво читать исходник, но в PHP ведь есть штатные функции сравнения текстовых строк на предмет "насколько они похожи". Алгоритм Левенштейна, насколько я помню, хотя и напрочь уже не помню, в чем суть :). Оно тут не поможет?

PS. Почитавши. Расстояние Левенштейна, а не алгоритм. Но сути не меняет. Есть PHP-функция levenshtein(str1, str2), вычисляющая оное расстояние. По минимуму значения этой функции с довольно высокой долей достоверности можно подбирать аналоги по названию товара. Один товар может быть совершенно не аналогом, но выборка из пяти-семи ближайших по минимуму функции товаров наверняка будет в большинстве своем аналогами.
  • 0
http://cpu.badisoft.ru (тестовый сайт), http://badisoft.ru (модули)

#3 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 02:50 AM

Я уже писал, что все варианты сравнения строк (Левенштейн, Оливер, Филипс), встроенные в php к сожалению не подходят в нашем конкретном случае, т.к. они зависимы от расстановки символов в строке, то есть для них строки: а) тетя-дядя-баба-деда и б) дядя-баба-деда-тетя будут разными. Более того, некоторые требуют транслитерации символов. В нашем случае нужно тупо сравнить два массива, т.к. совпадение элементов, ИМХО, нам нужно 100%, то есть хар-ка, скажем "кузов"=>"сталь" должен быть сопоставим только с такой же хар-кой у другой модели, чтобы этот элемент считался нами совпадающим и т.д. Короче сложно объяснить, если интересно, сам посмотри к какому виду приводят эти функции строку и как ее оценивают потом в % соответствия. В любом случае сравнение 2-х массивов походу быстрее даже работает, чем эти функции.

Ну, с другой стороны, я и непротив применить иной алгоритм сравнения, особенно если он прописан во встроенной php-функции, нужно протестировать на реальных строках и оценить нужный нам процент совпадения...

-------------------------------

P.S. Поправил немного код (была ошибочка с usort-ом). А также есть вариант вписания в массив "аналогов" еще и стандартных "рекомендуемых" товаров - если они в Админке были залинкованы, то выводятся вначале списка аналогов (думаю, так будет правильно).
  • 1

#4 badisoft

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 5075 сообщений
Репутация: 786
Мастер

Отправлено 23 February 2012 - 03:17 AM

то есть для них строки: а) тетя-дядя-баба-деда и б) дядя-баба-деда-тетя будут разными

Разными - это понятно. Вопрос лишь в численном выражении НАСКОЛЬКО разными. Левенштейн для этого и придуман.
Я не готов сейчас вникать в суть алгоритма (пред-23-февраля, корпоратив, бла-бла-бла, тяжело), но подозреваю, что как раз приведенные примеры дадут довольно минимальный результат. (чуть позже) собственно, и дают.
echo levenshtein('тетя-дядя-баба-деда','тетя-дядя-баба-деда')."\n";
echo levenshtein('тетя-дядя-баба-деда','дядя-баба-деда-тетя')."\n";
echo levenshtein('тетя-дядя-баба-деда','ядядабабедадетят---')."\n";
echo levenshtein('тетя-дядя-баба-деда','ядядядяддядядядядяд')."\n";
echo levenshtein('тетя-дядя-баба-деда','hghfgfghshgghghfggf')."\n";

Этот код выдает
0
18
23
21
35
т.е. ровно то, что и хотелось. 'тетя-дядя-баба-деда' наиболее близок к 'дядя-баба-деда-тетя'.
Левенштейн, видимо, не совсем уж полный дурак был :).

---------- Сообщение объединено ----------

Более того, некоторые требуют транслитерации символов

Это какие конкретно из трех упомянутых? :)
  • 0
http://cpu.badisoft.ru (тестовый сайт), http://badisoft.ru (модули)

#5 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 03:49 AM

То, что мистер Левенштейн не дурак, ясно из одной фамилии )))
Впринципе согласен, что можно Л использовать, но нужно протестировать на реальных строках, т.к. например сравнение:
echo levenshtein('тетя-дядя-баба-деда','деда-тетя')."\n";
echo levenshtein('тетя-дядя-баба-деда','баба-деда')."\n";

дает 10 и 14 соответственно - что не очевидно для нашего случая с характеристиками, исходя из того что levenshtein('тетя-дядя-баба-деда','дядя-баба-деда-тетя') дает 18... гмм...

Это какие конкретно из трех упомянутых?

Оливер по-моему )))
  • 0

#6 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 10:19 AM

вот вариант, который стоит у меня


$cifra = 4; //количество последних товаров для выбора
$percent = 75;
$counter = 0;
$result_similar = array();

$search_for_similar_meta_tags = db_fetch_assoc(db_query("SELECT meta_description,meta_keywords FROM ".PRODUCTS_TABLE." WHERE productID=".$productID.""));

$similar_meta_tags = db_query("select s.productID, s.name, s.Price, s.enabled, s.meta_description, s.meta_keywords, t.filename FROM ".PRODUCTS_TABLE." AS s LEFT JOIN ".PRODUCT_PICTURES."
AS t on (s.default_picture=t.photoID AND s.productID=t.productID) WHERE s.categoryID!=1 AND s.enabled=1 AND s.productID!=".$productID." ORDER by RAND(NOW())");


while ($row = db_fetch_row($similar_meta_tags))
{
similar_text(metaphone($search_for_similar_meta_tags["meta_keywords"]),metaphone($row["meta_keywords"]),$sim);
if ($sim > $percent )
{
$counter += 1;
if ($counter > $cifra) break;
if (strlen($row["filename"])>0 && file_exists( "data/small/".$row["filename"])){
$row["filename"] = "small/".$row["filename"];
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;

}else{
$row["filename"] = "empty.gif";
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;
}
}
}
$smarty->assign("similar_products", $result_similar);

  • 0
Изображение Изображение Изображение

#7 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 12:45 PM

вот вариант, который стоит у меня

Дай плз. раскладку примера для реальных строк типа: "строка/строка/%". Дык. а как у тебя без транслита все это работает? Функция кириллицу выбрасывает же.
  • 0

#8 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 02:28 PM

Дай плз. раскладку примера для реальных строк типа: "строка/строка/%".

вот сдесь не понял, что нужно написать??


Функция кириллицу выбрасывает же.

упс )) не проверял все возможно
довалено: даже если так, функцию транслита не кто не отменял, добавить ее и все будет нормально, чуть позже проверю, отпишусь
еще добавлено: но при транслите metaphone, может отрабатывать некорректно ввиду фонем, только английского языка, и левинштайн может показаться лучше, но нужны тесты

тест 1: используется base64_encode

echo "тетя-дядя-баба-деда - ".metaphone(base64_encode("тетя-дядя-баба-деда"))."
";
echo "дядя-баба-деда-тетя - ".metaphone(base64_encode("дядя-баба-деда-тетя"))."
";
similar_text(metaphone(base64_encode("тетя-дядя-баба-деда")),metaphone(base64_encode("дядя-баба-деда-тетя")),$sim);
echo $sim."%";
?>


вывод этого кода

тетя-дядя-баба-деда - UKSKTLKTKSK
дядя-баба-деда-тетя - PKKLTLTKS
70%



тест 2: используется транслит

echo "тетя-дядя-баба-деда - ".metaphone(translit("тетя-дядя-баба-деда"))."
";
echo "дядя-баба-деда-тетя - ".metaphone(translit("дядя-баба-деда-тетя"))."
";
similar_text(metaphone(translit("тетя-дядя-баба-деда")),metaphone(translit("дядя-баба-деда-тетя")),$sim);
echo $sim."%";
function translit($str)
{
$tr = array(
"А"=>"a","Б"=>"b","В"=>"v","Г"=>"g","Д"=>"d","Е"=>"e","Ж"=>"j","З"=>"z","И"=>"i","Й"=>"y","К"=>"k","Л"=>"l","М"=>"m","Н"=>"n",
"О"=>"o","П"=>"p","Р"=>"r","С"=>"s","Т"=>"t","У"=>"u","Ф"=>"f","Х"=>"h","Ц"=>"ts","Ч"=>"ch","Ш"=>"sh","Щ"=>"sch","Ъ"=>"","Ы"=>"yi","Ь"=>"",
"Э"=>"e","Ю"=>"yu","Я"=>"ya","а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d","е"=>"e","ж"=>"j","з"=>"z","и"=>"i","й"=>"y","к"=>"k","л"=>"l",
"м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r","с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"h","ц"=>"ts","ч"=>"ch","ш"=>"sh","щ"=>"sch","ъ"=>"y",
"ы"=>"yi","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya"," "=> "_","."=> "",","=> "","/"=> "_","’"=> "","°"=> "","+"=> "_");
return strtr($str,$tr);
}
?>


вывод этого кода

тетя-дядя-баба-деда - TTYTYTYBBTT
дядя-баба-деда-тетя - TYTYBBTTTTY
72.727272727273%



---------- Сообщение объединено ----------

Вывод после 2 тестов:
при использовании base64_encode работает относительно нормально, но приходится поднимать процент сравнения, примерно до 82%, и иногда бывают проскакивают товары с неособо похожим описанием, а вот при использовании translit, дела обстоят значительно лучше, я пока остановился на нем

вот готовый код
///////////////////similar_products
function translit($str)
{
$tr = array(
"А"=>"a","Б"=>"b","В"=>"v","Г"=>"g","Д"=>"d","Е"=>"e","Ж"=>"j","З"=>"z","И"=>"i","Й"=>"y","К"=>"k","Л"=>"l","М"=>"m","Н"=>"n",
"О"=>"o","П"=>"p","Р"=>"r","С"=>"s","Т"=>"t","У"=>"u","Ф"=>"f","Х"=>"h","Ц"=>"ts","Ч"=>"ch","Ш"=>"sh","Щ"=>"sch","Ъ"=>"","Ы"=>"yi","Ь"=>"",
"Э"=>"e","Ю"=>"yu","Я"=>"ya","а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d","е"=>"e","ж"=>"j","з"=>"z","и"=>"i","й"=>"y","к"=>"k","л"=>"l",
"м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r","с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"h","ц"=>"ts","ч"=>"ch","ш"=>"sh","щ"=>"sch","ъ"=>"y",
"ы"=>"yi","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya"," "=> "_","."=> "",","=> "","/"=> "_","’"=> "","°"=> "","+"=> "_");
return strtr($str,$tr);
}

$cifra = 4; //количество последних товаров для выбора
$percent = 75;
$counter = 0;
$result_similar = array();

$search_for_similar_meta_tags = db_fetch_assoc(db_query("SELECT meta_description,meta_keywords FROM ".PRODUCTS_TABLE." WHERE productID=".$productID.""));

$similar_meta_tags = db_query("select s.productID, s.name, s.Price, s.enabled, s.meta_description, s.meta_keywords, t.filename FROM ".PRODUCTS_TABLE." AS s LEFT JOIN ".PRODUCT_PICTURES."
AS t on (s.default_picture=t.photoID AND s.productID=t.productID) WHERE s.categoryID!=1 AND s.enabled=1 AND s.productID!=".$productID." ORDER by RAND(NOW())");


while ($row = db_fetch_row($similar_meta_tags))
{
similar_text(metaphone(translit($search_for_similar_meta_tags["meta_keywords"])),metaphone(translit($row["meta_keywords"])),$sim);
if ($sim > $percent )
{
$counter += 1;
if ($counter > $cifra) break;
if (strlen($row["filename"])>0 && file_exists( "data/small/".$row["filename"])){
$row["filename"] = "small/".$row["filename"];
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;

}else{
$row["filename"] = "empty.gif";
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;
}
}
}
$smarty->assign("similar_products", $result_similar);
//////////////////similar end


---------- Сообщение объединено ----------

Ну и напоследок, поскольку идея где-то мелькала, для вывода похожих товаров по характеристикам необходимо взять все характеристики в массив и сделать serialize, таким образом можно при данном примере сравнивать массивы характеристик
  • 0
Изображение Изображение Изображение

#9 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 03:33 PM

У Левенштейна пока есть такая проблема:

echo levenshtein('тетя-дядя-баба-деда','деда-тетя')."\n";
echo levenshtein('тетя-дядя-баба-деда','баба-деда')."\n";

дает 10 и 14 соответственно - что не очевидно для нашего случая с характеристиками, исходя из того что levenshtein('тетя-дядя-баба-деда','дядя-баба-деда-тетя') дает 18... гмм...


Вот сравнение разных функций:

тетя дядя баба деда сын дочь - к этой строке подбираем аналоги
баба дочь тетя дядя сын деда | levenshtein:16 | massive:100% | metaphone:69%
тетя дядя мама баба папа деда сын дочь внук внучка | levenshtein:22 | massive:75% | metaphone:74%
дочь внук баба тетя дядя деда сын внучка | levenshtein:23 | massive:86% | metaphone:67%
тетя дядя баба деда | levenshtein:9 | massive:80% | metaphone:81%
дядя баба деда тетя | levenshtein:13 | massive:80% | metaphone:67%

Какие тут видны проблемы? Обрати внимание, что наша осн. строка и вторая идентичны, просто изменен порядок слов... Можно, конечно, производить сортировку слов в алфавитном порядке в строках, а потом сравнивать.. сейчас нужно попробывать что будет...

---------- Сообщение объединено ----------

Вот сравнение для этих же строк, прошедших сортировку по алфавиту:

баба деда дочь дядя сын тетя - к этой строке подбираем аналоги
баба внук внучка деда дочь дядя мама папа сын тетя | levenshtein:22 | massive:75% | metaphone:74%
баба внук внучка деда дочь дядя сын тетя | levenshtein:12 | massive:86% | metaphone:82%
баба деда дядя тетя | levenshtein:9 | massive:80% | metaphone:81%
баба деда дочь дядя сын тетя | levenshtein:0 | massive:100% | metaphone:100%
баба деда дядя тетя | levenshtein:9 | massive:80% | metaphone:81%

Похоже, картина меняется в положительную сторону, это касается всех функций... Однако строки приходится прогонять через сортировщик...

---------- Сообщение объединено ----------

Ну и напоследок, поскольку идея где-то мелькала, для вывода похожих товаров по характеристикам необходимо взять все характеристики в массив и сделать serialize, таким образом можно при данном примере сравнивать массивы характеристик

Это добавляет дополнительные запросы в Базу, что не есть хорошо, особенно если кол-во товаров и характеристик большое.

Теперь нужно понять какой из методов сравнения работает шустрее...
  • 0

#10 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 03:59 PM

как вариант пока вижу делать explode(",")->array->sort->serialize->metaphone
  • 0
Изображение Изображение Изображение

#11 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 04:14 PM

Теперь нужно понять какой из методов сравнения работает шустрее...

Как и ожидалось, работают они примерно одинаково, т.к. малое кол-во товаров в тестовом магазине, вот выборка:
1-ое значение - massive, 2-ое - metaphone, 3-е - levenshtein

Всего: 634 товара
Данные: 0.176 сек | 0.225 сек | 0.190 сек
Шаблон: 0.388 сек | 0.393 сек | 0.397 сек
Работа с БД: 0.021 сек | 0.024 сек | 0.023 сек
Общее время работы: 0.585 сек | 0.633 сек | 0.610 сек
Запросов в БД: 70 | 75 | 69
  • 0

#12 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 04:28 PM

вроде бы подошли к идеальному варианту, вот код

echo "тетя,дядя,баба,деда - ".metaphone(translit("тетя,дядя,баба,деда"))."
";
echo "дядя,баба,деда,тетя - ".metaphone(translit("дядя,баба,деда,тетя"))."
";
similar_text(metaphone(translit(serialize(sort(explode(",","тетя,дядя,баба,деда"))))),metaphone(translit(serialize(sort(explode(",","дядя,баба,деда,тетя"))))),$sim);
echo $sim."%";

function translit($str)
{
$tr = array(
"А"=>"a","Б"=>"b","В"=>"v","Г"=>"g","Д"=>"d","Е"=>"e","Ж"=>"j","З"=>"z","И"=>"i","Й"=>"y","К"=>"k","Л"=>"l","М"=>"m","Н"=>"n",
"О"=>"o","П"=>"p","Р"=>"r","С"=>"s","Т"=>"t","У"=>"u","Ф"=>"f","Х"=>"h","Ц"=>"ts","Ч"=>"ch","Ш"=>"sh","Щ"=>"sch","Ъ"=>"","Ы"=>"yi","Ь"=>"",
"Э"=>"e","Ю"=>"yu","Я"=>"ya","а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d","е"=>"e","ж"=>"j","з"=>"z","и"=>"i","й"=>"y","к"=>"k","л"=>"l",
"м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r","с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"h","ц"=>"ts","ч"=>"ch","ш"=>"sh","щ"=>"sch","ъ"=>"y",
"ы"=>"yi","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya"," "=> "_","."=> "",","=> "","/"=> "_","’"=> "","°"=> "","+"=> "_");
return strtr($str,$tr);
}
?>


вывод этого дела

тетя,дядя,баба,деда - TTYTYTYBBTT
дядя,баба,деда,тетя - TYTYBBTTTTY
100%



---------- Сообщение объединено ----------

Как и ожидалось, работают они примерно одинаково, т.к. малое кол-во товаров в тестовом магазине, вот выборка:
1-ое значение - massive, 2-ое - metaphone, 3-е - levenshtein

Всего: 634 товара
Данные: 0.176 сек | 0.225 сек | 0.190 сек
Шаблон: 0.388 сек | 0.393 сек | 0.397 сек
Работа с БД: 0.021 сек | 0.024 сек | 0.023 сек
Общее время работы: 0.585 сек | 0.633 сек | 0.610 сек
Запросов в БД: 70 | 75 | 69


levenstein - требует доп математической операции для сравнения процента, а это уже прирост в нагрузке
  • 0
Изображение Изображение Изображение

#13 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 04:32 PM

вроде бы подошли к идеальному варианту, вот код

Ну похоже на правду, еще нужно добавить strtolower() чтобы исключить опечатки в регистре...
  • 0

#14 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 05:34 PM

Ну похоже на правду, еще нужно добавить strtolower() чтобы исключить опечатки в регистре...


помоему metaphone и так все приводит к одному регистру

UPD: так, вылезли проблемы с sort-serialize-explode
UPD2: проблема именно в sort, в том что на выходе имеем bool, а не масив

---------- Сообщение объединено ----------

решение использовать обратную функцию

function sort_explode_callback($delimiter,$array_string)
{
$arr = explode($delimiter,$array_string);
sort($arr);
return $arr;
}


и будет примерно так

similar_text(metaphone(translit(serialize(sort_explode_callback(",","тетя,дядя,баба,деда")))),metaphone(translit(serialize(sort_explode_callback(",","дядя,баба,деда,тетя")))),$sim);


---------- Сообщение объединено ----------

в общем вот рабочий код, и самое главное он качественно отбирает товары ))

///////////////////similar_products
function translit($str)
{
$tr = array(
"А"=>"a","Б"=>"b","В"=>"v","Г"=>"g","Д"=>"d","Е"=>"e","Ж"=>"j","З"=>"z","И"=>"i","Й"=>"y","К"=>"k","Л"=>"l","М"=>"m","Н"=>"n",
"О"=>"o","П"=>"p","Р"=>"r","С"=>"s","Т"=>"t","У"=>"u","Ф"=>"f","Х"=>"h","Ц"=>"ts","Ч"=>"ch","Ш"=>"sh","Щ"=>"sch","Ъ"=>"","Ы"=>"yi","Ь"=>"",
"Э"=>"e","Ю"=>"yu","Я"=>"ya","а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d","е"=>"e","ж"=>"j","з"=>"z","и"=>"i","й"=>"y","к"=>"k","л"=>"l",
"м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r","с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"h","ц"=>"ts","ч"=>"ch","ш"=>"sh","щ"=>"sch","ъ"=>"y",
"ы"=>"yi","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya"," "=> "_","."=> "",","=> "","/"=> "_","’"=> "","°"=> "","+"=> "_");
return strtr($str,$tr);
}

function sort_explode_callback($delimiter,$array_string)
{
$arr = explode($delimiter,$array_string);
sort($arr);
return $arr;
}


$cifra = 4; //количество последних товаров для выбора
$percent = 70;
$counter = 0;
$explode_char = ",";
$result_similar = array();

$search_for_similar_meta_tags = db_fetch_assoc(db_query("SELECT meta_description,meta_keywords FROM ".PRODUCTS_TABLE." WHERE productID=".$productID.""));

$similar_meta_tags = db_query("select s.productID, s.name, s.Price, s.enabled, s.meta_description, s.meta_keywords, t.filename FROM ".PRODUCTS_TABLE." AS s LEFT JOIN ".PRODUCT_PICTURES."
AS t on (s.default_picture=t.photoID AND s.productID=t.productID) WHERE s.categoryID!=1 AND s.enabled=1 AND s.productID!=".$productID." ORDER by RAND(NOW())");


while ($row = db_fetch_row($similar_meta_tags))
{
similar_text(metaphone(translit(serialize(sort_explode_callback($explode_char,$search_for_similar_meta_tags["meta_keywords"])))),metaphone(translit(serialize(sort_explode_callback($explode_char,$row["meta_keywords"])))),$sim);
if ($sim > $percent )
{
$counter += 1;
if ($counter > $cifra) break;
if (strlen($row["filename"])>0 && file_exists( "data/small/".$row["filename"])){
$row["filename"] = "small/".$row["filename"];
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;

}else{
$row["filename"] = "empty.gif";
$row["cena"] = $row["Price"];
$row["Price"] = show_price($row["Price"]);
$result_similar[] = $row;
}
}
}
$smarty->assign("similar_products", $result_similar);
//////////////////similar end



P.S. для магазинов с большим количеством товара !!!необходимо!!! оптимизировать "ORDER by RAND()", иначе база будет вешатся
  • 0
Изображение Изображение Изображение

#15 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 10:07 PM

А почему ты не хочешь запускать большую выборку из базы только для подпавших под условия совпадения ($percent), да еще их можно сократить по кол-ву ($cifra), тогда нужно пройтись только по meta_keywords всех товаров, да и то вычти сразу товары у которых Cклад <= 0, которые enabled=0 и у которых нет цены, и те, которые в 1-ой категории, а потом для 4-х штук с большим %-ом сделать уже полную выгрузку...

Теперь осталось пакетно укомплектовать meta_keywords для всех товаров нужными доп. характеристиками...
  • 0

#16 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 10:33 PM

А почему ты не хочешь запускать большую выборку из базы только для подпавших под условия совпадения ($percent)

процент совпадения можно вычислить только после запроса к базе и получения meta_keywords, и чтобы не нагружать базу, он должен быть один

да еще их можно сократить по кол-ву ($cifra)

нельзя, так как LIMIT даст, например всего 4 записи из выборки, а вдруг они не подойдут


да и то вычти сразу товары у которых Cклад <= 0, которые enabled=0

это уже вычтено + вычтен текущий продукт + мне нужно выводить товары, даже у которых нет цены
в этой строке WHERE s.categoryID!=1 AND s.enabled=1 AND s.productID!=".$productID."
  • 0
Изображение Изображение Изображение

#17 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 10:58 PM

Не ))) я не это имею ввиду, посмотри мой код в шапке - там 2 запроса (1ый берет только meta_keywords, а второй уже "большой" запрос всех данным по моделям попавшим под наш фильтр).
  • 0

#18 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 11:11 PM

Не ))) я не это имею ввиду, посмотри мой код в шапке - там 2 запроса (1ый берет только meta_keywords, а второй уже "большой" запрос всех данным по моделям попавшим под наш фильтр).


у тебя в шапке вот этот код

  while($row = db_fetch_row($similar_meta_tags)) {
$res_sim = array_unique(explode(" ", strtolower($row["meta_keywords"])));
$result_arr = array_intersect($res_cur, $res_sim);
$sim_cur = round((count($result_arr) / ((count($res_cur) + count($res_sim)) / 2)) * 100);
if ($sim_cur >= $sim) {
$ssr = db_fetch_assoc(db_query("SELECT s.productID, s.categoryID, s.name, s.Price, s.uri, s.uri_opt_val, t.filename FROM ".PRODUCTS_TABLE." AS s LEFT JOIN ".PRODUCT_PICTURES." AS t on (s.default_picture=t.photoID AND s.productID=t.productID) WHERE s.productID = ".$row["productID"]));
if (strlen($ssr["filename"])>0 && file_exists( "data/small/".$ssr['uri_opt_val']."/".$ssr['filename'])) {
$ssr["filename"] = "small/".$ssr['uri_opt_val']."/".$ssr['filename'];
} else {
$ssr["filename"] = "empty.gif";
}
$ssr["similarity"] = $sim_cur;
$result_sim[] = $ssr;
}


делает как минимум 4(назовем это - минимальное число совпавдений) запроса к базе

а в приведенном мной коде, всего один запрос))
  • 0
Изображение Изображение Изображение

#19 micehide

    Продвинутый пользователь

  • Assistent vsupport.club
  • PipPipPip
  • 359 сообщений
Репутация: 124
Мастер

Отправлено 23 February 2012 - 11:26 PM

Дык, я имею ввиду принцип, можно же указать в запросе WHERE productID IN (1,2,3,...) - перечислить отобранные productID, или ты считаешь что одна полная выборка по большому кол-ву товаров из двух таблиц будет меньше нагружать базу, чем 2 выборки поскромнее? (кстати, посмотри кол-во запросов из базы для product_detailed, у меня их 69 ))))
  • 0

#20 eugene_wb

    Продвинутый пользователь

  • Модераторы
  • 827 сообщений
Репутация: 167
Мастер

Отправлено 23 February 2012 - 11:38 PM

Дык, я имею ввиду принцип, можно же указать в запросе WHERE productID IN (1,2,3,...) - перечислить отобранные productID, или ты считаешь что одна полная выборка по большому кол-ву товаров из двух таблиц будет меньше нагружать базу, чем 2 выборки поскромнее? (кстати, посмотри кол-во запросов из базы для product_detailed, у меня их 69 ))))


Нагружать будет меньше, причем намного, вот ссылка на инфу


А в твоем варианте, скрипт будет постоянно юзать базу при просмотре товара, а из кеша брать только общий запрос на мета теги ))
  • 0
Изображение Изображение Изображение