Áp dụng ElasticSearch để tìm kiếm thông minh hơn trong ứng dụng Rails
Trong một ứng dụng web, di động hay bất kì ứng dụng này thì phần tìm kiếm là sẽ xuất hiện rất nhiều và là một phần không thể thiếu, nó tiết kiệm thời gian sử dụng cho người dùng cũng như làm cho ứng dụng của chính chúng ta thông minh hơn, và việc tìm kiếm càng thông minh và càng đưa ra những gợi ...
Trong một ứng dụng web, di động hay bất kì ứng dụng này thì phần tìm kiếm là sẽ xuất hiện rất nhiều và là một phần không thể thiếu, nó tiết kiệm thời gian sử dụng cho người dùng cũng như làm cho ứng dụng của chính chúng ta thông minh hơn, và việc tìm kiếm càng thông minh và càng đưa ra những gợi sát nghĩa với cụm từ người dùng muốn thì sẽ giúp nâng cao trải nghiệm của người dùng lên rất nhiều. Ví dụ nếu bạn viết sai chính tả những gì bạn đang cố gắng tìm kiếm, thì việc đưa ra gợi ý sát với những gì chúng ta muốn tìm hoặc tự động sửa là những tính năng tuyệt vời, như chúng ta có thể thấy trên các trang web như Google hoặc Facebook.
Để thực hiện tất cả các tính năng này chỉ sử dụng một cơ sở dữ liệu quan hệ như MySQL hoặc Postgres thì không đơn giản. Vì lý do này, tôi đang sử dụng Elasticsearch, mà bạn có thể nghĩ đến như một cơ sở dữ liệu được xây dựng và tối ưu hóa đặc biệt cho tìm kiếm. Nó là mã nguồn mở và nó được xây dựng trên Apache Lucene.
Một trong những tính năng hay nhất của Elasticsearch là cho thấy chức năng của nó bằng cách sử dụng REST API, do đó, nó có thể sử dụng với hầy hết tất cả các ngôn ngữ lập trình và với Ruby on Rails framework này đã có sẵn gem để có thể sử dụng với Elasticsearch.
Trong bài viết trước của mình ở đây mình đã tóm tắt cấu trúc, cách nó tìm kiếm, cách sử dụng của Elasticsearch với Rest cũng như điểm mạnh điểm yếu của cộng cụ này. Các bạn costheer sử dụng bài viết của mình để thao khảo hoặc vào chính trang chủ của Elasticsearch để biết thêm nhiều tính năng nâng cao hơn cho phù hợp với dự án của mình.
Elasticsearch sử dụng cho tìm kiếm full-text vì vậy mình sẽ bắt tay vào làm mợt dự án nhỏ về blog app và người dùng có thể tìm kiếm thông minh dựa vào nội dung văn bản.
Khỏi tạo Rails App
$ rails new blog $ cd blog $ bundle install $ rails s
Tạo Articles Controller
Tạo Articles Controller bằng cách sử dụng Rails generator, thêm các routes vào config/routes.rb và thêm các phương thức để hiển thị, tạo và liệt kê danh sách các bài viết.
$ rails g controller articles
Mở config/routes.rb và thêm Articles resources vào
Blog::Application.routes.draw do root to: 'articles#index' resources :articles end
Thêm các phương thức to create, show, and index vào Articles Controller
def index @articles = Article.all end def show @article = Article.find params[:id] end def new end def create @article = Article.new article_params if @article.save redirect_to @article else render 'new' end end private def article_params params.require(:article).permit :title, :text end
Article Model
Tạo một model đơn giản cho một bài viết
$ rails g model Article title:string text:text $ rake db:migrate
Views
Tạo form để tạo bài viết mới (app/views/articles/new.html.erb)
<h1>New Article</h1> <%= form_for :article, url: articles_path do |f| %> <% if not @article.nil? and @article.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to '<- Back', articles_path %>
Xem nội dung một bài viết (app/views/articles/show.html.erb)
<p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <%= link_to '<- Back', articles_path %>
Liệt kê danh sách các bài viết (app/views/articles/index.html.erb)
<h1>Articles</h1> <ul> <% @articles.each do |article| %> <li> <h3> <%= article.title %> </h3> <p> <%= article.text %> </p> </li> <% end %> </ul> <%= link_to 'New Article', new_article_path %>
Lòng vòng mãi mình mới nhớ sao không dùng
$ rails generate scaffold Article title:string text:text
luôn cho bài demo của mình, có phải đỡ mất thời gian hơn không ^^!
Sau đó các bạn hãy khởi động lại server và thêm một vài bài viết để xem thử. Phần trên đó chỉ mới là việc cái rails và nó là đơn giản. Tiếp theo chúng ta hãy cùng cài Elasticsearch vào áp dụng nó vào project nhỏ này.
Cài đặt Elasticsearch
Các bạn vào đây để cài tải bản mới nhất phù hợp với hệ điều hành mình đang sử dụng, hãy nhớ máy bạn đã cài sẵn java để Elasticsearch có thể hoạt động được.
Với ubuntu bạn hãy tải bản deb về và sử dụng lệnh dưới đây để cài
$ sudo dpkg -i elasticsearch-[version].deb
Bạn có thể config hoặc để mặc định sau đó để khỏi động service của nó hãy sử dụng lệnh
$ sudo systemctl enable elasticsearch.service $ sudo systemctl start elasticsearch.service
Mở trình duyệt và truy cập vào địa chỉ: http://localhost:9200 nếu thành công màn hình sẽ xuất hiện nội dung tương tự như sau:
{ "name" : "5cP3-lz", "cluster_name" : "elasticsearch", "cluster_uuid" : "uWSboHpfSUiGjAjiEuzyrw", "version" : { "number" : "6.3.2", "build_flavor" : "default", "build_type" : "deb", "build_hash" : "053779d", "build_date" : "2018-07-20T05:20:23.451332Z", "build_snapshot" : false, "lucene_version" : "7.3.1", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" }
Cài ElasticSearch vào ứng dụng Rails
Thêm 2 gem này vào ứng gem file
gem "elasticsearch-model" gem "elasticsearch-rails"
và sau đó nãy nhớ sử dụng bundle install và khới đọng lại rails
Search Controller
Tạo Search Controller
$ rails g controller search
Và thêm method search vào Search Controller
def search if params[:term].nil? @articles = [] else @articles = Article.search params[:term] end end
Tích hợp ElasticSearch vào model
Sửa lại model Article tương tự như sau
require "elasticsearch/model" class Article < ActiveRecord::Base include Elasticsearch::Model include Elasticsearch::Model::Callbacks end Article.import # tự động đồng bộ với ElasticSearch
Search Route
Thêm đoạn code vào routes file
get "search", to: "search#search"
Search View
Tạo mợt file app/views/search/search.html.erb
<h1>Articles Search</h1> <%= form_for search_path, method: :get do |f| %> <p> <%= f.label "Search for" %> <%= text_field_tag :q, params[:term] %> <%= submit_tag "Go", name: nil %> </p> <% end %> <ul> <% @articles.each do |article| %> <li> <h3> <%= link_to article.title, controller: "articles", action: "show", id: article._id%> </h3> </li> <% end %> </ul>
Đến đây thành quả chúng ta đặt được có dạng như sau
Highlighting kết quả tìm kiếm
Trên nhiều trang web, bạn có thể xem trên trang kết quả tìm kiếm các cụm từ bạn đã tìm kiếm được đánh dấu bằng cách tô sáng hoặc gạch chân. Điều này là rất dễ dàng khi sử dụng Elasticsearch.
Thay đối phương thức tìm kiếm mặc định trong app/models/article.rb
def self.search(query) __elasticsearch__.search( { query: { multi_match: { query: query, fields: ['title', 'text'] } }, highlight: { pre_tags: ['<em>'], post_tags: ['</em>'], fields: { title: {}, text: {} } } } ) end
Bây giờ phương thức tìm kiếm sẽ bao bọc các kết quả phù hợp với truy vấn nhưng nó có chứa cả các thẻ HTML được chỉ định(<em>). Vì lý do này, chúng ta cũng cần cập nhật trang kết quả tìm kiếm để có thể hiển thị thẻ HTML một cách chính xác. Để làm như vậy, hãy chỉnh sửa app/views/search/search.html.erb
<h1>Search Results</h1> <% if @articles %> <ul class="search_results"> <% @articles.each do |article| %> <li> <h3> <%= link_to article.try(:highlight).try(:title) ? article.highlight.title[0].html_safe : article.title, controller: "articles", action: "show", id: article._id %> </h3> <% if article.try(:highlight).try(:text) %> <% article.highlight.text.each do |snippet| %> <p><%= snippet.html_safe %>...</p> <% end %> <% end %> </li> <% end %> </ul> <% else %> <p>Your search did not match any documents.</p> <% end %>
Trang trí cho kết quả tìm kiếm bằng một vài thuộc tính css. Tạo file app/assets/stylesheets/search.scss cho highlighted tag
.search_results em { background-color: yellow; font-style: normal; font-weight: bold; }
Bây giờ ta sẽ được kết quả kìm kiếm là dạng như sau Chắc hẵn chúng ta vẫn chưa tháy tìm kiếm này thông minh hơn ở điểm nào chỉ có điều dễ nhận thấy là việc highlight kết quả tìm kiếm khá là dễ dàng. Bây giờ chúng ta tiếp tục để nâng cao sự thông minh của công cụ tìm kiếm.
Sử dụng gem searchkick
Đầu tiên hãy thêm gem "searchkick" vào project của chúng ta
gem "searchkick"
Searchkick là gem đi liền với Elasticsearch giúp cho việc tìm kiếm thông minh và tiện lợi cho ngời dùng hơn rất nhiều, nó có thể gợi ý cho người dùng kết quả tìm kiếm ngay cả khi người dùng có viết sai chính tả ngoài ra còn có rất nhiều ưu điểm khác. Các bạn có thể đọc tại đây.
Trong model Article ta thêm searchkick vào tương tự như sau:
class Article < ActiveRecord::Base searchkick end
Chúng ta cần phải reindex các bài viết một lần nữa
rake searchkick:reindex CLASS=Article
Chúng ta sửa method search trong Search Controller thành
class SearchController < ApplicationController def search if params[:term].nil? @articles = [] else term = params[:term] @articles = Article.search term, fields: [:text], highlight: true end end end
Tiếp đó chính ta sửa file views/search/search.html.erb thành
<h2>Search Results for: <i><%= params[:term] %></i></h2> <% if @articles %> <ul class="search_results"> <% @articles.with_details.each do |article, details| %> <li> <h3> <%= link_to article.title, controller: "articles", action: "show", id: article.id %> </h3> <p><%= details[:highlight][:text].html_safe %>...</p> </li> <% end %> </ul> <% else %> <p>Your search did not match any documents.</p> <% end %>
Vậy là đã xong, hãy cùng xem kết quả
Bài viết này mình đã giới thiệu với các bạn cách kết hợp ElasticSearch - một công cụ tìm kiếm mạnh với framework Ruby on Rails, bài viết này chỉ đề cập một phần chức năng nhỏ của tìm kiếm với ElasticSearch và gem searchkick thực tế nó hiện đại và thông minh hơn rất nhiều nữa. Trong bài viết tiếp theo chúng ta cùng tiếp tục tìm hiểu những kỹ thuật nâng cao hơn của gem searchkick và ElasticSearch và cùng với đó mình sẽ làm thêm chức năng Autosuggest cho các bài viết.Mọng mọi người sẽ ủng hộ mình trong những bài viết tiếp theo.