Typesense — поисковый движок с открытым кодом: десятки миллисекунд и терпимость к опечаткам. Его ниша — каталог товаров, документация и база знаний, где Elasticsearch тяжёл, а SQL LIKE не даёт релевантности. Ниже — запуск, схема, импорт, запросы с фасетами, ключи, снапшоты и диагностика.

Чем Typesense отличается

Движок написан на C++, распространяется одним бинарником и не требует JVM. Инвертированный индекс держится в памяти, а документы — на диске. Отсюда предсказуемая задержка: для каталога на сотни тысяч товаров хватает сервера с 4–8 ГБ памяти.

  • Опечатки и префиксный поиск, инфиксный поиск включается отдельно.
  • Фасеты, фильтры, сортировка и геопоиск в одном запросе.
  • Векторный и гибридный поиск для семантических сценариев.
  • Кластеризация через Raft и снапшоты для резервного копирования.

Typesense не является аналитической базой. Он не заменяет PostgreSQL для транзакций и не строит сложные агрегаты. Его задача — быстро найти и отранжировать заранее проиндексированные документы.

Запуск

Для проверки удобен Docker: ключ задаётся флагом, данные выносятся в том, порт 8108 открывают при необходимости. В продакшене закрепите тег, а не latest.

docker run -d --name typesense \
  -p 8108:8108 \
  -v typesense-data:/data \
  typesense/typesense:latest \
  --data-dir /data --api-key=change-me --enable-cors

Без Docker ставят бинарник и службу systemd. Ключ храните в файле с правами 600: Typesense читает переменные TYPESENSE_.

[Unit]
Description=Typesense
After=network-online.target

[Service]
User=typesense
EnvironmentFile=/etc/typesense/typesense.env
ExecStart=/usr/local/bin/typesense-server \
  --data-dir=/var/lib/typesense --api-port=8108 \
  --peering-port=8107 --enable-cors
Restart=on-failure

[Install]
WantedBy=multi-user.target

После старта проверьте здоровье. Ответ {"ok":true} означает, что процесс жив, но не гарантирует доступность коллекций.

curl -s http://localhost:8108/health
curl -s -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  http://localhost:8108/collections

Схема коллекции

Коллекция — таблица с явной схемой. Поле facet участвует в фасетах, sort разрешает сортировку, optional допускает пропуск значения. Для векторов задают num_dim.

{
  "name": "products",
  "fields": [
    {"name": "title", "type": "string"},
    {"name": "brand", "type": "string", "facet": true},
    {"name": "price", "type": "float", "sort": true},
    {"name": "description", "type": "string", "optional": true},
    {"name": "embedding", "type": "float[]", "num_dim": 384, "optional": true}
  ],
  "default_sorting_field": "price"
}

Схему создают POST-запросом. Поле добавляют PATCH: старые документы получат пустое значение, поэтому новое поле делают optional. Переименование требует переиндексации.

curl -s -X POST http://localhost:8108/collections \
  -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d @schema.json

Импорт документов

Документы загружаются в JSONL: объект на строку. Параметр action задаёт поведение при совпадении id: create, upsert, update или emplace. Для первой загрузки берут upsert, чтобы повторный запуск не падал.

curl -s -X POST \
  "http://localhost:8108/collections/products/documents/import?action=upsert" \
  -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  --data-binary @products.jsonl

Ответ приходит по строке: {"success":true} или объект с ошибкой. Разбирайте его скриптом и останавливайте загрузку при высокой доле ошибок. Частые причины — несовпадение типа, отсутствие обязательного поля и слишком длинная строка. Большие файлы делите по 10–20 тысяч.

PATCH меняет описание коллекции, но не пересчитывает старые документы. Если включить новое поле в поиск, старые записи не станут по нему находиться, пока их не перезальют.

Поиск с опечатками

Запрос идёт в /documents/search. query_by перечисляет поля по важности, num_typos задаёт число опечаток, per_page ограничивает выдачу. Для коротких слов допуск снижается автоматически.

curl -s -G http://localhost:8108/collections/products/documents/search \
  -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  --data-urlencode "q=наушники" \
  --data-urlencode "query_by=title,description" \
  --data-urlencode "num_typos=2" \
  --data-urlencode "per_page=20"

Префиксный поиск включён по умолчанию: «науш» находит «наушники». Для подстроки включают infix=always или infix=fallback, но это дороже по памяти. search_cutoff_ms ограничивает время: при перегрузке лучше вернуть частичный результат, чем держать соединение.

Релевантность настраивают через query_by_weights, prioritize_exact_match и drop_tokens_threshold. Не начинайте с десятка параметров: соберите реальные запросы.

Фильтры и фасеты

Фильтр отделяет точные значения от ранжирования. Синтаксис использует двоеточие, для массивов — скобки. Числа сравнивают операторами, строки перечисляют через запятую.

curl -s -G http://localhost:8108/collections/products/documents/search \
  -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  --data-urlencode "q=наушники" \
  --data-urlencode "query_by=title" \
  --data-urlencode "filter_by=brand:=[Sony,Bose] && price:<=30000" \
  --data-urlencode "facet_by=brand" \
  --data-urlencode "max_facet_values=10"

Фасеты возвращают counts для чекбоксов интерфейса. Если фасетов много, ограничьте max_facet_values и используйте facet_query. Для диапазонов стройте фильтры на фронтенде.

Ключи и доступ

Админский ключ даёт полный доступ и не должен попадать в браузер. Для поиска выпускают ключ с действием documents:search и списком коллекций. Значение показывается один раз — сохраните его в секретах.

curl -s -X POST http://localhost:8108/keys \
  -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description":"search-only","actions":["documents:search"],"collections":["products"]}'

Ключ ограничивают по времени, но это не вся защита. Настройте HTTPS через прокси, ограничьте частоту запросов и не индексируйте лишние персональные данные.

Снапшоты и диагностика

Снапшот создаёт копию данных. Храните её вне сервера и проверяйте восстановление. Для кластера снапшот делают на каждом узле или копируют том.

curl -s -X POST \
  "http://localhost:8108/operations/snapshot?snapshot_path=/var/backups/typesense-$(date +%F)" \
  -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY"
curl -s -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  http://localhost:8108/stats.json

stats.json показывает задержки и число запросов. Если p95 растёт, проверьте память, индекс и тяжёлые запросы. Журнал ищется через journalctl, место — через df. При нехватке RAM движок читает с диска.

Итог

Typesense закрывает задачу мгновенного поиска без тяжёлой инфраструктуры: один бинарник, схема, JSONL-импорт и запросы с опечатками. Начните с Docker или systemd, опишите поля и фасеты, выпустите search-only ключ, настройте снапшоты и следите за памятью. Для каталога и базы знаний этого достаточно; для аналитики — PostgreSQL.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте