使用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服务器。