いいオフィスが無料で使える!
いいオフィスが無料で使える!
2019.06.14
#50
バックエンドへの道

「Laravel Scout」でElasticsearchを使ってみる

エリカ

こんにちは、エリカです。

今回は、Laravelで全文検索を実装するアプローチについてです。全文検索といえば、とにかく情報量が多そうな Elasticsearch が候補のひとつになるかと思います。

それでは、Laravel から Elasticsearch をどのように扱えばいいのでしょうか。

Laravelの公式パッケージに、Laravel Scout があります。

Laravel Scout(スカウト、斥候)は、Eloquentモデルへ、シンプルなドライバベースのフルテキストサーチを提供します。モデルオブサーバを使い、Scoutは検索インデックスを自動的にEloquentレコードと同期します。
現在、ScoutはAlgoliaドライバを用意しています。カスタムドライバは簡単に書けますので、独自の検索を実装し、Scoutを拡張できます。

この Laravel Scout を利用して、Elasticsearch を利用するところまでを試してみます。

Elasticsearchの準備

まずは、手元で、 Elasticsearch を動かしておきます。Laradockなら elasticsearch コンテナを起動するだけです。簡単ですね。

docker-compose up -d elasticsearch

Laravelに必要パッケージのインストール

次に、 Laravelに必要なパッケージをインストールしていきます。

composer require laravel/scout
composer require elasticsearch/elasticsearch

laravel/scout がドライバベースの検索エンジンです。デフォルトのドライバとして algolia が用意されているのみですので、これを elasticsearch を利用するドライバに置き換えていきます。

設定ファイルの準備

検索用の設定ファイルを準備します。artisan コマンドを使えば、自動で作成できます。

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

そうして作成された config/scout.php に下記のようにインデックス名やホスト名を追加しておきます。

'elasticsearch' => [
        'index' => env('ELASTICSEARCH_INDEX', 'scout'),
        'hosts' => [
            env('ELASTICSEARCH_HOST', 'http://localhost'),
        ],
    ],

.env ファイルで、 ELASTICSEARCH_HOST を指定します。また、検索エンジンも elasticsearch を利用するように指定します。

SCOUT_DRIVER=elasticsearch
ELASTICSEARCH_HOST=http://elasticsearch

ただ、この時点では、 elasticsearch という検索ドライバは存在していませんので、それを作成します。

Elasticsearchを扱うLaravel Scoutドライバの作成

それでは、 Elasticsearch を扱う検索ドライバを Scout に追加します。

まずは、 app/Scout/ElasticsearchEngine.php を作成します。

<?php

namespace App\Scout;

use Elasticsearch\Client as Elastic;
use Laravel\Scout\Builder;
use Laravel\Scout\Engines\Engine;

class ElasticsearchEngine extends Engine
{
    protected $elastic;
    protected $index;

    public function __construct(Elastic $elastic, $index)
    {
        $this->elastic = $elastic;
        $this->index = $index;
    }

    public function update($models)
    {
        // TODO: Implement update() method.
    }

    public function delete($models)
    {
        // TODO: Implement delete() method.
    }

    public function search(Builder $builder)
    {
        // TODO: Implement search() method.
    }

    public function paginate(Builder $builder, $perPage, $page)
    {
        // TODO: Implement paginate() method.
    }

    public function mapIds($results)
    {
        // TODO: Implement mapIds() method.
    }

    public function map(Builder $builder, $results, $model)
    {
        // TODO: Implement map() method.
    }

    public function getTotalCount($results)
    {
        // TODO: Implement getTotalCount() method.
    }

    public function flush($model)
    {
        // TODO: Implement flush() method.
    }
}

作成したドライバの登録

次に、ElasticsearchServiceProvider.phpを用意し、先ほど作成した、 Elasticsearch エンジンを elasticsearch という検索ドライバ名で利用できるようにします。

<?php

namespace App\Providers;

use App\Scout\ElasticsearchEngine;
use Elasticsearch\ClientBuilder;
use Illuminate\Support\ServiceProvider;
use Laravel\Scout\EngineManager;

class ElasticsearchServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        resolve(EngineManager::class)->extend('elasticsearch', function ($app) {
            return new ElasticsearchEngine(
                ClientBuilder::fromConfig([
                    'hosts' => config('scout.elasticsearch.hosts'),
                ]),
                config('scout.elasticsearch.index')
            );
        });
    }

    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

EloquentとLaravel Scoutの同期

EloquentにSearchableトレイトを指定すると、Eloquentの各種イベントで検索エンジンドライバのメソッドが自動的に呼び出されるようになります。

class Post extends Model
{
    use Searchable;
}

たとえば、Post::create() などの更新処理が行われた場合は、 ElasticsearchEngine::update() が呼び出されます。

あとは、ElasticsearchEngine の各メソッドでどのようなデータのやりとりを行なっていくかの記述をしていけばいいだけです。

まとめ

Elasticsearchとの連携もLaravelなら簡単ですね。

2