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

Полнотекстовый поиск с использованием полнотекстовых индексов

Experimental feature. Learn more.
Not supported in ClickHouse Cloud

Полнотекстовые индексы являются экспериментальным типом вторичных индексов, которые предоставляют быстрые возможности текстового поиска для String или FixedString колонок. Основная идея полнотекстового индекса заключается в том, чтобы хранить сопоставление от "терминов" к строкам, которые содержат эти термины. "Термины" — это токенизированные ячейки строковой колонки. Например, ячейка строки "I will be a little late" по умолчанию токенизируется на шесть терминов: "I", "will", "be", "a", "little" и "late". Другой вид токенизатора — это n-граммы. Например, результат токенизации 3-граммами будет 21 термин: "I w", " wi", "wil", "ill", "ll ", "l b", " be" и т. д. Чем более мелко токенизируются входные строки, тем больше и полезнее будет получившийся полнотекстовый индекс.

примечание

Полнотекстовые индексы являются экспериментальными и не должны использоваться в производственных средах. Они могут измениться в будущем несовместимым образом, например, в отношении их DDL/DQL синтаксиса или характеристик производительности/сжатия.

Использование

Чтобы использовать полнотекстовые индексы, сначала включите их в конфигурации:

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

примечание

В предыдущих версиях ClickHouse соответствующее название типа индекса было inverted.

где N указывает токенизатор:

  • full_text(0) (или короче: full_text()) устанавливает токенизатор на "токены", т. е. разбивает строки по пробелам,
  • full_text(N) с N от 2 до 8 устанавливает токенизатор на "ngrams(N)"

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

  • full_text(ngrams, max_rows_per_postings_list): Использовать заданное max_rows_per_postings_list (при условии, что это не 0)
  • full_text(ngrams, 0): Нет ограничения на максимальное количество строк в списке публикаций
  • full_text(ngrams): Использовать максимальное количество строк по умолчанию, равное 64К.

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

Чтобы использовать индекс, не требуются специальные функции или синтаксис. Обычные предикаты строкового поиска автоматически используют индекс. В качестве примеров рассмотрим:

Полнотекстовый индекс также работает с колонками типа Array(String), Array(FixedString), Map(String) и Map(String).

Как и для других вторичных индексов, каждая часть колонки имеет свой собственный полнотекстовый индекс. Более того, каждый полнотекстовый индекс внутренне делится на "сегменты". Наличие и размер сегментов обычно непрозрачно для пользователей, но размер сегмента определяет потребление памяти во время создания индекса (например, при слиянии двух частей). Параметр конфигурации "max_digestion_size_per_segment" (по умолчанию: 256 МБ) контролирует объем данных, читаемых из основной колонки, прежде чем будет создан новый сегмент. Увеличение параметра повышает промежуточное потребление памяти при создании индекса, но также улучшает производительность поиска, поскольку в среднем необходимо проверить меньше сегментов для оценки запроса.

Полнотекстовый поиск по набору данных Hacker News

Давайте посмотрим на улучшение производительности полнотекстовых индексов на большом наборе данных с большим объемом текста. Мы будем использовать 28,7M строк комментариев на популярном сайте Hacker News. Вот таблица без полнотекстового индекса:

28,7M строк находятся в файле Parquet на S3 — давайте вставим их в таблицу hackernews:

Рассмотрим следующий простой поиск термина ClickHouse (и его различные вариации по регистру) в колонке comment:

Обратите внимание, что выполнение запроса занимает 3 секунды:

Мы используем ALTER TABLE и добавляем полнотекстовый индекс на строчные символы колонки comment, а затем материализуем его (это может занять некоторое время - подождите, пока он будет материализирован):

Мы запускаем тот же запрос...

...и замечаем, что запрос выполняется в 4 раза быстрее:

Мы также можем искать один или все из нескольких терминов, т. е. дизъюнкции или конъюнкции:

примечание

В отличие от других вторичных индексов, полнотекстовые индексы (пока) сопоставляются с номерами строк (идентификаторами строк), а не с идентификаторами гранул. Причина этого дизайна — производительность. На практике пользователи часто ищут несколько терминов одновременно. Например, предикат фильтрации WHERE s LIKE '%little%' OR s LIKE '%big%' может быть непосредственно оценен с помощью полнотекстового индекса, формируя объединение списков идентификаторов строк для терминов "little" и "big". Это также означает, что параметр GRANULARITY, предоставленный при создании индекса, не имеет значения (в будущем он может быть убран из синтаксиса).