🔎
MeiliSearch使ってみる Rails編
↑前回、簡単にMeiliSearchを使ってみたので今回はRailsから使ってみるのを試してみます
環境
- OS: Ubuntu 20.04
- MeiliSearch: 0.22.0
- Ruby: 3.0.2
- Rails: 6.1.4.1
Rails環境を作る
rails new rails-sample --api --database=postgresql -B -M -C -S -J -T --skip-yarn --skip-jbuilder
Dockerfile
FROM ruby:3.0.2-alpine3.13 AS ruby
## Development
FROM ruby AS dev
RUN apk update \
&& apk add --no-cache \
gcc \
g++ \
libc-dev \
linux-headers \
make \
postgresql \
tzdata \
git \
graphviz \
&& apk add --virtual build-dependencies --no-cache \
postgresql-dev \
libxml2-dev \
build-base \
curl-dev
RUN mkdir -p /app
ENV HOME /app
WORKDIR $HOME
COPY ./Gemfile* ./
ARG bundle_install_options="--without development test doc --jobs 4"
RUN bundle config github.https true \
&& bundle install $bundle_install_options \
&& apk del build-dependencies
COPY . /app
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
## Production
FROM ruby AS prod
RUN apk update \
&& apk add --no-cache \
gcc \
g++ \
libc-dev \
linux-headers \
make \
postgresql \
tzdata \
&& cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&& apk del --purge tzdata
RUN mkdir -p /app
ENV HOME /app
ENV RAILS_ENV production
WORKDIR $HOME
COPY /usr/local/bundle /usr/local/bundle
COPY /app/app $HOME/app
COPY /app/bin $HOME/bin
COPY /app/config $HOME/config
COPY /app/db $HOME/db
COPY /app/lib $HOME/lib
COPY /app/public $HOME/public
COPY /app/Gemfile* $HOME/
COPY /app/Rakefile $HOME/
COPY /app/config.ru $HOME/
RUN mkdir -p tmp/pids
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
docker-compose.yml
version: "3.7"
services:
meilisearch:
container_name: meilisearch
image: getmeili/meilisearch:v0.22.0
volumes:
- meili-data:/data.ms
environment: []
ports:
- 7700:7700
# 追加
postgres:
image: postgres:13.2-alpine
container_name: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST=postgres
- POSTGRES_DB=rails_sample_dev
ports:
- "5433:5432"
volumes:
- pg-data:/var/lib/postgresql/data
rails:
container_name: rails
build:
context: ./rails-sample
args:
bundle_install_options: --jobs 10
target: dev
volumes:
- ./rails-sample:/app
environment:
RAILS_LOG_TO_STDOUT: 1
TZ: Asia/Tokyo
ports:
- 3300:3000
volumes:
meili-data:
driver: local
pg-data:
driver: local
Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.0.2'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
gem 'rails', '~> 6.1.4', '>= 6.1.4.1'
# Use postgresql as the database for Active Record
gem 'pg', '~> 1.1'
# Use Puma as the app server
gem 'puma', '~> 5.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.4', require: false
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
# gem 'rack-cors'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
gem 'listen', '~> 3.3'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
# 追加
gem "meilisearch-rails", "~> 0.2.3"
検索対象のモデルはUserにします
/app # bin/rails g model user
Running via Spring preloader in process 74
invoke active_record
create db/migrate/20211004144942_create_users.rb
create app/models/user.rb
20211004144942_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :given_name, null: false
t.string :family_name, null: false
t.integer :age
t.timestamps
end
end
end
あとでフルネームを検索対象にする為に、姓・名を分けておきました。
データ投入確認
/app # bin/rails db:migrate
== 20211004144942 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0076s
== 20211004144942 CreateUsers: migrated (0.0077s) =============================
/app # bin/rails c
Running via Spring preloader in process 136
Loading development environment (Rails 6.1.4.1)
irb(main):001:0> User.create!(given_name: "太郎", family_name: "山田", age: 10)
TRANSACTION (0.1ms) BEGIN
User Create (0.5ms) INSERT INTO "users" ("given_name", "family_name", "age", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["given_name", "太郎"], ["family_name", "山田"], ["age", 10], ["created_at", "2021-10-04 14:56:38.430087"], ["updated_at", "2021-10-04 14:56:38.430087"]]
TRANSACTION (2.4ms) COMMIT
=> #<User:0x00007febd8d77a00 id: 1, given_name: "太郎", family_name: "山田", age: 10, created_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00, updated_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00>
irb(main):002:0> User.create!(given_name: "花子", family_name: "鈴木", age: nil)
TRANSACTION (0.3ms) BEGIN
User Create (0.4ms) INSERT INTO "users" ("given_name", "family_name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["given_name", "花子"], ["family_name", "鈴木"], ["created_at", "2021-10-04 14:56:53.526337"], ["updated_at", "2021-10-04 14:56:53.526337"]]
TRANSACTION (2.5ms) COMMIT
=> #<User:0x00007febd62b9b60 id: 2, given_name: "花子", family_name: "鈴木", age: nil, created_at: Mon, 04 Oct 2021 14:56:53.526337000 UTC +00:00, updated_at: Mon, 04 Oct 2021 14:56:53.526337000 UTC +00:00>
Rails内のMeiliSearch設定
これを見て進めていきます
MeiliSearchの設定
config/initializers/meilisearch.rb
MeiliSearch.configuration = {
meilisearch_host: "http://meilisearch:7700",
meilisearch_api_key: ENV["MEILI_MASTER_KEY"],
}
Modelの設定
app/models/user.rb
class User < ApplicationRecord
include MeiliSearch
meilisearch {
attribute :given_name, :family_name, :age
attributes_to_highlight ['*']
}
end
Indexにデータを登録
/app # bin/rails c
Running via Spring preloader in process 82
Loading development environment (Rails 6.1.4.1)
irb(main):001:0> User.search("山田")
=> []
当然まだデータが無いので結果はからっぽですね
データを入れます
irb(main):002:0> User.reindex!
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1000]]
=> nil
検索してみます
irb(main):003:0> User.search("山田")
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 1]]
=> [#<User:0x00007f7999f004f0 id: 1, given_name: "太郎", family_name: "山田", age: 10, created_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00, updated_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00>]
取れましたね。
irb(main):009:0> u = User.search('山田').first
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 1]]
=> #<User:0x00007f7998a04bf0 id: 1, given_name: "太郎", family_name: "山田", age: 10, created_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00, updated_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00>
irb(main):010:0> u.formatted
=> {"id"=>"1", "given_name"=>"太郎", "family_name"=>"<em>山田</em>"}
ハイライトされたデータは formatted
で取得できるみたいです。
Indexへのデータ追加・更新・削除
定期的に reindex!
してもいいんですが、create時に自動で連携してほしいですね
irb(main):004:0> User.create!(given_name: "拓哉", family_name: "木村", age: 50)
TRANSACTION (0.4ms) BEGIN
User Create (0.6ms) INSERT INTO "users" ("given_name", "family_name", "age", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["given_name", "拓哉"], ["family_name", "木村"], ["age", 50], ["created_at", "2021-10-04 15:14:40.150366"], ["updated_at", "2021-10-04 15:14:40.150366"]]
TRANSACTION (2.5ms) COMMIT
=> #<User:0x00007f7996db8ed8 id: 3, given_name: "拓哉", family_name: "木村", age: 50, created_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00, updated_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00>
irb(main):005:0> User.search("木村")
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 3]]
=> [#<User:0x00007f7996b4a098 id: 3, given_name: "拓哉", family_name: "木村", age: 50, created_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00, updated_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00>]
どうやら、デフォルトでそうなるみたいです。
destroyも同様です
irb(main):011:0> User.last
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<User:0x00007f79971970e0 id: 3, given_name: "拓哉", family_name: "木村", age: 50, created_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00, updated_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00>
irb(main):012:0> User.last.destroy
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.1ms) BEGIN
User Destroy (0.3ms) DELETE FROM "users" WHERE "users"."id" = $1 [["id", 3]]
TRANSACTION (0.6ms) COMMIT
=> #<User:0x00007f799712ac88 id: 3, given_name: "拓哉", family_name: "木村", age: 50, created_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00, updated_at: Mon, 04 Oct 2021 15:14:40.150366000 UTC +00:00>
irb(main):013:0> User.search("木村")
=> []
ActiveRecordからデータを更新してみます
irb(main):001:0> User.last
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<User:0x00007f03d68fbdb8 id: 7, given_name: "ぱなこ", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:25:15.523735000 UTC +00:00>
irb(main):002:0> User.last.update!(age: 60)
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.1ms) BEGIN
User Update (0.5ms) UPDATE "users" SET "age" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["age", 60], ["updated_at", "2021-10-05 12:28:42.820953"], ["id", 7]]
TRANSACTION (2.4ms) COMMIT
=> true
irb(main):003:0> u = User.search("木村").first
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 4]]
=> #<User:0x00007f03d7e1d9f8 id: 4, given_name: "拓哉", family_name: "木村", age: 66, created_at: Mon, 04 Oct 2021 15:21:40.019247000 UTC +00:00, updated_at: Mon, 04 Oct 2021 15:25:07.344447000 UTC +00:00>
irb(main):004:0> u.update!(age: 100)
TRANSACTION (0.2ms) BEGIN
User Update (0.4ms) UPDATE "users" SET "age" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["age", 100], ["updated_at", "2021-10-05 12:29:56.073582"], ["id", 4]]
TRANSACTION (2.4ms) COMMIT
=> true
irb(main):005:0> User.search("木村").first.formatted
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 4]]
=> {"id"=>"4", "given_name"=>"拓哉", "family_name"=>"<em>木村</em>", "age"=>100}
ちゃんと更新されてますね。
非同期で更新
ActiveJobとSidekiqを使う為、先に準備します
Sidekiq準備
docker-compose.yml
version: "3.7"
x-rails-envs: &rails-envs
RAILS_LOG_TO_STDOUT: 1
REDIS_URL: redis://redis:6379/0
TZ: Asia/Tokyo
MEILI_MASTER_KEY: api-key
x-rails-build: &rails-build
context: ./rails-sample
args:
bundle_install_options: --jobs 10
target: dev
services:
meilisearch:
container_name: meilisearch
image: getmeili/meilisearch:v0.22.0
volumes:
- meili-data:/data.ms
environment:
MEILI_MASTER_KEY: api-key
ports:
- 7700:7700
postgres:
image: postgres:13.2-alpine
container_name: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST=postgres
- POSTGRES_DB=rails_sample_dev
ports:
- "5433:5432"
volumes:
- pg-data:/var/lib/postgresql/data
redis:
container_name: redis
image: redis:6.2.5-alpine3.14
ports:
- 6380:6379
volumes:
- redis-data:/data
command: redis-server --appendonly yes
rails:
container_name: rails
build: *rails-build
volumes:
- ./rails-sample:/app
command: /bin/sh -l -c 'bundle && bundle exec puma -C config/puma.rb'
environment: *rails-envs
ports:
- 3300:3000
depends_on:
- postgres
- redis
sidekiq-worker:
build: *rails-build
command: /bin/sh -l -c 'bundle && bundle exec sidekiq -c 2 -q default -q meilisearch'
restart: always
volumes:
- ./rails-sample:/app
environment: *rails-envs
depends_on:
- postgres
- redis
volumes:
meili-data:
driver: local
pg-data:
driver: local
redis-data:
driver: local
-
redis
を追加 -
sidekiq-worker
を追加- MeiliSearchは
meilisearch
という queueにJobをキューイングするらしいので、オプションでmeilisearchキューも処理するように設定
- MeiliSearchは
-
rails
とsidekiq-worker
はビルド設定と環境変数が同じなので共用の値として定義した
Gemfile
...
# 以下追加
gem "sidekiq", "~> 6.2"
gem "hiredis", "~> 0.6.3"
gem "redis-namespace", "~> 1.8"
config/initializer/sidekiq.rb
redis_url = ENV["REDIS_URL"]
redis_password = ENV["REDIS_PASSWORD"]
redis_config = { url: redis_url, password: redis_password, driver: :hiredis, namespace: :sidekiq }
Sidekiq.configure_server do |config|
config.redis = redis_config
end
Sidekiq.configure_client do |config|
config.redis = redis_config
end
config/application.rb
module RailsSample
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.1
...
config.active_job.queue_adapter = :sidekiq
end
end
起動確認
❯ docker-compose exec rails sh
/app # sidekiq
2021-10-05T12:04:58.813Z pid=109 tid=2h5 INFO: Booting Sidekiq 6.2.2 with redis options {:url=>"redis://redis:6379/0", :password=>nil, :driver=>:hiredis, :namespace=>:sidekiq}
m,
`$b
.ss, $$: .,d$
`$$P,d$P' .,md$P"'
,$$$$$b/md$$$P^'
.d$$$$$$/$$$P'
$$^' `"/$$$' ____ _ _ _ _
$: ,$$: / ___|(_) __| | ___| | _(_) __ _
`b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` |
$$: ___) | | (_| | __/ <| | (_| |
$$ |____/|_|\__,_|\___|_|\_\_|\__, |
.d$$ |_|
2021-10-05T12:04:58.888Z pid=109 tid=2h5 INFO: Booted Rails 6.1.4.1 application in development environment
2021-10-05T12:04:58.888Z pid=109 tid=2h5 INFO: Running in ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-musl]
2021-10-05T12:04:58.888Z pid=109 tid=2h5 INFO: See LICENSE and the LGPL-3.0 for licensing details.
2021-10-05T12:04:58.888Z pid=109 tid=2h5 INFO: Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org
2021-10-05T12:04:58.919Z pid=109 tid=2h5 INFO: Starting processing, hit Ctrl-C to stop
起動しましたね。
Index更新を非同期化
app/models/user.rb
class User < ApplicationRecord
include MeiliSearch
meilisearch enqueue: true do
attribute :given_name, :family_name, :age
attributes_to_highlight ['*']
end
end
meilisearch
に enqueue: true
という引数を指定する。
実行してみましょう
irb(main):001:0> User.create!(given_name: "はなこ", family_name: "田中", age: 1)
TRANSACTION (0.1ms) BEGIN
User Create (0.4ms) INSERT INTO "users" ("given_name", "family_name", "age", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["given_name", "はなこ"], ["family_name", "田中"], ["age", 1], ["created_at", "2021-10-05 12:19:55.103263"], ["updated_at", "2021-10-05 12:19:55.103263"]]
TRANSACTION (2.4ms) COMMIT
Enqueued MeiliSearch::MSJob (Job ID: 04ff0d39-d334-4add-80c5-1fdf036468e6) to Sidekiq(meilisearch) with arguments: #<GlobalID:0x00007f03d7bc6c18 @uri=#<URI::GID gid://rails-sample/User/7>>, "ms_index!"
=> #<User:0x00007f03d7db8850 id: 7, given_name: "はなこ", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00>
MeiliSearch::MSJobがキューイングされたっぽいですね
Sidekiqのログを見てみます
sidekiq-worker_1 | 2021-10-05T12:21:23.990Z pid=1 tid=18nt class=MeiliSearch::MSJob jid=82a00e7bf6f3fc6c12c5b0d2 INFO: start
sidekiq-worker_1 | 2021-10-05T12:21:24.133Z pid=1 tid=18nt class=MeiliSearch::MSJob jid=82a00e7bf6f3fc6c12c5b0d2 elapsed=0.144 INFO: done
ちゃんと処理されてました
検索してみます
irb(main):003:0> User.search("はな")
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 7]]
=> [#<User:0x00007f03da149468 id: 7, given_name: "はなこ", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00>]
取れました。
念の為、ActiveRecordからデータを更新してみます。
irb(main):004:0> u = User.search("はな").first
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 7]]
=> #<User:0x00007f03d5b12e18 id: 7, given_name: "はなこ", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00>
irb(main):005:0> u.update!(given_name: "ぱなこ")
TRANSACTION (0.4ms) BEGIN
User Update (0.4ms) UPDATE "users" SET "given_name" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["given_name", "ぱなこ"], ["updated_at", "2021-10-05 12:25:15.523735"], ["id", 7]]
TRANSACTION (2.4ms) COMMIT
Enqueued MeiliSearch::MSJob (Job ID: b5f42646-c3f2-4683-ab93-ff349ef5599b) to Sidekiq(meilisearch) with arguments: #<GlobalID:0x00007f03da562248 @uri=#<URI::GID gid://rails-sample/User/7>>, "ms_index!"
=> true
irb(main):006:0> User.search("はな")
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 7]]
=> [#<User:0x00007f03d8945bc8 id: 7, given_name: "ぱなこ", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:19:55.103263000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:25:15.523735000 UTC +00:00>]
irb(main):007:0> User.search("はな").first.formatted
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 7]]
=> {"id"=>"7", "given_name"=>"ぱ<em>な</em>こ", "family_name"=>"田中", "age"=>1}
更新時もJobが追加されてますね。
削除してみます
irb(main):003:0> User.last.destroy
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
TRANSACTION (0.1ms) BEGIN
User Destroy (0.3ms) DELETE FROM "users" WHERE "users"."id" = $1 [["id", 8]]
Enqueued MeiliSearch::MSJob (Job ID: 2ce16c3c-14f9-4451-a709-d32f99070efa) to Sidekiq(meilisearch) with arguments: #<GlobalID:0x00007f03d69cdc78 @uri=#<URI::GID gid://rails-sample/User/8>>, "ms_remove_from_index!"
TRANSACTION (1.6ms) COMMIT
=> #<User:0x00007f03d67df240 id: 8, given_name: "はなこ", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:32:29.613021000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:32:29.613021000 UTC +00:00>
irb(main):004:0> User.search("はな")
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 8]]
=> []
ちゃんと削除されました
Custom Attribute
モデルの属性以外の値をattributeに入れることもできるみたいです
app/models/user.rb
class User < ApplicationRecord
include MeiliSearch
meilisearch enqueue: true do
attribute :given_name, :family_name, :age
attribute :full_name do
"#{family_name} #{given_name}"
end
attributes_to_highlight ['*']
end
end
indexを作り直します
irb(main):005:0> User.reindex!
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1000]]
=> nil
まずは作り直す前の検索結果
irb(main):010:0> User.search("山田 太郎")
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 1]]
=> [#<User:0x00007f03d63843a8 id: 1, given_name: "太郎", family_name: "山田", age: 10, created_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00, updated_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00>]
irb(main):011:0> User.search("山田 太郎").first.formatted
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 [["id", 1]]
=> {"id"=>"1", "given_name"=>"<em></em>太郎", "family_name"=>"<em>山田</em>", "age"=>10}
次にfull_nameを追加した場合の検索結果
irb(main):014:0> User.search("山田 太郎")
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2) [["id", 1], ["id", 6]]
=>
[#<User:0x00007f03d68bb5b0 id: 1, given_name: "太郎", family_name: "山田", age: 10, created_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00, updated_at: Mon, 04 Oct 2021 14:56:38.430087000 UTC +00:00>,
#<User:0x00007f03d68bb4e8 id: 6, given_name: "たろう", family_name: "田中", age: 1, created_at: Tue, 05 Oct 2021 12:16:32.679603000 UTC +00:00, updated_at: Tue, 05 Oct 2021 12:16:32.679603000 UTC +00:00>]
irb(main):015:0> User.search("山田 太郎").map(&:formatted)
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2) [["id", 1], ["id", 6]]
=> [{"id"=>"1", "given_name"=>"<em></em>太郎", "family_name"=>"<em>山田</em>", "age"=>10, "full_name"=>"<em>山田</em> <em></em>太郎"}, {"id"=>"6", "given_name"=>"たろう", "family_name"=>"田中", "age"=>1, "full_name"=>"<em>田</em>中 たろう"}]
うーん、Afterの結果がちょっと ?
な感じではありますが、結果が変わりましたね。
まとめ
長くなってきたので今回はこの辺で...
- Railsから使う場合、MeiliSearchへの接続先設定と、Modelへの設定だけで簡単に使える
- indexの更新は自動的に実行されるので楽
- 非同期更新もデフォルトで対応している
-
ただしmeilisearchというキューになるので注意が必要
- 実はこれに気づかなくてちょっとハマった
- Jobを自前実装することもできるので、その場合はdefaultキューとか使えると思う
- 新たに導入した場合に既存データのindexingは
Model.reindex!
で済む - 総じて使いやすそう
TODO
- Railsから使ってみる
- Rustから使ってみる
- k8sでの運用(冗長化など)
Discussion