自分だけのクイズを作成しよう - Quipha
スポンサーリンク

【Laravel Scout】Meilisearchを使って全文検索を実装

Laravel

当サイトではアフィリエイト広告を利用しています。

検索

Meilisearchで確認

Meilisearchのダッシュボードで、検索ワードを指定して検索してみましょう。
非常に高速に検索結果が返ってきます。

Laravelで実装

web.phpに検索結果を返却する処理を追加します。
「ex」という文字列を検索して、Scoutを通じてMeilisearchから検索結果を取得します。

routes\web.php
use App\Models\Post;

Route::get('/search', function () {
    $posts = Post::search('ex')->get();  
    return $posts;
});

以下のURLにアクセスします。

http://localhost/search

応答結果を確認します。

もちろん日本語でも検索ができます。
Meilisearchの検索結果と同じ結果のJSONを取得することができました。

JSONの日本語を表示するために、Firefoxで確認しました。

データの登録・変更・削除

登録

モデルにSearchableを追加していますので、普通にDBに登録する処理でインデックスに登録できます。

use App\Models\Post;

$post = new Post;

$post->title = "タイトルです";
$post->body = "本文です";

$post->save();

DBに登録と同時に、インデックスにも登録されます。

インデックスを作成する条件を指定できます。
例えば、公開日が設定されているデータのみ、インデックスに登録することができます。

モデルを修正して、shouldBeSearchable関数を実装します。
以下のコードでは、公開日が設定されている場合にのみ、インデックスに登録できます。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Post extends Model
{
    use HasFactory;
    use Searchable;

    /**
     * 公開しているかどうか
     * 
     * @return bool
     */
    public function isPublished()
    {
        return !is_null($this->published_at);
    }

    /**
     * モデルを検索可能にする判定
     *
     * @return bool
     */
    public function shouldBeSearchable()
    {
        return $this->isPublished();
    }
}

saveなどで登録する際に適用されます。

変更

更新も通常通りの処理で、インデックスも更新されます。

use App\Models\Post;

$post = Post::find(101);

$post->title = "タイトル更新です";
$post->body = "本文更新です";

$post->save();

インデックスも更新されます。

削除

削除も同様で、以下のようなコードでインデックスから削除されます。

use App\Models\Post;

$post = Post::find(101);
$post->delete();
スポンサーリンク

APIキーの設定

Sailは標準でMeilisearchが使えるようになっていますが、マスターキーやAPIキーが設定されていないため、誰でも操作可能です。

開発で使う分には問題ありませんが、マスターキーを設定してみましょう。

docker-compose.ymlを修正します。
MEILI_MASTER_KEYを追加し、マスターキーを指定します。
‘master_key’という文字列を指定していますが、任意の文字列を指定してください。

    meilisearch:
        image: 'getmeili/meilisearch:latest'
        ports:
            - '${FORWARD_MEILISEARCH_PORT:-7700}:7700'
        volumes:
            - 'sail-meilisearch:/meili_data'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "wget", "--no-verbose", "--spider",  "http://localhost:7700/health"]
            retries: 3
            timeout: 5s
        environment:
            MEILI_MASTER_KEY: 'master_key'

docker-compose.ymlを変更後、Sailを再起動します。

Meilisearchのダッシュボードを開いてみましょう。
マスターキーを入力して、データにアクセスできるようになります。

マスターキーを追加すると、自動的に二つのキーが追加になります。

  • Default Search API Key (検索用APIキー)
  • Default Admin API Key (管理用APIキー)

以下のコマンドを実行し、APIキーを確認します。
‘master_key’はマスターキーを指定します。

$ curl \
  -X GET 'http://localhost:7700/keys' \
  -H 'Authorization: Bearer master_key'

以下のようなJSONデータが取得できました。
“key”という項目がAPIキーになります。

{"results":[
{"name":"Default Search API Key","description":"Use it to search from the frontend","key":"xxxxxxx","uid":"xxxxxxx","actions":["search"],"indexes":["*"],"expiresAt":null,"createdAt":"2022-10-19T13:33:29.943963186Z","updatedAt":"2022-10-19T13:33:29.943963186Z"},
{"name":"Default Admin API Key","description":"Use it for anything that is not a search operation. Caution! Do not expose it on a public frontend","key":"xxxxxxx","uid":"xxxxxxx","actions":["*"],"indexes":["*"],"expiresAt":null,"createdAt":"2022-10-19T13:33:29.934972068Z","updatedAt":"2022-10-19T13:33:29.934972068Z"}
],"offset":0,"limit":20,"total":2}

.envに、Default Admin API Keyのkeyを指定します。

MEILISEARCH_KEY=xxxx

APIキーを追加した場合は、モデルの保存時に正常にインデックスに登録できます。
ただしAPIキーが間違っている場合は、以下のエラーが表示されます。

The Authorization header is missing. It must use the bearer authorization method.

検索だけの権限が必要であれば、Default Search API Keyを使用しましょう。

APIキーによるアクセス制限ができますので、詳しくは公式サイトをご覧ください。

Master key and API keys — Meilisearch documentation
Protect your Meilisearch instance by setting a master key and configuring API keys with granular permissions.

データベースエンジンを使う

今回はLaravel Scoutを使いエンジンにMeilisearchを使いましたが、データベースを使用することもできます。

まずは、Meilisearchをエンジンとしている場合、どのようなSQL文を発行しているのか確認してみましょう。

\DB::enableQueryLog();

$posts = Post::search('赤')->get();

\Log::debug(\DB::getQueryLog());

ログファイルを確認しましょう。
IN句にIDを指定して検索していました。
ScoutはMeilisearchの検索結果からIDだけを取得し、Eloquentで取得し直している感じでしょうか。

[2022-10-19 13:48:41] local.DEBUG: array (
  0 => 
  array (
    'query' => 'select * from `posts` where `posts`.`id` in (89, 53, 90, 92, 41, 72, 52, 36, 38, 76, 98, 20, 1, 37, 84, 10, 91, 99, 97, 2)',
    'bindings' => 
    array (
    ),
    'time' => 2.76,
  ),
)  

次に、エンジンをデータベースに変更してみます。
.envを修正します。

SCOUT_DRIVER=database

これにより、Meilisearchは使用されず、データベースのみ使用されます。

モデルを修正し、以下を追加します。
検索対象の項目を指定します。

app\Models\Post.php
    /**
     * モデルに対するインデックス可能なデータの配列を取得
     *
     * @return array
     */
    public function toSearchableArray()
    {
        return [
            'title' => $this->title,
            'body' => $this->body,
        ];
    }

先ほど同様、検索を行いログを確認しました。
Like検索で直接データベースから検索していることが分かります。

[2022-10-19 14:04:20] local.DEBUG: array (
  0 => 
  array (
    'query' => 'select * from `posts` where (`posts`.`title` like ? or `posts`.`body` like ?) order by `id` desc',
    'bindings' => 
    array (
      0 => '%赤%',
      1 => '%赤%',
    ),
    'time' => 3.02,
  ),
)  

開発環境にMeilisearchが用意できない場合など、とりあえずデータベースでLaravel Scoutを介した実装を行うのに使えると思います。

ただのLike検索ですので、大量データの全文検索ではパフォーマンスが悪いと思いますが、本番環境ではMeilisearchに入れ替える、といった使い方ができると思います。

その他

初学者へ

Laravelを初めて触る方へ向け、手順やアドバイスをまとめました。

外部サーバーへ公開

作成したアプリは公開して使ってもらいましょう!
Laravelアプリケーションを外部公開する方法をまとめました。

脆弱性対策

脆弱性を抱えたアプリケーションの場合、攻撃を受ける可能性があり大変危険です。
作成したアプリケーションは、脆弱性対策も意識しましょう。

GitHubと連携

GitHubと連携する方法を解説しました。
プロジェクトの管理はGitHubを活用しましょう。

GitHub Copilot

GitHub Copilotを導入し、AIにコーディングをサポートしてもらうこともできます。

さいごに

今回は、Laravel Scoutを使い、検索エンジンにMeilisearchを使用して実装を行いました。
是非、全文検索を実装する際に参考にしてください。

他にも私のブログで、Laravelについて解説している記事がありますのでご覧ください。

\オススメ/

コメント

タイトルとURLをコピーしました