diff --git a/elasticsearch-model/lib/elasticsearch/model/indexing.rb b/elasticsearch-model/lib/elasticsearch/model/indexing.rb index ac507b079..624bc626b 100644 --- a/elasticsearch-model/lib/elasticsearch/model/indexing.rb +++ b/elasticsearch-model/lib/elasticsearch/model/indexing.rb @@ -393,7 +393,9 @@ def delete_document(options={}) 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 } + indexed_json = self.as_indexed_json + (self.attributes.keys.map(&:to_s) - changed_attributes.keys.map(&:to_s)).each{ |k| indexed_json.delete_if{ |a,b| a.to_s == k } } + indexed_json else changed_attributes end diff --git a/elasticsearch-model/test/unit/indexing_test.rb b/elasticsearch-model/test/unit/indexing_test.rb index b78651340..7a7c1b643 100644 --- a/elasticsearch-model/test/unit/indexing_test.rb +++ b/elasticsearch-model/test/unit/indexing_test.rb @@ -186,6 +186,8 @@ def self.before_save(&block) (@callbacks ||= {})[block.hash] = block end + def attributes; { :_id => 1, :foo => 'B', :bar => 'D', :baz => 'F' }; end + def changed_attributes; [:foo, :bar]; end def changes @@ -197,6 +199,31 @@ def as_indexed_json(options={}) end end + class ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJsonWithDerivedAttribute + extend Elasticsearch::Model::Indexing::ClassMethods + include Elasticsearch::Model::Indexing::InstanceMethods + + def self.before_save(&block) + (@callbacks ||= {})[block.hash] = block + end + + def attributes; { :_id => 1, :foo => 'B', :bar => 'D', :baz => 'F' }; end + + def changed_attributes; [:foo, :bar]; end + + def changes + {:foo => ['A', 'B'], :bar => ['C', 'D']} + end + + def as_indexed_json(options={}) + { :foo => 'B', :bar => 'D', :qux => derived_value } + end + + def derived_value + 'E' + end + end + should "register before_save callback when included" do ::DummyIndexingModelWithCallbacks.expects(:before_save).returns(true) ::DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods @@ -367,6 +394,49 @@ def as_indexed_json(options={}) instance.update_document end + should "exclude attributes not contained in changed_attributes from custom as_indexed_json during partial update" do + client = mock('client') + instance = ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new + + # Set the fake `changes` hash + instance.instance_variable_set(:@__changed_attributes, {'foo' => 'B' }) + + # Overload as_indexed_json + instance.expects(:as_indexed_json).returns({ 'foo' => 'BAR', 'bar' => 'BAZ' }) + + client.expects(:update).with do |payload| + assert_equal({'foo' => 'BAR'}, payload[:body][:doc]) + true + end + + instance.expects(:client).returns(client) + instance.expects(:index_name).returns('foo') + instance.expects(:document_type).returns('bar') + instance.expects(:id).returns('1') + + instance.update_document + end + + should "include derived attributes from custom as_indexed_json during partial update" do + client = mock('client') + instance = ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJsonWithDerivedAttribute.new + + # Set the fake `changes` hash + instance.instance_variable_set(:@__changed_attributes, {foo: 'B'}) + + client.expects(:update).with do |payload| + assert_equal({foo: 'B', qux: 'E'}, payload[:body][:doc]) + true + end + + instance.expects(:client).returns(client) + instance.expects(:index_name).returns('foo') + instance.expects(:document_type).returns('bar') + instance.expects(:id).returns('1') + + instance.update_document + end + should "update only the specific attributes" do client = mock('client') instance = ::DummyIndexingModelWithCallbacks.new