LoginSignup
6
5

More than 3 years have passed since last update.

Railsで「Chewy」を用いて簡単に検索機能を実装する

Last updated at Posted at 2019-08-27

はじめに

Chewyは、RailsのmodelをElasticsearchで扱うためのGemの1つです。
Chewyの日本語記事が大変少ないため、今回記事を書いてみようと思いました。

この記事では、Chewyを用いてとりあえず簡単な検索ができるまでの流れをまとめてみました。

とりあえず動かすことを目的としているので細かな解説は行なっていませんが、
今後少しずつまとめていけたらなと思います。

目標

「タイトル、著者名、出版社名から書籍を検索する」というシンプルな検索をChewyで実装する。

環境

ruby 2.5.3
rails 6.0.0
chewy 0.8.3
Elasticsearch 6.5.4

環境構築

1. Dockerfile,docker-compose.ymlなどを作成する。

chewy_sample
 ┣ docker
 ┃ ┣ es
 ┃ ┃ ┗ Dockerfile
 ┃ ┗ mysql
 ┃ ┃ ┣ Dockerfile
 ┃ ┃ ┗ my.cnf
 ┣ Dockerfile
 ┗ docker-compose.yml
./Dockerfile
FROM ruby:2.5.3-stretch

ENV BUNDLE_GEMFILE=/app/Gemfile \
    BUNDLE_JOBS=2 \
    RAILS_ENV=development \
    LANG=C.UTF-8

RUN apt-get update -qq
RUN apt-get install -y build-essential 
RUN apt-get install -y libpq-dev
RUN apt-get install -y nodejs

# ワーキングディレクトリの設定
RUN mkdir /app
WORKDIR /app
./docker-compose.yml
version: "3"
services:
  es:
    build: ./docker/es
    container_name: es
    environment:
      - cluster.name=rails-sample-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - chewy_sample_data:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
  kibana:
    image: docker.elastic.co/kibana/kibana:6.5.4
    environment:
      SERVER_NAME: localhost:5601
      ELASTICSEARCH_URL: http://chewy_sample:9200
    ports:
      - 5601:5601
    depends_on:
      - es
  db:
    environment:
      - MYSQL_ROOT_PASSWORD=docker
      - MYSQL_PASSWORD=docker
      - MYSQL_USER=docker
      - MYSQL_DATABASE=chewy_sample
    build: ./docker/mysql
    ports:
      - "3306:3306"
  rails:
    build: .
    volumes:
      - .:/app
      - vendor_bundle:/user/local/bundle
    ports:
      - "3003:3000"
    links:
      - db
      - es
    environment:
      - RAILS_DATABASE_USERNAME=root
      - RAILS_DATABASE_PASSWORD=docker
      - RAILS_DATABASE_NAME=rails_chewy_sample
      - RAILS_DATABASE_HOST=db
    tty: true
    stdin_open: true

volumes:
  chewy_sample_data:
    driver: local
  vendor_bundle:
    driver: local
./es/Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:6.5.4
# elasticsearchで日本語を扱うために、kuromojiプラグインを導入
RUN bin/elasticsearch-plugin install analysis-kuromoji
./mysql/Dockerfile
FROM mysql:5.7
RUN touch /var/log/mysql/mysqld.log # 指定の場所にログを記録するファイルを作る
my.cnf
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[client]
port    = 3306
socket    = /var/run/mysqld/mysqld.sock
default-character-set=utf8

[mysqld_safe]
pid-file  = /var/run/mysqld/mysqld.pid
socket    = /var/run/mysqld/mysqld.sock
nice    = 0

[mysqld]
user    = mysql
pid-file  = /var/run/mysqld/mysqld.pid
socket    = /var/run/mysqld/mysqld.sock
port    = 3306
basedir   = /usr
datadir   = /var/lib/mysql
tmpdir    = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp
character-set-server=utf8
expire_logs_days = 2
default-time-zone = '+9:00'

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address = 127.0.0.1

#log-error  = /var/log/mysql/error.log

# Recommended in standard MySQL setup
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

2. railsプロジェクトの作成

$ docker-compose up -d

# Nameの確認
$ docker ps 

# docker ps で確認したNameでrailsのコンテナに入る
$ docker exec -it chewy_sample_rails_1 /bin/bash

コンテナ内で下記を実行する

/app# bundle init

Gemfileが生成されるので下記のように編集する

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# コメントアウトを外す
gem "rails"

Gemfile編集後、コンテナ内で下記コマンドを実行する

# railsのコンテナ内
/app# bundle install
/app# bundle exec rails new .

3. 今回用いるGemを追加する

Gemfileに下記の行を追加する

Gemfile

# mysql2を追加
gem 'mysql2' 

# chewyを追加
gem 'chewy', '~> 0.8.3'

# fakerを追加
gem 'faker'
app# bundle install

4. 起動

app# rails s -p 3000 -b '0.0.0.0'

ここまでできたら、http://localhost:3000/にきちんと動作しているかアクセスして確認してみます。
welcome_rails.png
おなじみの画面が表示されたら、OK

Railsの日本語化

必要ないかもしれませんが、Railsの日本語化対応を行います。
下記の記事を参考にRailsの日本語化をさせて頂きました。
[初学者]Railsのi18nによる日本語化対応

また、fakerの日本語化を行います。
ja.ymlをダウンロードして、lib/locales/ja.ymlに配置します。

モデルの作成

書籍とその著者や出版社のデータを格納するテーブルを用意します。

$ rails g model Book title:string author_id:integer publisher_id:integer price:integer
$ rails g model Author name:string
$ rails g model Publisher name:string
app/models/author.rb
class Author < ApplicationRecord
  has_many :books
end
app/models/book.rb
class Book < ApplicationRecord
  belongs_to :author
  belongs_to :publisher
end
app/models/publisher.rb
class Publisher < ApplicationRecord
  has_many :books
end
$ rails db:migrate

Chewyの設定

Chewyの設定ファイルの作成

config/chewy.ymlの生成

$ rails g chewy:install
config/chewy.yml
# config/chewy.yml
# separate environment configs
test:
  host: "es:9250"
  prefix: "test"
development:
  host: "es:9200"

Indexの作成

インデックスを作成します。
今回は、本のタイトル、著者名、出版社名で検索

$ mkdir app/chewy
$ touch app/chewy/books_index.rb
app/chewy/books_index.rb
class BooksIndex < Chewy::Index
  define_type Book do
    field :title, type: 'text'
    field :author, type: 'text', value: ->(book) { book.author.name }
    field :publisher, type: 'text', value: ->(book) { book.publisher.name }
  end
end

テストデータの作成

検索できるか試すために、Fakerを用いてテストデータを作成します。
Fakerのロケールの関係で著者名だけが日本人名という変なデータになっちゃいますが、ご愛嬌ということで……

Chewy.strategyを用いることで、データの作成・更新更新に応じて、インデックスも作成・更新されるようになります。

db/seedsrb
Chewy.strategy(:atomic) do
  50.times do |i|
    Author.create(
      name: Faker::Book.author
    )

    Publisher.create(
      name: Faker::Book.publisher
    )

    Book.create(
      title: Faker::Book.title,
      author_id: i+1,
      publisher_id: i+1,
      price: 450,
    )
  end
end
$ rails db:seed

もし何らかの不具合で、データとインデックスに相違が生じた場合、下記のコマンドでインデックスのリセットができます。

$ rails chewy:reset

インデックスの作成を終えれば、検索を行うための準備は終了です。

検索してみる

検索を行うための準備が整いました。
rails consoleを開いて、書籍を検索してみましょう。

# 本のタイトルで検索
> result = BooksIndex.query(match: {title: '<検索したい文字列>'})
> result.to_a
# 著者名で検索
> result = BooksIndex.query(match: {author: '<検索したい文字列>'})
> result.to_a
# 出版社で検索
> result = BooksIndex.query(match: {publisher: '<検索したい文字列>'})
> result.to_a

終わりに

とりあえず検索はできるようになりました。
細かい解説は今後少しずつ上げていこうと思います。
自分自身まだわからないところもたくさんあるので誤っている点など多々あると思いますが、ご指摘いただけると幸いです。

参考にした記事

https://github.com/toptal/chewy#installation
https://qiita.com/yamashun/items/6ecaa6f161b4cf283db3

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5