elasticsearch-rails的更新回调处理令人疑惑不解
在使用elasticsearch-rails时,出现了更新后无法在Elasticsearch中反映的问题,以下是我当时查找到的备忘。
看一下elasticsearch-rails的问题,
-
- #5
-
- #37
-
- #52
-
- #125
- #182
经过讨论,截至2014-12-08,似乎已经确定了以下实施方案。
# https://github.com/elasticsearch/elasticsearch-rails/blob/master/elasticsearch-model/lib/elasticsearch/model/indexing.rb#L338
def update_document(options={})
if changed_attributes = self.instance_variable_get(:@__changed_attributes)
attributes = if respond_to?(:as_indexed_json)
self.as_indexed_json.select { |k,v| changed_attributes.keys.map(&:to_s).include? k.to_s }
else
changed_attributes
end
client.update(
{ index: index_name,
type: document_type,
id: self.id,
body: { doc: attributes } }.merge(options)
)
else
index_document(options)
end
end
根据 @__changed_attributes 和 as_indexed_json 进行对照,确认哪些属性已经更新,如果没有更新则使用 @__changed_attributes 进行处理。而 @__changed_attributes 是什么呢,
# https://github.com/elasticsearch/elasticsearch-rails/blob/4a61f1786696045561864567e0921db9b318aeaa/elasticsearch-model/lib/elasticsearch/model/proxy.rb#L62
before_save do |i|
i.__elasticsearch__.instance_variable_set(:@__changed_attributes,
Hash[ i.changes.map { |key, value| [key, value.last] } ])
end if respond_to?(:before_save) && instance_methods.include?(:changed_attributes)
在执行save之前,before_save将在ActiveModel::Dirty中设置并保存实体的值。
换句话说,在执行before_save之后改变的属性不会被检测到更新,因此如果在其他before_save中修改了属性,该属性就不会被更新。
before_save的执行顺序遵循Rails回调函数综述文章中的规定,如果有多个相同的before_save指定,它们将按照从上到下的顺序执行,因此,我认为最好将Elasticsearch::Model和Elasticsearch::Model::Callbacks都放在最后include进来。
require 'elasticsearch/model'
class Article < ActiveRecord::Base
before_save :update_pv_count
def update_pv_count
self.pv_count += 1
true
end
...
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
end
我认为之所以采用这种实现是为了实现只更新更新的属性并使其与Elasticsearch保持同步的节省资源的实现,但在运营中,由于需要频繁覆盖as_indexed_json函数,我认为这种通用处理方式并不能解决问题。顺便提一下,在第5个问题中,
after_update -> {
__elasticsearch__.index_document
}
所以,有一些人正常地進行更新處理。