
Первоначально опубликовано на https://manticoresearch.com/ru/blog/dict_keywords_32k/ 31 августа 2026
Как искать длинные хеши и ID с dict=‘keywords_32k’
Практическое руководство по поиску длинных хешей, event ID, message ID и email в Manticore Search: лимиты, точное сравнение, wildcard-поиск, токенизация, миграция и ограничения.
Полнотекстовый поиск обычно работает с обычными словами: названиями товаров, заголовками, комментариями и описаниями. Такие токены редко бывают длиннее нескольких десятков символов.
В логах и технических данных всё иначе. SHA-256 занимает 64 символа, а идентификаторы сообщений, correlation ID, идентификаторы событий и некоторые email-адреса могут быть ещё длиннее. При этом такое значение часто имеет смысл только целиком: если потерять его хвост, один ID легко спутать с другим.
Для таких случаев в Manticore Search появился dict='keywords_32k'.
keywords_32kдоступен начиная с Manticore Search 27.1.1. Для перевода существующей таблицы сkeywordsрекомендуется использовать версию 27.1.5 или новее.
В чём проблема обычного словаря
По умолчанию Manticore использует dict='keywords'. Максимальная длина токена при этом составляет 42 байта после нормализации.
Важно, что речь идёт именно о байтах, а не о символах. Для ASCII один символ занимает один байт, но в UTF-8 один символ может занимать несколько байт.
Если токен длиннее 42 байт, Manticore обрезает его:
при индексации документа;
при обработке поискового запроса.
Из-за этого запрос по полному значению не обязательно вернёт ноль результатов. Поскольку запрос тоже обрезается, документ может быть найден — но только по первым 42 байтам.
Проблема серьёзнее, чем может показаться: два разных ID с одинаковыми первыми 42 байтами становятся неразличимыми для полнотекстового поиска. Кроме того, найти значение по части, расположенной после 42-го байта, уже не получится.
Что меняет keywords_32k
dict='keywords_32k' увеличивает максимальную длину нормализованного токена до 32768 байт, то есть до 32 КБ.
Поведение |
|
|
|---|---|---|
Максимальная длина токена | 42 байта | 32768 байт |
Токен длиннее лимита | Обрезается | Пропускается с предупреждением |
Префиксный и инфиксный поиск | Поддерживается | Поддерживается |
Морфология для токенов длиннее 42 байт | Токен уже обрезан | Не применяется |
RT-таблицы | Поддерживаются | Поддерживаются |
Plain-таблицы | Поддерживаются | Поддерживаются |
Настройка задаётся для всей таблицы:
CREATE TABLE events ( message text, event_id text ) dict='keywords_32k';
Обычные короткие слова в такой таблице по-прежнему обрабатываются настроенной морфологией. Токены длиннее 42 байт сохраняются после нормализации, но без стемминга и лемматизации.
Для машинных идентификаторов это обычно именно то, что нужно: морфология для хешей и ID, как правило, просто не имеет смысла.
Когда нужен keywords_32k
Используйте его, если одновременно выполняются два условия:
Значение после токенизации может быть длиннее 42 байт.
Его нужно искать через
MATCH(), по префиксу или по подстроке.
Типичные примеры:
SHA-256 и другие длинные хеши;
event ID и message ID;
request ID, trace ID и другие технические идентификаторы;
длинные ключи записей;
email-адреса с длинной локальной или доменной частью;
технические значения из логов;
длинные идентификаторы с разделителями.
Однако keywords_32k нужен не для любого поиска по ID.
Если требуется только полное равенство
Если приложение всегда получает полный ID и нужно проверить только его точное равенство, достаточно строкового атрибута:
CREATE TABLE events ( message text, event_id string );
Тогда поиск выполняется обычным фильтром:
SELECT id, message FROM events WHERE event_id = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08';
Если нужны и точное сравнение, и полнотекстовый поиск
Используйте string attribute indexed:
CREATE TABLE events ( message text, event_id string attribute indexed ) dict='keywords_32k';
В этом случае Manticore хранит исходное значение как строковый атрибут, позволяет фильтровать по нему через WHERE и одновременно индексирует его для MATCH() и wildcard-поиска.
Поиск по полному токену
Создадим таблицу и добавим 64-символьный SHA-256:
DROP TABLE IF EXISTS events; CREATE TABLE events ( message text, event_id string attribute indexed ) dict='keywords_32k'; INSERT INTO events (id, message, event_id) VALUES ( 1, 'delivery accepted', '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' );
Полнотекстовый поиск по полному нормализованному токену:
SELECT id, message FROM events WHERE MATCH( '@event_id 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' );
Оператор @event_id ограничивает поиск нужным полем. Без него Manticore будет искать значение во всех полнотекстовых полях таблицы.
Кавычки вокруг одного простого буквенно-цифрового токена здесь не нужны. Они обозначают фразовый поиск, а не превращают MATCH() в побайтовое сравнение исходной строки.
Полнотекстовое совпадение и точное равенство — не одно и то же
MATCH() работает с результатом токенизации и нормализации. На результат могут влиять charset_table, приведение к нижнему регистру, blend_chars, ignore_chars, словоформы и другие настройки обработки текста.
Для строгого сравнения сохранённого значения используйте строковый атрибут:
SET collation_connection='binary'; SELECT id, message FROM events WHERE event_id = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08';
Практическое правило простое:
WHERE event_id = ...— строгое сравнение сохранённой строки;MATCH('@event_id ...')— поиск нормализованного токена;MATCH('@event_id prefix*')— префиксный поиск;MATCH('@event_id *fragment*')— поиск по подстроке.
Как проверить токенизацию
Перед загрузкой большого объёма данных полезно убедиться, что Manticore действительно видит значение как один токен:
CALL KEYWORDS( '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', 'events' );
В столбце normalized должен появиться полный 64-символьный хеш. CALL KEYWORDS особенно полезен для значений с точками, дефисами, @, двоеточиями, слешами и символами разных алфавитов.
Префиксный поиск и поиск по подстроке
Для поиска внутри токена включите min_infix_len:
DROP TABLE IF EXISTS events_infix; CREATE TABLE events_infix ( message text, event_id string attribute indexed ) dict='keywords_32k' min_infix_len='4';
Поиск по префиксу:
SELECT id, message FROM events_infix WHERE MATCH('@event_id 9f86d081*');
Поиск по подстроке:
SELECT id, message FROM events_infix WHERE MATCH('@event_id *b2b0b822*');
При положительном min_infix_len становится доступен и префиксный поиск. В production-системах не разрешайте слишком короткие фрагменты, выбирайте min_infix_len с учётом реальных данных, используйте expansion_limit, проверяйте производительность на словаре production-размера и ограничивайте поиск конкретным полем через @field.
Email и другие значения с разделителями
keywords_32k меняет только максимальную длину токена. Он не определяет, где токен начинается и заканчивается.
По умолчанию точка, @, дефис и другие символы могут разделять значение на несколько токенов. Если email-адрес или message ID нужно индексировать ещё и целиком, можно использовать blend_chars:
DROP TABLE IF EXISTS mail_events; CREATE TABLE mail_events ( sender string attribute indexed, subject text ) dict='keywords_32k' blend_chars='., @, -' min_infix_len='4';
Символы из blend_chars позволяют индексировать значение двумя способами: как один цельный токен и как отдельные части значения. Благодаря этому можно искать как весь email-адрес, так и отдельные его части.
Как перевести существующую таблицу
Для RT-таблицы настройку можно изменить через ALTER TABLE:
ALTER TABLE events dict='keywords_32k';
Но изменение затронет только документы, добавленные или заменённые после этого. Уже существующие документы не будут автоматически токенизированы заново. Их длинные токены останутся в старом, обрезанном виде до переиндексации.
Порядок действий:
Обновить
dict.Проверить настройку через
SHOW CREATE TABLE.Переиндексировать или повторно загрузить существующие документы.
Проверить несколько длинных значений через
CALL KEYWORDSиMATCH().
Для plain-таблицы нужно изменить конфигурацию на dict = keywords_32k, применить настройки через ALTER TABLE ... RECONFIGURE, если это подходит для вашего процесса, и полностью перестроить таблицу из источника данных.
Почему dict='crc' не решает эту задачу
dict='crc' хранит контрольные суммы ключевых слов вместо исходного текста, но не увеличивает допустимую длину токена. Исключение из обычного лимита в 42 байта реализовано именно в dict='keywords_32k'.
Текущие ограничения
На момент публикации у dict='keywords_32k' есть несколько ограничений:
CALL SUGGESTиCALL QSUGGESTне поддерживаются;его нельзя использовать в percolate-таблицах;
для токенов длиннее 42 байт не работает подсветка в snippets или highlights;
indextool --dumpdictне умеет выгружать такой словарь;полнотекстовый оператор
REGEXработает сdict='keywords', но не сkeywords_32k.
Не индексируйте секреты
Возможность искать длинное значение не означает, что его стоит сохранять в поисковом индексе. Без крайней необходимости не индексируйте API-ключи, bearer-токены, сессионные cookie, приватные ключи, пароли, токены сброса пароля и другие данные, предоставляющие доступ к системе.
Если секрет нужно сопоставлять по точному значению, безопаснее заранее вычислить подходящий хеш и хранить только его.
Краткий чек-лист
Перед включением keywords_32k проверьте:
Действительно ли нормализованный токен длиннее 42 байт?
Нужен ли полнотекстовый или wildcard-поиск, а не только
WHERE value = ...?Видит ли
CALL KEYWORDSвсё значение как один токен?Нужны ли
blend_charsдля точек, дефисов,@и других разделителей?Не слишком ли мал
min_infix_len?Ограничено ли число термов, в которые может развернуться wildcard-запрос?
Переиндексированы ли старые документы?
Не содержит ли поле секретных данных?
Не зависит ли приложение от подсветки,
SUGGEST, percolate или полнотекстовогоREGEX?
Итог
dict='keywords_32k' решает конкретную проблему: позволяет полнотекстовому индексу хранить нормализованные токены длиной до 32768 байт вместо обычных 42 байт.
Он хорошо подходит для длинных хешей, event ID, message ID, email-адресов и других машинных идентификаторов. При этом важно помнить три вещи:
keywords_32kувеличивает максимальную длину токена, но не меняет правила токенизации;MATCH()по полному токену не равен строгому сравнению исходной строки;после изменения настройки существующие документы необходимо переиндексировать.
Если нужно только точное равенство, используйте строковый атрибут. Если нужны и точное сравнение, и поиск по частям значения, используйте string attribute indexed вместе с dict='keywords_32k'.

