#はじめに
Laravel Scout は、Eloquentモデルに全文検索を簡単に追加できるドライバー方式のソリューションです。モデルオブザーバーを使い、Scoutは検索インデックスをEloquentのレコードと自動的に同期します。
現在、ScoutはAlgolia、Meilisearch、Typesense、およびMySQL / PostgreSQL(database)ドライバーを同梱しています。さらに、Scoutにはローカル開発用に設計された「コレクション」ドライバーがあり、外部依存やサードパーティサービスを必要としません。また、カスタムドライバーの作成も簡単で、自分の検索実装でScoutを拡張できます。
#インストール
まず、Composerパッケージマネージャーを使ってScoutをインストールします:
composer require laravel/scout
Scoutをインストールしたら、vendor:publish ArtisanコマンドでScoutの設定ファイルを公開してください。このコマンドはscout.php設定ファイルをアプリケーションのconfigディレクトリに公開します:
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
最後に、検索可能にしたいモデルにLaravel\Scout\Searchableトレイトを追加します。このトレイトはモデルオブザーバーを登録し、モデルと検索ドライバーの同期を自動で行います:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
}
#キューイング
Scoutの使用に必須ではありませんが、キュードライバーの設定を強く推奨します。キューワーカーを動かすことで、モデル情報を検索インデックスに同期する操作をキューに入れられ、アプリケーションのウェブインターフェイスの応答速度が大幅に向上します。
キュードライバーを設定したら、config/scout.phpのqueueオプションをtrueに設定してください:
'queue' => true,
queueオプションがfalseでも、AlgoliaやMeilisearchなど一部のScoutドライバーは常に非同期でインデックス処理を行うことに注意してください。つまり、Laravelアプリケーション内でインデックス操作が完了しても、検索エンジン側で新しいレコードがすぐに反映されない場合があります。
Scoutジョブが利用する接続とキューを指定したい場合、queue設定を配列で定義できます:
'queue' => [
'connection' => 'redis',
'queue' => 'scout'
],
もちろん、接続とキューをカスタマイズした場合は、その接続とキューでジョブを処理するキューワーカーを起動してください:
php artisan queue:work redis --queue=scout
#ドライバーの前提条件
#Algolia
Algoliaドライバーを使う場合は、config/scout.phpでAlgoliaのidとsecret認証情報を設定してください。認証情報を設定したら、ComposerでAlgolia PHP SDKをインストールする必要があります:
composer require algolia/algoliasearch-client-php
#Meilisearch
Meilisearchは非常に高速なオープンソースの検索エンジンです。ローカルマシンへのインストール方法がわからない場合は、Laravel公式のDocker開発環境であるLaravel Sailを利用できます。
Meilisearchドライバーを使う場合は、ComposerでMeilisearch PHP SDKをインストールしてください:
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
その後、SCOUT_DRIVER環境変数とMeilisearchのhostおよびkey認証情報をアプリケーションの.envファイルに設定します:
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey
Meilisearchの詳細については、Meilisearchドキュメントを参照してください。
また、Meilisearchのバイナリバージョンに対応したmeilisearch/meilisearch-phpのバージョンをインストールしているか、Meilisearchのバイナリ互換性に関するドキュメントで確認してください。
Meilisearchを利用しているアプリケーションでScoutをアップグレードする際は、Meilisearchサービス自体の追加の破壊的変更を必ず確認してください。
#Typesense
Typesenseは高速なオープンソース検索エンジンで、キーワード検索、セマンティック検索、ジオ検索、ベクター検索をサポートしています。
セルフホストするか、Typesense Cloudを利用できます。
TypesenseをScoutで使い始めるには、ComposerでTypesense PHP SDKをインストールしてください:
composer require typesense/typesense-php
次に、SCOUT_DRIVER 環境変数と、Typesense のホストおよび API キーの認証情報をアプリケーションの .env ファイルに設定してください:
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost
必要に応じて、インストールのポート、パス、プロトコルも指定できます:
TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http
Typesenseコレクションの追加設定やスキーマ定義は、アプリケーションのconfig/scout.php設定ファイルにあります。詳細はTypesenseドキュメントを参照してください。
#Typesenseに保存するデータの準備
Typesenseを使う場合、検索可能なモデルはtoSearchableArrayメソッドを定義し、モデルの主キーを文字列にキャストし、作成日時をUNIXタイムスタンプに変換する必要があります:
/**
* モデルのインデックス可能なデータ配列を取得します。
*
* @return array<string, mixed>
*/
public function toSearchableArray()
{
return array_merge($this->toArray(),[
'id' => (string) $this->id,
'created_at' => $this->created_at->timestamp,
]);
}
Typesenseコレクションのスキーマはアプリケーションのconfig/scout.phpで定義してください。コレクションスキーマはTypesenseで検索可能な各フィールドのデータ型を記述します。利用可能なスキーマオプションの詳細はTypesenseドキュメントを参照してください。
Typesenseコレクションのスキーマを変更したい場合は、scout:flushとscout:importを実行して既存のインデックスデータを削除しスキーマを再作成するか、TypesenseのAPIを使ってインデックスデータを削除せずにスキーマを変更できます。
検索可能なモデルがソフトデリート可能な場合は、対応するTypesenseスキーマに__soft_deletedフィールドをconfig/scout.phpで定義してください:
User::class => [
'collection-schema' => [
'fields' => [
// ...
[
'name' => '__soft_deleted',
'type' => 'int32',
'optional' => true,
],
],
],
],
#動的な検索パラメータ
Typesenseはoptionsメソッドを使った検索操作時に、検索パラメータを動的に変更できます:
use App\Models\Todo;
Todo::search('Groceries')->options([
'query_by' => 'title, description'
])->get();
#設定
#モデルのインデックス設定
各Eloquentモデルは特定の検索「インデックス」と同期され、そのモデルの検索可能なレコードをすべて含みます。つまり、インデックスはMySQLのテーブルのようなものです。デフォルトでは、モデルは通常の「テーブル」名に対応するインデックスに保存されます。通常はモデル名の複数形ですが、モデルのsearchableAsメソッドをオーバーライドしてインデックス名をカスタマイズできます:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
/**
* モデルに関連付けられたインデックス名を取得します。
*/
public function searchableAs(): string
{
return 'posts_index';
}
}
#検索可能なデータの設定
デフォルトでは、モデルのtoArrayの全データが検索インデックスに保存されます。同期するデータをカスタマイズしたい場合は、モデルのtoSearchableArrayメソッドをオーバーライドしてください:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
/**
* モデルのインデックス可能なデータ配列を取得します。
*
* @return array<string, mixed>
*/
public function toSearchableArray(): array
{
$array = $this->toArray();
// データ配列をカスタマイズ...
return $array;
}
}
Meilisearchなど一部の検索エンジンは、フィルター操作(>, <など)を正しい型のデータに対してのみ行います。そのため、これらの検索エンジンを使い検索可能データをカスタマイズする場合は、数値を正しい型にキャストしてください:
public function toSearchableArray()
{
return [
'id' => (int) $this->id,
'name' => $this->name,
'price' => (float) $this->price,
];
}
#フィルター可能なデータとインデックス設定の構成(Meilisearch)
Scoutの他のドライバーとは異なり、Meilisearchはフィルター可能属性、ソート可能属性、その他のサポートされる設定フィールドなどのインデックス検索設定を事前に定義する必要があります。
フィルター可能属性はScoutのwhereメソッドでフィルターに使う属性、ソート可能属性はorderByメソッドでソートに使う属性です。インデックス設定はアプリケーションのscout設定ファイル内のmeilisearch設定のindex-settings部分で調整します:
use App\Models\User;
use App\Models\Flight;
'meilisearch' => [
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
'key' => env('MEILISEARCH_KEY', null),
'index-settings' => [
User::class => [
'filterableAttributes'=> ['id', 'name', 'email'],
'sortableAttributes' => ['created_at'],
// その他の設定フィールド...
],
Flight::class => [
'filterableAttributes'=> ['id', 'destination'],
'sortableAttributes' => ['updated_at'],
],
],
],
モデルがソフトデリート可能で、かつ index-settings 配列に含まれている場合、Scout はそのインデックスでソフトデリートされたモデルのフィルタリングを自動的にサポートします。ソフトデリート可能なモデルのインデックスに対して他にフィルタ可能またはソート可能な属性を定義しない場合は、そのモデルの index-settings 配列に空のエントリを追加するだけで構いません。
'index-settings' => [
Flight::class => []
],
アプリケーションのインデックス設定を構成した後、scout:sync-index-settings Artisan コマンドを実行する必要があります。このコマンドは、現在設定されているインデックス設定を Meilisearch に通知します。便利なため、このコマンドをデプロイプロセスの一部に組み込むことをおすすめします。
php artisan scout:sync-index-settings
#モデルIDの設定
デフォルトでは、Scout はモデルの主キーを検索インデックスに保存されるモデルの一意のID/キーとして使用します。この動作をカスタマイズしたい場合は、モデル上で getScoutKey と getScoutKeyName メソッドをオーバーライドできます。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class User extends Model
{
use Searchable;
/**
* モデルのインデックスに使用される値を取得します。
*/
public function getScoutKey(): mixed
{
return $this->email;
}
/**
* モデルのインデックスに使用されるキー名を取得します。
*/
public function getScoutKeyName(): mixed
{
return 'email';
}
}
#モデルごとの検索エンジン設定
検索時、Scout は通常アプリケーションの scout 設定ファイルで指定されたデフォルトの検索エンジンを使用します。ただし、特定のモデルの検索エンジンはモデル上で searchableUsing メソッドをオーバーライドすることで変更できます。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Engines\Engine;
use Laravel\Scout\EngineManager;
use Laravel\Scout\Searchable;
class User extends Model
{
use Searchable;
/**
* モデルのインデックスに使用されるエンジンを取得します。
*/
public function searchableUsing(): Engine
{
return app(EngineManager::class)->engine('meilisearch');
}
}
#ユーザーの識別
Scout は Algolia 使用時にユーザーを自動識別することも可能です。認証済みユーザーを検索操作に関連付けることで、Algolia のダッシュボードで検索分析を確認する際に役立ちます。アプリケーションの .env ファイルで SCOUT_IDENTIFY 環境変数を true に設定するとユーザー識別を有効化できます。
SCOUT_IDENTIFY=true
この機能を有効にすると、リクエストのIPアドレスと認証済みユーザーの主な識別子も Algolia に渡され、ユーザーによる検索リクエストに関連付けられます。
#データベース/コレクションエンジン
#データベースエンジン
データベースエンジンは現在 MySQL と PostgreSQL のみサポートしています。
アプリケーションが小〜中規模のデータベースを扱う場合や負荷が軽い場合、Scout の「database」エンジンで始めるほうが便利かもしれません。データベースエンジンは既存のデータベースから結果をフィルタリングする際に「where like」句や全文検索インデックスを使用し、クエリに適した検索結果を判定します。
データベースエンジンを使用するには、SCOUT_DRIVER 環境変数を database に設定するか、アプリケーションの scout 設定ファイルで直接 database ドライバーを指定します。
SCOUT_DRIVER=database
データベースエンジンを優先ドライバーとして指定したら、検索可能なデータを設定してください。その後、モデルに対して検索クエリを実行できます。Algolia、Meilisearch、Typesense のインデックスを作成するための検索エンジンのインデックス作成は、データベースエンジン使用時には不要です。
#データベース検索戦略のカスタマイズ
デフォルトでは、データベースエンジンは 検索可能に設定したすべてのモデル属性に対して「where like」クエリを実行します。ただし、場合によってはパフォーマンスが悪化することがあります。そのため、データベースエンジンの検索戦略は、特定のカラムに全文検索クエリを使うか、文字列のプレフィックス(example%)のみを検索する「where like」制約を使うように設定できます(文字列全体の検索 %example% ではなく)。
この動作を定義するには、モデルの toSearchableArray メソッドに PHP 属性を割り当てます。追加の検索戦略が割り当てられていないカラムは、デフォルトの「where like」戦略を使い続けます。
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;
/**
* モデルのインデックス可能なデータ配列を取得します。
*
* @return array<string, mixed>
*/
#[SearchUsingPrefix(['id', 'email'])]
#[SearchUsingFullText(['bio'])]
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'bio' => $this->bio,
];
}
カラムに全文検索クエリ制約を指定する前に、そのカラムに全文検索インデックスが割り当てられていることを確認してください。
#コレクションエンジン
ローカル開発中に Algolia、Meilisearch、Typesense の検索エンジンを自由に使えますが、「collection」エンジンで始めるほうが便利な場合があります。コレクションエンジンは既存のデータベースから結果を「where」句とコレクションフィルタリングで取得し、クエリに適した検索結果を判定します。このエンジンを使う場合、検索可能なモデルを「インデックス」する必要はなく、単にローカルデータベースから取得されます。
コレクションエンジンを使うには、SCOUT_DRIVER 環境変数を collection に設定するか、アプリケーションの scout 設定ファイルで直接 collection ドライバーを指定します。
SCOUT_DRIVER=collection
コレクションドライバーを優先ドライバーとして指定したら、モデルに対して検索クエリを実行できます。Algolia、Meilisearch、Typesense のインデックス作成は、コレクションエンジン使用時には不要です。
#データベースエンジンとの違い
一見すると「database」と「collection」エンジンはかなり似ています。どちらも直接データベースとやり取りして検索結果を取得します。ただし、コレクションエンジンは全文検索インデックスや LIKE 句を使わず、すべての可能なレコードを取得して Laravel の Str::is ヘルパーで検索文字列がモデル属性値に含まれるか判定します。
コレクションエンジンは Laravel がサポートするすべてのリレーショナルデータベース(SQLite や SQL Server を含む)で動作するため最も移植性が高い検索エンジンですが、Scout のデータベースエンジンより効率は劣ります。
#インデックス作成
#バッチインポート
既存プロジェクトに Scout を導入する場合、すでにデータベースにあるレコードをインデックスにインポートする必要があるかもしれません。Scout は scout:import Artisan コマンドを提供しており、これを使って既存のすべてのレコードを検索インデックスにインポートできます。
php artisan scout:import "App\Models\Post"
flush コマンドはモデルのすべてのレコードを検索インデックスから削除するために使えます。
php artisan scout:flush "App\Models\Post"
#インポートクエリの変更
バッチインポートのためにモデルを取得するクエリを変更したい場合は、モデルに makeAllSearchableUsing メソッドを定義できます。ここで、インポート前に必要なリレーションのイーガーロードを追加するのに適しています。
use Illuminate\Database\Eloquent\Builder;
/**
* すべてのモデルを検索可能にする際に使用するクエリを変更します。
*/
protected function makeAllSearchableUsing(Builder $query): Builder
{
return $query->with('author');
}
makeAllSearchableUsing メソッドは、モデルのバッチインポートにキューを使う場合には適用されないことがあります。ジョブでモデルコレクションを処理するとき、リレーションは復元されません。
#レコードの追加
モデルに Laravel\Scout\Searchable トレイトを追加したら、モデルインスタンスを save または create するだけで自動的に検索インデックスに追加されます。Scout を キュー使用に設定している場合、この操作はキューワーカーによってバックグラウンドで実行されます。
use App\Models\Order;
$order = new Order;
// ...
$order->save();
#クエリ経由でのレコード追加
Eloquent クエリを使ってモデルのコレクションを検索インデックスに追加したい場合は、Eloquent クエリに searchable メソッドをチェーンできます。searchable メソッドはクエリの結果をチャンク処理し、レコードを検索インデックスに追加します。Scout をキュー使用に設定している場合、すべてのチャンクはキューワーカーによってバックグラウンドでインポートされます。
use App\Models\Order;
Order::where('price', '>', 100)->searchable();
Eloquent リレーションシップインスタンスに対しても searchable メソッドを呼べます。
$user->orders()->searchable();
または、すでにメモリ上に Eloquent モデルのコレクションがある場合は、コレクションインスタンスに対して searchable メソッドを呼び出し、対応するインデックスにモデルインスタンスを追加できます。
$orders->searchable();
searchable メソッドは「アップサート」操作と考えられます。つまり、モデルレコードがすでにインデックスに存在する場合は更新され、存在しない場合は追加されます。
#レコードの更新
検索可能なモデルを更新するには、モデルインスタンスのプロパティを更新してデータベースに save するだけで構いません。Scout は自動的に検索インデックスへの変更を反映します。
use App\Models\Order;
$order = Order::find(1);
// 注文を更新...
$order->save();
Eloquent クエリインスタンスに対しても searchable メソッドを呼び出してモデルのコレクションを更新できます。モデルが検索インデックスに存在しない場合は作成されます。
Order::where('price', '>', 100)->searchable();
リレーションシップ内のすべてのモデルの検索インデックスレコードを更新したい場合は、リレーションシップインスタンスに対して searchable を呼び出せます。
$user->orders()->searchable();
または、すでにメモリ上にある Eloquent モデルのコレクションに対して searchable メソッドを呼び出し、対応するインデックス内のモデルインスタンスを更新できます。
$orders->searchable();
#インポート前のレコード変更
モデルのコレクションを検索可能にする前に準備が必要な場合があります。例えば、リレーションをイーガーロードしてリレーションデータを効率的に検索インデックスに追加したい場合です。これを実現するには、該当モデルに makeSearchableUsing メソッドを定義します。
use Illuminate\Database\Eloquent\Collection;
/**
* 検索可能にするモデルのコレクションを変更します。
*/
public function makeSearchableUsing(Collection $models): Collection
{
return $models->load('author');
}
#レコードの削除
インデックスからレコードを削除するには、単にモデルをデータベースから delete すればよいです。これは ソフトデリート モデルを使っている場合でも可能です。
use App\Models\Order;
$order = Order::find(1);
$order->delete();
モデルを取得せずにレコードを削除したい場合は、Eloquent クエリインスタンスの unsearchable メソッドを使えます。
Order::where('price', '>', 100)->unsearchable();
リレーションシップ内のすべてのモデルの検索インデックスレコードを削除したい場合は、リレーションシップインスタンスに対して unsearchable を呼び出せます。
$user->orders()->unsearchable();
あるいは、すでにメモリ内に Eloquent モデルのコレクションがある場合は、コレクションインスタンスの unsearchable メソッドを呼び出して、対応するインデックスからモデルインスタンスを削除できます。
$orders->unsearchable();
#インデックスの一時停止
モデルの一連の Eloquent 操作を行う際に、モデルデータを検索インデックスに同期させたくない場合があります。その場合は withoutSyncingToSearch メソッドを使えます。このメソッドはクロージャを1つ受け取り、即座に実行します。クロージャ内で行われたモデル操作はインデックスに同期されません。
use App\Models\Order;
Order::withoutSyncingToSearch(function () {
// モデル操作を実行...
});
#条件付きで検索可能なモデルインスタンス
モデルを特定の条件下でのみ検索可能にしたい場合があります。例えば、App\Models\Post モデルが「下書き」と「公開済み」の2つの状態を持つとします。「公開済み」の投稿のみ検索可能にしたい場合、モデルに shouldBeSearchable メソッドを定義できます。
/**
* モデルが検索可能かどうかを判定します。
*/
public function shouldBeSearchable(): bool
{
return $this->isPublished();
}
shouldBeSearchable メソッドは、save および create メソッド、クエリ、またはリレーションを通じてモデルを操作する場合にのみ適用されます。searchable メソッドでモデルやコレクションを直接検索可能にすると、shouldBeSearchable メソッドの結果が上書きされます。
shouldBeSearchable メソッドは Scout の「database」エンジン使用時には適用されません。なぜなら、検索可能なデータは常にデータベースに保存されるためです。データベースエンジンで同様の動作を実現したい場合は、where句を使ってください。
#検索
モデルの検索は search メソッドから開始できます。search メソッドは、モデルを検索するために使用される単一の文字列を受け取ります。続けて検索クエリに get メソッドをチェーンすることで、指定した検索クエリに一致する Eloquent モデルを取得します:
use App\Models\Order;
$orders = Order::search('Star Trek')->get();
Scout の検索結果は Eloquent モデルのコレクションなので、ルートやコントローラーから直接返すと自動的に JSON に変換されます。
use App\Models\Order;
use Illuminate\Http\Request;
Route::get('/search', function (Request $request) {
return Order::search($request->search)->get();
});
Eloquent モデルに変換される前の生の検索結果を取得したい場合は、raw メソッドを使えます。
$orders = Order::search('Star Trek')->raw();
#カスタムインデックス
検索クエリは通常、モデルの searchableAs メソッドで指定されたインデックスに対して実行されます。ただし、within メソッドを使うと、検索対象のカスタムインデックスを指定できます。
$orders = Order::search('Star Trek')
->within('tv_shows_popularity_desc')
->get();
#where句
Scout では検索クエリにシンプルな「where」句を追加できます。現在は基本的な数値の等価チェックのみサポートしており、主に所有者IDで検索範囲を絞るのに便利です。
use App\Models\Order;
$orders = Order::search('Star Trek')->where('user_id', 1)->get();
さらに、whereIn メソッドを使うと、指定したカラムの値が与えられた配列に含まれているかを確認できます。
$orders = Order::search('Star Trek')->whereIn(
'status', ['open', 'paid']
)->get();
whereNotIn メソッドは、指定したカラムの値が与えられた配列に含まれていないことを確認します。
$orders = Order::search('Star Trek')->whereNotIn(
'status', ['closed']
)->get();
検索インデックスはリレーショナルデータベースではないため、より高度な「where」句は現在サポートされていません。
アプリケーションで Meilisearch を使う場合は、Scout の「where」句を利用する前に、フィルタ可能な属性を設定する必要があります。
#ペジネーション
モデルのコレクションを取得するだけでなく、paginate メソッドで検索結果をページネーションできます。このメソッドは、通常の Eloquent クエリをページネーションした場合と同様に、Illuminate\Pagination\LengthAwarePaginator インスタンスを返します。
use App\Models\Order;
$orders = Order::search('Star Trek')->paginate();
paginate メソッドの第1引数に1ページあたりの取得件数を指定できます。
$orders = Order::search('Star Trek')->paginate(15);
結果を取得したら、通常の Eloquent クエリのように Blade で結果を表示し、ページリンクをレンダリングできます。
<div class="container">
@foreach ($orders as $order)
{{ $order->price }}
@endforeach
</div>
{{ $orders->links() }}
もちろん、ページネーション結果を JSON で取得したい場合は、ルートやコントローラーからページネーターインスタンスを直接返せます。
use App\Models\Order;
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
return Order::search($request->input('query'))->paginate(15);
});
検索エンジンは Eloquent モデルのグローバルスコープ定義を認識しないため、Scout のページネーションを使うアプリケーションではグローバルスコープを利用しないか、Scout 検索時にグローバルスコープの制約を再現してください。
#ソフトデリート
インデックスされたモデルがソフトデリートされていて、ソフトデリートされたモデルも検索したい場合は、config/scout.php の soft_delete オプションを true に設定してください。
'soft_delete' => true,
この設定が true の場合、Scout はソフトデリートされたモデルを検索インデックスから削除しません。代わりに、インデックスされたレコードに隠し属性 __soft_deleted を設定します。その後、withTrashed または onlyTrashed メソッドを使ってソフトデリートされたレコードを検索できます。
use App\Models\Order;
// 削除済みレコードも含めて結果を取得...
$orders = Order::search('Star Trek')->withTrashed()->get();
// 削除済みレコードのみを取得...
$orders = Order::search('Star Trek')->onlyTrashed()->get();
ソフトデリートされたモデルを forceDelete で完全に削除すると、Scout は自動的に検索インデックスからも削除します。
#エンジン検索のカスタマイズ
検索エンジンの検索動作を高度にカスタマイズしたい場合は、search メソッドの第2引数にクロージャを渡せます。例えば、このコールバックで検索オプションにジオロケーションデータを追加してから Algolia に検索クエリを渡せます。
use Algolia\AlgoliaSearch\SearchIndex;
use App\Models\Order;
Order::search(
'Star Trek',
function (SearchIndex $algolia, string $query, array $options) {
$options['body']['query']['bool']['filter']['geo_distance'] = [
'distance' => '1000km',
'location' => ['lat' => 36, 'lon' => 111],
];
return $algolia->search($query, $options);
}
)->get();
#Eloquent 結果クエリのカスタマイズ
Scout が検索エンジンからマッチする Eloquent モデルのリストを取得した後、Eloquent は主キーで該当モデルを取得します。このクエリは query メソッドでカスタマイズできます。query メソッドは Eloquent クエリビルダーインスタンスを引数に取るクロージャを受け取ります。
use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;
$orders = Order::search('Star Trek')
->query(fn (Builder $query) => $query->with('invoices'))
->get();
このコールバックは関連するモデルがすでにアプリケーションの検索エンジンから取得された後に呼び出されるため、query メソッドを結果の「フィルタリング」に使うべきではありません。代わりに Scout の where 句 を使用してください。
#カスタムエンジン
#エンジンの作成
組み込みの Scout 検索エンジンが要件に合わない場合は、独自のカスタムエンジンを作成して Scout に登録できます。エンジンは Laravel\Scout\Engines\Engine 抽象クラスを継承すべきです。この抽象クラスには、カスタムエンジンで実装が必須の8つのメソッドがあります。
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 mapIds($results);
abstract public function map(Builder $builder, $results, $model);
abstract public function getTotalCount($results);
abstract public function flush($model);
これらのメソッドの実装例は Laravel\Scout\Engines\AlgoliaEngine クラスで確認できます。このクラスは各メソッドの実装方法を学ぶ良い出発点になります。
#エンジンの登録
カスタムエンジンを作成したら、Scoutのエンジンマネージャーのextendメソッドを使ってScoutに登録できます。ScoutのエンジンマネージャーはLaravelのサービスコンテナから解決できます。extendメソッドはApp\Providers\AppServiceProviderクラスのbootメソッド、またはアプリケーションで使用している他のサービスプロバイダから呼び出してください:
use App\ScoutExtensions\MySqlSearchEngine;
use Laravel\Scout\EngineManager;
/**
* アプリケーションサービスの初期化。
*/
public function boot(): void
{
resolve(EngineManager::class)->extend('mysql', function () {
return new MySqlSearchEngine;
});
}
エンジンを登録したら、アプリケーションの config/scout.php 設定ファイルでデフォルトの Scout driver として指定できます。
'driver' => 'mysql',