Skip to content

Commit 26a3ba2

Browse files
committed
Fixed, that models with custom as_indexed_json method are properly serialized during update
Previously, the `update_document` method simply intercepted the changes to the model, via the `@__changed_attributes` variable, and used these directly. This caused models with a custom serialization method to be incorrectly serialized, namely unwanted attributes were added. This patch looks for `as_indexed_json` defined on the model, and when it finds it, filters the changed attributes through the keys. Closes elastic#75 Related: * elastic#59 * elastic#57 * elastic#52 * elastic#40 * elastic#37 * elastic#5
1 parent ef1f55c commit 26a3ba2

File tree

3 files changed

+106
-1
lines changed

3 files changed

+106
-1
lines changed

elasticsearch-model/lib/elasticsearch/model/indexing.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,17 @@ def delete_document(options={})
333333
#
334334
def update_document(options={})
335335
if changed_attributes = self.instance_variable_get(:@__changed_attributes)
336+
attributes = if respond_to?(:as_indexed_json)
337+
changed_attributes.select { |k,v| self.as_indexed_json.keys.include? k }
338+
else
339+
changed_attributes
340+
end
341+
336342
client.update(
337343
{ index: index_name,
338344
type: document_type,
339345
id: self.id,
340-
body: { doc: changed_attributes } }.merge(options)
346+
body: { doc: attributes } }.merge(options)
341347
)
342348
else
343349
index_document(options)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require 'test_helper'
2+
3+
module Elasticsearch
4+
module Model
5+
class ActiveRecordCustomSerializationTest < Elasticsearch::Test::IntegrationTestCase
6+
7+
class ::ArticleWithCustomSerialization < ActiveRecord::Base
8+
include Elasticsearch::Model
9+
include Elasticsearch::Model::Callbacks
10+
11+
mapping do
12+
indexes :title
13+
end
14+
15+
def as_indexed_json(options={})
16+
as_json(options.merge root: false).slice('title')
17+
end
18+
end
19+
20+
context "ActiveRecord model with custom JSON serialization" do
21+
setup do
22+
ActiveRecord::Schema.define(:version => 1) do
23+
create_table ArticleWithCustomSerialization.table_name do |t|
24+
t.string :title
25+
t.string :status
26+
end
27+
end
28+
29+
ArticleWithCustomSerialization.delete_all
30+
ArticleWithCustomSerialization.__elasticsearch__.create_index! force: true
31+
end
32+
33+
should "index only the title attribute when creating" do
34+
ArticleWithCustomSerialization.create! title: 'Test', status: 'green'
35+
36+
a = ArticleWithCustomSerialization.__elasticsearch__.client.get \
37+
index: 'article_with_custom_serializations',
38+
type: 'article_with_custom_serialization',
39+
id: '1'
40+
41+
assert_equal( { 'title' => 'Test' }, a['_source'] )
42+
end
43+
44+
should "index only the title attribute when updating" do
45+
ArticleWithCustomSerialization.create! title: 'Test', status: 'green'
46+
47+
article = ArticleWithCustomSerialization.first
48+
article.update_attributes title: 'UPDATED', status: 'red'
49+
50+
a = ArticleWithCustomSerialization.__elasticsearch__.client.get \
51+
index: 'article_with_custom_serializations',
52+
type: 'article_with_custom_serialization',
53+
id: '1'
54+
55+
assert_equal( { 'title' => 'UPDATED' }, a['_source'] )
56+
end
57+
end
58+
59+
end
60+
end
61+
end

elasticsearch-model/test/unit/indexing_test.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,25 @@ def changes
129129
end
130130
end
131131

132+
class ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson
133+
extend Elasticsearch::Model::Indexing::ClassMethods
134+
include Elasticsearch::Model::Indexing::InstanceMethods
135+
136+
def self.before_save(&block)
137+
(@callbacks ||= {})[block.hash] = block
138+
end
139+
140+
def changed_attributes; [:foo, :bar]; end
141+
142+
def changes
143+
{:foo => ['A', 'B'], :bar => ['C', 'D']}
144+
end
145+
146+
def as_indexed_json(options={})
147+
{ :foo => 'B' }
148+
end
149+
end
150+
132151
should "register before_save callback when included" do
133152
::DummyIndexingModelWithCallbacks.expects(:before_save).returns(true)
134153
::DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
@@ -251,6 +270,25 @@ def changes
251270

252271
instance.update_document
253272
end
273+
274+
should "exclude attributes not contained in custom as_indexed_json during partial update" do
275+
client = mock('client')
276+
instance = ::DummyIndexingModelWithCallbacksAndCustomAsIndexedJson.new
277+
278+
# Set the fake `changes` hash
279+
instance.instance_variable_set(:@__changed_attributes, {foo: 'B', bar: 'D' })
280+
281+
client.expects(:update).with do |payload|
282+
assert_equal({foo: 'B'}, payload[:body][:doc])
283+
end
284+
285+
instance.expects(:client).returns(client)
286+
instance.expects(:index_name).returns('foo')
287+
instance.expects(:document_type).returns('bar')
288+
instance.expects(:id).returns('1')
289+
290+
instance.update_document
291+
end
254292
end
255293

256294
context "Re-creating the index" do

0 commit comments

Comments
 (0)