使用Amazon Elasticsearch Service,在Rails应用程序中实现快速全文搜索

背景指的是一个事物发生的环境或上下文。

由于产品需要全文搜索功能,所以决定使用ElasticSearch进行开发,然而我却没有ElasticSearch的实施和运营经验。
因此最近尝试引入了最新发布的亚马逊Elasticsearch服务。

亚马逊 Elasticsearch 服务

亚马逊 Elasticsearch 服务

Amazon Elasticsearch Service は、AWS クラウドで Elasticsearch を簡単にデプロイ、
操作、スケーリングできるようにするマネージドサービスです。

对于我在创业公司工作的情况来说,我希望尽可能地把时间投入到服务开发中,尽量减少对基础设施的关注。在这一点上,我认为引入亚马逊 Elasticsearch 服务是个不错的选择。

设立亚马逊 Elasticsearch 服务

域名

指定域名。
请注意,例如,如果指定了coubic域名,则endpoint会被前缀”search-“,变为search-coubic-xxxxxxxxxxxxxxxxx.aws-region.es.amazonaws.com。

群集

ElasticSearch以一个服务器作为搜索服务器运行。此外,为了处理大量数据并实现容错性,它还可以在多个服务器之间协同工作。
在ElasticSearch中,将这些服务器群称为集群,将组成群的每个服务器称为节点。
根据服务需求进行集群等设置。
您可以通过(http://calculator.s3.amazonaws.com/index.html?lng=ja_JP)模拟计算费用预估。
※我无法保证这是准确的费用。

访问策略

根据服务结构的需要,进行访问策略的设定。

申请

目的

我们将使商品可以通过标题和详细信息进行全文搜索。

珍宝

我們將安裝一個可以輕鬆使用Rails與Amazon Elasticsearch Service的Gem。

gem 'elasticsearch-model'
gem 'elasticsearch-dsl'

elasticsearch-model为ActiveRecord提供了一个方便地使用ElasticSearch的功能。
elasticsearch-dsl为Ruby提供了一个简单编写ElasticSearch搜索查询的DSL。

连接设置

指定连接到亚马逊 Elasticsearch 服务。

Elasticsearch::Model.client =
    Elasticsearch::Client.new hosts: [
                                  {host: 'your-aws-es-endpoint',
                                   port: '80'}]

在这里,我在不指定端口的情况下遇到了一些困难。

从索引到搜索的流程。

在开始实施搜索功能之前,要先大致了解ElasticSearch的工作原理。

    • インデキシング

ElasticSearchに送られてきたデータを処理し、インデックスに保存する。

アナライズ

インデキシングの時にデータの内容を解析し分割しインデックスに書き込む。

検索

クエリの条件を満たすドキュメントを探す。

アナライザ

検索時にクエリの操作を指定する。

我們按照索引建立和搜索順序來進行查看。

索引名称确定

索引是ElasticSearch中存储数据的位置。如果类比关系型数据库,可以说它相当于表。
本次假设是按照模型单元创建索引。

module Searchable
  extend ActiveSupport::Concern

  included do
    include Elasticsearch::Model
    index_name  [model_name.collection, Rails.env].join('_')
  end
end

模式映射

在中文中重新表达如下内容:
“模式映射用于定义索引结构。索引可以包含多种类型,但在这里我们只定义了一个类型。我们将通过模式映射来定义一个具有以下属性的类型:Course。
– 名称:字符串
– 描述:文本。”

module CourseSearchable
  extend ActiveSupport::Concern

  included do
    include Searchable

    settings index: {
                 analysis: {
                      tokenizer: {
                          kuromoji_tokenizer: {
                            type: 'kuromoji_tokenizer',
                            mode: 'search'
                          }
                      },
                      filter: {
                         pos_filter: {
                             type:     'kuromoji_part_of_speech',
                             stoptags: %w(助詞-格助詞-一般 助詞-終助詞),
                         },
                         greek_lowercase_filter: {
                             type:     'lowercase',
                             language: 'greek',
                         },
                      },
                      char_filter: {
                          custom_mapping: {
                              type: 'mapping',
                              mappings: ['カ => カ', 'ガ=>ガ']
                          }
                      },
                      analyzer: {
                          kuromoji_analyzer: {
                             type:      'custom',
                             tokenizer: 'kuromoji_tokenizer',
                             filter: %w(kuromoji_baseform pos_filter greek_lowercase_filter cjk_width),
                             char_filter: %w(custom_mapping)
                         }
                      }
                 }
             }

    mappings dynamic: 'false' do
      indexes :name, analyzer: 'kuromoji_analyzer', index: 'analyzed'
      indexes :description, analyzer: 'kuromoji_analyzer', index: 'analyzed'
    end

    def as_indexed_json(options = {})
      attributes
          .symbolize_keys
          .slice(:name, :description)
    end
  end
end

实际上,该课程模型中包含了CourseSearchable模块。

class Course < ActiveRecord::Base
  include CourseSearchable
end

创建索引

从Rails控制台向Amazon Elasticsearch Service创建索引。

Course.__elasticsearch__.create_index! force: true

我认为您可以从Amazon Elasticsearch Service的仪表板上确认已创建了索引。

数据输入

我将前期商品数据从Rails控制台插入到亚马逊Elasticsearch服务中。

Course.__elasticsearch__.import

搜索 (search)

根据输入的关键词构建一个进行全文搜索的查询,并执行搜索。

module CourseSearchable
  extend ActiveSupport::Concern
    .....省略  

    def self.search(query, size)
      search_definition = Elasticsearch::DSL::Search.search {
        size size         
        query do
          multi_match do
            query keyword
            fields %w(name description)
          end
        end
      }
      __elasticsearch__.search(search_definition)
    end
  end
end

实际上,在进行搜索时,我们从ElasticSearch的搜索结果中查询RDBMS,并为搜索结果进行补充。

courses = Course.search(keyword, SEARCH_LIMIT)
results =  Course.where course_id: courses.records.to_a.map(&:id)

文档更新

当 Course 模型被创建或更新时,会通知 ElasticSearch 更新文档。为了考虑用户体验,使用 ActiveJob 异步处理。

class IndexerJob < ActiveJob::Base
  include Elasticsearch::Model

  queue_as :default

  def perform(course)
    course.__elasticsearch__.update_document
  end
end

结束

我们的网站能够进行全文搜索,功能很简单易用。
您可以从我们的服务中观看演示。
https://popcorn.coubic.com/search?category=13&location=1&q=%E8%82%A9%E3%81%93%E3%82%8A

参考书籍的选定

弹性搜索引擎ElasticSearch服务器。

广告
将在 10 秒后关闭
bannerAds