Help us understand the problem. What is going on with this article?

elasticsearch-railsのupdateコールバック処理がモヤモヤ

More than 5 years have passed since last update.

elasitcsearch-railsで、更新時に何故かElasticsearchに反映されなかったので、その時に調べたメモです。

elasitcsearch-railsのissueをみてみると、

と、色々ディスカッションされていたようで、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)

before_saveでsave実行前に設定されていて、実体はActiveModel::Dirtyのchangesの値です。

つまり、この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のissueでは、

after_update -> {
  __elasticsearch__.index_document
}

で、普通に更新処理している方もおりました。

mercari
フリマアプリ「メルカリ」を、グローバルで開発しています。
https://tech.mercari.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした