•   Новости. Советы. Уроки.
Блог / Советы и уроки / Изучаем Elasticsearch в связке с Laravel

Изучаем Elasticsearch в связке с Laravel

Michael Stivala
Виталий Николенко
11.03.2016
Перевод статьи Learning Elasticsearch with Laravel

В этом обзорном уроке мы изучим Elasticsearch; основные принципы работы поиска и как подружить его c Laravel, Homestead и даже Forge. Хотя на эту тему есть материалы, я не смог найти статьи, которая в краткой форме описывала бы весь процесс установки и настройки в предельно сжатые сроки

Что такое Elasticsearch и зачем он мне?

Elasticsearch - полнотекстовый поисковый движок. Устанавливаете его на сервер, и общаетесь с ним по HTTP Rest Api. После установки, в него заливаются данные, по котором позднее надо будет искать. Он позволяет производить поиск по очень большому набору данных, при этом очень быстро, при помощи функционально-богатого механизма запросов.

И хотя полнотекстовый поиск можно реализовать средствами SQL баз данных, он не получится таким мощным и легко расширяемым как Elasticsearch. Одно из преимуществ Elasticsearch - его документо-ориентированость, что позволяет хранить данные в развернутом виде. Сложные сущности, построенные при помощи сложных запросов с join превращаются в один объект. Но, при необходимости можно добавить и отношения и вложенные объекты.

Одни из ключевых функций, которые интересуют меня – «нечеткий» поиск, который позволяет искать с ошибками в написании; мощный механизм сортировки по релевантности; возможность получить результаты по «похожему запросу» если нет точного совпадения.

Установка Elasticsearch

Я сделал скрипт для установки последней версии Java и Elasticsearch http://forgerecipes.com/recipes/73. На homestead Elasticsearch можно установить подключившися по SSH скопировать скрипт в терминал и прогнать его. После этого Elasticsearch должен установиться и запуститься! Запустите curl 'localhost:9200’ в терминале чтобы убедиться, что Elasticsearch работает.

Общение Elasticsearch из PHP

Теперь, когда окружение установлено, можно приступить к использованию поиска! Официальный PHP клиент лежит на github и его можно установить через composer: composer require elasticsearch/elasticsearch

Хотя вполне можно использовать классы Elasticsearch Client and ClientBuilder напрямую, давайте сделаем собственную обертку для них. Так мы сможем построить гибкий Api и если пакет Elasticsearch вдруг сломается при очередном обновлении нам всего лишь надо будет подправить свою обертку.

Простая обертка может выглядеть, как показано ниже, но я сделал и более навороченный вариант

<?php

namespace App\Elastic;

use Elasticsearch\Client;

class Elastic
{
    protected $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    /**
     * Index a single item
     *
     * @param  array  $parameters [index, type, id, body]
     */
    public function index(array $parameters)
    {
        return $this->client->index($parameters);
    }

    /**
     * Delete a single item
     *
     * @param  array  $parameters
     */
    public function delete(array $parameters)
    {
        return $this->client->delete($parameters);
    }

    public function search(array $parameters)
    {
        return $this->client->search($parameters);
    }
}

Чтобы это заработало, надо прописать зависимости в сервис-провайдере. Я также добавил ведение логов, чтобы легче было выловить баги. Вот что получилось:

public function register()
{
    $this->app->bind(Elastic::class, function ($app) {
        return new Elastic(
            ClientBuilder::create()
                ->setLogger(ClientBuilder::defaultLogger(storage_path('logs/elastic.log')))
                ->build()
        );
    });
}

Теперь мы можем создавать новые экземпляры класса Elastic через сервис-контейнер Laravel и взаимодействовать с Elasticsearch!

$elastic = app(App\Elastic\Elastic::class);

$elastic->index([
    'index' => 'blog',
    'type' => 'post',
    'id' => 1,
    'body' => [
        'title' => 'Hello world!'
        'content' => 'My first indexed post!'
    ]
]);

Индексация

Elasticsearch организует данные в индексы и типы которые могут быть связаны с SQL базами данных и таблицами. Прежде чем мы сможем насладиться могущественным функционалом Elasticsearch нам надо заполнить его данными. Для того, чтобы держать индекс актуальным, можно повесить хуки на события Eloquent модели «saved» и «deleted»

<?php

public function boot()
{
    $elastic = $this->app->make(App\Elastic\Elastic::class);

    Post::saved(function ($post) use ($elastic) {
        $elastic->index([
            'index' => 'blog',
            'type' => 'post',
            'id' => $post->id,
            'body' => $post->toArray()
        ]);
    });

    Post::deleted(function ($post) use ($elastic) {
        $elastic->delete([
            'index' => 'blog',
            'type' => 'post',
            'id' => $post->id,
        ]);
    });
}

А начальный импорт может выглядеть примерно так:

$elastic = $this->app->make(App\Elastic\Elastic::class);

Post::chunk(100, function ($posts) use ($elastic) {
    foreach ($posts as $post) {
        $elastic->index([
            'index' => 'blog',
            'type' => 'post',
            'id' => $post->id,
            'body' => $post->toArray()
        ]);
    }
});

Поиск

Теперь, когда у нас индекс наполнен данными, самое время поискать! Давайте представим, что у нас обычное поле для ввода поисковой строки и нам надо выдать релевантные результаты

У Elasticsearch довольно функциональный язык запросов.

Для своего примера я хочу реализовать следующие задачи: • Произвести поиск по нескольким полям наших записей с учетом релевантности • Разрешить орфографические ошибки как в запросе, так и в самом тексте постов

Параметры поиска будут выглядеть примерно так:

$parameters = [
    'index' => 'blog',
    'type' => 'post',
    'body' => [
        'query' => $query
    ]
];
$response = $elastic->search($parameters); 

Теперь заполним нашу переменную $query . Elasticsearch возвращает результаты, отсортированые по релевантности прямо из коробки . Искать по нескольким полям можно при помощи multi_match поиска, в котором полям можно расставить веса для расчета релевантности.

$query = [
    'multi_match' => [
        'query' => $search,
        'fields' => ['title^3', 'content'],
    ],
];

Разрешить орфографические ошибки можно про помощи «нечеткого» поиска. Не вдаваясь в детали, делается это одной строкой:

$query = [
    'multi_match' => [
        'query' => $search,
        'fuzziness' => 'AUTO',
        'fields' => ['title^3', 'content'],
    ],
];

Вот и все! Наш сервер Elasticsearch выдаст нам результат поиска, отсортированый по релевантности и с учетом возможных орфографических ошибок.

Если работать с Elasticsearch то еще много чего надо изучить, и приведенный код, если его использовать на продакшен сервере, надо улучшать. Но получив эти начальные знания, дальше уж точно разберетесь.

Полезные ресурсы

Ссылки на полезные материалы, которые я нашел в сети (на английском языке) Документация Elasticsearch Очень подробная, точно ответит на многие ваши вопросы

Выступление Ben Corlett’s на конференции Laracon Освещены основные моменты установки и использования Elasticsearch с Laravel.

Spatie’s Пакет для управдления индексам Пакет для работы с Elasticsearch и Algolia. Посмотрел как пример того, как другие разработчики управляются с индексами

Интеграция Elasticsearch –Сделанослюбовью Отличная краткое руководство по интеграци Elasticsearch с Laravel 4.2 приложением

Elasticquent Связывает модели Laravel с типами Elasticsearch