Эффективная реализация факсированного поиска в реляционных базах данных

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

Я могу получить все элементы, имеющие назначенные категории, используя INNER JOINs и получить количество элементов в все категории с использованием COUNT и GROUP BY, однако я не уверен, как он будет масштабироваться для миллионов объектов и тысяч тегов. Особенно счет.

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

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

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

Edit:

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Используйте логические столбцы для "многих" элементов, таких как категории или теги, например, если есть три метки: "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 мс

Ответ 4

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

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

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