Эффективная реализация граненого поиска в реляционных базах данных - PullRequest
16 голосов
/ 04 декабря 2009

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

Я могу получить все элементы с назначенными категориями, используя ВНУТРЕННИЕ СОЕДИНЕНИЯ и получить количество элементов во всех категориях, используя COUNT и GROUP BY , однако я не уверен, как он будет масштабироваться для миллионов объектов и тысяч тегов. Особенно подсчет.

Я знаю, что есть некоторые нереляционные решения, такие как Lucene + SOLR , но я нашел также некоторые реализации на основе СУБД с закрытыми исходными кодами, которые, как говорят, обладают высокой степенью предпринимательства, как FacetMap.com или Endeca , поэтому должен существовать эффективный способ выполнения фасетного поиска в реляционных базах данных.

Есть ли у кого-нибудь опыт в многогранном поиске и могли бы дать несколько советов?

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

Edit:

Пример многогранной навигации можно найти здесь: Фламенко .

В настоящее время у меня есть стандартная схема с тремя таблицами (items, tags и items_tags, как описано здесь: http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html#toxi) плюс таблица для фасетов. Каждый тег имеет свой фасет.

Ответы [ 4 ]

5 голосов
/ 04 декабря 2009

Я могу только подтвердить то, что говорит Нильс. СУБД не подходят для многомерного поиска. Я работал с некоторыми умными решениями, счетчиками кэширования, использованием триггеров и так далее. Но, в конце концов, внешний выделенный индексатор всегда побеждает.

МОЖЕТ, если вы преобразуете свои данные в многомерную модель и подаете их в какой-нибудь OLAP [я имею в виду движок MDX] - они будут работать хорошо. Но это кажется слишком сложным решением, и это определенно НЕ в режиме реального времени.

Напротив, решение с выделенным механизмом индексации (думайте, Lucene, думайте, Sphinx ) может быть принято почти в реальном времени с инкрементными обновлениями индекса.

5 голосов
/ 04 декабря 2009

IMO, реляционные базы данных не так хороши в поиске. Вы получите лучшую производительность от специальной поисковой системы (например, Solr / Lucene).

2 голосов
/ 03 сентября 2015

Faceted Search - аналитическая проблема, а это значит, что размерный дизайн - хорошая ставка. Ака, то, что вы ищете, должно быть в табличной форме.

Включите все интересующие вас столбцы в аналитическую таблицу.

Положите непрерывные значения в ведра.

Используйте логические столбцы для «многих» элементов, таких как категории или теги, например, если есть три тега «foo», «bar» и «baz», у вас будет три логических столбца.

Используйте материализованное представление для создания аналитической таблицы.

Индекс дерьма из этого. Некоторые базы данных поддерживают индексы для этого типа приложений.

Фильтровать только один раз.

Объедините ваши результаты.

Создание предварительно агрегированных материализованных представлений для общих запросов.

Эта статья также может вам помочь: https://blog.jooq.org/2017/04/20/how-to-calculate-multiple-aggregate-functions-in-a-single-query/

with filtered as (
    select
    *
    from cars_analytic
    where
        [some search conditions]
)

--for each facet:

select
    'brand' as facet,
    brand as value,
    count(*) as count
from
    filtered
group by
    brand

union

select
    'cool-tag' as facet,
    'cool-tag'as value,
    count(*) as count
from
    filtered
where
    cool_tag

union

...


-- sort at the end
order by
    facet,
    count desc,
    value

100 000 записей с 5 гранями за ~ 150 мс

0 голосов
/ 07 декабря 2009

Что касается количества, зачем тянуть их через SQL? В любом случае вам придется перебирать набор результатов в вашем коде, так почему бы не подсчитать это?

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

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

...