Интеграция Typesense для поиска на сайте
Typesense — поисковый движок с открытым исходным кодом, написан на C++. Позиционируется как облачная альтернатива Algolia с возможностью self-hosted развёртывания. Время ответа стабильно ниже 50 мс даже на корпусах с миллионами документов. Поддерживает векторный поиск через HNSW, семантический поиск через embeddings и классический полнотекстовый поиск.
Отличия от Meilisearch
| Характеристика | Typesense | Meilisearch |
|---|---|---|
| Язык реализации | C++ | Rust |
| Кластеризация | Встроенная (Raft) | Отсутствует в OSS |
| Векторный поиск | Да (HNSW) | Да (с v1.6) |
| Geo-поиск | Да, нативный | Да |
| Аналитика запросов | Встроенная | Через сторонние инструменты |
| Строгая схема | Обязательна | Опциональна |
Установка
# docker-compose.yml
services:
typesense:
image: typesense/typesense:0.25.2
command: >
--data-dir /data
--api-key=${TYPESENSE_API_KEY}
--listen-port=8108
--enable-cors
volumes:
- typesense_data:/data
ports:
- "8108:8108"
Схема коллекции
В Typesense данные организованы в коллекции со строгой схемой. Схему нужно определить до добавления документов:
{
"name": "products",
"fields": [
{ "name": "id", "type": "string" },
{ "name": "name", "type": "string" },
{ "name": "description", "type": "string" },
{ "name": "price", "type": "float", "facet": true },
{ "name": "category", "type": "string", "facet": true },
{ "name": "brand", "type": "string", "facet": true },
{ "name": "in_stock", "type": "bool", "facet": true },
{ "name": "rating", "type": "float", "optional": true },
{ "name": "location", "type": "geopoint","optional": true }
],
"default_sorting_field": "rating"
}
Поля типа auto позволяют не указывать схему — Typesense определит типы автоматически. Удобно для прототипирования, но не для продакшена.
PHP-интеграция
composer require php-http/guzzle7-adapter typesense/typesense-php
use Typesense\Client;
$client = new Client([
'api_key' => env('TYPESENSE_API_KEY'),
'nodes' => [['host' => 'localhost', 'port' => '8108', 'protocol' => 'http']],
'connection_timeout_seconds' => 2,
]);
// Upsert документа
$client->collections['products']->documents->upsert([
'id' => (string) $product->id,
'name' => $product->name,
'description' => strip_tags($product->description),
'price' => (float) $product->price,
'category' => $product->category->slug,
'brand' => $product->brand->name,
'in_stock' => $product->stock > 0,
'rating' => (float) $product->rating,
]);
Пакетная индексация (import):
$documents = $products->map(fn($p) => [
'id' => (string) $p->id,
'name' => $p->name,
'price' => (float) $p->price,
'category' => $p->category->slug,
'in_stock' => $p->stock > 0,
])->toArray();
$client->collections['products']->documents->import(
$documents,
['action' => 'upsert']
);
Поиск с фасетами
$results = $client->collections['products']->documents->search([
'q' => $query,
'query_by' => 'name,description,brand',
'query_by_weights' => '3,1,2',
'filter_by' => 'in_stock:true && price:[100..5000]',
'facet_by' => 'category,brand,price',
'max_facet_values' => 20,
'sort_by' => 'rating:desc',
'per_page' => 20,
'page' => 1,
]);
// $results['facet_counts'] — агрегации для отображения фильтров с количеством
Векторный поиск
Typesense принимает embedding-векторы для семантического поиска. Модель генерации эмбеддингов — на стороне приложения:
// Индексация с вектором (OpenAI text-embedding-3-small, dim=1536)
$client->collections['products']->documents->upsert([
'id' => '123',
'name' => 'Ноутбук Dell XPS 15',
'embedding' => $vector, // float[]
]);
// Гибридный поиск: текст + вектор
$results = $client->collections['products']->documents->search([
'q' => 'мощный ноутбук для работы',
'query_by' => 'name,embedding',
'vector_query' => 'embedding:([], k:10)',
]);
Синонимы
// Многонаправленный синоним
$client->collections['products']->synonyms->upsert('notebook-synonyms', [
'synonyms' => ['ноутбук', 'лаптоп', 'laptop', 'notebook'],
]);
// Однонаправленный
$client->collections['products']->synonyms->upsert('iphone-synonyms', [
'root' => 'iphone',
'synonyms' => ['айфон', 'apple phone'],
]);
Laravel Scout + Typesense
composer require typesense/laravel-scout-typesense-driver
// config/scout.php
'driver' => 'typesense',
'typesense' => [
'client-settings' => [
'api_key' => env('TYPESENSE_API_KEY'),
'nodes' => [['host' => 'localhost', 'port' => '8108', 'protocol' => 'http']],
],
'model-settings' => [
Product::class => [
'collection-schema' => [
'fields' => [
['name' => 'name', 'type' => 'string'],
['name' => 'price', 'type' => 'float', 'facet' => true],
['name' => 'in_stock', 'type' => 'bool', 'facet' => true],
],
],
],
],
],
Geo-поиск
Поле типа geopoint принимает [lat, lng]. Поиск по радиусу:
$results = $client->collections['stores']->documents->search([
'q' => '*',
'query_by' => 'name',
'filter_by' => 'location:(55.7558, 37.6173, 10 km)',
'sort_by' => 'location(55.7558, 37.6173):asc',
]);
Аналитика запросов
Typesense записывает статистику поисковых запросов: топ запросов, запросы без результатов. Полезно для SEO-анализа и настройки синонимов.
Сроки работ
| Этап | Время |
|---|---|
| Развёртывание, схема коллекции | 1 день |
| Индексатор + синхронизация | 2 дня |
| Поиск с фасетами на frontend | 2–3 дня |
| Векторный поиск (опционально) | 2 дня дополнительно |
| Тесты, релевантность | 1 день |
Стандартная интеграция без векторного поиска — 6–7 рабочих дней.







