Help us understand the problem. What is going on with this article?

インクリメンタルサーチ+複数語検索を作る

More than 1 year has passed since last update.

Ruby 2.5.1
Rails 5.2.3

今回作るもの

インクリメンタルサーチ + 複数語検索
スペースがある場合複数のキーワードで検索することができ、後の方の検索語でヒットするものから上部に表示します。
画面がチカチカしないように、ゆっくり表示します。

画像は現在作っている、色の図鑑みたいなアプロケーションです。

検索が好きな方、インクリメンタルサーチをつけたい方のお役に立てば幸いです。

Image from Gyazo

Gyazo.gifの無料枠で3つ検索するの撮り直しにコード書くよりも時間がかかっています。

ビューの準備

入力フォームと、検索結果を表示するための箱を用意してください。

フォームに入力された値を送って、帰ってきた結果を表示

$(function(){
    $(".入力欄のクラス").on("keyup",function(){
      var input = $(".入力欄のクラス名").val();
      $(".検索結果表示部分のクラス").empty();
//非同期通信で、フォームのキーワードを送ります。
      $.ajax({
        type: 'GET',
        url: '/search/new',
        data: { keyword: input },
        dataType: 'json',
      })
      .done(function(colors){
//検索結果をcolorsという名前の配列として受け取ります。もし、検索結果がある場合は以下の処理を行います。
        if (colors.length !== 0) {
          $('.検索結果表示部分のクラス').empty();
//検索結果として帰ってきた配列それぞれに対して以下の処理を行います。
          colors.forEach(function(color){
//検索結果として表示させたいHTMLを記述してください。
            var html = `<div class="result">
                          <div class="result__text">
                            ${color.name}
                            <br>
                            (${color.red},${color.green},${color.blue})
                          </div>
                          <a class="result__cell" style="background-color:rgb(${color.red},${color.green},${color.blue});" href=""></a>
                        </div>`
//HTMLを検索結果表示部分に差し込みます。(画面がチカチカするの防ぐため、200ミリ秒でフェードインしながら)
            $('.検索結果表示部分のクラス').append(html).hide().fadeIn(200);            ;
          });
        }
        else {
//検索した結果、0件だった場合
          $(".検索結果表示部分のクラス").empty();
//検索結果がないことを示すHTMLを記述します。
          var html = `<div class="result">
                          <div class="result__text">
                            お探しの色は見つかりませんでした
                          </div>
                          <a class="result__cell" href="/color/new"></a>
                        </div>`
            $('.検索結果表示部分のクラス').append(html);
        }
      })
    });
});

コントローラ

def new
#フォームから送られてきたキーワードをスペースで区切って配列に
    keywords = params[:keyword]&.split(/[[:blank:]]+/)
#検索結果を入れるための空の配列を用意
    @colors = []
#複数に分割したキーワードで検索
    keywords.each do |keyword| 
      next if keyword == "" #キーワードが空であれば、すぐ次の検索をかける
#nameのカラムから探す
      @colors += Color.where('name LIKE(?)', "%#{keyword}%") 
    end 
#重複するものがあれば消去
    @colors.uniq! 
#後の検索語でヒットしたものから、表示配列の順番をひっくり返す
    @colors.reverse!
    respond_to do |format|
      format.html
      format.json
    end
  end

インクリメンタルサーチは、せっかちさんのための検索です。きっと複数語検索をかけているということは、1語目の検索結果に満足していないということです。ですので、リバースをかけることにしました。

*別途、jbuilderは用意する必要があります。

!について

!をつけると破壊的なメソッドになるという説明がありますが、どういうことでしょうか。簡単な例で実験してみましょう。
下記の例でjoinは配列の出力時の改行を消すために付記したものです。

#!なしの場合
numbers = [1,2,3,4,5]
puts numbers.reverse.join #=> 54321
puts numbers.join #=> 12345

#!の場合
numbers = [1,2,3,4,5]
puts numbers.reverse!.join #=> 54321
puts numbers.join #=> 54321

今回の場合ですと、!がついたreverseの場合はレシーバ(.の左側)自体に変更が及んでいます。イメージとしては下記のような形です。

numbers = [1,2,3,4,5]
numbers = numbers.reverse
puts numbers.join #=> 54321

!の有無でどのような違いになるのか、勘違いしてしまうと大変なので、使用する場合にはそれぞれのメソッドのリファレンスをご覧になることをおすすめします。

終わりに

検索フォーム適当に作って、ページ遷移させて適当に値引っ張ってくるだけで終わることもできるのですが、色々と工夫ができます。こういうの考えている時ってサーバーサイド楽しいな〜と感じます。是非是非、色々遊んでみてください!
ありがとうございました!

SQLの軽量化や正規表現について

非常に有意義なコメントをいただいたので是非お読みになってください!
せっかくなので本文はそのまま改善の余地のある見本の形で残しておくことにします!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away