🌟

【API 作成編】Rails API × Meilisearch SDK をつかってみる

2024/01/12に公開

こちらの記事の続きです。

これまでの振り返り

前々回、Docker を用いて、 Rails × Postgresql × Meilisearch の環境を作成しました。

前回は、meilisearch-rails gem を導入しました。
Postgresql の users テーブルにテストデータの投入と、上記 gem を用いて Meilisearch インデックスの構築を行いました。
また、ActiveRecord メソッドからレコードを操作することで、その変更(レコード作成、更新、削除)が Meilisearch のインデックスにも同期されることを確認しました。

今回は、検索 API を作成してみて実際どんな取り回しになるのかを試してみたいと思います。
また、ついでに レコード作成、更新、削除 API も作成してみて、users テーブルと Meilisearch のインデックスが無事に同期されるのかどうかも確認してみます。

事前準備

コントローラのテンプレート作成とルート設定をします。

コントローラ作成

controller を作成します。
以下のコマンドを実行して、users_controller のテンプレートを作成しておきます。

rails g controller api/users/users_controller

ルート設定

ルートの設定も変更しておきます。
API 想定なので、一応 api/ 以下で実行できるようにします。

config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :users do
      get "", to: "users#index"
      post "", to: "users#create"
      patch ":id", to: "users#update"
      delete ":id", to: "users#delete"
    end
  end
end

API 実装

さきに users_controller.rb を載せます。

api/users/users_controller.rb
class Api::Users::UsersController < ApplicationController

  def index
    user = User.ms_search(query_param)
    render json: user,
           root: 'data',
           adapter: :json
  end

  def create
    user = User.create!(required_params)
    render json: user,
           root: 'data',
           adapter: :json
  end

  def update
    user = User.find(params[:id])
    user.update!(required_params)
    render json: user,
           root: 'data',
           adapter: :json
  end

  def delete
    user = User.find(params[:id])
    user.destroy!
    render json: user,
           root: 'data',
           adapter: :json
  end

  private
    def query_param
      return "" if params[:q].blank?
      params[:q]
    end

    def required_params
      params.require(:user).permit(:user_system_id, :name, :name_ruby, :url)
    end
end

index について

エンドポイントは api/users で GET メソッドで呼び出された際に実行されます。

クエリパラメータとして q を渡し、検索ワードとして User.ms_search メソッドの引数として利用されます。
q が渡されなかった場合は、空文字を検索ワードとして渡すように query_param メソッドで制御しています。

疎通確認
curl -X GET "localhost:3000/api/users?q=100006"
# => [
#      {
#        "data": {
#	   "id":100006,
#	   "user_system_id":"",
#	   "name":"updated_post_name",
#	   "name_ruby":"updated_post_name"...
#        }
#      }...
#    ]

create について

エンドポイントは api/users で POST メソッドで呼び出された際に実行されます。

users テーブルにレコードが作成され、Meilisearch 側の User インデックスにもドキュメントが新しく作成されます。

疎通確認
curl \
    -X POST http://localhost:3000/api/users \
    -d '{"user_system_id": "", "name": "post_name", "name_ruby": "post_name", "url": "test@example.com"}' \
    -H "Content-Type: application/json"
# => {
#      "data": {
#        "id":100007,
#        "user_system_id":"",
#        "name":"post_name",
#        "name_ruby":"post_name", ...
#      }
#    }

Meilisearch のプレビュー画面でも確認する。
右上にあるインデックス選択ボックスにドキュメントの総数が表示されているので、増減を確認することができます。

update について

エンドポイントは api/users/{任意のid} で PATCH メソッドで呼び出された際に実行されます。

users テーブルの特定レコードが更新され、Meilisearch 側の User インデックスのプライマリーキーが {任意のid} となっているドキュメントも更新されます。

疎通確認
curl -X PATCH http://localhost:3000/api/users/100007 \
     -d '{"name": "updated_post_name", "name_ruby": "updated_post_name"}' \
     -H "Content-Type: application/json"
# => {
#      "data": {
#        "id":100007,
#        "user_system_id":"",
#        "name":"updated_post_name",
#        "name_ruby":"updated_post_name", ...
#      }
#    }

create のときと同様に、Meilisearch のプレビュー画面で確認することができます。

delete について

エンドポイントは api/users/{任意のid} で DELETE メソッドで呼び出された際に実行されます。

users テーブルの特定レコードが削除され、Meilisearch 側の User インデックスのプライマリーキーが {任意のid} となっているドキュメントも削除されます。

疎通確認
curl -X DELETE http://localhost:3000/api/users/100007 \
     -H "Content-Type: application/json"
# => {
#      "data": {
#        "id":100007,
#        "user_system_id":"",
#        "name":"updated_post_name",
#        "name_ruby":"updated_post_name", ...
#      }
#    }

create のときと同様に、Meilisearch のプレビュー画面で確認することができます。
検索に出てこなくなるのでわかりにくいですが、右上にあるインデックス選択ボックスにドキュメントの総数が表示されているので、増減を確認することができます。

さいごに

前回も書きましたが、やっぱり自動的にインデックスを同期する機能がうれしいです。

今回は、meilisearch-rails の最低限の機能を使ってみましたが、実際の開発で使用するとなると以下のようなこともしたくなります。

  1. 検索フィールドの指定
  • 例) name フィールドを指定して検索する
  • 今回使用した users テーブルに限らずですが、文字列としては似がちになるが意味は全く異なるデータ(名前と都道府県とか、メールアドレスのドメインとブログ URL のドメインとか)が、同一ドキュメントに存在する仕様は避けられないと思います。
    そうした場合、全てのフィールドを指定して検索してしまうと、東京都のユーザーを検索したいのに「東さん」がヒットしてしまったり、@gmail.com アドレスのユーザーを検索したいのに、foobar.com ブログを運営しているユーザーがヒットしてしまったりします。
    なので、この点を考慮する必要はあるかなと思います。
  1. 条件つきインデックス
  • RDB テーブルの特定フィールドの値によって、Meilisearch にインデックスするかどうかを決める
  • 例) status フィールドの値が active のレコードのみインデックスする
  • meilisearch-sdk の魅力として、自動インデックス機能を挙げていました。
    しかし、実際の users テーブルには、アカウントの有効・無効を示すフィールドであったり、posts テーブルで記事の公開・非公開を示すフィールドであったりと、レコードの状態を示すフィールドがある場合があります。
    そのような場合、アカウント無効のデータや下書き中の記事は検索対象にしたくない場合もあり、その場合はそもそもインデックスしたくないです。
    この場合、meilisearch-sdk には条件つきインデックスを制御することができる機能があるので活用したいです。

これらはぱっと思いついただけで、他にもいろいろと考慮したいことがたくさんあると思います。
都度調べて記事に残していきたいと思います。

参考

https://github.com/meilisearch/meilisearch-rails

Discussion