Представьте: у вас на сайте тысячи товаров, и пользователи жалуются, что поиск работает медленно. Знакомая ситуация? Elasticsearch — это как супер-быстрый библиотекарь, который находит нужную книгу за секунду, даже если вы не помните точное название. В этой статье мы разберем, как подружить Yii2 с Elasticsearch, чтобы ваш поиск летал, а пользователи были довольны. Я покажу простые примеры, которые можно сразу применить в проекте.
Что такое Elasticsearch и зачем он нужен в Yii2?
Elasticsearch — это поисковый движок, который хранит данные в виде документов и умеет искать по ним очень быстро. В отличие от MySQL, он не использует сложные JOIN-запросы, а работает с индексами. Представьте, что MySQL — это шкаф с папками, а Elasticsearch — это картотека, где на каждой карточке написано, где что лежит. Для Yii2 это особенно полезно, если у вас много данных (логи, товары, статьи) и нужен полнотекстовый поиск.
Основные сценарии использования:
- Поиск по каталогу товаров с фильтрами.
- Поиск по блогу или документации.
- Аналитика и агрегация данных (например, сколько пользователей купили товар за месяц).
Установка и настройка Elasticsearch в Yii2
Для начала нужно установить Elasticsearch на сервер. Если вы используете Docker, добавьте в docker-compose.yml:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
environment:
- discovery.type=single-node
ports:
- "9200:9200"
После запуска проверьте, что Elasticsearch работает: откройте в браузере http://localhost:9200. Вы должны увидеть JSON с информацией о версии.
Теперь установите расширение для Yii2 через Composer:
composer require yiisoft/yii2-elasticsearch
Добавьте компонент в конфигурацию приложения (config/web.php или config/console.php):
'components' => [
'elasticsearch' => [
'class' => 'yii\elasticsearch\Connection',
'nodes' => [
['http_address' => 'http://localhost:9200'],
// можно добавить несколько узлов для отказоустойчивости
],
],
],
Готово! Теперь Yii2 умеет общаться с Elasticsearch.
Создание модели и индексация данных
В Elasticsearch данные хранятся в индексах (аналог таблиц в MySQL). Для работы с ними создайте Active Record модель. Например, для товаров:
namespace app\models;
use yii\elasticsearch\ActiveRecord;
class Product extends ActiveRecord
{
// Указываем, в каком индексе хранить данные
public static function index()
{
return 'products';
}
// Маппинг полей (типы данных)
public static function mapping()
{
return [
'properties' => [
'id' => ['type' => 'integer'],
'name' => ['type' => 'text'],
'price' => ['type' => 'float'],
'description' => ['type' => 'text'],
'created_at' => ['type' => 'date'],
],
];
}
// Правила валидации (опционально)
public function rules()
{
return [
[['name', 'price'], 'required'],
[['price'], 'number'],
];
}
}
Чтобы создать индекс и применить маппинг, выполните команду в контроллере или консоли:
$product = new Product();
$product->setAttributes([
'id' => 1,
'name' => 'iPhone 15',
'price' => 999.99,
'description' => 'Самый новый смартфон Apple',
'created_at' => date('c'),
]);
$product->save(); // Автоматически индексирует документ
Для массовой индексации используйте batch-запросы:
$products = Product::find()->batch(100); // Получаем пачками из Elasticsearch
foreach ($products as $product) {
$product->save();
}
Поиск с фильтрами и подсветкой результатов
Теперь самое интересное — поиск. Допустим, пользователь ищет «iPhone». Напишем простой поисковый запрос:
$query = Product::find()->query(['match' => ['name' => 'iPhone']]);
$results = $query->all();
Но если нужно искать по нескольким полям и с фильтрами, используйте более сложный запрос:
$searchQuery = [
'bool' => [
'must' => [
['multi_match' => [
'query' => 'iPhone',
'fields' => ['name^3', 'description'], // name важнее в 3 раза
]],
],
'filter' => [
['range' => ['price' => ['gte' => 500, 'lte' => 1500]]],
],
],
];
$query = Product::find()->query($searchQuery);
$products = $query->all();
Чтобы выделить совпадения в тексте (подсветка), добавьте параметр highlight:
$query = Product::find()->query(['match' => ['name' => 'iPhone']])
->highlight(['fields' => ['name' => new \stdClass(), 'description' => new \stdClass()]]);
$results = $query->all();
foreach ($results as $product) {
echo $product->getHighlight('name'); // Вернет массив с подсвеченными фрагментами
}
| Параметр | Описание | Пример |
|---|---|---|
| query | Основной поисковый запрос | ['match' => ['field' => 'value']] |
| filter | Фильтры (без влияния на релевантность) | ['range' => ['price' => ['gte' => 100]]] |
| highlight | Подсветка совпадений | ['fields' => ['name' => new stdClass()]] |
| sort | Сортировка результатов | ['price' => 'asc'] |
Product::find()->suggest('term', ['term' => ['field' => 'name']]). Это отлично работает для поисковых подсказок.
Обновление и удаление данных
Обновление документа в Elasticsearch похоже на сохранение — просто измените атрибуты и вызовите save(). Но есть нюанс: если вы изменили только одно поле, Elasticsearch перезапишет весь документ. Чтобы обновить частично, используйте метод updateAttributes():
$product = Product::get(1);
$product->updateAttributes(['price' => 899.99]);
Удаление:
$product = Product::get(1);
$product->delete();
Массовое удаление по условию:
Product::deleteAll(['match' => ['status' => 'deleted']]);
Синхронизация с MySQL: как не потерять данные
Главная проблема — данные в MySQL и Elasticsearch могут рассинхронизироваться. Например, товар изменили в админке, а в поиске осталась старая версия. Решение: использовать события в модели Yii2.
Добавьте в модель Product (MySQL) обработчики:
use app\models\elastic\ProductIndex;
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes);
$elasticProduct = ProductIndex::findOne($this->id) ?: new ProductIndex();
$elasticProduct->setAttributes([
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
'description' => $this->description,
'created_at' => $this->created_at,
]);
$elasticProduct->save();
}
public function afterDelete()
{
parent::afterDelete();
$elasticProduct = ProductIndex::findOne($this->id);
if ($elasticProduct) {
$elasticProduct->delete();
}
}
Также можно использовать очередь (например, RabbitMQ) для асинхронной синхронизации, чтобы не замедлять запросы пользователей.
Заключение
Интеграция Yii2 с Elasticsearch — это не rocket science. Вы устанавливаете расширение, создаете модель, индексируете данные и ищете. Главное — помнить о синхронизации и не хранить в Elasticsearch то, что не нужно искать. Начните с малого: добавьте поиск по товарам или статьям, и вы увидите, как скорость вырастет в разы.
Что в итоге:
- Elasticsearch ускоряет поиск по большим объемам данных.
- Yii2 предоставляет удобный Active Record для работы с ним.
- Синхронизацию лучше автоматизировать через события или очереди.
Попробуйте прямо сейчас — создайте индекс и проиндексируйте пару записей. Уверен, результат вас впечатлит.

