Є вагомі причини використовувати повнотекстовий пошук на основі інвертованого індексу та моделі оцінки релевантності. Найголовніша з них — коли вам потрібна функція Search, де перший результат має бути найбільш точним. Саме для цього створювалися пошукові системи.
Хоча інвертований індекс іноді перевершує класичні індекси баз даних, пам'ятайте, що це не основне його призначення.
У цій статті ми розрізнятимемо поняття «пошук» та «запит» так:
- «Search» (Пошук) — це отримання інформації, ранжованої за релевантністю. Перший документ у списку є найбільш відповідним, а наступні — все менше, згідно з алгоритмом оцінки.
- «Query» (Запит) — це пошук даних за певними критеріями (як у звичайних базах даних), де черговість результатів за релевантністю не є пріоритетною.
Раніше впровадження пошукових рушіїв вимагало налаштування та підтримки окремої системи, вивчення нового API та боротьби з технічними нюансами. Це було настільки складно, що розробники часто погоджувалися на низьку якість пошуку, аби лише уникнути зайвого клопоту.
# Вступ: Нова ера MongoDB Search
Щоб усунути ці бар'єри, розробники інтегрували MongoDB Search та Vector Search безпосередньо в основне програмне забезпечення. Тепер вони доступні через єдине підключення та API. Цей потужний функціонал є у безкоштовній версії MongoDB Community (для локального запуску) та в хмарній платформі Atlas.
Цей матеріал орієнтований на розробників Laravel. Ми покажемо, як реалізувати Full Text Search (FTS) у вже існуючій базі даних MongoDB.
# Реалізація на Laravel
Ми використовуватимемо репозиторій на GitHub для демонстрації роботи MongoDB Search у Laravel. Я створив спеціальний тег, оскільки репозиторій оновлюватиметься.
У файлі README.md ви знайдете детальну інструкцію: від вибору налаштувань до прикладів команд та порад щодо виправлення помилок.
# Що знадобиться для запуску
- Налаштоване середовище розробки PHP/Laravel.
- Рекомендуємо використовувати GitHub Codespaces — це готове середовище, що не потребує налаштування.
- Безкоштовний кластер MongoDB Atlas із завантаженою базою даних mflix_sample.
- Ось інструкції, як створити кластер та завантажити тестові дані.
# Підключення Laravel до MongoDB
Ми припускаємо, що ваш кластер MongoDB Atlas уже працює і дані завантажені. Нас цікавить база sample_mflix, зокрема колекція "movies".
# Налаштування з'єднання
Доступ до кластера: Переконайтеся, що ваша IP-адреса додана до списку дозволених (IP Access List) у налаштуваннях Atlas. Якщо ви працюєте через публічний Wi-Fi, можливо, доведеться дозволити доступ з усіх адрес (що не дуже безпечно).
Налаштування в Laravel: Створіть файл .env на основі .env.example. Знайдіть рядок DB_CONNECTION=mongodb і оновіть DB_DSN, вказавши рядок підключення вашого кластера з логіном та паролем.
Якщо ви використовуєте Codespaces, виконайте ці команди для ініціалізації:
# створення .env
cp .env.example .env
# встановлення бібліотек
composer install
# генерація ключа додатка
php artisan key:generate
Залежно від середовища, базові URL будуть різними:
| Середовище | Приклад URL |
|---|---|
| Local PHP | http://localhost:8000/api/hello |
| Codespaces | https://[unique-id]-8000.app.github.dev/api/hello |
| Docker | http://127.0.0.1:8080/api/hello |
Гнучкість схеми MongoDB дозволяє не використовувати міграції на цьому етапі. Запустіть додаток командою:
php artisan serve
Перевірте з'єднання за допомогою curl:
# Перевірка роботи додатка
curl {{BASE_URL}}/api/hello
# Перевірка з'єднання з MongoDB
curl {{BASE_URL}}/api/mongodb-test
# Оптимізація пошуку: $text Index проти MongoDB Search (на базі Lucene)
# Чому LIKE та Regex не підходять
Для пошуку тексту розробники часто використовують регулярні вирази (regex). Проте regex зазвичай спричиняє повне сканування індексу (B-tree scan), що сповільнює роботу при зростанні бази даних.
Текстовий індекс MongoDB трохи кращий: він підтримує токенізацію, видаляє стоп-слова (the, a, and…) та використовує стемінг (зведення слів до основи). Але на такому індексі неможливо використовувати regex.
Головна проблема цих методів — відсутність справжньої релевантності. Найновіша стаття в блозі може не бути найбільш відповідною для пошукового запиту.
# MongoDB Search: Потужність Lucene у вашій базі
MongoDB Search базується на Lucene — відкритому рушії, на якому працюють найбільші пошукові системи світу. Раніше він був лише в Atlas, але з вересня 2025 року доступний і в Community edition.
Він працює через aggregation pipeline, тому виглядає як звичайний запит MongoDB і не потребує додаткових зусиль від DevOps.
Переваги MongoDB Search:
- Fuzzy matching (нечітка відповідність): знайде "Smartphone", навіть якщо користувач припустився помилки.
- Підтримка мов: враховує особливості мовлення.
- BM25: набагато кращий алгоритм оцінки релевантності.
- Autocomplete: підказки в реальному часі.
# Створення індексу Full-Text Search
Спочатку створимо пошуковий індекс (той самий інвертований індекс). В Laravel це виглядає так:
$indexName = config('fulltext.index.name');
$collectionName = config('vector.collection');
$searchFields = ['title', 'plot', 'fullplot', 'cast', 'directors'];
$fieldMappings = [];
foreach ($searchFields as $field) {
$fieldMappings[$field] = ['type' => 'string'];
}
$result = $collection->createSearchIndex([
'mappings' => [
'dynamic' => false,
'fields' => $fieldMappings
]
], ['name' => $indexName]);
Запустіть створення індексу командою:
php artisan fulltext:create-index
# Використання MongoDB $search в Laravel Eloquent
Приклад простого ("наївного") пошуку в MovieSearchTextController:naive():
$results = Movie::query()
->aggregate()
->search(
operator: Search::text(
path: config('fulltext.index.fields'),
query: $query
),
index: config('fulltext.index.name')
)
->addFields(score: ['$meta' => 'searchScore'])
->limit(config('fulltext.search.limit'))
->get();
Ми використовуємо $meta, щоб побачити внутрішню оцінку (score) релевантності. Щоб отримати найкращі результати, потрібно налаштувати "вагу" полів.
# Налаштування ваги полів (Field Weighting)
Пошук фільмів зазвичай має таку структуру пріоритетів: назва (title) — найважливіша, далі акторський склад (cast) та короткий опис (plot).
Ми встановимо такі коефіцієнти:
- Точний збіг назви: 10x
- Частковий збіг назви: 7x
- Актори (Cast): 5x
- Короткий опис (Plot): 3x
- Режисери: 2x
- Повний опис (Fullplot): 1x
Реалізація в MovieSearchTextController::weighted() використовує оператор Search::compound з масивом should, де кожному полю призначено свій boost.
# Порівняння результатів: Naive vs Weighted
# Запит: "The Godfather"
Naive Search часто помиляється. Наприклад, він може видати фільм C(r)ook на першому місці, бо слово "Godfather" там зустрічається в короткому описі та становить більший відсоток тексту (ефект "розмиття ключових слів").
Weighted Search виправляє це завдяки 10-кратному посиленню назви. Уся трилогія «Хрещений батько» опиняється вгорі списку з величезним відривом у балах.
# Запит: "Tom Hanks"
Тут система ранжує результати залежно від того, чи є Том Хенкс актором, режисером чи просто згадується в сюжеті. Хоча в назвах фільмів на кшталт "Tom Sawyer" слово "Tom" теж дає бали, правильне налаштування ваги полів (Cast boost 4x) виводить фільми з реальним Томом Хенксом на перші позиції.
# Висновок
Ми лише торкнулися можливостей MongoDB Search. Використання ваги полів перетворює сирі дані на інтуїтивно зрозумілий інструмент для користувача. Якщо ви вже використовуєте MongoDB, ви отримали пошук рівня Lucene без потреби в додатковій інфраструктурі.
Пошук — це ітеративний процес. Кожен набір даних унікальний, тому важливо постійно тестувати та налаштовувати ваги, щоб результати максимально відповідали бізнес-цілям.