Laravel Scoutを使って全文検索algoliaの使い方も同時に理解

Laravel ScoutはLaravelのドキュメントサイトでも利用されているような全文検索を行うためのパッケージです。Laravel Scoutでは検索機能にalgoliaを利用することで高速に検索結果が表示されるだけでなくLaravel側で更新したデータをalogolia内に保存したデータと同期することができます。
algoliaにデータをimportすることでローカルのデータからデータを取得するのではなくalgoliaにアクセスを行いalgoliaに保存されているデータから検索結果を取得しています。検索だけではなくデータの追加や更新もLaravel Scoutを通してalgoliaと同期できるため、一度設定を行ってしまえばローカルのデータベースにアクセスしているように処理を行うためalogliaを意識することはありません。
本文書ではalgoliaのアカウントの作成から検索方法の実行方法、ローカルデータベースとalgoliaのデータ同期について確認を行っていきます。
目次
algoliaについて
検索機能についてこれまで気にしたことがなければalgoliaという名前を聞いたことがない人もいるのではないでしょうか。algoliaはSearch as a serviceとして検索機能のみをクラウドサービスのSaaSとして提供しておりLaravelのドキュメントの検索に利用されているだけではなくTailwindcss, Vue.js, Nuxt.js, Reactなど本ブログで紹介するオープソースのプロダクトだけではなくさまざまなサイトで利用されています。実際にそれらのサイトで検索を実施してみると検索結果が高速で表示されることを体感できるかと思います。
algoliaが利用されているかどうかは検索結果の右下に表示される”search by algolia”で判断することができます。

algoliaの使い方の流れをブログを例に説明します。サーバに保存されたブログのタイトルやコンテンツのデータをalgoliaにインポートします。ブログに検索機能を実装し検索を実施するとAPI経由でalgoliaに検索文字列が渡されます。渡した文字列を使ってalgolia内で処理が行われすぐに検索結果を戻すので戻した結果をサーバが受け取り表示させます。流れは非常にシンプルで仕組み自体は難しいものではありません。


Laravel環境の構築
Laravel Scoutパッケージをインストールする前にLaravelの環境を構築します。Laravel構築の手順については詳細な説明は行っていません。
laravel newコマンドで新規のLaravelプロジェクトを作成します。
% laravel new laravel_scout
データベースは簡易的に作成できるSQLiteを利用します。
% touch database/database.sqlite
.envファイルを開いてDATABASE_CONNECTIONをsqliteに変更します。DB_CONNECTION以外の環境変数のDB_*は削除してください。
DB_CONNECTION=sqlite
php migrateコマンドを実行してテーブルが作成できることを確認します。
% php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (3.05ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (2.77ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (1.91ms)
Laravel Scoutを利用して検索を行うために利用するのはusersテーブルです。usersテーブルにダミーデータを登録するためにLaravel Seeding機能を利用します。
日本語のダミーデータが作成できるようにconfig/app.phpファイルのfaker_localeの値をja_JPに変更します。
'faker_locale' => 'ja_JP',
次にdatabase¥seeders¥DatabaseSeeder.phpファイルを開いて100名分のユーザデータを登録できるように変更を行います。
public function run()
{
\App\Models\User::factory(100)->create();
}
php artisan db:seedコマンドを実行してダミーデータをusersテーブルに追加します。
% php artisan db:seed
Database seeding completed successfully.
本文書ではデータベースの管理ソフトのTablePlusを利用して作成したSQLiteデータベースにアクセスします。100件分のダミーデータを確認することができます。

Laravel/Scoutパッケージのインストール
Laravelのインストールと環境の構築が完了したら、Laravel/Scoutのパッケージのインストールを行います。
% composer require laravel/scout
インストール完了後、下記のコマンドでScoutのConfigファイルの作成を行います。configフォルダの中にscout.phpファイルが作成されます。
% php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
検索を行いたいモデルにLaravel¥Scout¥Seachableトレイトを設定します。app¥Models¥User.phpファイルを開いて設定を行ってください。
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Scout\Searchable;
class User extends Authenticatable
{
use HasFactory, Notifiable, Searchable;
Algoliaでのアカウント作成
Algoliaでアカウントを作成するためにalgolia.comにアクセスします。トライアル期間が14日間と表示されていますがフリープランも準備されています。アカウントの作成後、アクセスするためのAPIキーを取得します。

アカウント作成画面が表示されるのでメールアドレスを入力するかGmail, GitHubのアカウントを利用してください。
クレジットカードの入力は必要ありません。

名前の入力画面が表示されるので、必須項目を入力してください。電話番号は必須ではありません。

最後の入力項目で会社の名前や規模、役割を選択してください。

メールが送信されるのでメールがalgoliaから届いているかメールボックスを確認してください。

Algoliaから送信されたメールを確認して” Confirm my email”ボタンをクリックしてください。

データセンターの場所を選択する画面が表示されます。日本からのレスポンスタイムは77MSであることがわかります。Japanを選択して”Continue Configuration”ボタンをクリックしてください。

アカウントの作成が完了するとalgoliaのダッシュボードが表示されます。

以上でalogoliaのアカウントの作成は完了です。
フリープランへの変更
ダッシュボードの上部にトライアル期間の残り日数が表示されているのでフリープランへ変更するために”Click here to upgrade your plan”をクリックします。

クリックするとプランの選択画面が表示されます。Freeを選択してください。

選択後スクロールをして”Review and Confirm”ボタンをクリックしてください。

terms of serviceやPrivacy Policyを確認しチェックしてUpdate Planボタンをクリックしてください。

Laravel Scoutの設定
先ほど作成したscout.phpファイルを開いてAlgoliaのAPIキーの設定を行います。
'algolia' => [
'id' => env('ALGOLIA_APP_ID', ''),
'secret' => env('ALGOLIA_SECRET', ''),
],
idとsecretを設定しますがALGOLIA_APP_IDとALGOLIA_SECRETは.envファイルから設定を行います。
.envファイルを開いてALGOLIA_APP_IDとALGOLIA_SECRETの環境変数を追加してAlgoliaから取得できるApplication IDとAdmin API KEYを設定します。
ALGOLIA_APP_ID=XXXXXX
ALGOLIA_SECRET=XXXXXXXXXXXXXXXXXXXXXXX
左のメニューのAPI KeysをクリックしてApplication IDとAdmin Keyを取得してください。Admin Keyを利用することで検索だけではなくデータの登録を行うことができます。


idとsecretの設定ができたらalgoliasearch-client-phpのインストールを行います。
% composer require algolia/algoliasearch-client-php

ここまでの設定が完了するとusersテーブルの内容をalogoliaにインポートすることができます。
インポートはphp artisan scout:importコマンドを利用します。インポートの完了メッセージが表示されているのでalgoliaのダッシュボードを確認します。
% php artisan scout:import "App\Models\User"
Imported [App\Models\User] models up to ID: 100
All [App\Models\User] records have been imported.
ダッシュボードのOverviewを確認するとレコードが100件登録されていることが確認できます。

データをimportすることでalgoliaを利用するために必要となるタスクの中でindexの作成とデータのimportが完了します。最後に検索する属性の設定する必要があります。


usersテーブル内のどの列の情報を検索対象とするかをSearchable attributesで選択します。nameとemailを選択します。設定完了後、検索を実施するとnameとemailにある値のみが検索対象となります。

Laravel Scoutの動作確認
検索を行う
Userモデルを使って検索を行ってみましょう。まずはalogoliaからデータが取得できるかweb.phpにコードを記述して確認します。
UserモデルではSearchableトレイトを利用しているのでsearchメソッドを使うことができます。searchメソッドの引数に検索したい文字を入力することで検索結果が戻されます。
Route::get('/users',function(){
$users = \App\Models\User::search('山')->get();
dd($users);
});
実行すると文字列の”山”を含むデータが18件取得することできました。
Algoliaを利用するためにインストールしたalgoliasearch-client-phpパッケージのみを利用しても下記のように検索を行うことができます。Searchableトレイトの中でも同様の処理が行われています。
Route::get('/users',function(){
// $users = \App\Models\User::search('山')->get();
$client = Algolia\AlgoliaSearch\SearchClient::create(
env('ALGOLIA_APP_ID'),
env('ALGOLIA_SECRET')
);
$index = $client->initIndex('users');
$results = $index->search('山');
dd($results);
});
戻り値は配列ですがhitsに18件のデータが含まれていることが確認できます。

データの追加
通常はコントローラーを作成してデータの作成を行いますが、簡易的に行えるtinkerを利用してユーザデータを追加してみましょう。tinkerはLaravelのインストールフォルダでphp artisan tinkerで起動します。
% php artisan tinker
Psy Shell v0.10.6 (PHP 7.4.10 — cli) by Justin Hileman
>>> $user = new App\Models\User;
=> App\Models\User {#3366}
>>> $user->name = "明智光秀";
=> "明智光秀"
>>> $user->email = "kiringakuru@test.com";
=> "kiringakuru@test.com"
>>> $user->password = bcrypt('password');
=> "$2y$10$ImaIyjf7sGrJNvLLblRSIuCblqcnSvmIOaVjs8iQM4n6HjegkBQs."
>>> $user->save();
ユーザを登録後にalogoliaのダッシュボードでレコード数を確認します。1件追加したので101件になっていることが確認できます。またダッシュボード上で追加したユーザの名前を検索することができます。

データの更新
先ほど追加したデータの名前を更新してalgoliaに更新が反映されるか確認を行います。
>>> $user->name = '明智十兵衛';
=> "明智十兵衛"
>>> $user->update();
ダッシュボードで検索すると更新した名前になっていることが確認できます。

データの削除
最後にデータの削除も確認しておきます。
>>> $user = App\Models\User::find(102);
=> App\Models\User {#4359
id: "102",
name: "明智光秀",
email: "kiringakuru@test.com",
email_verified_at: null,
created_at: "2021-02-18 09:57:13",
updated_at: "2021-02-18 09:57:13",
}
>>> $user->delete();
=> true
一度削除をすると以下のエラーメッセージでalgoliaへの反映が失敗しましたが再度同じ流れで実行すると問題なく削除も反映されました。
GuzzleHttp\Exception\ConnectException with message ‘cURL error 28: Operation timed out after 2001 milliseconds with 0 out of 0 bytes received (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://XXXXXX.algolia.net/1/indexes/users/batch’
このようにLaravel上で行ったUserモデルへの処理がすべてalgoliaに自動で反映されていることがわかりました。
algolia上のレコードをすべて削除
Laravelからalgolia上のレードを一括で削除したい場合は下記のコマンドで可能です。
% php artisan scout:flush "App\Models\User"
algoliaのダッシュボードからもデータの一括削除、個別削除は可能です。
Indexを削除
alogolia上のIndexのUsersを削除したい場合はダッシュボードから行うことができます。
IndicesのページにあるManage indexのプルダウンメニューからDeleteを選択します。

削除の確認画面が表示されるので削除したい場合はDELETEを入力して”Delete”ボタンをクリックしてください。


algolia上のindexの名前を変更する
デフォルトではusersテーブルをalgoliaにimportするとindex名はusersという名前になります。index名を指定するためにUser.phpファイルのsearchableAsメソッドに変更したい名前を入力します。ここではuser_indexという名前にしています。任意の名前をつけてみてください。
public function searchableAs()
{
return 'user_index';
}
変更後に一括でimportを行うとindex名がuser_indexになっていることが確認できます。
% php artisan scout:import "App\Models\User"
Imported [App\Models\User] models up to ID: 100
All [App\Models\User] records have been imported.
index名はダッシュボードで確認することができます。

algoliaでindexを作成後にsearchableAsメソッドで設定した名前を変更するとalgoriaからデータは取得できなくなります。searchableAsメソッドで指定した値をalgoriaにアクセスする際に利用していためです。

ダッシュボード上ではindex名の変更も可能のようです(未実施)。
IndexのレコードのObject IDを変更
デフォルトではレコードのObjectIDにはテーブルのid列が設定されます。これを変更したい場合はUser.phpファイルに以下を追加することでObjectIDがidからemailに変更になります。
public function getScoutKey()
{
return $this->email;
}


importするテーブルの列を制限する
デフォルトではimportを行うとすべてのデータがalgolia側に保存されます。検索対象にならない列をimportさせないこともできます。
User.phpファイルののtoSearchableArrayメソッドの中でunsetメソッドを利用してimportしない列の情報を削除します。
public function toSearchableArray()
{
$array = $this->toArray();
unset($array['created_at']);
unset($array['updated_at']);
unset($array['email_verified_at']);
return $array;
}
時刻に関する列をimportの対象外としたのでalgolia上のusersのデータを一括でimportするとusersデータは下記のように表示されます。

ここまででLaravel Scoutとalogoliaの基本的な使用方法を理解することができました。次回の文書ではVue.jsまたはReactを利用して検索機能をUI WidgetsのInstantSearchを利用して実装します。