Інтеграція Meilisearch для пошуку на сайті
Meilisearch — рушій повнотекстового пошуку, написаний на Rust. Час відповіді на більшості корпусів до 50 мс, вбудована підтримка опечаток, нечіткий пошук, фасетна фільтрація. Встановлюється як окремий процес поруч з основним додатком.
Коли це потрібно
Стандартний LIKE '%запит%' у PostgreSQL перестає справлятися приблизно при 50 000 записах в індексі — зростають час запиту та навантаження на базу. Meilisearch вирішує по-іншому: будує перевернутий індекс окремо, запити обходять БД.
Підходить для:
- Каталогів товарів з фільтрами за атрибутами
- Блогів та баз знань (10 000+ статей)
- Довідників користувачів, адрес, товарів в B2B-кабінетах
- Пошуку по документації
Архітектура інтеграції
Browser → Backend API → Meilisearch HTTP API
↓
PostgreSQL (джерело даних)
Індексатор (Queued Job / Cron)
Meilisearch не замінює основну БД. Дані живуть у PostgreSQL, в Meilisearch попадає тільки те, що потрібно для пошуку. Синхронізація — через черги при змінах записів або періодичним ребілдом індексу.
Установка та конфігурація
Docker Compose:
services:
meilisearch:
image: getmeili/meilisearch:v1.7
environment:
MEILI_MASTER_KEY: "${MEILI_MASTER_KEY}"
MEILI_ENV: production
volumes:
- meili_data:/meili_data
ports:
- "7700:7700"
Настройка атрибутів індексу:
{
"searchableAttributes": ["name", "description", "sku", "brand"],
"filterableAttributes": ["category_id", "brand", "in_stock", "price"],
"sortableAttributes": ["price", "created_at", "rating"],
"rankingRules": ["words", "typo", "proximity", "attribute", "sort", "exactness"],
"typoTolerance": {
"enabled": true,
"minWordSizeForTypos": { "oneTypo": 5, "twoTypos": 9 }
}
}
Синхронізація даних
Laravel Scout + офіційний драйвер:
// composer require laravel/scout meilisearch/meilisearch-php
use Laravel\Scout\Searchable;
class Product extends Model
{
use Searchable;
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => strip_tags($this->description),
'brand' => $this->brand->name,
'category_id' => $this->category_id,
'price' => $this->price,
'in_stock' => $this->stock > 0,
];
}
}
Первинна індексація:
php artisan scout:import "App\Models\Product"
Пошук із фільтрами:
$results = Product::search($query)
->where('in_stock', true)
->where('category_id', $categoryId)
->orderBy('price')
->paginate(20);
Фасетна фільтрація
Meilisearch повертає агрегації по фасетах за один запит:
$client = new \Meilisearch\Client('http://localhost:7700', $apiKey);
$response = $client->index('products')->search($query, [
'filter' => ['category_id = 5', 'price 100 TO 5000'],
'facets' => ['brand', 'color', 'in_stock'],
'hitsPerPage' => 20,
'page' => 1,
]);
// $response->getFacetDistribution() — масив з кількостями по кожному фасету
Пошук безпосередньо з браузера
Прямі запити з JavaScript до Meilisearch через Search-only API Key — ключ із обмеженими правами (тільки пошук по конкретним індексам). Зменшує затримку, убирає зайвий hop.
import { MeiliSearch } from 'meilisearch'
const client = new MeiliSearch({
host: 'https://search.example.com',
apiKey: SEARCH_ONLY_KEY,
})
const results = await client.index('products').search(query, {
filter: 'in_stock = true',
limit: 10,
})
Мультимовний пошук
Окремий індекс для кожної мови (products_ru, products_en) або один з мовним фільтром. Meilisearch підтримує російський стемминг через словники.
Графіки та етапи робіт
| Етап | Зміст | Час |
|---|---|---|
| Настройка інфраструктури | Docker, SSL, API-ключі | 1 день |
| Схема індексу | Атрибути, ранжування, фасети | 1 день |
| Інтеграція в бекенд | Scout / прямий клієнт, синхронізація | 2–3 дні |
| UI компонент пошуку | Автодоповнення, фасети, пагінація | 2–3 дні |
| Тестування | Навантаження, релевантність, edge cases | 1 день |
Усього: 7–9 робочих днів для типового каталогу.
Моніторинг
Meilisearch надає метрики через /metrics у Prometheus-форматі (при включеній опції). Ключові показники: розмір індексу, час індексування завдань, кількість запитів у секунду. Статус завдань індексації доступний через /tasks.







