Laravel ScoutはEloquentモデルに対し、全文検索を提供するパッケージです。
AlgoliaやElasticsearchを利用するほどでもない小規模なLaravelプロジェクト向けのLaravel Scoutについて記します。
Laravel Scoutについて思うところ
とあるプロジェクトで一人で社内向けの小規模なシステム開発をしており、Laravel Scoutによる全文検索の機能追加を検討したのですが、正直面倒だなと思っていました。というのも、公式ではドライバにAlgoliaを推奨しているのですが、開発中のシステムは外部サービスと連携するほど大それたものでもないし、一人で開発しているということもあり、管理コストが煩わしく感じました。
大体同じ理由でElasticsearchも面倒です。どうにかindexファイルを内部で保持しつつ、Laravel Scoutを利用できないかと調べていたところ、TNTSearchと連携すると要件とマッチする実装ができることがわかりました。
本記事ではLaravel Scout + TNTSearchによるお手軽全文検索の追加について記します。
導入
環境
- php >= 7.0.0
- laravel/framework 5.5.*
- laravel/scout ^3.0
- teamtnt/laravel-scout-tntsearch-driver ^3.0
Scoutインストール
$ composer require laravel/scout
以下のコマンドを実行し、scoutのconfigファイルを作成します。
$ php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
TNTSearchドライバをインストール
Laravel ScoutのドライバとしてTNTSearchを利用する場合は以下をインストールする必要があります。
$ composer require teamtnt/laravel-scout-tntsearch-driver
導入が完了したら、config/app.php
へプロバイダ登録をします。
'providers' => [
// ...
Laravel\Scout\ScoutServiceProvider::class,
TeamTNT\Scout\TNTSearchScoutServiceProvider::class,
],
.envファイルにて、SCOUT_DRIVER
を設定します。
SCOUT_DRIVER=tntsearch
次に、scout関連の設定ファイルを弄ります。indexファイルの出力先を設定します。config/scout.php
を以下のように設定します。
'tntsearch' => [
'storage' => storage_path(),
'asYouType' => false,
'fuzziness' => true,
'fuzzy' => [
'prefix_length' => 2,
'max_expansions' => 50,
'distance' => 2,
]
]
Eloquentモデルと関連付け
検索対象としたいEloquentモデルに対し、以下の設定をします。
-
Laravel\Scout\Searchable
トレイトを追加 -
toSearchableArray()
追加- 検索対象としたいモデルデータ設定
<?php
namespace App\Models;
+ use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
+ use Searchable;
...
+ public function toSearchAbleArray()
+ {
+ return [
+ 'id' => $this->id,
+ 'firstName' => $this->firstName,
+ 'mail' => $this->mail,
+ .....
+ ];
+ }
検索対象としてるモデルでリレーションを含めたい場合は以下のようにします。
public function book() {
return $this->hasMany('App\Models\Book')
}
public function toSearchableArray() {
return [
'id' => $this->id,
'firstName' => $this->firstName,
'email' => $this->email,
'book_name' => $this->book->name, // リレーション
];
}
indexファイル作成
バッチによるインデックス作成
全文検索用のインデックスファイルを作成します。
$ php artisan make:command IndexUsers
クエリによるインデックスの作成ができます。
TNTSearchによるindexerでの作成をするなり、Eloquentのクエリの結果を登録するなり、ニーズに合わせてください。
public function handle()
{
$indexer = TNTSearch::createIndex('users.index');
$indexer->query('SELECT id, firstname, lastname, email, phone, bio FROM users;');
$indexer->run();
// or
App\Models\User::get(['firstname', 'email'])->searchable();
}
}
作成したコマンドを実行すると、config/scout.php
のstorage
に設定したパスにバイナリ形式のインデックスファイルが生成されます。storage_path()
に設定した場合はstorageディレクトリ内に生成されます。
動作確認
php artisan tinker
で動作確認ができます。
$ php artisan tinker
>>> App\Models\User::search('john')->get()
検索にヒットした結果のリレーション取得について
例えばscoutによる検索結果を取得したeloquentモデルのcollectionで、リレーションを追加で取得したいケースがあるかと思います。
例えばscoutでuserの一覧を取得し、その結果よりリレーションであるitem
モデルも取得したい場合、下手に実装するとN+1クエリになる可能性があります。この場合はEagerロードを利用することでN+1を避けることができます。
function searchUsersWithItemData($query) {
// scoutで全文検索し、paginate化して取得
$users = App\Models\Users::search($query)->paginate(50);
// usersのリレーションであるcompanyモデル追加
$users->setCollection($users->load('item'));
return $users;
}
まとめ
他のサービス・システムと連携することなくLaravel Scoutの機能を追加することができました。
データの規模によってはこれを利用するまでもないケースもあるかもしれませんが、現状はこれで要件を満たせているので満足しています。
小規模Laravelプロジェクトにおすすめです。検討事項に含めてみてはいかがでしょうか。