イントロダクション

Laravel Scout(スカウト、斥候)は、Eloquentモデルへ、シンプルなドライバベースのフルテキストサーチを提供します。モデルオブサーバを使い、Scoutは検索インデックスを自動的にEloquentレコードと同期します。

現在、ScoutはAlgoliaドライバを用意しています。カスタムドライバは簡単に書けますので、独自の検索を実装し、Scoutを拡張できます。

インストール

最初に、Composerパッケージマネージャを使い、Scoutをインストールします。

composer require laravel/scout

次に、config/app.php設定ファイルのproviders配列へ、ScoutServiceProviderを追加します。

Laravel\Scout\ScoutServiceProvider::class,

Scoutサービスプロバイダを登録したら、vendor:publish Artisanコマンドを使い、Scout設定を公開(Laravel用語で、リソースや設定を開発者が変更可能な場所へ用意すること)します。このコマンドは、configディレクトリ下にscout.php設定ファイルを公開します。

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

最後に、検索可能にしたいモデルへ、Laravel\Scout\Searchableトレイトを追加します。このトレイトはモデルオブザーバを登録し、サーチドライバとモデルの同期を取り続けます。

<?php

namespace App;

use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use Searchable;
}

キュー

Scoutを厳格(リアルタイム)に利用する必要が無いのであれば、このライブラリを使用する前にキュードライバの設定を考えてみるべきでしょう。キューワーカの実行により、モデルの情報を検索インデックスに同期する全操作をキューイングでき、アプリケーションのWebインターフェイスのレスポンス時間を改善できるでしょう。

キュードライバを設定したら、config/scout.php設定ファイルのqueueオプション値をtrueに設定してください。

'queue' => true,

ドライバの事前要件

Algolia

Algoliaドライバを使用する場合、Algolia idsecret接続情報をconfig/scout.php設定ファイルで設定する必要があります。接続情報を設定し終えたら、Algolia PHP SDKをComposerパッケージマネージャで、インストールする必要があります。

composer require algolia/algoliasearch-client-php

設定

モデルインデックスの設定

各Eloquentモデルは、検索可能レコードすべてを含む、指定された検索「インデックス」と同期されます。言い換えれば、各インデックスはMySQLテーブルのようなものであると、考えられます。デフォルトで、各モデルはそのモデルの典型的な「テーブル」名に一致するインデックスへ保存されます。通常、モデルの複数形ですが、モデルのsearchableAsメソッドをオーバーライドすることで、このモデルのインデックスを自由にカスタマイズ可能です。

<?php

namespace App;

use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use Searchable;

    /**
     * モデルのインデックス名取得
     *
     * @return string
     */
    public function searchableAs()
    {
        return 'posts_index';
    }
}

検索可能データの設定

デフォルトでは、指定されたモデルのtoArray形態全体が、検索インデックスへ保存されます。検索インデックスと同期するデータをカスタマイズしたい場合は、そのモデルのtoSearchableArrayメソッドをオーバーライドできます。

<?php

namespace App;

use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use Searchable;

    /**
     * モデルのインデックス可能なデータ配列の取得
     *
     * @return array
     */
    public function toSearchableArray()
    {
        $array = $this->toArray();

        // 配列のカスタマイズ…

        return $array;
    }
}

インデックス

バッチ取り込み

既存プロジェクトにScoutをインストールする場合、検索ドライバへ取り込むために必要なデータベースレコードは、既に存在しています。Scoutは既存の全レコードを検索インデックスへ取り込むために使用する、import Artisanコマンドを提供しています。

php artisan scout:import "App\Post"

レコード追加

モデルにLaravel\Scout\Searchableトレイトを追加したら、必要なのはモデルインスタンスをsaveすることです。これにより自動的に検索インデックスへ追加されます。Scoutでキューを使用する設定にしている場合は、この操作はキューワーカにより、バックグランドで実行されます。

$order = new App\Order;

// ...

$order->save();

クエリによる追加

Eloquentクエリにより、検索インデックスへモデルのコレクションを追加したい場合は、Eloquentクエリにsearchableメソッドをチェーンします。searchableメソッドは、クエリの結果をチャンクへ分割し、レコードを検索エンジンへ追加します。この場合も、Scoutでキューを使用する設定をしていれば、キューワーカが全チャンクをバックグランドで追加します。

// Eloquentクエリにより追加
App\Order::where('price', '>', 100)->searchable();

// リレーションにより、レコードを追加することもできる
$user->orders()->searchable();

// コレクションにより、追加することもできる
$orders->searchable();

searchableメソッドは"upsert(update+insert)"操作と考えられます。言い換えれば、モデルレコードがインデックスへ既に存在していれば、更新されます。検索エンジンに存在していなければ、インデックスへ追加されます。

レコード更新

検索可能モデルを更新するには、モデルインスタンスのプロパティを更新し、saveでモデルをデータベースへ保存します。Scoutは自動的に変更を検索インデックスへ保存します。

$order = App\Order::find(1);

// 注文を更新…

$order->save();

モデルのコレクションを更新するためにも、Eloquentクエリのsearchableメソッドを使用します。検索エンジンにモデルが存在していない場合は、作成します。

// Eloquentクエリによる更新
App\Order::where('price', '>', 100)->searchable();

// リレーションによる更新も可能
$user->orders()->searchable();

// コレクションによる更新も可能
$orders->searchable();

レコード削除

インデックスからレコードを削除するには、データベースからモデルをdeleteで削除するだけです。この形態による削除は、モデルのソフト削除と互換性があります。

$order = App\Order::find(1);

$order->delete();

レコードを削除する前に、モデルを取得したくない場合は、Eloquentクエリインスタンスかコレクションに対し、unsearchableメソッドを使用します。

// Eloquentクエリによる削除
App\Order::where('price', '>', 100)->unsearchable();

// リレーションによる削除も可能
$user->orders()->unsearchable();

// コレクションによる削除も可能
$orders->unsearchable();

インデックスの一時停止

Eloquentモデルをバッチ処理するが、検索インデックスへモデルデータを同期したくない場合も時々あります。withoutSyncingToSearchメソッドを使用することで可能です。このメソッドは、即時に実行されるコールバックを1つ引数に取ります。コールバック中のモデル操作は、インデックスへ同期されることはありません。

App\Order::withoutSyncingToSearch(function () {
    // モデルアクションの実行…
});

検索

searchメソッドにより、モデルの検索を開始しましょう。searchメソッドはモデルを検索するために使用する文字列だけを引数に指定します。getメソッドを検索クエリにチェーンし、指定した検索クエリに一致するEloquentモデルを取得できます。

$orders = App\Order::search('Star Trek')->get();

Scoutの検索ではEloquentモデルのコレクションが返されるため、ルートやコントローラから直接結果を返せば、自動的にJSONへ変換されます。

use Illuminate\Http\Request;

Route::get('/search', function (Request $request) {
    return App\Order::search($request->search)->get();
});

Where節

Scoutは検索クエリに対して"WHERE"節を単に追加する方法も提供しています。現在、この節としてサポートしているのは、基本的な数値の一致を確認することだけで、主にIDにより検索クエリを絞り込むために使用します。検索インデックスはリレーショナル・データベースではないため、より上級の"WHERE"節は現在サポートしていません。

$orders = App\Order::search('Star Trek')->where('user_id', 1)->get();

ペジネーション

コレクションの取得に付け加え、検索結果をpaginateメソッドでページづけできます。このメソッドは、Paginatorインスタンスを返しますので、Eloquentクエリのペジネーションと同様に取り扱えます。

$orders = App\Order::search('Star Trek')->paginate();

paginateメソッドの第1引数として、各ページごとに取得したいモデル数を指定します。

$orders = App\Order::search('Star Trek')->paginate(15);

結果が取得できたら、通常のEloquentクエリのペジネーションと同様に、結果を表示し、Bladeを使用してページリンクをレンダーできます。

<div class="container">
    @foreach ($orders as $order)
        {{ $order->price }}
    @endforeach
</div>

{{ $orders->links() }}

カスタムエンジン

エンジンのプログラミング

組み込みのScout検索エンジンがニーズに合わない場合、独自のカスタムエンジンを書き、Scoutへ登録してください。エンジンは、Laravel\Scout\Engines\Engine抽象クラスを拡張してください。この抽象クラスは、カスタムエンジンが実装する必要のある、5つのメソッドを持っています。

use Laravel\Scout\Builder;

abstract public function update($models);
abstract public function delete($models);
abstract public function search(Builder $builder);
abstract public function paginate(Builder $builder, $perPage, $page);
abstract public function map($results, $model);

これらのメソッドの実装をレビューするために、Laravel\Scout\Engines\AlgoliaEngineクラスが役に立つでしょう。このクラスは独自エンジンで、各メソッドをどのように実装すればよいかの、良い取り掛かりになるでしょう。

エンジンの登録

カスタムエンジンを書いたら、Scoutエンジンマネージャのextendメソッドを使用し、Scoutへ登録します。AppServiceProviderかアプリケーションで使用している他のサービスプロバイダーのbootメソッドで、extendメソッドを呼び出してください。たとえば、MySqlSearchEngineを書いた場合、次のように登録します。

use Laravel\Scout\EngineManager;

/**
 * 全アプリケーションサービスの登録
 *
 * @return void
 */
public function boot()
{
    resolve(EngineManager::class)->extend('mysql', function () {
        return new MySqlSearchEngine;
    });
}

エンジンが登録できたら、Scoutのデフォルトdriverとして、config/scout.php設定ファイルで設定します。

'driver' => 'mysql',