Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
9
Help us understand the problem. What is going on with this article?
@t-sato

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
}

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

9
Help us understand the problem. What is going on with this article?
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
mercari
フリマアプリ「メルカリ」を、グローバルで開発しています。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
9
Help us understand the problem. What is going on with this article?