Полнотекстовый поиск — это как поиск иголки в стоге сена, только стог — это ваша база данных, а иголка — нужная информация. Если вы используете Yii2 и хотите, чтобы поиск был быстрым и умным, Elasticsearch станет отличным решением. В этой статье я расскажу, как подружить Yii2 с Elasticsearch и настроить полнотекстовый поиск.
Что такое Elasticsearch и зачем он нужен в Yii2?
Elasticsearch — это поисковый движок, построенный на Lucene. Он умеет быстро искать по большим объёмам текста, анализировать запросы, учитывать опечатки и ранжировать результаты по релевантности. В Yii2 есть официальное расширение yiisoft/yii2-elasticsearch, которое упрощает интеграцию.
Зачем это нужно? Представьте, что у вас интернет-магазин с миллионом товаров. Пользователь вводит «красные кроссовки Nike». Обычный SQL LIKE-запрос будет медленным и неучтёт морфологию. Elasticsearch же найдёт все кроссовки, даже если в описании написано «кроссовок» или «Nike Air». Разница очевидна.
Установка и настройка Elasticsearch
Сначала установите Elasticsearch. Скачайте с официального сайта, распакуйте и запустите. Для разработки хватит одного узла. Проверьте работу:
curl -X GET "localhost:9200"
В ответ вы должны увидеть JSON с информацией о кластере. Теперь установим расширение для Yii2 через Composer:
composer require yiisoft/yii2-elasticsearch
После установки настройте компонент в конфиге приложения (обычно config/web.php или config/main.php):
'components' => [
'elasticsearch' => [
'class' => 'yii\elasticsearch\Connection',
'hosts' => [
'http://localhost:9200',
],
],
],
Если Elasticsearch требует аутентификации, добавьте логин и пароль в массив hosts.
Создание модели для работы с индексом
В Elasticsearch данные хранятся в индексах, аналогичных таблицам в БД. Создадим модель, которая будет представлять индекс с товарами. Наследуем её от yii\elasticsearch\ActiveRecord.
<?php
namespace app\models;
use yii\elasticsearch\ActiveRecord;
class Product extends ActiveRecord
{
public static function index()
{
return 'products'; // имя индекса
}
public static function type()
{
return 'product'; // тип (в новых версиях необязателен)
}
public function attributes()
{
return ['id', 'name', 'description', 'price', 'category'];
}
public function rules()
{
return [
[['name', 'description'], 'string'],
[['price'], 'number'],
[['category'], 'string'],
];
}
}
Обратите внимание: метод attributes() возвращает список полей, которые будут храниться в индексе. Метод index() задаёт имя индекса. По умолчанию Yii2 использует имя класса, но лучше указать явно.
Индексация данных и поиск
Чтобы добавить документ в индекс, просто создайте экземпляр модели, заполните атрибуты и вызовите save():
$product = new Product();
$product->id = 1;
$product->name = 'Кроссовки Nike Air';
$product->description = 'Удобные красные кроссовки для бега';
$product->price = 7990;
$product->category = 'Обувь';
$product->save();
Для массовой индексации используйте bulk() или createCommand(). Например, перенесём все товары из MySQL:
$products = \app\models\ProductSql::find()->all();
$bulk = Yii::$app->elasticsearch->createBulkCommand();
foreach ($products as $sqlProduct) {
$bulk->addAction('index', [
'index' => 'products',
'type' => 'product',
'id' => $sqlProduct->id,
'body' => [
'name' => $sqlProduct->name,
'description' => $sqlProduct->description,
'price' => $sqlProduct->price,
'category' => $sqlProduct->category,
],
]);
}
$bulk->execute();
Теперь поиск. Yii2 предлагает удобный метод find(), который возвращает объект запроса. Можно искать по всем полям или по конкретному:
$query = Product::find()->query([
'multi_match' => [
'query' => 'красные кроссовки',
'fields' => ['name', 'description'],
],
]);
$results = $query->all();
Elasticsearch автоматически анализирует запрос: разбивает на слова, приводит к нижнему регистру, ищет совпадения. Результаты сортируются по релевантности (score). Вы можете добавить фильтры, сортировку, агрегации — всё через массив.
Продвинутые настройки: анализаторы, маппинг, подсветка
Чтобы поиск был ещё умнее, настройте анализаторы. Например, для русского языка используйте анализатор russian. Создайте индекс с нужными настройками через API Elasticsearch:
PUT /products
{
"settings": {
"analysis": {
"analyzer": {
"my_russian": {
"type": "russian"
}
}
}
},
"mappings": {
"product": {
"properties": {
"name": {
"type": "text",
"analyzer": "my_russian"
},
"description": {
"type": "text",
"analyzer": "my_russian"
}
}
}
}
}
Теперь при индексации текст будет обрабатываться русским анализатором: учитываться морфология, стоп-слова. Это значительно улучшит качество поиска.
Подсветка найденных фрагментов (highlighting) делается так:
$query = Product::find()->query([
'multi_match' => [
'query' => 'кроссовки',
'fields' => ['name', 'description'],
],
])->highlight([
'fields' => [
'name' => new \yii\elasticsearch\Query()->highlight(),
'description' => new \yii\elasticsearch\Query()->highlight(),
],
]);
В результатах будет массив highlight с кусками текста, где найдены совпадения, обёрнутыми в <em>.
_analyze API, чтобы проверить, как анализатор разбивает текст на токены. Это помогает отладить поиск.
Сравнение возможностей Elasticsearch и SQL LIKE
| Критерий | Elasticsearch | SQL LIKE |
|---|---|---|
| Скорость на больших данных | Высокая (инвертированный индекс) | Низкая (полный перебор) |
| Морфология | Поддерживается через анализаторы | Нет |
| Ранжирование по релевантности | Есть (score) | Нет |
| Подсветка результатов | Есть | Нет |
Как видите, Elasticsearch выигрывает по многим параметрам. Но не спешите отказываться от SQL: для простых задач LIKE может быть достаточно. Elasticsearch стоит внедрять, когда поиск — ключевая функция.
Синхронизация данных между БД и Elasticsearch
Одна из главных задач — поддерживать актуальность индекса. Если данные меняются в MySQL, их нужно обновлять и в Elasticsearch. Есть несколько подходов:
- Синхронно при сохранении. В методе
afterSave()модели Yii2 обновляйте документ в Elasticsearch. Просто, но замедляет запись. - Через очередь. Отправляйте задание в RabbitMQ или Redis, а воркер обновляет индекс. Асинхронно и надёжно.
- Периодическая переиндексация. По крону запускайте полную переиндексацию. Подходит, если данные меняются редко.
Вот пример синхронного обновления в модели:
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes);
$elasticProduct = new \app\models\Product();
$elasticProduct->id = $this->id;
$elasticProduct->name = $this->name;
$elasticProduct->description = $this->description;
$elasticProduct->price = $this->price;
$elasticProduct->category = $this->category;
$elasticProduct->save();
}
Не забудьте обработать удаление: в afterDelete() удаляйте документ из индекса.
Заключение
Интеграция Yii2 с Elasticsearch — это мощный инструмент для создания быстрого и релевантного поиска. Вы настроили соединение, создали модель, научились индексировать и искать данные, а также синхронизировать их с основной БД. Помните о правилах: явно указывайте индекс, используйте анализаторы для морфологии, следите за актуальностью данных.
Если вы только начинаете, начните с простого поиска, а затем добавляйте фильтры и подсветку. Elasticsearch гибок и масштабируем, так что ваш поиск будет летать.
Студия WNDER

