使用Rails和Elasticsearch创建搜索功能并尝试各种操作-文档的操作
首先
我们将介绍如何通过Rails操作Elasticsearch文档的方法。
这篇文章的前提是使用elasticsearch-rails插件。
此外,本文中提及的示例和环境是基于使用Rails和Elasticsearch创建搜索功能并进行各种试验的 – 第1部分:根据示例应用程序创建的。如果您有关于这个示例的疑问,请参考相关资料。
文档操作
我们将Elasticsearch中注册的数据称为文档。对于这些文档,我们将进行读取、注册、更新等CRUD操作。
创建 or 注册
导入
要同时注册数据,可以使用import。 import的默认行为是将模型对应的表的所有数据都注册进去。
# Mangaモデルに対応するデータをすべてimport
Manga.import
指定条件的import
如果你只想注册特定条件的记录,可以使用scope。
included do
include Elasticsearch::Model
# 1時間以内に更新があったレコードを取得するscopeを追加
scope :recent, lambda {
where("updated_at > ?", 1.hour.ago)
}
如果要使用scope功能,可以通过将scope名称作为参数传递来实现,如下所示:scope: {scope名}。
Manga.import scope: 'recent'
索引文件
如果要逐条注册,则可以使用index_document。如果已经注册过了,则更新文档。
manga = Manga.last
manga.__elasticsearch__.index_document
我们是如何判断已注册文件的方法
我已经写了如果已经注册,则更新文档,但是对于Elasticsearch来说,它如何确定文档是否已经注册呢?
每个文档都有一个唯一的_id来标识它,并通过索引进行存储,这样可以使用GET API或ids查询来查找文档。
根据报告,似乎可以通过_id来判断是否唯一。
然而,对于Elasticsearch 6.0之前的版本,可以在一个文档中设置多个类型,因此将type和_id以井号(#)连接起来的字符串被称为_uid,表示_uid是否唯一。
说回Rails的话题,如果要使用elasticsearch-rails注册文档,其默认行为是将模型的id作为elasticsearch的_id进行注册。因此,如果已经存在和模型id相同的_id,将进行更新操作;否则,将进行新建操作。
如果模型中没有id字段,我无法测试其行为。
閱讀(獲得・查找)
搜索
您可以使用search方法进行获取和搜索。
Manga.__elasticsearch__.search({条件})
您可以根据要搜索的“词”本身或“查询DSL※”哈希(传递给to_hash的实例)来传递条件。
※查询DSL文档
# 例) ワード
Manga.__elasticsearch__.search('ゆるキャン')
# 例) クエリDSLを使う
Manga.__elasticsearch__.search({query: { match: { title: 'ゆるキャン'} }})
如果只是简单的搜索,只需要提供搜索词就足够使用了,但在实际运行中,我认为很多情况下会使用查询DSL进行各种自定义。
更新
更新文件
使用 update_document 可以将模型的更新内容反映到 Elasticsearch。
manga = Manga.last
manga.update(title: "タイトル updated")
# update_documentでElasticsearchに更新内容を反映させる
manga.__elasticsearch__.update_document
=> {"_index"=>"es_manga_development", "_type"=>"_doc", "_id"=>"14", "_version"=>7, "result"=>"noop", "_shards"=>{"total"=>0, "successful"=>0, "failed"=>0}}
# 更新結果の確認
response = Manga.__elasticsearch__.search({query: { match: { id: 14} }})
response.results.first._source.title
Manga Search (7.8ms) {index: "es_manga_development", type: "_doc", body: {query: {match: {id: 14}}}}
=> "タイトル updated"
manga.update(title: "タイトル updated2")
# もう一度更新すると _version がインクリメントされる
manga.__elasticsearch__.update_document
=> {"_index"=>"es_manga_development", "_type"=>"_doc", "_id"=>"14", "_version"=>8, "result"=>"noop", "_shards"=>{"total"=>0, "successful"=>0, "failed"=>0}}
您可以指定选项(请参考文档以了解可指定的选项)。
manga.__elasticsearch__.update_document(retry_on_conflict: 3)
更新文档属性
您可以使用 update_document_attributes 来指定要更新的字段并进行更新。
manga = Manga.last
manga.title
=> "タイトル before update"
# update_document_attributesで更新したいフィールドを指定して更新
manga.__elasticsearch__.update_document_attributes(title: "タイトル updated with attribute")
# 結果を確認
response = Manga.__elasticsearch__.search({query: { match: { id: 14} }})
response.results.first._source.title
=> "タイトル updated with attribute"
# DBへの保存は行っていないため更新されていない
manga.reload.title
=> "タイトル before update"
当查看gem源代码时,可以发现它将在body中直接设置指定的attributes字段,因此只会更新传递给attributes的字段。
def update_document_attributes(attributes, options={})
client.update(
{ index: index_name,
type: document_type,
id: self.id,
body: { doc: attributes } }.merge(options) # attributesをdocに設定している
)
end
可使用与update_document相同的选项来更新update_document_attributes。
删除
删除文档
删除操作可以使用delete_document来进行。
manga = Manga.last
manga.__elasticsearch__.delete_document
=> {"_index"=>"es_manga_development",
"_type"=>"_doc",
"_id"=>"14",
"_version"=>11,
"result"=>"deleted",
"_shards"=>{"total"=>2, "successful"=>1, "failed"=>0},
"_seq_no"=>10,
"_primary_term"=>2}
也可以指定选项。(可以参考文档查看可选指定项)
manga.__elasticsearch__.delete_document(refresh: true)