簡介 (Introduction)
Laravel Scout 提供一個簡單且以驅動程式為基礎的解決方案,用於為你的 Eloquent 模型 新增全文搜尋功能。透過模型觀察者(model observers),Scout 能自動將搜尋索引與你的 Eloquent 資料列保持同步。
目前 Scout 隨附的驅動包括 Algolia、Meilisearch、Typesense 以及 MySQL / PostgreSQL(database)驅動。此外,Scout 還包含一個適用於本機開發的 "collection" 驅動,該驅動不需要外部相依或第三方服務。若內建驅動不符合需求,也可以自行撰寫並擴充自訂驅動。
安裝 (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 trait 加到你想要啟用搜尋功能的模型上。此 trait 會註冊一個模型觀察者,讓模型自動與搜尋驅動同步:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
}
Queueing
當使用非 database 或 collection 的驅動時,強烈建議在使用 Scout 前先設定 佇列驅動。啟動佇列工作程序能將所有同步模型資料到搜尋索引的操作放入佇列,顯著提升你應用程式網頁介面的回應時間。
在你設定好佇列驅動後,請在 config/scout.php 中把 queue 選項設為 true:
'queue' => true,
即使 queue 選項設為 false,仍需注意某些 Scout 驅動(例如 Algolia 與 Meilisearch)會以非同步方式索引記錄。也就是說,雖然 Laravel 應用程式已完成索引操作,但搜尋引擎端可能尚未立即反映新增或更新的記錄。
若要指定 Scout 作業使用的連線與佇列,您可以把 queue 設定為一個陣列:
'queue' => [
'connection' => 'redis',
'queue' => 'scout'
],
當然,若你自訂了 Scout 作業的連線與佇列,請啟動相對應的佇列工作程序來處理該連線與佇列上的工作:
php artisan queue:work redis --queue=scout
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 驅動,請透過 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-php 版本與你使用的 Meilisearch 二進位版本相容,詳細相容性請參考 Meilisearch 關於二進位相容性的文件。
[!WARNING] 當你在使用 Meilisearch 的應用上升級 Scout 時,務必檢查 Meilisearch 服務本身是否有任何額外的破壞性變更,請參考其發行記錄:Meilisearch releases。
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
如果你使用 Laravel Sail,可能需要把 TYPESENSE_HOST 設為與 Docker 容器名稱相符。你也可選擇性地設定安裝的連接埠、路徑與協定:
TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http
有關 Typesense 集合(collections)的額外設定與 schema 定義,請在 config/scout.php 找到相關項目。更多資訊請參閱 Typesense 文件。
在 Typesense 中準備可儲存的資料 (Preparing Data For Storage In Typesense)
使用 Typesense 時,模型的 toSearchableArray 方法需將模型主鍵轉為字串,並把建立時間轉為 UNIX 時間戳記,範例如下:
/**
* 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 定義集合(collection)schema。集合 schema 用來描述各搜尋欄位的資料型別。欲瞭解所有可用的 schema 選項,請查閱 Typesense 文件。
若需在定義後變更 Typesense 集合的 schema,可執行 scout:flush 與 scout:import 來刪除既有索引資料並重新建立 schema;或使用 Typesense 的 API 修改集合 schema,而不移除任何已索引的資料。
如果你的模型支援軟刪除(soft deletes),應在 config/scout.php 對應的 Typesense schema 中定義 __soft_deleted 欄位:
User::class => [
'collection-schema' => [
'fields' => [
// ...
[
'name' => '__soft_deleted',
'type' => 'int32',
'optional' => true,
],
],
],
],
動態搜尋參數 (Typesense Dynamic Search Parameters)
Typesense 允許在透過 options 方法執行搜尋時動態修改 搜尋參數,範例:
use App\Models\Todo;
Todo::search('Groceries')->options([
'query_by' => 'title, description'
])->get();
設定 (Configuration)
設定模型索引 (Configuring Model Indexes)
每個 Eloquent 模型會與一個搜尋「索引」(index)同步,該索引包含該模型所有可搜尋的記錄。換句話說,你可以把索引想像成類似於 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';
}
}
設定可搜尋資料 (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;
}
}
某些搜尋引擎(例如 Meilisearch)只會在正確資料型別上執行過濾操作(>、< 等)。因此,使用這類搜尋引擎並自訂可搜尋資料時,請確認數值欄位已轉成正確的資料型別:
public function toSearchableArray()
{
return [
'id' => (int) $this->id,
'name' => $this->name,
'price' => (float) $this->price,
];
}
設定索引選項(Algolia) (Configuring Indexes For Algolia)
有時你會想在 Algolia 索引上設定額外的參數。雖然可以透過 Algolia 的 UI 管理這些設定,但直接在應用程式的 config/scout.php 中管理索引設定,會更方便且可加入部署流程,避免手動調整並確保在多環境間的一致性。你可以設定可過濾屬性(filterable attributes)、排序規則(ranking)、分面(faceting)等選項,或任何其他支援的設定(參見 Algolia 文件)。
To get started, add settings for each index in your application's config/scout.php configuration file:
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
為 Meilisearch 設定可過濾欄位與索引選項 (Configuring Filterable Data For Meilisearch)
和其他 Scout 驅動不同,Meilisearch 需要你事先定義索引的搜尋設定,例如可過濾屬性(filterable attributes)、可排序屬性(sortable attributes)等。
可過濾屬性是指在使用 Scout 的 where 方法時會用到的欄位;可排序屬性是指在使用 orderBy 方法時會用到的欄位。你可以在 config/scout.php 中的 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
設定模型索引鍵(Model ID) (Configuring The Model Id)
預設情況下,Scout 會使用模型的主鍵作為儲存在索引中的唯一識別鍵。如果需要自訂此行為,可以在模型中覆寫 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';
}
}
針對每個模型指定搜尋引擎 (Configuring Search Engines Per Model)
進行搜尋時,Scout 通常會使用 config/scout.php 中指定的預設搜尋引擎。若要為特定模型指定其他搜尋引擎,可以在模型中覆寫 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');
}
}
使用者識別 (Identifying Users)
若使用 Algolia,Scout 也允許自動識別使用者。將已認證使用者與搜尋操作關聯,可幫助你在 Algolia 儀表板查看搜尋分析。可在 .env 檔中將 SCOUT_IDENTIFY 設為 true 以啟用此功能:
SCOUT_IDENTIFY=true
Enabling this feature will also pass the request's IP address and your authenticated user's primary identifier to Algolia so this data is associated with any search request that is made by the user.
Database / Collection Engines
Database(資料庫)引擎 (Database Engine)
[!WARNING] 資料庫引擎目前僅支援 MySQL 與 PostgreSQL。
database 引擎是使用 Laravel Scout 的最快捷方式,它會使用 MySQL / PostgreSQL 的全文索引或 LIKE 條件從既有資料庫過濾結果,來決定符合查詢的搜尋結果。
若要使用資料庫引擎,只需將 .env 的 SCOUT_DRIVER 設為 database,或在 config/scout.php 中直接指定 database 驅動:
SCOUT_DRIVER=database
設定資料庫引擎為預設驅動後,請先 設定可搜尋的資料,然後即可開始對模型執行 搜尋查詢。使用資料庫引擎時,不需要額外將資料索引至 Algolia、Meilisearch 或 Typesense 等搜尋引擎。
自訂資料庫搜尋策略
預設情況下,資料庫引擎會對所有你在 可搜尋資料 中設定的模型屬性使用 where like 查詢。然而在某些情況下,這樣可能造成效能不佳。因此,你可以為資料庫引擎指定搜尋策略,使得部分欄位改用全文搜尋(full text)查詢,或只使用 where like 的前綴比對(example%)而非在字串任意位置搜尋(%example%)。
要定義這些行為,你可將 PHP 屬性(attributes)套用至模型的 toSearchableArray 方法。未指定額外搜尋策略的欄位將沿用預設的 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,
];
}
[!WARNING] 在將欄位指定為使用全文查詢(full text)約束前,請先確保該欄位已被建立 全文索引。
Collection(集合)引擎 (Collection Engine)
雖然在本機開發時,你可以使用 Algolia、Meilisearch 或 Typesense 等外部搜尋引擎,但若要快速上手,使用 collection 引擎會更方便。collection 引擎會從現有資料庫抓取結果並使用 where 條件與集合過濾來判斷符合查詢的記錄。使用此引擎時,不需要把模型「索引」到外部搜尋服務,因為資料會直接從本地資料庫取出。
要使用 collection 引擎,只需將 SCOUT_DRIVER 環境變數設為 collection,或在應用程式的 scout 設定檔中直接指定 collection 驅動:
SCOUT_DRIVER=collection
設定 collection 驅動為預設之後,你就可以開始對模型執行 搜尋查詢。使用 collection 引擎時,不需要將資料索引到 Algolia、Meilisearch 或 Typesense 這類搜尋引擎。
Differences From Database Engine
On first glance, the "database" and "collections" engines are fairly similar. They both interact directly with your database to retrieve search results. However, the collection engine does not utilize full text indexes or LIKE clauses to find matching records. Instead, it pulls all possible records and uses Laravel's Str::is helper to determine if the search string exists within the model attribute values.
collection 引擎具有最高的移植性,支援 Laravel 支援的所有關聯式資料庫(包括 SQLite、SQL Server 等);但其效能遠不及 Scout 的 database 引擎。
索引管理 (Indexing)
批量匯入 (Batch Import)
若你在既有專案中安裝 Scout,可能已經有需要匯入索引的資料。Scout 提供 scout:import Artisan 指令,可用來把既有資料匯入搜尋索引:
php artisan scout:import "App\Models\Post"
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 load 關聯,以便在匯入前載入相關資料:
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');
}
[!WARNING] 當使用佇列批次匯入模型時,
makeAllSearchableUsing方法可能不適用。由於模型集合在作業(job)中處理時,關聯可能不會被恢復,請參見有關佇列處理關聯的說明。
新增記錄 (Adding Records)
在模型加入 Laravel\Scout\Searchable trait 後,只要 save 或 create 該模型實例,該記錄就會自動新增到搜尋索引。若你已設定 Scout 使用 佇列,此操作會由佇列工作程序在背景執行:
use App\Models\Order;
$order = new Order;
// ...
$order->save();
透過查詢新增記錄 (Adding Records Via Query)
如果想透過 Eloquent 查詢把一批模型新增到索引,可在查詢上連接 searchable 方法。searchable 會把查詢結果分塊(chunk)並將記錄加入索引。若 Scout 使用佇列,這些分塊將由佇列工作程序在背景匯入:
use App\Models\Order;
Order::where('price', '>', 100)->searchable();
你也可以在 Eloquent 關聯實例上呼叫 searchable 方法:
$user->orders()->searchable();
或者,若你已有一個載入於記憶體中的 Eloquent 模型集合,可在該集合上呼叫 searchable 方法,將模型加入對應的索引:
$orders->searchable();
[!NOTE] >
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)
有時你可能需要在將模型加入索引前先準備該集合。例如,先 eager load 某個關聯,以便更有效率地把關聯資料加入索引。為此,可在對應模型上定義 makeSearchableUsing 方法:
use Illuminate\Database\Eloquent\Collection;
/**
* Modify the collection of models being made searchable.
*/
public function makeSearchableUsing(Collection $models): Collection
{
return $models->load('author');
}
移除記錄 (Removing Records)
要將記錄從索引中移除,只要從資料庫 delete 該模型即可。即便模型支援 軟刪除,同樣適用:
use App\Models\Order;
$order = Order::find(1);
$order->delete();
若不想先查詢模型就刪除索引記錄,可在 Eloquent 查詢上使用 unsearchable 方法:
Order::where('price', '>', 100)->unsearchable();
若要移除某個關聯內所有模型的索引記錄,可在關聯實例上呼叫 unsearchable:
$user->orders()->unsearchable();
或是,如果你已有一個已載入的模型集合,可在該集合上呼叫 unsearchable 以將它們從索引移除:
$orders->unsearchable();
若要將模型的所有記錄從對應索引移除,可使用 removeAllFromSearch 方法:
Order::removeAllFromSearch();
暫停索引同步 (Pausing Indexing)
如果你需要對模型進行一系列批次操作,但不希望在此期間同步資料到搜尋索引,可使用 withoutSyncingToSearch 方法。此方法接受一個立即執行的 closure,在 closure 內發生的任何模型操作都不會同步到索引:
use App\Models\Order;
Order::withoutSyncingToSearch(function () {
// Perform model actions...
});
條件式可搜尋模型實例 (Conditionally Searchable Model Instances)
有時你可能只想在特定條件下讓模型可搜尋。例如,你的 App\Models\Post 可能處於「草稿(draft)」或「已發布(published)」兩種狀態,你可能希望只有「已發布」的文章能被搜尋到。可透過在模型中定義 shouldBeSearchable 方法來達成:
/**
* Determine if the model should be searchable.
*/
public function shouldBeSearchable(): bool
{
return $this->isPublished();
}
shouldBeSearchable 方法僅在透過 save、create、查詢或關聯操作模型時套用。直接在模型或集合上呼叫 searchable 將會覆蓋 shouldBeSearchable 的結果。
[!WARNING] 當使用 Scout 的
database引擎時,shouldBeSearchable方法不適用,因為所有可搜尋資料已經儲存在資料庫中。若要達成類似行為,請改用 where 條件。
搜尋 (Searching)
你可以使用 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();
自訂索引 (Custom Indexes)
搜尋查詢通常會在模型 searchableAs 指定的索引上執行。不過,你也可以使用 within 方法來指定要搜尋的自訂索引:
$orders = Order::search('Star Trek')
->within('tv_shows_popularity_desc')
->get();
Where 條件 (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 條件目前尚未支援。
[!WARNING] 若應用程式使用 Meilisearch,請務必先設定 可過濾屬性,才能使用 Scout 的
where條件。
分頁 (Pagination)
除了取得模型集合外,你也可以使用 paginate 方法分頁搜尋結果。此方法會回傳 Illuminate\Pagination\LengthAwarePaginator 實例,就像你對傳統 Eloquent 查詢進行分頁一樣:
use App\Models\Order;
$orders = Order::search('Star Trek')->paginate();
你可以在 paginate 方法的第一個參數指定每頁要取得的模型數量:
$orders = Order::search('Star Trek')->paginate(15);
取回結果後,你可以使用 Blade 顯示結果並渲染頁面連結,與對傳統 Eloquent 查詢分頁的使用方式相同:
<div className="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);
});
[!WARNING] 由於搜尋引擎並不會知道你的 Eloquent 模型所定義的全域範圍(global scope),在使用 Scout 分頁時應避免使用全域範圍;或在透過 Scout 搜尋時,重新在查詢中套用這些範圍限制。
軟刪除 (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();
[!NOTE] 當軟刪除模型被永久刪除(使用
forceDelete)時,Scout 會自動將它從搜尋索引移除。
自訂搜尋引擎查詢行為 (Customizing Engine Searches)
若需對搜尋引擎的查詢行為進行進階自訂,可把 closure 作為 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 結果查詢 (Customizing The Eloquent Results Query)
當 Scout 從搜尋引擎取得符合條件的模型 ID 列表後,Eloquent 會根據主鍵取回所有對應的模型。你可以透過呼叫 query 方法來自訂此查詢。query 方法接受一個回呼,並會接收 Eloquent 查詢建構器(Builder)作為參數:
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 條件。
自訂驅動(Custom Engines)
實作自訂引擎 (Writing The Engine)
如果內建的 Scout 搜尋引擎無法滿足你的需求,你可以撰寫自訂引擎並註冊到 Scout 中。自訂引擎需繼承 Laravel\Scout\Engines\Engine 抽象類別,該類別定義了八個必須實作的方法:
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 方法註冊引擎。引擎管理器可從 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',