Шаблон проектирования для развернутого / отфильтрованного поиска - PullRequest
9 голосов
/ 28 февраля 2010

Я собираюсь создать мощную функцию поиска для сайта, аналогичную расширенному поиску NewEgg, например,

http://www.newegg.com/Product/ProductList.aspx?Submit=ENE&N=2010150014%201035507776&name=7200%20RPM

Я работаю с различными объектами, похожими на продукты, которые имеют разные критерии. Кто-нибудь может порекомендовать хороший дизайн для создания поисковой системы, как NewEgg's?

Ответы [ 2 ]

22 голосов
/ 28 февраля 2010

Хранение данных «по вертикали», то есть в формате Entity-Attribute-Value (EAV), наряду с управлением схемами [мета], управляемым данными, неявным для EAV, обеспечить структуру, в которой атрибуты каждого продукта «независимы» друг от друга. Это, в свою очередь, облегчает реализацию детализации (т. Е. Управляемого уточнения запроса, когда на каждом этапе конечному пользователю предоставляется список возможных атрибутов, все еще применимых, для каждого такого атрибута список возможных значений).

Небольшое предостережение заключается в том, что это лучше применимо к меньшим каталогам (скажем, менее 1 миллиона продуктов), поскольку модель EAV может создавать некоторые проблемы с производительностью и / или масштабированием для больших баз данных. Фактический размер, при котором производительность вызывает озабоченность, зависит от специфики каталога (среднее количество атрибутов на продукт, общность атрибутов между продуктами другого типа, общая сложность «онтологии» и т. Д.), Но EAV довольно путь для небольших каталогов. В дополнение к поддержке описанной фильтрации детализации, она вводит гибкую схему данных (возможность добавлять / удалять атрибуты и / или типы продуктов и т. Д., Не требуя изменения физической схемы (базы данных); только логическая схема изменены).

Редактировать : более подробная информация / ресурсы по EAV
Правда, статья в Википедии об этом несколько абстрактна ...
В двух словах, модель выделяет следующие понятия:

  • Сущность (она же продукт или предмет) = «запись» в традиционном реляционном жаргоне
  • Атрибут = «столбец» (он же «поле») в языке РСУБД
  • Значение = числовое (или строковое, или иное) значение данного столбца для данной записи.
  • Тип (он же категория) = [свободно] «таблица» в СУБД, т. Е. Набор записей, которые, как правило, имеют один и тот же набор атрибутов.

Чтобы проиллюстрировать это, скажем, каталогом товаров для электроники, объектом может быть конкретный «Монитор с плоским экраном», его типом может быть «Устройства отображения», его атрибуты «Размер», «Разрешение», «Цена» и т. Д. .

При использовании EAV основная часть информации сохраняется в двух таблицах, называемых, например, таблицей Product и таблицей ProductAttributes:

Product table  
   "ProductID" (primary key, the "EntityId")
    "TypeId" 
    optionally, some common attributes found in all/most other Products, say...
      price
      ManufacturerId
      Photo

ProductAttributes table
    "ProductID" (Foreign Key to Product table)
    "AttributeID"  (FK to Attribute table)
    "Value"   (actual value; note: sometimes we can have several SQL fields for
               this say IntValue, StringValue, DateValue, allowing to store 
               values in their natural format)

Приведенные выше таблицы составляют основную часть данных и дополняются таблицами, хранящими [логическую] схему каталога, также известную как «метаданные». Эти таблицы включают в себя:

  • Таблица атрибутов, в которой определены атрибуты: имя, тип данных, isRequired и т. Д.
  • Таблица типов, в которой определены типы (категории): Имя, возможно, родительский тип в случае иерархических онтологий.
  • Type_Attributes, где перечислены возможные атрибуты для данного типа (например: «Телевизор» имеет атрибут «Количество каналов», «Размер экрана» и т. Д., А «Видеомагнитофон» имеет атрибут «Количество головок», « Формат поддерживается "," Цвет кузова "и т. Д.

Все это может показаться несколько сложным по сравнению с традиционным подходом, согласно которому логическая схема «жестко закодирована» в схеме SQL, т. Е. У нас есть одна таблица «TVSets» с набором столбцов, по одному на атрибут, а затем «VCR msgstr "таблица со своим собственным, другим набором столбцов / атрибутов. Однако при таком подходе логика приложения каким-то образом приводит к жесткому кодированию (если только через косвенное отображение на карте сортов) имен таблиц и столбцов.
Напротив, модель EAV позволяет программе обнаруживать список возможных типов и, для каждого типа, список возможных атрибутов (либо обязательных, либо необязательных). Кроме того, поскольку все значения атрибутов хранятся в одной и той же таблице, можно фильтровать атрибуты независимо от типа (или подтипа) продукта. Например, чтобы получить все предметы дешевле, чем 50 долларов (при другом подходе нам, возможно, пришлось посмотреть на десятки таблиц).

Вернуться к функции «детализации» ...
Как только начальный поиск выполнен (скажем, поиск во всех продуктах, где имя [полнотекстовое индексирование] содержит слово «экран»), таблица ProductAttributes может создать отдельный список всех различных атрибутов AttributeID (следовательно, имя атрибута при поиске в таблице атрибутов) для товар удовлетворяет этому первому критерию поиска.
После того, как пользователь выберет данный атрибут (скажем, «Производитель»), таблица ProductAttributes может создать отдельный список производителей (вместе с количеством продуктов для каждого производителя) (альтернативно, такую ​​информацию можно искать сначала, а не лениво, когда пользователь запрашивает это).
Затем пользователь выбирает данного производителя (или нескольких), и запускается новый запрос, чтобы уменьшить начальный список результатов. Список возможных атрибутов (а внутри каждого атрибута список возможных значений) уменьшается, так как некоторые первоначально выбранные продукты (объекты) теперь исключены.
Процесс продолжается, предоставляя конечному пользователю управляемый поиск в каталоге. Конечно, пользователь может отказаться и т.д.

Чтобы помочь с этим многословным объяснением (или, возможно, еще больше запутать читателя ...), следующий фрагмент обеспечивает более точное указание того, как эта структура может использоваться для осуществления поиска. Этот код адаптирован для имен таблиц, использованных в приведенном выше объяснении, и может содержать несколько опечаток, но, как правило, дает представление о вещах. Кроме того, это написано с помощью общего табличного выражения (CTE), но вполне может быть записано как подзапрос. Также не то, чтобы мы не объединялись с таблицами логической схемы (метаданных), но это также можно сделать, чтобы получить имена атрибутов, имя типа и тому подобное непосредственно в наборе результатов.
Как указывалось ранее, запросы и логика, поддерживающие эту архитектуру, более сложны, но также более универсальны и терпимы к изменениям типа хранимых элементов и их атрибутов. Конечно, этот тип запроса генерируется динамически на основе текущего списка критериев поиска, предоставленного конечным пользователем.

WITH SearchQry AS (
  SELECT ROW_NUMBER() OVER (ORDER BY P.EntityId ASC) AS RowNum,  
         P.EntityId AS EId
         FROM  Products P
         INNER JOIN ProductAttributes PA1 ON P.EntitityId = PA1.EntityId and PA1.AttributeID = <some attribute id, say for Manufacturer> 
         INNER JOIN ProductAttributes PA2 ON P.EntitityId = PA2.EntityId and PA2.AttributeID = <some other attribute id, say for Color>
         -- here for additional PAn JOINs as more criteria is added
         WHERE  P.ProductType IN (ProdId_x, ProdId_y, ProdId_z)  -- for example where these x,y,z Ids correspond to say "TV Sets", "LapTop Computers" and "PDAs" respectively
            AND PA1.Value = 'SAMSUNG' -- for example
            AND PA2.Value = 'YELLOW' -- for example
         GROUP BY P.EntityId
   )  

SELECT  P.EntityId, PA.AttributeId, PA.Value -- PA.IntValue (if so structured)
FROM (SELECT * FROM SearchQry WHERE RowNum BETWEEN  1 AND  15)  AS S
JOIN ProductAttributes PA ON PA.EntityId = S.EId
INNER JOIN Products P on P.EntityID = PA.EntityId
ORDER BY P.EntityId, P.AttributeId  -- or some other sort order

Извините за длинное объяснение, возможно, [возможно] есть лучшее описание этого онлайн, но я его не нашел ...

2 голосов
/ 07 марта 2010

Вместо использования реляционной базы данных используйте отдельную систему полнотекстового поиска для развертки или граненого поиска, например, «Solr». Рано или поздно «группировка» станет проблемой производительности при запросе базы данных.

Стоит взглянуть

Сервер поиска Solr

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...