Search engine

Nhắc đến Google chúng ta nghĩ ngay đến công cụ tìm kiếm lớn nhất thế giới. Trong thời đại bùng nổ thông tin số như hiện tại thì công tụ tìm kiếm là thứ không thể thiếu. Hầu hết các trang web đều trang bị chức năng tìm kiếm, tuy không thể so sánh được với Google nhưng cũng giúp ích cho người dùng trong việc tìm kiếm nội dung trên trang.

Với ruby on rails, làm thế nào để thiết kế và tạo ra 1 tiện ích tìm kiếm như thế? Bài viết này sẽ đưa ra câu trả lời cho câu hỏi đó. Đầu tiên chúng tôi giới thiệu về search engin Sorl, và gem Sunspot. Sau đó sẽ bàn luận về 1 vấn đề trong tìm kiếm và cách giải quyết khi sử dụng sorl và sunspot.

I. Solr là gì?

Solr là search search mã nguồn mở rất phổ biến. Nó hỗ trợ nhiều tính năng như full-text search, hit highlighting, faceted search, near real-time indexing, dynamic clustering, database integration, rich document (Word, PDF, ...) handling, and geospatial.

II. Sunspot là gì?

Sunspot là thư viện tương tác giữa rails application và Solr.

III. Cài đặt và cách sử dụng Sunspot, Solr

1. Cài đặt

Thêm gem vào Gemfile:

 gem 'sunspot_rails'
 gem 'sunspot_solr'

Sau khi chạy bundle install, chạy lệnh sau để khởi tạo sunspot. Khi chạy lệnh này, file cấu hình mặc định sunspot.xml sẽ được tạo ra. Đây chính là file khai báo thông tin cần thiết để kết nối tới solr server như hostname, port ...

 $rails g sunspot_rails:install

 #config/sunspot.xml
 production:
  solr:
  hostname: localhost
  port: 8983
  log_level: WARNING

 staging:
  solr:
  hostname: localhost
  port: 8080
  log_level: WARNING

 development:
  solr:
  hostname: localhost
  port: 8982
  log_level: INFO

 test:
  solr:
  hostname: localhost
  port: 8981
  log_level: WARNING

Và chạy Solr server:

rake sunspot:solr:start

Chạy lệnh để index dữ liệu:

rake sunspot:reindex

2. Cách sử dụng

Giả sử có bảng advices với các thuộc tính như title, user_id, state, created_at, update_at. Khi chúng ta muốn tìm kiếm advice dựa trên thuộc tính title thì class Advice trong model sẽ cài đặt như sau:

class Advice < ActiveRecord::Base
 attr_accessible :title, :user_id, :state, :created_at, :updated_at
 searchable do
    text :title, :stored => true
    integer :user_id
    string :state
    time :created_at
 end
end

Khi viết xong model, như đã hướng dẫn ở trên, chúng ta dùng lệnh rake sunspot:reindex để nhập dữ liệu cho solr. Và controller Searches cài đặt như dưới đây.

 class SearchesController < ApplicationController
  def search
  search = Advice.search do
     fulltext params[:title]
     with(:user_id, params[:user_id])
     with(:state, params[:state])
     paginate(:per_page => 5, :page => params[:page])
     order_by :created_at, :desc
  end
  seach.total # tổng số kết quả tìm được
  search.results # mảng advice tìm được
  end
 end

Trong đó:

fulltext params[:title] khai báo tìm kiếm những Advice có title chứa từ khoá params[:title]. Cùng với một số điều kiện như:
with(:user_id, params[:user_id]) với owner có id là params[:user_id].
with(:state, params[:state]) với state là params[:state]

Và một số tính năng khác như phân trang, xắp xếp kết quả.

IV. Một vấn đề trong tìm kiếm

Trong tiếng Nhật, hiragana và katakana chỉ là 2 cách viết, không hề khác nhau về mặt ý nghĩa. Nên yêu cầu đặt ra đối với tính năng tìm kiếm là phải đưa ra kết quả giống nhau khi search với từ khoá dưới đang hiragana hoặc katakana. Nhưng solr ko làm được điều đó. Nguyên nhân là do solr sử dụng nguyên văn dữ liệu đầu vào để index và sử dụng index đó vào việc tìm kiếm. Nếu dữ liệu đầu vào là hiragana thì index dưới dạng hiragana, và tương tự đối với katakana. Và hoàn toàn không có cơ chế nhận biết giữa hiragana và katakana.

Ví dụ: ひらがな ー ヒラガナ

Ý tưởng để giải quyết vấn đề này khá đơn giản, đó là chuyển đổi dữ liệu cần tìm kiếm và từ khoá về cùng 1 định dạng katakana hoặc hiragana. Chuyển đổi text từ katakana sang hiragana ta có thể thực hiện dễ dàng bằng NKF. Tương tự như ví dụ ở trên nhưng thay đổi một chút trong model và controller seach như dưới đấy.

Chuyển title sang định dạng hiragana.

 class Advice < ActiveRecord::Base
   attr_accessible :title, :user_id, :state, :created_at, :updated_at
   searchable do
     text :title, :stored => true do
       NKF.nkf(--hiragana -w”, title)
     end
     integer :user_id
     string :state
     time :created_at
   end
 end

Khi tìm kiếm, ta cũng chuyển từ khoá sang định dạng hiragana.

 Advice.search do
   fulltext NKF.nkf(--hiragana -w”, params[:title])
 end

V. Conclusion

Từ vấn đề trên có thể mở rộng vấn đề tìm kiếm mà dữ liệu ko nhất thiết phải lưu trữ trong cơ sở dữ liệu, chỉ cần có 1 bộ chuyển đổi kiểu như NKF.

Ứng dụng như khi tìm kiếm mà muốn xuất ra kết quả dưới dạng nhiều ngôn ngữ. Giả sử đối với 2 ngôn ngữ tiếng Anh và tiếng Việt: English là Tiếng Anh Vietnamese là Tiếng Việt

Làm sao tìm được những dữ liệu chứa “Tiếng Anh” khi từ khoá nhập vào là “English”?

Tương tự như NKF, ta chỉ việc xây dựng thư viện chuyển đổi (ví dụ như từ điển) về 1 định đạng tiếng Anh hoặc tiếng Việt để solr index. Như vậy ta sẽ tìm được kết quả ở cả hai loại ngôn ngữ.

VI. Tài liệu tham khảo

http://lucene.apache.org/solr/

http://sunspot.github.com/

http://apidock.com/ruby/NKF