見出し画像

Ruby on RailsでAWS CloudSearchを利用した検索機能を実装する

検索機能をWebサービスで用いるのは、一般的でしょう。検索と言っても、完全一致や部分一致など様々な要件があり、場合によってはインデックスが使えず、処理に時間を要してしまうことがあります。

今回は、簡単な設定で強力な検索機能を使うことができる AWS CloudSearch を利用して、検索機能を実装して行きたいと思います。

CloudSearchとは?

 AWSが提供するマネージドな全文検索サービスです。簡単なマウス操作で検索ドメインを作成でき、全文検索や範囲検索など、様々な機能を用いることができます。


実際に作成していきます。

AWS SDKをインストールする

まずaws-sdkをインストールします。

# Gemfile
 
​gem 'aws-sdk', '~> 3'

config情報も事前にinitializerで定義しておきます。

# config/initializers/aws.rb

Aws.config.update({
 access_key_id:     ENV["AWS_ACCESS_KEY_ID"],
 secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
 region:            ENV["AWS_REGION"]
})

CloudSearchにデータを登録する

続いてDBに定義している情報を CloudSearch に登録していきます。例として、Articleテーブル(カラム:id, title, author)を登録していきます。

事前にAWS Console 上で CloudSearch の検索ドメインの設定を行っておきます。(こちらについては別途紹介できればと思います。)

今回はバッチ処理で一括アップロードします。アップロードの上限は、「 10 秒ごとに 1 つ、バッチあたり 5 MB 」に制限されいるので、ある程度データを区切った方が良いでしょう。

※ endpointはダッシュボードのsearch-endpointを使用します。また登録する際にはidをベースに登録処理を行います。

# lib/tasks/update_cloud_search.rake

desc 'CloudSearchにArticle情報をアップロードする'
task :update_cloud_search => :environment do
   client = Aws::CloudSearchDomain::Client.new(
       endpoint: ENV['CLOUDSEARCH_SEARCH_ENDPOINT']
   )
 
  # 500件ずつ登録
   start_id  = 1
   end_id    = 500
   increment = 500

   loop do
    articles = Article.where(id: start_id..end_id)

    break if articles.empty?

    upload_document = []

    articles.each do |article|
      upload_document.push(
         {
             type: 'add',
             id: article.id,
             fields: {
               title: article.title,
               author: article.author
             }
           }
         )
    end

    client.upload_documents(
       content_type: 'application/json',
       documents:    upload_document.to_json
    )

    start_id  = start_id + increment
    end_id    = end_id   + increment
  end
end

bundle exec rake update_cloud_search を実行して、CloudSearchにデータの登録が完了しました。

CloudSearchを用いた検索機能を実装する

実際に登録したデータを用いて、検索機能を実装していきます。

検索対象が今後も増えることを想定し、AWSが推奨しているディープページ分割という方法を用いて実装していきます。

フリーワードを検索キーとして、titleとauthorを横断検索していきます。検索を行うと、このワードに一致・部分一致したArticleのidが返却されます。

※ 後にidでwhere検索を行いたいので、flattenで平坦化を行っています。keywordがnilの場合、500エラーが起こるので、制御が必要です。

def fetch_id_by_cloud_search(keyword)
  cloud_search_hit_ids = []
  
  client = Aws::CloudSearchDomain::Client.new(endpoint: ENV['CLOUDSEARCH_GOURMET_SEARCH_ENDPOINT'])
       
  cursor = 'initial'
  loop do
    page = client.search(
       query: keyword,
       size: 10000, # CloudSearchの上限数
       query_parser: 'lucene',
       cursor: cursor,
       return: 'id'
    )
    cursor = page.hits.cursor
    break if page.data.hits.hit.empty?

    cloud_search_hit_ids << page.data.hits.hit.map(&:id)
  end

  cloud_search_hit_ids.flatten
end

戻り値を用いて、ActiveModelに返却する場合は以下のようにします。

Article.where(id: fetch_id_by_cloud_search('田中'))
 
# 例
# Article id: 1, title: アフロ田中, author: のりつけ雅春
# Article id: 2, title: ホゲホゲ日記, author: 田中太郎

まとめ

AWS CloudSearchを用いて、検索機能を実装しました。ActiveRecordのメソッドのみでは、実装しずらい横断検索もCloudSearchを利用することで簡単に実装が可能です。

参考

https://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/what-is-cloudsearch.html






この記事が気に入ったらサポートをしてみませんか?