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を検索に利用しているReact
algoliaを検索に利用しているReact

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

laravelとalgoliaの関係
laravelとalgoliaの関係
検索機能がサーバ側で簡単にできるようにUIウィジットが提供されています。Laravel Scoutのように他のフレームワークでもフレームワークと連携できるライブラリが存在します。

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件分のダミーデータを確認することができます。

TablePlusでユーザデータの確認
TablePlusでユーザデータの確認

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キーを取得します。

Algoliaトップページ
Algoliaトップページ

アカウント作成画面が表示されるのでメールアドレスを入力するかGmail, GitHubのアカウントを利用してください。

クレジットカードの入力は必要ありません。

メールアドレス入力画面
メールアドレス入力画面

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

個人情報の入力
個人情報の入力

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

会社、規模、役割の入力
会社、規模、役割の入力

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

メールアドレスの確認
メールアドレスの確認

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

メールの内容
メールの内容

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

データセンターの選択
データセンターの選択

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

Algoliaのダッシュボード
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とキーをAlgoliaから取得
IDとキーをAlgoliaから取得
Search-Only KeyとAdmin API KeyがありますがLaravel Scoutでは検索だけではなくデータの追加・更新・削除も行うのでAdmin API Keyを利用します。

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


 % composer require algolia/algoliasearch-client-php
algoliasearch-client-phpパッケージ単独でもalgoliaにアクセスを行い各種設定を行うことが可能です。algoliaのドキュメントにも使用方法は記載されているので理解を深めたい人はぜひローカルにパッケージをインストールして動作確認してみてください。

ここまでの設定が完了すると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件登録されていることが確認できます。

100件のデータ登録を確認
100件のデータ登録を確認

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

algoiaではindexの名前はlaravelのテーブル名に対応するのでデフォルトの設定ではimportしたusersテーブルと同じ名前がindexの名前usersになります。通常のindexは索引という意味でテーブルの列のイメージが強いので最初はalgoliaのindexの意味がわかりにくいかもしれません。
検索する属性を決める
検索する属性を決める

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

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件になっていることが確認できます。またダッシュボード上で追加したユーザの名前を検索することができます。

alogoliaに自動で登録
alogoliaに自動で登録

データの更新

先ほど追加したデータの名前を更新して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を選択します。

Indexの削除処理
Indexの削除処理

削除の確認画面が表示されるので削除したい場合はDELETEを入力して”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名はダッシュボードで確認することができます。

index名の変更の確認
index名の変更の確認

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

indexの名前が一致しない場合のエラー
indexの名前が一致しない場合のエラー

ダッシュボード上ではindex名の変更も可能のようです(未実施)。

IndexのレコードのObject IDを変更

デフォルトではレコードのObjectIDにはテーブルのid列が設定されます。これを変更したい場合はUser.phpファイルに以下を追加することでObjectIDがidからemailに変更になります。


public function getScoutKey()
{
    return $this->email;
}
ObjectIDをemailに変更
ObjectIDをemailに変更
LaravelのScoutでObjectIDを元にデータを取得するメソッドを見つけることができませんでしたがalogliaのライブラリのメソッドにはObjectIDを利用するgetObjectメソッドがあります。

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データは下記のように表示されます。

importする列を制限した場合
importする列を制限した場合

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