Làm thế nào sử dụng Elasticsearch trong rails với gem Searchkick

Tại sao lại sử dụng gem searckick khi dùng elasticsearch? Vì gem searchkick giúp cho việc tìm kiếm trở nên đơn giản hơn, cho kết quả tốt hơn, thân thiện với nhà phát triển

  • Truy vấn giống như SQL- mà bạn không cần phải học 1 ngôn ngữ truy vấn mới
  • Reindex không có thời gian chết
  • Dễ dàng sử dụng
  • Autocomplete
  • Làm việc với ActiveRecord, Mongoid, and NoBrainer

Cách cài đặt

Chúng ta thêm gem "searchkick" vào Gemfile

source "https://rubygems.org"
ruby "2.2.3"

...
gem "searchkick"
...

Thêm searchkick vào model bạn muốn thực hiện việc tìm kiếm

class User < ActiveRecord::Base
  searchkick
end

Sau đó thêm data vào search index bằng cách vào rails c gõ:

User.reindex

Sử dụng kết quả trả về

Tìm kiếm trả về một đối tượng Searchkick::Results. Trả về có dạng 1 array có thể sử dụng các method như

results = User.search("hanoi")
results.size
results.any?
results.each { |result| ... }

Mặc định, các id được tìm kiếm từ elasticsearch sẽ được tìm kiếm trong csdl. Ví dụ khi chọn User.search("hanoi") kết quả sẽ trả về các id của user.

{"_index"=>"users_development_20161221085009191", "_type"=>"user", "_id"=>"4", "_score"=>0.1486337},
{"_index"=>"users_development_20161221085009191", "_type"=>"user", "_id"=>"1", "_score"=>0.076713204}]}}>

Để lấy tất cả thông tin User về ta có thể sử dụng câu lệnh:

User.search("hanoi", load: false)

Dữ liệu trả về bản ghi có dạng

"_index"=>"users_development_20161221085009191",
"_type"=>"user",
"_id"=>"1",
"_score"=>0.076713204,
"_source"=>{"id"=>1, "email"=>"[email protected]", "name"=>"ngoc vu", "city"=>"hanoi", "created_at"=>"2016-12-19T03:06:47.000Z", "updated_at"=>"2016-12-19T03:06:47.000Z"}}]}}>

Để lấy tổng số kết quả trả về:

results.total_count

Để tìm kiếm tất cả: Sử dụng *

User.search "*"

Tùy chỉnh tìm kiếm

  • Exact Matches: Tìm kiếm chính xác những gì đã nhập. Ví dụ muốn tìm city có tên là hanoi
    User.search "hanoi", fields: [{city: :exact}]
  • Partial Matches: Theo mặc định kết quả trả về phải phù hợp với tất cả các từ
    User.search "lan hien" # lan AND hien

Ta có thể thay đổi cách tìm kiếm bằng cách:

User.search "lan hien", operator: "or" # lan OR hien

Ngoài ra có các option khác như

:word # default
:word_start
:word_middle
:word_end
:text_start
:text_middle
:text_end

Demo

  • Ở View: Ta xây dựng giao diện tìm kiếm user đơn giản như
    <%= form_tag users_path, method: :get, class: "navbar-form navbar-right", role: "search" do %>
     <p>
        <%= text_field_tag :search, params[:search], class: "form-control",
            placeholder: "Search...", id: "search", autocomplete: "off" %>
        <%= submit_tag "Search", name: nil, class: "btn btn-default" %>
      </p>
    <% end %>
    <div class="row">
      <% @users.each do |user| %>
        <div class="col-sm-6 col-md-3">
          <div class="thumbnail">
            <%= user.email %>
          </div>
        </div>
      <% end %>
    </div>

  • Ở Controller:
    class UsersController < ApplicationController
      def index
        if params[:search].present?
          @ users = User.search(params[:search])
        else
          @ users = User.all
        end
      end
    end
  • Ở Model:
     class User < ActiveRecord::Base
      searchkick text_start: [:email]
    end

Như vậy ta có thể tìm kiếm đơn giản:

search.png

Giờ chúng ta nâng cấp tìm kiếm sao cho thân thiện với người sử dụng:

  • Autocomplete: Tự động gợi ý những gì người dùng sẽ gõ giúp cho việc tìm kiếm trở lên nhanh chóng và dễ dàng hơn

a.png

Đầu tiên xác định trường mà chúng ta định sử dụng tính năng này. giúp cho việc truy vấn trở lên nhanh chóng hơn: Ở đây mình dùng tìm kiếm trên email

class User < ActiveRecord::Base
  searchkick word_start: [:email]
end

Reindex và tìm kiếm

User.search "[email protected]", match: :word_start

Để bắt đầu ta thêm autocomplete vào model

class User < ActiveRecord::Base
  searchkick word_start: [:email], autocomplete: ['name']
end

Thêm router:

Rails.application.routes.draw do
  resources :users do
      collection do
        get :autocomplete
    end
  end
end

Thêm hàm autocomplete vào controller

class UsersController < ApplicationController
  ...
  def autocomplete
    render json: User.search(params[:term], {
      fields: ["email"],
      limit: 10
      }).map(&:email)
  end
end

Ở view ta thêm


<%= stylesheet_link_tag "//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css" %>
<%= javascript_include_tag "//code.jquery.com/ui/1.11.2/jquery-ui.js" %>

..

<script>
  $("#search").autocomplete({
    source: "/users/autocomplete.json",
    minLength: 2
  });
</script>

Vậy là ta đã hoàn thành autocomplete

Screenshot from 2016-12-27 17:58:09.png

Tham khảo

https://github.com/ankane/searchkick http://aimeeault.com/2016/02/05/how-to-use-searchkick-and-elasticsearch-in-your-rails-app-for-complex-search-indexing/

http://www.rubydoc.info/gems/searchkick/0.4.1/frames

https://github.com/ankane/searchkick


All Rights Reserved