Laravel Scout
はじめに
Laravel Scout は、Eloquentモデルに全文検索を追加するためのシンプルでドライバーベースのソリューションを提供します。モデルオブザーバーを使用すると、Scout は自動的に検索インデックスを Eloquent レコードと同期させます。
現在、Scout には Algolia、Meilisearch、Typesense、および MySQL / PostgreSQL (database
) ドライバーが含まれています。さらに、Scout には外部依存関係やサードパーティーサービスを必要としないローカル開発用途に設計された "collection" ドライバーも含まれています。さらに、カスタムドライバーの作成は簡単であり、独自の検索実装で 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を使用するために厳密に必要とされるわけではありませんが、ライブラリを使用する前にキュードライバーを設定することを強くお勧めします。キューワーカーを実行すると、Scoutがモデル情報を検索インデックスに同期するすべての操作をキューに入れることができ、アプリケーションのWebインターフェースの応答時間が大幅に向上します。
キュードライバーを設定したら、config/scout.php
設定ファイル内のqueue
オプションの値をtrue
に設定してください:
'queue' => true,
queue
オプションがfalse
に設定されている場合でも、AlgoliaやMeilisearchなどの一部のScoutドライバーは常にレコードを非同期でインデックス化します。つまり、Laravelアプリケーション内でインデックス操作が完了しても、検索エンジン自体が新しいレコードや更新されたレコードをすぐに反映しない場合があります。
Scoutジョブが利用する接続とキューを指定するには、queue
構成オプションを配列として定義できます:
'queue' => [
'connection' => 'redis',
'queue' => 'scout'
],
もちろん、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 は、驚くほど高速でオープンソースの検索エンジンです。Meilisearch をローカルマシンにインストールする方法がわからない場合は、Laravel Sail を使用することができます。Laravel Sail は、Laravel の公式にサポートされている Docker 開発環境です。
Meilisearch ドライバを使用する場合は、Composer パッケージマネージャを介して Meilisearch PHP SDK をインストールする必要があります:
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
次に、アプリケーションの .env
ファイル内で SCOUT_DRIVER
環境変数、および Meilisearch の host
と key
認証情報を設定してください:
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 をセルフホストするか、Typesense Cloud を使用することができます。
Scout と一緒に Typesense を使用するために、Composer パッケージマネージャを介して Typesense PHP SDK をインストールしてください:
composer require typesense/typesense-php
次に、アプリケーションの .env
ファイル内で SCOUT_DRIVER
環境変数、および Typesense のホストと API キー認証情報を設定してください:
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost
必要に応じて、インストールのポート、パス、およびプロトコルを指定することもできます:
TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http
Typesenseコレクションの追加の設定とスキーマ定義は、アプリケーションの config/scout.php
構成ファイル内で見つけることができます。Typesenseに関する詳細情報については、Typesense documentation を参照してください。
Typesenseでのデータの準備
Typesenseを利用する際には、検索可能なモデルは、モデルのプライマリキーを文字列にキャストし、作成日時をUNIXタイムスタンプに変換する toSearchableArray
メソッドを定義する必要があります:
/**
* Get the indexable data array for the model.
*
* @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 documentation を参照してください。
定義した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;
/**
* Get the name of the index associated with the model.
*/
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;
/**
* Get the indexable data array for the model.
*
* @return array<string, mixed>
*/
public function toSearchableArray(): array
{
$array = $this->toArray();
// Customize the data array...
return $array;
}
}
Meilisearchなどの一部の検索エンジンは、正しい型のデータに対してのみフィルタ操作(>
, <
など)を実行します。これらの検索エンジンを使用し、検索可能データをカスタマイズする場合は、数値値が正しい型にキャストされていることを確認する必要があります。
public function toSearchableArray()
{
return [
'id' => (int) $this->id,
'name' => $this->name,
'price' => (float) $this->price,
];
}
フィルタ可能データとインデックス設定の設定(Meilisearch)
Scoutの他のドライバとは異なり、Meilisearchでは、フィルタ可能な属性、ソート可能な属性、およびその他のサポートされている設定フィールドなどのインデックス検索設定を事前に定義する必要があります。
フィルタ可能な属性は、Scoutのwhere
メソッドを呼び出す際にフィルタリングする属性であり、ソート可能な属性は、Scoutの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'],
// Other settings fields...
],
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;
/**
* Get the value used to index the model.
*/
public function getScoutKey(): mixed
{
return $this->email;
}
/**
* Get the key name used to index the model.
*/
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;
/**
* Get the engine used to index the model.
*/
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の「データベース」エンジンを使用すると便利かもしれません。データベースエンジンは、既存のデータベースから結果をフィルタリングする際に「where like」句と全文検索インデックスを使用し、クエリに適用される検索結果を決定します。
データベースエンジンを使用するには、単純にSCOUT_DRIVER
環境変数の値をdatabase
に設定するか、アプリケーションのscout
構成ファイルで直接database
ドライバを指定します:
SCOUT_DRIVER=database
好きなドライバとしてデータベースエンジンを指定した後は、検索可能なデータを構成する必要があります。その後、モデルに対して検索クエリを実行することができます。Algolia、Meilisearch、またはTypesenseのインデックスをシードするために必要なインデックス作成などの検索エンジンインデックス作成は、データベースエンジンを使用する際には不要です。
データベース検索戦略のカスタマイズ
デフォルトでは、データベースエンジンは、検索可能として構成したすべてのモデル属性に対して「where like」クエリを実行します。ただし、一部の状況では、これがパフォーマンスの低下につながる可能性があります。そのため、指定した列の一部がフルテキスト検索クエリを使用するか、文字列のプレフィックスを検索するために「where like」制約のみを使用するようにデータベースエンジンの検索戦略を構成できます。
この動作を定義するには、モデルのtoSearchableArray
メソッドにPHP属性を割り当てることができます。追加の検索戦略動作が割り当てられていない列は引き続きデフォルトの「where like」戦略を使用します:
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;
/**
* Get the indexable data array for the model.
*
* @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検索エンジンを使用することは自由ですが、「コレクション」エンジンを使用すると便利かもしれません。コレクションエンジンは、既存のデータベースからの結果に対して「where」句とコレクションフィルタリングを使用して、クエリに適用可能な検索結果を決定します。このエンジンを使用する場合、検索可能なモデルを「インデックス化」する必要はありません。それらは単にローカルデータベースから取得されます。
コレクションエンジンを使用するには、SCOUT_DRIVER
環境変数の値をcollection
に設定するか、アプリケーションのscout
構成ファイルで直接collection
ドライバーを指定します:
SCOUT_DRIVER=collection
お好みのドライバーとしてコレクションエンジンを指定した後は、モデルに対して検索クエリを実行することができます。Algolia、Meilisearch、またはTypesenseのインデックスをシードするために必要なインデックス作成など、コレクションエンジンを使用する際には不要です。
データベースエンジンとの違い
一見すると、「データベース」と「コレクション」エンジンはかなり似ています。どちらもデータベースと直接やり取りして検索結果を取得します。ただし、コレクションエンジンは、完全なテキストインデックスやLIKE
句を使用して一致するレコードを見つけることはありません。代わりに、すべての可能なレコードを取得し、検索文字列がモデル属性値内に存在するかどうかを判断するためにLaravelのStr::is
ヘルパーを使用します。
コレクションエンジンは、Laravelでサポートされているすべてのリレーショナルデータベース(SQLiteやSQL Serverを含む)で動作するため、最もポータブルな検索エンジンです。ただし、Scoutのデータベースエンジンよりも効率が低いです。
インデックス作成
一括インポート
既存のプロジェクトにScoutをインストールしている場合、インデックスにインポートする必要があるデータベースレコードがすでにあるかもしれません。Scoutには、すべての既存レコードを検索インデックスにインポートするために使用できるscout:import
アーティザンコマンドが用意されています:
php artisan scout:import "App\Models\Post"
flush
コマンドを使用して、モデルのすべてのレコードを検索インデックスから削除することができます:
php artisan scout:flush "App\Models\Post"
インポートクエリの変更
一括インポートのためにすべてのモデルを取得するために使用されるクエリを変更したい場合は、モデルにmakeAllSearchableUsing
メソッドを定義することができます。これは、モデルをインポートする前に必要なイーガーリレーションシップの読み込みなどを追加するのに適した場所です。
use Illuminate\Database\Eloquent\Builder;
/**
* Modify the query used to retrieve models when making all of the models searchable.
*/
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
メソッドは「upsert」操作と見なすことができます。つまり、モデルレコードがすでにインデックスに存在する場合、更新されます。検索インデックスに存在しない場合は、インデックスに追加されます。
レコードの更新
検索可能なモデルを更新するには、モデルインスタンスのプロパティを更新し、モデルをデータベースに save
するだけです。Scout は変更を自動的に検索インデックスに保存します:
use App\Models\Order;
$order = Order::find(1);
// Update the order...
$order->save();
また、Eloquent クエリインスタンスに searchable
メソッドを呼び出して、モデルのコレクションを更新することもできます。モデルが検索インデックスに存在しない場合、作成されます:
Order::where('price', '>', 100)->searchable();
関連するすべてのモデルの検索インデックスレコードを更新する場合は、関連インスタンスで searchable
を呼び出すことができます:
$user->orders()->searchable();
または、メモリ内に既に Eloquent モデルのコレクションがある場合は、コレクションインスタンスで searchable
メソッドを呼び出して、対応するインデックス内のモデルインスタンスを更新できます:
$orders->searchable();
インポート前にレコードを修正する
時には、検索可能になる前にモデルのコレクションを準備する必要がある場合があります。たとえば、リレーションシップを一括読み込みして、リレーションシップデータを効率的に検索インデックスに追加したい場合があります。これを実現するには、対応するモデルに makeSearchableUsing
メソッドを定義します:
use Illuminate\Database\Eloquent\Collection;
/**
* Modify the collection of models being made searchable.
*/
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();
対応するインデックスからすべてのモデルレコードを削除するには、removeAllFromSearch
メソッドを呼び出します:
Order::removeAllFromSearch();
インデックスの一時停止
時には、モデルの一括 Eloquent 操作を実行する際に、モデルデータを検索インデックスに同期させずに行う必要がある場合があります。これは、withoutSyncingToSearch
メソッドを使用して行います。このメソッドは、即座に実行される単一のクロージャを受け入れます。クロージャ内で発生するすべてのモデル操作は、モデルのインデックスに同期されません:
use App\Models\Order;
Order::withoutSyncingToSearch(function () {
// Perform model actions...
});
条件付き検索可能なモデルインスタンス
時には、特定の条件の下でのみモデルを検索可能にする必要があります。例えば、App\Models\Post
モデルが "draft" と "published" の2つの状態のいずれかにあると想像してみてください。"published" の投稿のみを検索可能にしたい場合があります。これを実現するために、モデルに shouldBeSearchable
メソッドを定義することができます:
/**
* Determine if the model should be searchable.
*/
public function shouldBeSearchable(): bool
{
return $this->isPublished();
}
shouldBeSearchable
メソッドは、save
メソッドや create
メソッド、クエリ、またはリレーションシップを介してモデルを操作する場合にのみ適用されます。searchable
メソッドを使用してモデルやコレクションを直接検索可能にすると、shouldBeSearchable
メソッドの結果が上書きされます。
shouldBeSearchable
メソッドは、Scoutの "database" エンジンを使用する場合には適用されません。なぜなら、すべての検索可能なデータは常にデータベースに保存されるからです。データベースエンジンを使用する場合に同様の動作を実現するには、代わりに where clauses を使用する必要があります。
検索
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 Clauses
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」句を利用する前に、アプリケーションのフィルタ可能な属性を構成する必要があります。
Pagination
モデルのコレクションを取得するだけでなく、paginate
メソッドを使用して検索結果をページ分割することもできます。このメソッドは、従来のEloquentクエリをページ分割した場合と同様に、Illuminate\Pagination\LengthAwarePaginator
インスタンスを返します。
use App\Models\Order;
$orders = Order::search('Star Trek')->paginate();
paginate
メソッドに最初の引数としてページごとに取得するモデルの数を渡すことで、ページごとに取得するモデルの数を指定できます。
$orders = Order::search('Star Trek')->paginate(15);
結果を取得したら、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;
// Include trashed records when retrieving results...
$orders = Order::search('Star Trek')->withTrashed()->get();
// Only include trashed records when retrieving results...
$orders = Order::search('Star Trek')->onlyTrashed()->get();
forceDelete
を使用してソフト削除されたモデルが完全に削除されると、Scoutはそれを検索インデックスから自動的に削除します。
エンジン検索のカスタマイズ
もし、エンジンの検索動作を高度にカスタマイズする必要がある場合は、search
メソッドの第二引数としてクロージャを渡すことができます。たとえば、このコールバックを使用して、検索クエリが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 を使用してそのエンジンを登録できます。Scout のエンジンマネージャは、Laravel のサービスコンテナから解決できます。App\Providers\AppServiceProvider
クラスの boot
メソッドやアプリケーションで使用される他のサービスプロバイダから extend
メソッドを呼び出す必要があります。
use App\ScoutExtensions\MySqlSearchEngine;
use Laravel\Scout\EngineManager;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
resolve(EngineManager::class)->extend('mysql', function () {
return new MySqlSearchEngine;
});
}
エンジンを登録したら、アプリケーションの config/scout.php
構成ファイルで、それをデフォルトの Scout driver
として指定できます。
'driver' => 'mysql',