メインコンテンツまでスキップ
バージョン: 13.x

Laravel Scout

Introduction​

Laravel Scout は、Eloquent models に全文検索を追加するための、シンプルなドライバベースのソリューションを提供します。モデルオブザーバを使用すると、Scout が検索インデックスと Eloquent のレコードを自動的に同期します。

Scout には、MySQL / PostgreSQL の全文検索インデックスと LIKE 句を使って既存のデータベースを検索する、組み込みの database エンジンが用意されています。外部サービスは必要ありません。ほとんどのアプリケーションでは、これだけで十分です。Laravel で利用できる検索オプションの概要については、search documentationを参照してください。

Scout には、非常に大規模な環境でのタイプミス耐性、ファセットフィルタリング、ベクトル検索、地理検索などの機能が必要な場合に利用できる Algolia、Meilisearch、Typesense、Turbopuffer 用のドライバも含まれています。ローカル開発用には「コレクション」ドライバも利用でき、custom engines を自由に作成することもできます。

Installation​

まず、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;
}

Queueing​

database エンジンまたは collection エンジン以外のエンジンを使用する場合は、ライブラリを使用する前に queue driver を設定することを強くおすすめします。キューワーカーを実行すると、モデルの情報を検索インデックスに同期するすべての処理を Scout のキューに入れられるため、アプリケーションのWebインターフェイスのレスポンスタイムを大幅に改善できます。

キュードライバを構成したら、config/scout.php 構成ファイルの queue オプションの値を true に設定します。

'queue' => true,

queue オプションが false に設定されている場合でも、Algolia や Meil​​isearch などの一部の Scout ドライバは常に非同期でレコードのインデックスを作成することに留意することが重要です。言い換えれば、Laravel アプリケーション内でインデックス操作が完了したとしても、検索エンジン自体には新しいレコードや更新されたレコードがすぐに反映されない可能性があります。

Scout ジョブが使用する接続とキューを指定するには、queue 構成オプションを配列として定義できます。

'queue' => [
'connection' => 'redis',
'queue' => 'scout'
],

もちろん、Scout ジョブが使用する接続とキューをカスタマイズする場合は、キューワーカーを実行して、その接続とキューでジョブを処理する必要があります。

php artisan queue:work redis --queue=scout

Unique Jobs​

書き込みの多いアプリケーションでは、同じモデルレコードに対して Scout が重複したジョブをキューに入れるのを防ぎたい場合があります。MakeSearchableUniquely と RemoveFromSearchUniquely のジョブクラスを、通常はサービスプロバイダの boot メソッド内で登録することで、ユニークなインデックス作成ジョブを有効にできます。

use Laravel\Scout\Jobs\MakeSearchableUniquely;
use Laravel\Scout\Jobs\RemoveFromSearchUniquely;
use Laravel\Scout\Scout;

Scout::makeSearchableUsing(MakeSearchableUniquely::class);
Scout::removeFromSearchUsing(RemoveFromSearchUniquely::class);

これらのジョブは Laravel の unique job locks を使用し、対応するジョブがすでにキューに入っている間、同じ検索可能なモデルレコードに対する重複したインデックス作成操作がディスパッチされるのを防ぎます。

Driver Prerequisites​

Algolia​

Algolia ドライバを使用する場合は、config/scout.php 構成ファイルで Algolia id および secret 資格情報を構成する必要があります。認証情報を設定したら、Composer パッケージ マネージャーを介して Algolia PHP SDK をインストールする必要もあります。

composer require algolia/algoliasearch-client-php

Meilisearch​

Meilisearch は、全文検索、セマンティック検索、ハイブリッド検索に対応した高速なオープンソースの検索エンジンです。Meilisearch をローカルマシンにインストールする方法がわからない場合は、Laravel が公式にサポートする Docker 開発環境の Laravel Sail を利用できます。本番環境では、Meilisearch をself-hostするか、Meilisearch Cloudを利用できます。

Meilisearch ドライバを使用する場合は、Composer パッケージ マネージャーを介して Meil​​isearch PHP SDK をインストールする必要があります。

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle

次に、アプリケーションの .env ファイル内で、SCOUT_DRIVER 環境変数と Meil​​isearch host および key 資格情報を設定します。

SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey

Meilisearch の詳細については、Meilisearch documentationを参照してください。

Typesense​

Typesense は超高速のオープンソース検索エンジンで、キーワード検索、セマンティック検索、地理検索、ベクトル検索をサポートしています。

Typesense を self-host するか、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

Laravel Sail を使用している場合は、Docker コンテナ名に合わせて TYPESENSE_HOST 環境変数を調整する必要がある場合があります。また、インストール時のポート、パス、プロトコルを指定することもできます。

TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http

Typesense コレクションの追加の設定とスキーマ定義は、アプリケーションの config/scout.php 構成ファイル内にあります。 Typesense の詳細については、Typesense documentation を参照してください。

Turbopuffer​

Turbopuffer は、全文検索、セマンティック検索、ハイブリッド検索をサポートする検索エンジンです。Turbopuffer ドライバを使用するには、SCOUT_DRIVER 環境変数を設定し、Turbopuffer の API キーを指定してください。

SCOUT_DRIVER=turbopuffer
TURBOPUFFER_API_KEY=tpuf_...
TURBOPUFFER_REGION=gcp-us-central1

TURBOPUFFER_REGION 環境変数は省略可能で、デフォルト値は gcp-us-central1 です。

Configuration​

Configuring Searchable Data​

デフォルトでは、特定のモデルの 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;
}
}

Configuring Model Engines​

検索する場合、Scout は通常、アプリケーションの scout 構成ファイルで指定されたデフォルトの検索エンジンを使用します。ただし、特定のモデルの検索エンジンは、モデルの searchableUsing メソッドをオーバーライドすることで変更できます。

<?php

namespace App\Models;

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

class User extends Model
{
use Searchable;

/**
* Get the engine used to index the model.
*/
public function searchableUsing(): Engine
{
return Scout::engine('meilisearch');
}
}

Database / Collection Engines​

Database Engine​

データベースエンジンが現在サポートしているのは MySQL と PostgreSQL です。どちらも高速な全文カラムインデックスに対応しています。

database エンジンは、MySQL / PostgreSQL フルテキスト インデックスと LIKE 句を使用して、既存のデータベースを直接検索します。多くのアプリケーションにとって、これは検索を追加する最も簡単で実用的な方法であり、外部サービスや追加のインフラストラクチャは必要ありません。

データベース エンジンを使用するには、SCOUT_DRIVER 環境変数を database に設定します。

SCOUT_DRIVER=database

構成が完了したら、define your searchable data を実行し、モデルに対して executing search queries を開始できます。サードパーティ エンジンとは異なり、データベース エンジンは個別のインデックス作成手順を必要とせず、データベース テーブルを直接検索します。

データベースエンジンは、pgvector 拡張機能を使用した PostgreSQL で、セマンティック検索とハイブリッド検索をサポートしています。まず、モデルのテーブルに NULL を許容するベクターカラムと全文検索インデックスを追加します。モデルが保存された後に Scout が埋め込みを保存するため、ベクターカラムは NULL を許容しなければなりません。

Schema::ensureVectorExtensionExists();

Schema::table('articles', function (Blueprint $table) {
// ...

$table->vector('embedding', dimensions: 1536)->nullable();
$table->vectorIndex('embedding');
$table->fullText(['title', 'body']);
});

次に、モデルに toSearchableEmbedding メソッドを定義します。このメソッドは、Scoutが埋め込むソーステキスト、または事前計算した埋め込み配列を返します。Scoutはデフォルトで埋め込みを embedding カラムに保存します。別のカラムを使用するには、モデルに searchableEmbeddingColumn メソッドを定義してください。

Customizing Database Searching Strategies​

デフォルトでは、データベース エンジンは、検索可能に設定したすべてのモデル属性(configured as searchable)に対して LIKE クエリを実行します。ただし、より効率的な検索戦略を特定の列に割り当てることができます。 SearchUsingFullText 属性はその列に対してデータベースのフルテキスト インデックスを使用しますが、SearchUsingPrefix は文字列全体 (%example%) 内を検索するのではなく、文字列の先頭 (example%) のみに一致します。

この動作を定義するには、モデルの toSearchableArray メソッドに PHP 属性を割り当てます。属性のない列は、引き続きデフォルトの 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,
];
}

カラムに全文検索クエリ制約を指定する前に、そのカラムに full text index が設定されていることを確認してください。

Collection Engine​

「コレクション」エンジンは、迅速なプロトタイプ、非常に小さなデータセット (数百レコード)、またはテストの実行を目的としています。データベースからすべての可能なレコードを取得し、Laravel の Str::is ヘルパを使用してそれらを PHP でフィルタリングするため、インデックス作成やデータベース固有の機能は必要ありません。些細な使用例を超える場合は、代わりに database engine を使用する必要があります。

コレクションエンジンを使用するには、単に SCOUT_DRIVER 環境変数の値を collection に設定するか、アプリケーションの scout 構成ファイルで collection ドライバを直接指定します。

SCOUT_DRIVER=collection

コレクション ドライバを優先ドライバとして指定したら、モデルに対して executing search queries を開始できます。コレクション エンジンを使用する場合、Algolia、Meilisearch、または Typesense インデックスのシードに必要なインデックス作成などの検索エンジンのインデックス作成は不要です。

Differences From Database Engine​

データベース エンジンはフルテキスト インデックスと LIKE 句を使用して一致するレコードを効率的に検索しますが、コレクション エンジンはすべてのレコードを取得し、PHP でフィルタリングします。コレクション エンジンは、Laravel でサポートされるすべてのリレーショナル データベース (SQLite および SQL Server を含む) で動作するため、最も移植性の高いオプションです。ただし、データベース エンジンよりも効率が大幅に低いため、大規模なデータセットでは使用しないでください。

Third-Party Engine Configuration​

次の構成オプションは、Algolia、Meilisearch、Typesense などのサードパーティの検索エンジンを使用する場合にのみ関係します。 database engine を使用している場合は、このセクションをスキップしてください。

Configuring Model Indexes​

サードパーティ エンジンを使用する場合、各 Eloquent モデルは、そのモデルの検索可能なすべてのレコードを含む特定の検索「インデックス」と同期されます。デフォルトでは、各モデルは、モデルの一般的な「テーブル」名に一致するインデックスに保存されます。通常、これはモデル名の複数形です。ただし、モデルの 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';
}
}

searchableAs メソッドはデータベースエンジンを使用する場合、効果がありません。データベースエンジンは常にモデルのデータベーステーブルを直接検索します。

Configuring the Model 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';
}
}

データベースエンジンを使用する場合、getScoutKey と getScoutKeyName メソッドは効果がありません。データベースエンジンでは常にモデルの主キーが使用されます。

Algolia​

Index Settings​

Algolia インデックスに追加の設定を構成することが必要になる場合があります。これらの設定は Algolia UI を介して管理できますが、場合によっては、アプリケーションの config/scout.php 構成ファイルから直接、インデックス構成の望ましい状態を管理する方が効率的です。

このアプローチにより、アプリケーションの自動展開パイプラインを通じてこれらの設定を展開できるため、手動による構成が回避され、複数の環境間での一貫性が確保されます。フィルター可能な属性、ランキング、ファセット、または any other supported settings を構成できます。

まず、アプリケーションの config/scout.php 構成ファイルに各インデックスの設定を追加します。

use App\Models\User;
use App\Models\Flight;

'algolia' => [
'id' => env('ALGOLIA_APP_ID', ''),
'secret' => env('ALGOLIA_SECRET', ''),
'index-settings' => [
User::class => [
'searchableAttributes' => ['id', 'name', 'email'],
'attributesForFaceting'=> ['filterOnly(email)'],
// Other settings fields...
],
Flight::class => [
'searchableAttributes'=> ['id', 'destination'],
],
],
],

特定のインデックスの基礎となるモデルが論理的に削除可能で、index-settings 配列に含まれている場合、Scout はそのインデックス上の論理的に削除されたモデルのファセットのサポートを自動的に組み込みます。ソフト削除可能なモデル インデックスに対して定義する他のファセット属性がない場合は、そのモデルの index-settings 配列に空のエントリを追加するだけで済みます。

'index-settings' => [
Flight::class => []
],

アプリケーションのインデックス設定を構成した後、scout:sync-index-settings Artisan コマンドを呼び出す必要があります。このコマンドは、現在構成されているインデックス設定を Algolia に通知します。便宜上、このコマンドを展開プロセスの一部にするとよいでしょう。

php artisan scout:sync-index-settings

Identifying Users​

Scout を使用すると、Algolia の使用時にユーザーを自動識別できます。認証されたユーザーを検索操作に関連付けると、Algolia のダッシュボード内で検索分析を表示するときに役立つ場合があります。ユーザー識別を有効にするには、アプリケーションの .env ファイルで SCOUT_IDENTIFY 環境変数を true として定義します。

SCOUT_IDENTIFY=true

この機能を有効にすると、リクエストの IP アドレスと認証されたユーザーのプライマリ識別子も Algolia に渡されるため、このデータはユーザーが行った検索リクエストに関連付けられます。

Meilisearch​

Index Settings​

Meilisearch では、フィルター可能な属性、並べ替え可能な属性、other supported settings fields などのインデックス検索設定を事前に定義する必要があります。

フィルター可能な属性は、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

Meilisearch でセマンティック検索またはハイブリッド検索を使用するには、インデックス設定でエンベッダを構成し、検索可能な各モデルの埋め込み設定を構成します。

'meilisearch' => [
// ...
'index-settings' => [
Article::class => [
'embedders' => [
'default' => [
'source' => 'userProvided',
'dimensions' => 1536,
],
],
],
],
'model-settings' => [
Article::class => [
'embedding' => [
'embedder' => 'default',
'dimensions' => 1536,
],
],
],
],

モデルの toSearchableEmbedding メソッドは、Scout が Laravel AI SDK を使って埋め込むソーステキスト、または事前計算済みの埋め込み配列を返せます。設定を更新したら、scout:sync-index-settings コマンドを実行してください。

また、埋め込みの driver に meilisearch を設定して、Meilisearch のネイティブ埋め込みを使用することもできます。このモードでは、Meilisearch が設定済みの embedder を使ってドキュメントとクエリの埋め込みを生成するため、dimensions オプションと toSearchableEmbedding メソッドは必要ありません。

'meilisearch' => [
'index-settings' => [
Article::class => [
'embedders' => [
'default' => [
'source' => 'openAi',
'apiKey' => env('OPENAI_API_KEY'),
'model' => 'text-embedding-3-small',
'documentTemplate' => 'An article titled {{ doc.title }}: {{ doc.body }}',
],
],
],
],
'model-settings' => [
Article::class => [
'embedding' => [
'embedder' => 'default',
'driver' => 'meilisearch',
],
],
],
],

ネイティブの埋め込みを使用する場合、Scout はインデックス対象のドキュメントにベクトルを生成・追加しません。ただし、検索オプションの vector を使って、事前に計算したクエリベクトルを指定できます。他のインデックス設定と同様に、埋め込みモデルの設定を更新した後は scout:sync-index-settings コマンドを実行してください。

利用可能な埋め込みソースとそのオプションについて詳しくは、Meilisearch hybrid search documentationを参照してください。

Searchable Data Types​

Meilisearch は、正しい型のデータに対してのみフィルター操作 (>、< など) を実行します。検索可能なデータをカスタマイズするときは、数値が正しい型にcastされていることを確認する必要があります。

public function toSearchableArray()
{
return [
'id' => (int) $this->id,
'name' => $this->name,
'price' => (float) $this->price,
];
}

Typesense​

Preparing Searchable Data​

Typesense を利用する場合、検索可能なモデルは、モデルの主キーを文字列にcastし、作成日を UNIX タイムスタンプにcastする toSearchableArray メソッドを定義する必要があります。

/**
* Get the indexable data array for the model.
*
* @return array<string, mixed>
*/
public function toSearchableArray(): array
{
return array_merge($this->toArray(),[
'id' => (string) $this->id,
'created_at' => $this->created_at->timestamp,
]);
}

また、アプリケーションの config/scout.php ファイルで Typesense コレクション スキーマを定義する必要があります。コレクション スキーマは、Typesense を介して検索可能な各フィールドのデータ型を記述します。利用可能なすべてのスキーマ オプションの詳細については、Typesense documentation を参照してください。

Typesense コレクションのスキーマを定義した後に変更する必要がある場合は、scout:flush および scout:import を実行します。これにより、既存のインデックス付きデータがすべて削除され、スキーマが再作成されます。または、Typesense の API を使用して、インデックス付きデータを削除せずにコレクションのスキーマを変更することもできます。

検索可能なモデルが論理的に削除可能な場合は、アプリケーションの config/scout.php 構成ファイル内のモデルに対応する Typesense スキーマで __soft_deleted フィールドを定義する必要があります。

User::class => [
'collection-schema' => [
'fields' => [
// ...
[
'name' => '__soft_deleted',
'type' => 'int32',
'optional' => true,
],
],
],
],

Embeddings​

セマンティック検索とハイブリッド検索を有効にするには、モデルの Typesense 設定で embedding 設定とベクトルフィールドを定義します。デフォルトでは、Scout は Laravel AI SDK を使用して埋め込みを生成します。

use App\Models\Article;

'model-settings' => [
Article::class => [
'collection-schema' => [
'fields' => [
['name' => 'title', 'type' => 'string'],
['name' => 'embedding', 'type' => 'float[]', 'num_dim' => 1536],
],
],
'search-parameters' => ['query_by' => 'title'],
'embedding' => [
'attribute' => 'embedding',
'dimensions' => 1536,
],
],
],

モデルの toSearchableEmbedding メソッドは、Scout が埋め込みを生成する対象のソーステキスト、または事前計算済みの埋め込み配列を返す必要があります。

public function toSearchableEmbedding(): string|array
{
return $this->title.' '.$this->body;
}

Dynamic Search Parameters​

Typesense を使用すると、options メソッド経由で検索操作を実行するときに、search parameters を動的に変更できます。

use App\Models\Todo;

Todo::search('Groceries')->options([
'query_by' => 'title, description'
])->get();

Turbopuffer​

Turbopuffer では、モデルごとにスキーマと検索可能な属性が必要です。scout 設定ファイル内の turbopuffer 設定にある model-settings 配列で、これらを定義してください。

use App\Models\Article;

'turbopuffer' => [
// ...
'model-settings' => [
Article::class => [
'searchable-attributes' => [
'title' => 3,
'body' => 1,
],
'schema' => [
'title' => ['type' => 'string', 'full_text_search' => true],
'body' => ['type' => 'string', 'full_text_search' => true],
'status' => ['type' => 'string'],
],
],
],
],

searchable-attributes に割り当てる数値は、相対的な BM25 の重みです。上の例では、記事タイトルの一致によって加算されるスコアは、本文の一致によるスコアの 3 倍です。

セマンティック検索とハイブリッド検索を有効にするには、モデルの設定に embedding 設定とベクトルスキーマを追加します。

'turbopuffer' => [
// ...
'model-settings' => [
Article::class => [
'searchable-attributes' => [
'title' => 3,
'body' => 1,
],
'embedding' => [
'attribute' => 'embedding',
'dimensions' => 1536,
],
'schema' => [
'title' => ['type' => 'string', 'full_text_search' => true],
'body' => ['type' => 'string', 'full_text_search' => true],
'embedding' => ['type' => '[1536]f32', 'ann' => true],
],
],
],
],

モデルの toSearchableEmbedding メソッドは、Scout が埋め込むソーステキスト、または事前計算済みの埋め込み配列を返す必要があります。Scout はLaravel AI SDKを使ってソーステキストの埋め込みを生成します。

また、Laravel AI SDK をインストールしたり、toSearchableEmbedding メソッドを定義したりせずに、Turbopuffer のネイティブ埋め込みを使用することもできます。埋め込みドライバを turbopuffer に設定し、検索可能なソース属性に embed スキーマを設定してください。

'embedding' => [
'driver' => 'turbopuffer',
'attribute' => 'embedding_text',
],

'schema' => [
// ...
'embedding_text' => [
'type' => 'string',
'embed' => [
'model' => 'voyage/voyage-4',
'dimensions' => 1024,
'attribute' => 'embedding',
],
],
],

モデルの toSearchableArray 出力には、ソース属性を含める必要があります。

Third-Party Engine Indexing​

このセクションで説明するインデックス機能は、主にサードパーティの検索エンジン(Algolia、Meilisearch、Typesense、Turbopuffer)を使用する場合に関係します。データベースエンジンはデータベーステーブルを直接検索するため、手動でインデックスを管理する必要はありません。

Batch Import​

既存のプロジェクトに Scout をインストールする場合は、インデックスにインポートする必要があるデータベース レコードがすでに存在する可能性があります。 Scout は、既存のすべてのレコードを検索インデックスにインポートするために使用できる scout:import Artisan コマンドを提供します。

php artisan scout:import "App\Models\Post"

既存のすべてのレコードを queued jobs を使用してインポートするには、scout:queue-import コマンドを使用できます。

php artisan scout:queue-import "App\Models\Post" --chunk=500

flush コマンドを使用して、モデルのすべてのレコードを検索インデックスから削除できます。

php artisan scout:flush "App\Models\Post"

Modifying the Import Query​

バッチインポート用にすべてのモデルを取得するために使用されるクエリを変更したい場合は、モデルに makeAllSearchableUsing メソッドを定義できます。ここは、モデルをインポートする前に必要となる可能性のあるリレーションの Eager Loading を追加するのに適した場所です。

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 メソッドは、キューを使ってモデルを一括インポートする場合には適用できないことがあります。ジョブがモデルのコレクションを処理するとき、リレーションは復元されません(not restored)。

Adding Records​

Laravel\Scout\Searchable トレイトをモデルに追加したら、save または create でモデルインスタンスを保存するだけで、検索インデックスに自動的に追加されます。 Scout を use queues に構成した場合、この操作はキューワーカーによってバックグラウンドで実行されます。

use App\Models\Order;

$order = new Order;

// ...

$order->save();

Adding Records via Query​

Eloquent クエリを使ってモデルのコレクションを検索インデックスに追加する場合は、Eloquent クエリに searchable メソッドをチェーンできます。searchable メソッドはクエリの chunk the results を実行し、レコードを検索インデックスに追加します。また、Scout でキューを使用するよう設定している場合は、すべてのチャンクがキューワーカーによってバックグラウンドでインポートされます。

use App\Models\Order;

Order::where('price', '>', 100)->searchable();

Eloquent リレーションシップ インスタンスで searchable メソッドを呼び出すこともできます。

$user->orders()->searchable();

または、メモリ内に Eloquent モデルのコレクションがすでにある場合は、コレクション インスタンスで searchable メソッドを呼び出して、モデル インスタンスを対応するインデックスに追加することもできます。

$orders->searchable();

searchable メソッドは「upsert」操作と見なせます。つまり、モデルのレコードがインデックスにすでに存在する場合は更新され、存在しない場合は検索インデックスに追加されます。

Updating Records​

検索可能なモデルを更新するには、モデルインスタンスのプロパティを更新し、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();

Modifying Records Before Importing​

場合によっては、モデルのコレクションを検索可能にする前に準備する必要がある場合があります。たとえば、関係データを検索インデックスに効率的に追加できるように、関係を一括ロードすることができます。これを実現するには、対応するモデルで makeSearchableUsing メソッドを定義します。

use Illuminate\Database\Eloquent\Collection;

/**
* Modify the collection of models being made searchable.
*/
public function makeSearchableUsing(Collection $models): Collection
{
return $models->load('author');
}

Conditionally Updating the Search Index​

デフォルトでは、Scout は、どの属性が変更されたかに関係なく、更新されたモデルのインデックスを再作成します。この動作をカスタマイズしたい場合は、モデルに searchIndexShouldBeUpdated メソッドを定義できます。

/**
* Determine if the search index should be updated.
*/
public function searchIndexShouldBeUpdated(): bool
{
return $this->wasRecentlyCreated || $this->wasChanged(['title', 'body']);
}

Removing Records​

インデックスからレコードを削除するには、データベースからモデルを delete するだけです。soft deleted モデルを使用している場合でも、この方法で削除できます。

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();

Pausing Indexing​

場合によっては、モデル データを検索インデックスに同期せずに、モデルに対して Eloquent 操作のバッチを実行する必要がある場合があります。これは、withoutSyncingToSearch メソッドを使用して行うことができます。このメソッドは、ただちに実行される単一のクロージャを受け入れます。クロージャ内で発生するモデル操作はモデルのインデックスに同期されません。

use App\Models\Order;

Order::withoutSyncingToSearch(function () {
// Perform model actions...
});

Conditionally Searchable Model Instances​

場合によっては、特定の条件下でのみモデルを検索可能にすることが必要な場合があります。たとえば、「ドラフト」と「公開」の 2 つの状態のいずれかにある App\Models\Post モデルがあるとします。 「公開された」投稿のみを検索可能にしたい場合があります。これを実現するには、モデルに shouldBeSearchable メソッドを定義します。

/**
* Determine if the model should be searchable.
*/
public function shouldBeSearchable(): bool
{
return $this->isPublished();
}

shouldBeSearchable メソッドは、save および create メソッド、クエリ、または関係を通じてモデルを操作する場合にのみ適用されます。 searchable メソッドを使用してモデルまたはコレクションを直接検索可能にすると、shouldBeSearchable メソッドの結果がオーバーライドされます。

Scout の「database」エンジンを使用する場合、検索可能なデータは常にデータベースに保存されるため、shouldBeSearchable メソッドは適用されません。database エンジンで同様の動作を実現するには、代わりに where clauses を使用してください。

Searching​

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();

データベース、Meilisearch、Typesense、Turbopuffer の各エンジンは、クエリの意味に基づいてレコードを照合するセマンティック検索をサポートしています。Scout が埋め込みを生成する場合、セマンティック検索とハイブリッド検索には Laravel AI SDK が必要です。Typesense's native embeddings、Turbopuffer's native embeddings、および事前計算済みのクエリベクトルには Laravel AI SDK は必要ありません。

選択したエンジンの埋め込みを設定したら、検索クエリに対して semantic メソッドを呼び出します。

$articles = Article::search('staying cool in the summer')
->semantic()
->get();

選択したエンジンが対応している場合は、最小類似度のしきい値を指定できます。

$articles = Article::search('renewable energy storage')
->semantic(minSimilarity: 0.6)
->get();

全文検索とセマンティック検索を組み合わせるには、hybrid メソッドを使用します。最初の2つの引数で、テキスト検索結果とセマンティック検索結果の相対的な重みを指定します。

$articles = Article::search('renewable energy storage')
->hybrid(textWeight: 1, semanticWeight: 2)
->get();

Custom Indexes​

サードパーティ エンジンを使用して検索する場合、通常、検索クエリはモデルの 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();

=、!=、<、>、>=、<= 比較演算子を使用して、より高度なクエリを構築することもできます。

Order::search('Star Trek')
->where('status', '=', 'completed')
->where('is_refunded', '!=', true)
->where('total_price', '>', 100)
->where('shipping_cost', '<', 20)
->where('discount_percent', '>=', 10)
->where('item_count', '<=', 5)
->get();

さらに、whereIn メソッドを使用して、指定された列の値が指定された配列内に含まれていることを確認できます。

$orders = Order::search('Star Trek')->whereIn(
'status', ['open', 'paid']
)->get();

whereNotIn メソッドは、指定された列の値が指定された配列に含まれていないことを検証します。

$orders = Order::search('Star Trek')->whereNotIn(
'status', ['closed']
)->get();

アプリケーションで Meilisearch を使用している場合、Scout の「where」句を使う前に、アプリケーションの filterable attributes を設定する必要があります。

Customizing the Eloquent Results Query​

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();

サードパーティ エンジンを使用する場合、このコールバックは関連モデルが検索エンジンからすでに取得された後に呼び出されるため、結果の「フィルタリング」には使用しないでください。代わりに Scout where clauses を使用してください。ただし、データベース エンジンを使用する場合、query メソッドの制約はデータベース クエリに直接適用されるため、フィルタリングにも使用できます。

Pagination​

モデルのコレクションを取得するだけでなく、paginate メソッドを使って検索結果をページネーションすることもできます。このメソッドは、paginated a traditional Eloquent queryと同様に、Illuminate\Pagination\LengthAwarePaginator インスタンスを返します。

use App\Models\Order;

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

paginate メソッドの最初の引数として量を渡すことで、ページごとに取得するモデルの数を指定できます。

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

データベース エンジンを使用する場合は、simplePaginate メソッドを使用することもできます。ページ番号を表示できるように一致するレコードの総数を取得する paginate とは異なり、simplePaginate は現在のページの先に結果があるかどうかだけを判断するため、「前」と「次」のリンクのみが必要な大規模なデータセットの場合はより効率的になります。

$orders = Order::search('Star Trek')->simplePaginate(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 で検索するときにグローバルスコープの制約を再現してください。

Soft Deleting​

インデックス対象のモデルでsoft deletingを使用しており、ソフトデリート済みのモデルも検索する必要がある場合は、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 が検索インデックスから自動的に削除します。

Customizing Engine Searches​

エンジンの検索動作の高度なカスタマイズを実行する必要がある場合は、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();

Custom Engines​

Writing the Engine​

組み込みの Scout 検索エンジンの 1 つがニーズに合わない場合は、独自のカスタム エンジンを作成して 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 クラスでのこれらのメソッドの実装を確認すると役立つ場合があります。このクラスは、これらの各メソッドを独自のエンジンに実装する方法を学習するための良い出発点となります。

Registering the Engine​

カスタム エンジンを作成したら、Scout エンジン マネージャーの extend メソッドを使用して、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',