PHP, MySQL, эффективный алгоритм поиска по тегам

Я создаю интернет-магазин. Этот магазин позволяет пользователям фильтровать продукты на category, а пара дополнительных дополнительных фильтров, таких как brand, color и т.д.

В настоящий момент различные свойства хранятся в разных местах, но я хотел бы переключиться на систему на основе тегов. В идеале моя база данных должна хранить теги со следующими данными:

  • product_id
  • tag_url_alias (уникальный)
  • tag_type (уникальный) (категория, product_brand, product_color и т.д.)
  • tag_value (не уникально)

Первая цель

Я хотел бы найти product_id, которые связаны с любым между 1-5 конкретными тегами. Теги извлекаются из URL-адреса, ориентированного на SEO. Поэтому я буду извлекать уникальные строки (tag_url_alias) для каждого тега, но я не буду знать tag_type. Поиск будет пересечением, поэтому мой поиск должен вернуть product_id, которые соответствуют всем предоставленного tags.

Вторая цель

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

Например, мой текущий поиск предназначен для продуктов, соответствующих тегам:

Shoe + Black + Adidas

Теперь посетитель магазина может посмотреть на полученные продукты и узнать, какие черные туфли могут предложить другие бренды. Таким образом, они могут перейти в фильтр "бренд" и выбрать любой из других перечисленных брендов. Допустим, у них есть 2 разных варианта (на практике это, вероятно, будет намного больше), в результате чего выполняются следующие поисковые запросы:

Shoe + Black + Nike > 103 results
Shoe + Black + K-swiss > 0 results

В этом случае, если они видят марку "K-swiss", указанную как доступный выбор в своем фильтре, их поиск вернет 0 результатов.

Это явно разочаровывает пользователя... Я бы очень хорошо знал, что переключение "бренда" с "adidas" на "k-swiss" приведет к 0 результатам и просто удалит всю опцию из фильтра.

То же самое касается категорий, цветов и т.д.

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

Производительность

Я подозреваю, что моя база данных в конечном итоге будет содержать:

между 250 и 1000 уникальными тегами

И он будет содержать:

от 10.000 до 100.000 уникальных продуктов

Текущие идеи

Я сделал несколько поисков Google и нашел следующую статью: http://www.pui.ch/phred/archives/2005/06/tagsystems-performance-tests.html

Судя по этой статье, запуская сотни запросов для достижения второй цели, это будет очень медленный путь. Пример "toxy" может работать для моих нужд, и это может быть приемлемым для моей Первой цели, но это было бы неприемлемо медленным для Второй цели.

Я думал, что могу запускать отдельные запросы, которые соответствуют 1 tag, к связанным с ним product_id, кэшировать эти запросы, а затем вычислять пересечения по результатам. Но, вычислить эти пересечения в MySQL? или в PHP? Если я использую MySQL, есть ли какой-то конкретный способ кэшировать эти отдельные запросы или поставлять нужные индексы, которые мне нужны?

Я бы предположил, что вполне возможно даже кэшировать пересечения между двумя этими наборами tag/product_id. Количество пересечений будет ограничено тем фактом, что a tag_type может иметь только одно конкретное значение, но я не уверен, как эффективно управлять этим типом кэширования. Опять же, я не знаю, должен ли я делать это в MySQL или PHP. И если я сделаю это в MySQL, какой лучший способ хранить и комбинировать этот тип кэшированных результатов?

Ответ 1

Использование поисковой системы sphinx может сделать эту магию для вас. Его ОЧЕНЬ быстро, и даже может обрабатывать словоформы, что может быть полезно при запросах SEO.

В терминах sphinx создайте документ - "продукт", индексируйте по тегам, выберите правильный рейтинг для запроса (например, MATCH_ALL_WORDS) и выполните пакетный запрос с различными комбинациями тегов, чтобы получить наилучшие результаты. Не забудьте использовать такие кэши, как memcahed или любой другой.

Ответ 2

Я еще не тестировал это, но у вас должен быть один запрос для удовлетворения вашей второй цели, а не запуск нескольких сотен запросов... Ниже приведен пример, как это должно работать в целом. Идея состоит в том, чтобы одновременно объединить три разных запроса и группу по выделенному значению и собрать только те, которые имеют какие-либо результаты.

SELECT t1.product_id, count(*) FROM tagtable t1, tagtable t2, tagtable t3 WHERE 
t1.product_id = t2.product_id AND 
t2.product_id = t3.product_id AND
t1.tag_type='yourcategoryforShoe' AND t1.tag_value='Shoe' AND
t2.tag_type='product_color' AND t2.tag_value='Black' AND
t3.tag_type='brand'
GROUP BY t3.tag_value
HAVING count(*) > 0