Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Meilisearch: настройка лёгкого полнотекстового поиска

Meilisearch: настройка поиска с допуском опечаток

Meilisearch на практике: индексы, ранжирующие правила, допуск опечаток, фильтры, фасеты и синхронизация данных из источника.

Прямой ответ

Meilisearch — поисковый движок на Rust, ориентированный на мгновенный поиск с допуском опечаток. Отличие от кластерных движков — одна исполняемая файл-база и настройка через REST API без схемы индексации.

Запуск и создание индекса:

docker run -p 7700:7700 -e MEILI_MASTER_KEY=dev-key \
  -v $(pwd)/data.ms:/meili_data getmeili/meilisearch:v1.10
import httpx

BASE = ""
KEY = {"Authorization": "Bearer dev-key"}

httpx.post(f"{BASE}/indexes", json={"uid": "products", "primaryKey": "id"}, headers=KEY)

docs = [
    {"id": 1, "title": "Ноутбук 14 дюймов", "brand": "Acme", "price": 74000, "in_stock": True},
    {"id": 2, "title": "Ноутбук 16 дюймов", "brand": "Bravo", "price": 96000, "in_stock": False},
]
r = httpx.post(f"{BASE}/indexes/products/documents", json=docs, headers=KEY)
task = r.json()["taskUid"]

Добавление документов возвращает задачу, а не подтверждение индексации. Индексация выполняется асинхронно, поэтому результат проверяется по статусу задачи.

Настройка поиска выполняется через параметры индекса:

httpx.patch(f"{BASE}/indexes/products/settings", headers=KEY, json={
    "searchableAttributes": ["title", "brand"],
    "filterableAttributes": ["brand", "price", "in_stock"],
    "sortableAttributes": ["price"],
    "rankingRules": ["words", "typo", "proximity", "attribute", "sort", "exactness"],
})

r = httpx.post(f"{BASE}/indexes/products/search", headers=KEY,
               json={"q": "нотбук 16", "filter": "in_stock = true"})

Поиск по «нотбук» находит документ «Ноутбук», поскольку допуск опечаток включён по умолчанию; о механике — в статье про поиск при опечатках.

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

Ранжирующие правила и их влияние на выдачу

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

Правило Что делает
words Раньше документы, содержащие больше слов запроса
typo Раньше документы с меньшим числом опечаток
proximity Раньше документы, где слова ближе друг к другу
attribute Раньше совпадения в атрибутах, перечисленных раньше
sort Применяет явную сортировку запроса
exactness Раньше точные совпадения по значению атрибута

Правило words первым означает, что документ со всеми словами запроса outranks документ с одним точным совпадением. Перестановка typo перед words меняет поведение: документы с точным совпадением одного слова поднимаются выше документов с опечатками в нескольких словах.

Практическое следствие: порядок правил — основной инструмент настройки выдачи, и он проверяется на наборе реальных запросов, а не на отдельных примерах.

Правило attribute использует порядок из searchableAttributes. Поле заголовка, указанное первым, даёт совпадению в заголовке преимущество перед совпадением в описании.

Ограничения движка, которые влияют на архитектуру

Meilisearch рассчитан на сценарий мгновенного поиска по умеренному объёму документов, и три ограничения определяют границы применимости.

Отсутствие распределённого хранения в базовой модели. Данные размещаются на одном узле, поэтому отказ узла означает недоступность поиска до восстановления. Резервное копирование каталога данных и быстрая пересборка индекса из источника — обязательная часть эксплуатации.

Асинхронность операций записи. Добавление документов, изменение настроек и удаление возвращают задачу, а не результат. Процесс обновления настроек должен проверять статус задачи, иначе последующие операции выполняются над непроиндексированными данными.

Переиндексация при изменении настроек. Добавление атрибута в список фильтруемых либо сортируемых перестраивает индекс целиком. На объёме в несколько миллионов документов операция занимает заметное время, поэтому состав атрибутов определяется до загрузки данных.

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

Отдельный аспект — объём памяти. Индекс удерживается в памяти для обеспечения низкой задержки, поэтому размер индекса напрямую определяет требования к узлу. Сокращение числа индексируемых полей снижает потребление памяти без потери качества выдачи, поскольку поля, не участвующие в поиске и отображении, не нужны в индексе.

Практика: внедрение Meilisearch в сервис

1. Начинайте с одного индекса на сущность. Объединение разных сущностей в одном индексе усложняет настройку ранжирования, поскольку правила применяются ко всему индексу.

2. Объявляйте фильтруемые атрибуты до запуска. Добавление атрибута в список фильтруемых запускает переиндексацию всего индекса, что на больших объёмах занимает заметное время.

3. Указывайте первичный ключ явно. Автоматическое определение по полю id работает не всегда, а изменение первичного ключа требует пересоздания индекса.

4. Проверяйте статус задачи после добавления документов. Ответ с taskUid означает принятие, а не завершение индексации.

5. Ограничивайте размер документа. Поиск по полям, не участвующим в выдаче, увеличивает объём индекса без пользы.

6. Храните источник данных отдельно. Индекс восстанавливается из источника, поэтому источник остаётся системой записи; о порядке синхронизации — в статье про индексацию и синхронизацию.

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

8. Разделяйте ключи доступа. Ключ с правом чтения отличается от ключа с правом записи, и публичный интерфейс использует только читающий ключ.

Рекомендации:

  1. Не используйте Meilisearch как основную базу данных. Движок не предоставляет транзакций и связей, поэтому подходит для производного поискового индекса.
  2. Проверяйте поведение на пустом запросе. Пустой запрос возвращает документы в порядке ранжирующих правил, что используется для списка по умолчанию.
  3. Учитывайте объём памяти. Индекс размещается в памяти и на диске, поэтому размер индекса влияет на требования к узлу.
  4. Сравнивайте с возможностями базы данных. При умеренном объёме данных встроенный поиск может оказаться достаточным; о кластерных движках — в статье про полнотекстовый поиск в Elasticsearch.
  5. Фиксируйте версию движка. Поведение ранжирующих правил и формат настроек меняются между мажорными версиями.

Практический сценарий: фильтр отклоняется после добавления документа

Контекст: поиск работал, а запрос с фильтром по цене возвращал ошибку.

  1. Диагностика. Ответ содержал сообщение о том, что атрибут не объявлен фильтруемым. Документы были добавлены, а список filterableAttributes оставался пустым.
  2. Причина. Фильтрация требует явного объявления атрибута, поскольку по фильтруемым полям строится дополнительная структура. Объявление выполняется в настройках индекса, а не в запросе.
  3. Действия. (а) Добавили price, brand и in_stock в filterableAttributes; (б) дождались завершения задачи переиндексации, проверив её статус; (в) повторили запрос с фильтром — результат получен; (г) добавили проверку статуса задачи в процесс обновления настроек, чтобы не выполнять запросы до завершения переиндексации; (д) включили список фильтруемых атрибутов в конфигурацию, применяемую при развёртывании, чтобы настройка не терялась при пересоздании индекса.
  4. Результат. Фильтрация работает, а настройка индекса применяется автоматически при развёртывании.

Вывод: фильтрация в Meilisearch требует явного объявления атрибутов и переиндексации. Проверка статуса задачи после изменения настроек отличает принятое изменение от применённого, а включение настроек в конфигурацию развёртывания предотвращает их потерю.

FAQ

Поддерживает ли Meilisearch кластеризацию? Механизмы распределённого развёртывания развиваются отдельно от кластерной модели Elasticsearch, поэтому для крупных объёмов стоит проверить актуальные возможности версии.

Можно ли изменить первичный ключ индекса? Нет, первичный ключ фиксируется при создании. Изменение требует создания нового индекса и переноса документов.

Почему запрос с фильтром возвращает ошибку? Атрибут не объявлен в filterableAttributes. Объявление запускает переиндексацию, после которой фильтр доступен.

Нужно ли ждать индексации перед поиском? Да, добавление документов асинхронно. Поиск по новому документу возможен после завершения задачи индексации.

Источники: Meilisearch docs (filtering and sorting, settings API) — ссылки в шапке.