Scout 全文搜索
Scout 全文搜索
Laravel Scout
簡介
Laravel Scout 為 Eloquent 模型 的全文搜索提供了基于驅(qū)動的簡單的解決方案。通過使用模型觀察者, Scout 會自動同步 Eloquent 記錄的搜索索引。
目前, Scout 自帶一個 Algolia 驅(qū)動。不過,編寫自定義驅(qū)動也很簡單,你可以輕松的通過自己的搜索實現(xiàn)來擴展 Scout。
安裝
首先,通過 Composer 包管理器來安裝 Scout:
composer require laravel/scout
Scout 安裝完成后,使用 vendor:publish
Artisan 命令來生成 Scout 配置文件。這個命令將在你的 config
目錄下生成一個 scout.php
配置文件。
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
最后,在你要做搜索的模型中添加 Laravel\Scout\Searchable
trait。這個 trait 會注冊一個模型觀察者來保持模型和所有驅(qū)動的同步:
<?php namespace App; use Laravel\Scout\Searchable; use Illuminate\Database\Eloquent\Model; class Post extends Model{ use Searchable; }
隊列
雖然并不強制使用 Scout,但是在使用這個庫之前,強烈建議你配置一個 隊列驅(qū)動,使用它運行一個隊列來處理允許 Scout 將模型信息同步到搜索索引的所有操作,為你的應用的 web 接口提供更快的響應。
一旦你配置了隊列驅(qū)動程序,你的 config/scout.php
配置文件中 queue
選項的值要設置為 true
:
'queue' => true,
驅(qū)動必要條件
Algolia
使用 Algolia 驅(qū)動時,需要在 config/scout.php
配置文件配置你的 Algolia id
和 secret
憑證。配置好憑證之后,還需要使用 Composer 包管理器安裝 Algolia PHP SDK:
composer require algolia/algoliasearch-client-php:^2.2
配置
配置模型索引
每個 Eloquent 模型都是通過給定的 「索引」 進行同步,該 「索引」 包含所有可搜索的模型記錄。換句話說,你可以把每一個 「索引」 設想為一張 MySQL 數(shù)據(jù)表。默認情況下,每個模型都會被持久化到與模型的 「表」 名(通常是模型名稱的復數(shù)形式)相匹配的索引。你也可以通過重寫模型上的 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'; } }
配置可搜索數(shù)據(jù)
默認情況下,模型以完整的 toArray
格式持久化到搜索索引。如果要自定義同步到搜索索引的數(shù)據(jù),可以覆蓋模型上的 toSearchableArray
方法:
<?php namespace App; use Laravel\Scout\Searchable; use Illuminate\Database\Eloquent\Model; class Post extends Model{ use Searchable; /** * 獲取模型的可搜索數(shù)據(jù) * * @return array */ public function toSearchableArray() { $array = $this->toArray(); // Customize array... return $array; } }
配置模型 ID
默認情況下,Scout 將使用模型的主鍵作為搜索索引中存儲的唯一 ID 。 可以通過模型上的 getScoutKey
方法自定義:
<?php namespace App; use Laravel\Scout\Searchable; use Illuminate\Database\Eloquent\Model; class User extends Model{ use Searchable; /** * 獲取模型主鍵 * * @return mixed */ public function getScoutKey() { return $this->email; } }
索引
批量導入
如果你想安裝 Scout 到已存在的項目中,你可能已經(jīng)有了想要導入搜索驅(qū)動的數(shù)據(jù)庫記錄。Scout 提供了 Artisan 命令 import
用來導入所有已存在的記錄到搜索索引:
php artisan scout:import "App\Post"
flush
命令可用于從搜索索引中刪除所有模型的記錄:
php artisan scout:flush "App\Post"
添加記錄
當你將 Laravel\Scout\Searchable trait
添加到模型中,你需要做的就是 save
一個模型實例,它將自動被添加到搜索索引。如果你已經(jīng)將 Scout 配置為 使用隊列,那這個操作會在后臺由你的隊列工作進程來執(zhí)行:
$order = new App\Order;// ...$order->save();
通過查詢添加
如果你想通過 Eloquent 查詢構(gòu)造器將模型集合添加到搜索索引中,你也可以在 Eloquent 查詢構(gòu)造器上鏈式調(diào)用 searchable
方法。searchable
會把構(gòu)造器的查詢 結(jié)果分塊 并且將記錄添加到你的搜索索引里。同樣的,如果你已經(jīng)配置 Scout 為使用隊列,則所有的數(shù)據(jù)塊將在后臺由你的隊列工作進程添加:
// 通過 Eloquent 查詢構(gòu)造器增加.. App\Order::where('price', '>', 100)->searchable(); // 你也可以通過模型關(guān)系增加記錄... $user->orders()->searchable(); // 你也可以通過集合增加記錄... $orders->searchable();
searchable
方法可以被看做是「更新插入」的操作。換句話說,如果模型記錄已經(jīng)在你的索引里了,它就會被更新。如果搜索索引中不存在,則將其添加到索引中。
更新記錄
要更新可搜索的模型,只需要更新模型實例的屬性并將模型 save
到數(shù)據(jù)庫。Scout 會自動將更新同步到你的搜索索引中:
$order = App\Order::find(1); // 更新訂單... $order->save();
你也可以在 Eloquent 查詢語句上使用 searchable
方法來更新一個模型的集合。如果這個模型不存在你檢索的索引里,就會被創(chuàng)建:
// 通過 Eloquent 查詢更新... App\Order::where('price', '>', 100)->searchable(); // 你也可以通過數(shù)據(jù)間的關(guān)聯(lián)進行更新... $user->orders()->searchable(); // 你也可以通過數(shù)據(jù)集合進行更新... $orders->searchable();
刪除記錄
使用 delete
從數(shù)據(jù)庫中刪除該模型就可以移除索引里的記錄。這種刪除形式甚至與 軟刪除 的模型兼容:
$order = App\Order::find(1); $order->delete();
如果你不希望記錄在刪除之前被檢索到,可以在 Eloquent 查詢實例或集合上使用 unsearchable
方法:
// 通過 Eloquent 查詢刪除... App\Order::where('price', '>', 100)->unsearchable(); // 你可以通過數(shù)據(jù)間的關(guān)系進行刪除... $user->orders()->unsearchable(); // 你可以通過數(shù)據(jù)集合進行刪除... $orders->unsearchable();
暫停索引
你可能需要在執(zhí)行一批 Eloquent 操作的時候,不同步模型數(shù)據(jù)到搜索索引。此時你可以使用 withoutSyncingToSearch
方法來執(zhí)行此操作。這個方法接受一個立即執(zhí)行的回調(diào)。該回調(diào)中所有的操作都不會同步到模型的索引:
App\Order::withoutSyncingToSearch(function () { // 執(zhí)行模型操作... });
有條件的搜索模型實例
有時候你可能需要在某些條件下模型是可搜索的。例如,假設你有 App\Post
模型可能兩種狀態(tài)之一:「草稿」和「發(fā)布」。你可能只允許搜索 「發(fā)布」過的帖子。為了實現(xiàn)這一點,你需要在模型中定義一個 shouldBeSearchable
方法:
public function shouldBeSearchable(){ return $this->isPublished(); }
只有在通過 save
方法、查詢或關(guān)聯(lián)模型操作時,才應使用 shouldBeSearchable
方法。直接使用 searchable
方法將使模型或集合的可搜索結(jié)果覆蓋 shouldBeSearchable
方法的結(jié)果:
// 此處將遵循 "shouldBeSearchable" 結(jié)果... App\Order::where('price', '>', 100)->searchable(); $user->orders()->searchable();$order->save(); // 此處將覆蓋 "shouldBeSearchable" 結(jié)果... $orders->searchable(); $order->searchable();
搜索
你可以使用 search
方法來搜索模型。search 方法接受一個用于搜索模型的字符串。你還需要在搜索查詢上鏈式調(diào)用 get
方法,才能用給定的搜索語句查詢與之匹配的 Eloquent 模型:
$orders = App\Order::search('Star Trek')->get();
Scout 搜索返回 Eloquent 模型的集合,因此你可以直接從路由或控制器返回結(jié)果,它們會被自動轉(zhuǎn)換成 JSON 格式:
use Illuminate\Http\Request; Route::get('/search', function (Request $request) { return App\Order::search($request->search)->get(); });
如果你想在它們返回 Eloquent 模型前得到原結(jié)果,你應該使用 raw
方法:
$orders = App\Order::search('Star Trek')->raw();
搜索查詢通常會在模型的 searchableAs
方法指定的索引上執(zhí)行。當然,你也可以使用 within
方法指定應該搜索的自定義索引:
$orders = App\Order::search('Star Trek') ->within('tv_shows_popularity_desc') ->get();
Where 語句
允許你在搜索查詢中增加簡單的 "where" 語句。目前,這些語句只支持基本的數(shù)值等式檢查,并且主要是用于根據(jù)租戶 ID 進行的范圍搜索查詢。由于搜索索引不是關(guān)系型數(shù)據(jù)庫,因此當前不支持更高級的 "where" 語句:
$orders = App\Order::search('Star Trek')->where('user_id', 1)->get();
分頁
除了檢索模型的集合,你也可以使用 paginate
方法對搜索結(jié)果進行分頁。這個方法會返回一個就像 傳統(tǒng)的 Eloquent 查詢分頁 一樣的 Paginator
實例:
$orders = App\Order::search('Star Trek')->paginate();
你可以通過將數(shù)量作為第一個參數(shù)傳遞給 paginate
方法來指定每頁檢索多少個模型:
$orders = App\Order::search('Star Trek')->paginate(15);
獲取到檢索結(jié)果后,就可以使用 Blade 來渲染分頁鏈接和顯示檢索結(jié)果,就像傳統(tǒng)的 Eloquent 查詢分頁一樣:
<div class="container"> @foreach ($orders as $order) {{ $order->price }} @endforeach </div> {{ $orders->links() }}
軟刪除
如果你的索引模型是 軟刪除 ,并且你需要搜索軟刪除的模型,設置 config/scout.php
配置文件的 soft_delete
選項的值為 true
:
'soft_delete' => true,
當這個配置選項是 true
的時候, Scout 不會從搜索索引中移除軟刪除模型。相反,它會在索引記錄中設置一個隱藏 __soft_deleted
屬性。 然后,在搜索的時候,你可以使用 withTrashed
或 onlyTrashed
方法檢索軟刪除記錄:
// 搜索結(jié)果包括已刪除的記錄... $orders = App\Order::withTrashed()->search('Star Trek')->get(); // 搜索結(jié)果只含已刪除的記錄... $orders = App\Order::onlyTrashed()->search('Star Trek')->get();
{提示} 要永久刪除模型可以使用
forceDelete
來刪除,Scout 將自動的從搜索索引中移除模型。
自定義搜索引擎
如果需要自定義引擎的搜索行為,可以將回調(diào)作為第二個參數(shù)傳遞給 search
方法。例如,在將搜索查詢傳遞給 Algolia 之前,可以使用這個回調(diào)將地理位置數(shù)據(jù)添加到搜索請求中:
use Algolia\AlgoliaSearch\SearchIndex; App\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();
自定義引擎
寫引擎
如果內(nèi)置的 Scout 搜索引擎不能滿足你的需求,你可以編寫自定義的引擎并且將它注冊到 Scout。你的引擎需要繼承 Laravel\Scout\Engines\Engine
抽象類,這個抽象類包含了你自定義的引擎必須要實現(xiàn)的七個方法:
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($results, $model); abstract public function getTotalCount($results); abstract public function flush($model);
在 Laravel\Scout\Engines\AlgoliaEngine
類里查看這些方法的實現(xiàn)會對你有較大的幫助。這個類會為你在學習如何在自定義引擎中實現(xiàn)這些方法提供一個好的起點。
注冊引擎
一旦你寫好了自定義引擎,你可以用 Scout 引擎管理的 extend
方法將它注冊到 Scout。你只需要從 AppServiceProvider
下的 boot
方法或者應用中使用的任何一個服務提供器中調(diào)用 extend
方法。舉個例子,如果你寫好了一個 MySqlSearchEngine
,你可以像這樣去注冊它:
use Laravel\Scout\EngineManager; /** * 啟動任何的服務 * * @return void */ public function boot(){ resolve(EngineManager::class)->extend('mysql', function () { return new MySqlSearchEngine; }); }
引擎注冊后,你可以在 config/scout.php
配置文件中指定它為默認的 Scout driver
:
'driver' => 'mysql',
生成宏命令
如果你想要自定義生成器方法,你可以使用 Laravel\Scout\Builder
類下的 macro
方法。通常,定義 "macros" 時, 需要實現(xiàn) service provider's boot
方法:
<?php namespace App\Providers; use Laravel\Scout\Builder; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Response; class ScoutMacroServiceProvider extends ServiceProvider{ /** * 注冊應用的Scout 宏命令. * * @return void */ public function boot() { Builder::macro('count', function () { return $this->engine->getTotalCount( $this->engine()->search($this) ); }); } }
macro
函數(shù)接受一個名字作為第一個參數(shù),第二個參數(shù)為一個閉包函數(shù)。當
調(diào)用 Laravel\Scout\Builder
宏命令時,調(diào)用這個函數(shù).
App\Order::search('Star Trek')->count();