TojiTech No Programming, No Life.

Railsで複数ワードでAND検索やOR検索を実装する

背景

  • AND検索やOR検索をしたい

サンプルリポジトリ

こちらから、実装済みソースコードが見られます。

注意

サンプルのリポジトリは、Dockerで起動することを前提に作成されています。
Dockerを使用しない場合は、appフォルダ以下で通常のrailsの起動処理を行ってください。

なお、記事ではDockerを使用せず導入する手順で記載していますのでご注意ください。

Rails6で記述していますが、Rails5でも同様に動作すると思います。

ソースコードのコメントについて

基本的にコメントを多めに記載しています。こちらを参考に実装して、ロジックの理解を深めてください。

Seedファイルにユーザーの初期情報が入っています。データ登録画面は、今回は省略していますのでSeedでご利用ください

実装

今回、seedには、Fakerを使うのでseedを使用する場合は、適宜追加してください

group :development, :test do
  # Faker
  gem 'faker'
end
rails g controller homes index
rails g model post
class CreatePosts < ActiveRecord::Migration[6.1]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :body
      t.timestamps
    end
  end
end
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
#   movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
#   Character.create(name: 'Luke', movie: movies.first)

100.times.each do |tmp|
  Post.create([
                title: Faker::JapaneseMedia::StudioGhibli.movie,
                body: Faker::JapaneseMedia::StudioGhibli.quote
              ])
end
rails db:seed
class HomesController < ApplicationController
  def index; end

  def search
    # splitで正規表現を使ってキーワードを空白(全角・半角・連続)分割する
    #   連続した空白も除去するので、最後の“+”がポイント
    @keywords = params[:keywords].split(/[[:blank:]]+/)

    # 検索タイプ取得
    #   AND : AND検索 / OR : OR検索
    @type = params[:type]

    # 空のモデルオブジェクト作成(何も入っていない空配列のようなもの)
    @results = Post.none

    # タイプ別で検索実行
    if @type == 'AND'
      # -----------
      # AND検索
      # -----------
      @keywords.each_with_index do |keyword, i|
        # 1回目のループでは、1つ目のワードで検索
        #   結果を@resultsに詰め込む
        @results = Post.search(keyword) if i == 0

        # 2回目以降のループでは、1回目の結果を更にモデル定義の検索メソッドで絞り込みしていく
        #   結果を@resultsに詰め込む
        @results = @results.merge(@results.search(keyword))
      end
    else
      # -----------
      # OR検索
      # -----------
      @keywords.each do |keyword|
        # 単純にモデル定義の検索メソッドにデータを渡す。
        #   検索ワードの数だけor検索を行う
        #   結果を@resultsに詰め込む
        @results = @results.or(Post.search(keyword))
      end
    end

    render :index
  end
end
class Post < ApplicationRecord
  def self.search(keyword)
    # あいまい検索
    #   “?”に対してkeywordが順番に入る
    #   LIKEは、あいまい検索の意味で、“%”は、前後のあいまいという意味
    #   “#{keyword}”は、Rubyの式展開
    where('title LIKE ? OR body LIKE ?', "%#{keyword}%", "%#{keyword}%")
  end
end
<h1>Homes#index</h1>
<p>Find me in app/views/homes/index.html.erb</p>

<%= form_tag(search_path, method: :get) do %>
  <%= label_tag :keywords, '検索ワード' %>

  <%# 検索ワードを引く次ぐためにparams[:keywords] %>
  <%= text_field_tag :keywords, params[:keywords] %>

  <%# セレクトボックスの状態を引き継ぐためにoptions_for_selectのselectedオプションを使用 %>
  <%= select_tag('type', options_for_select([%w[AND AND], %w[OR OR]], { :selected => params[:type] })) %>

  <%= submit_tag '検索' %>
<% end %>

<%# @resultsにデータが入っていれば検索結果を表示する %>
<% if @results %>
  <h2><%= @keywords %> Search Result ( <%= @type %> )--></h2>
  <% @results.each do |result| %>
    <p>
      <%= result.title %><br>
      <small>
        <%= result.body %>
      </small>
    </p>
  <% end %>
<% end %>

最後に

以上で、実装は完了のはずです。うまく動かない場合は、サンプルリポジトリを確認してください。

Profile

Yuki Tojima

RubyやPhp、JavaScriptまわりのことを徒然と記録に残す技術ブログです。

至らぬところもあると思いますが、見守っていただけると幸いです。

記事のリクエストや、間違いなどありましたら X (旧 Twitter) のDMなどでお気軽にご連絡ください。

ytojima @TojiTech
プロフィール画像