elastictastic 0.5.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +161 -10
  3. data/lib/elastictastic/adapter.rb +84 -0
  4. data/lib/elastictastic/association.rb +6 -0
  5. data/lib/elastictastic/basic_document.rb +213 -0
  6. data/lib/elastictastic/bulk_persistence_strategy.rb +64 -19
  7. data/lib/elastictastic/callbacks.rb +18 -12
  8. data/lib/elastictastic/child_collection_proxy.rb +15 -11
  9. data/lib/elastictastic/client.rb +47 -24
  10. data/lib/elastictastic/configuration.rb +59 -4
  11. data/lib/elastictastic/dirty.rb +43 -28
  12. data/lib/elastictastic/discrete_persistence_strategy.rb +48 -23
  13. data/lib/elastictastic/document.rb +1 -85
  14. data/lib/elastictastic/embedded_document.rb +34 -0
  15. data/lib/elastictastic/errors.rb +17 -5
  16. data/lib/elastictastic/field.rb +3 -0
  17. data/lib/elastictastic/mass_assignment_security.rb +2 -4
  18. data/lib/elastictastic/middleware.rb +66 -84
  19. data/lib/elastictastic/multi_get.rb +30 -0
  20. data/lib/elastictastic/multi_search.rb +70 -0
  21. data/lib/elastictastic/nested_document.rb +3 -27
  22. data/lib/elastictastic/new_relic_instrumentation.rb +8 -8
  23. data/lib/elastictastic/observing.rb +8 -6
  24. data/lib/elastictastic/optimistic_locking.rb +57 -0
  25. data/lib/elastictastic/parent_child.rb +56 -54
  26. data/lib/elastictastic/persistence.rb +16 -16
  27. data/lib/elastictastic/properties.rb +136 -96
  28. data/lib/elastictastic/railtie.rb +1 -1
  29. data/lib/elastictastic/rotor.rb +105 -0
  30. data/lib/elastictastic/scope.rb +186 -56
  31. data/lib/elastictastic/server_error.rb +20 -1
  32. data/lib/elastictastic/test_helpers.rb +152 -97
  33. data/lib/elastictastic/thrift/constants.rb +12 -0
  34. data/lib/elastictastic/thrift/rest.rb +83 -0
  35. data/lib/elastictastic/thrift/types.rb +124 -0
  36. data/lib/elastictastic/thrift_adapter.rb +61 -0
  37. data/lib/elastictastic/transport_methods.rb +27 -0
  38. data/lib/elastictastic/validations.rb +11 -13
  39. data/lib/elastictastic/version.rb +1 -1
  40. data/lib/elastictastic.rb +148 -27
  41. data/spec/environment.rb +1 -1
  42. data/spec/examples/bulk_persistence_strategy_spec.rb +151 -23
  43. data/spec/examples/callbacks_spec.rb +65 -34
  44. data/spec/examples/dirty_spec.rb +160 -1
  45. data/spec/examples/document_spec.rb +168 -106
  46. data/spec/examples/middleware_spec.rb +1 -61
  47. data/spec/examples/multi_get_spec.rb +127 -0
  48. data/spec/examples/multi_search_spec.rb +113 -0
  49. data/spec/examples/observing_spec.rb +24 -3
  50. data/spec/examples/optimistic_locking_spec.rb +417 -0
  51. data/spec/examples/parent_child_spec.rb +73 -33
  52. data/spec/examples/properties_spec.rb +53 -0
  53. data/spec/examples/rotor_spec.rb +132 -0
  54. data/spec/examples/scope_spec.rb +78 -18
  55. data/spec/examples/search_spec.rb +26 -0
  56. data/spec/examples/validation_spec.rb +7 -1
  57. data/spec/models/author.rb +1 -1
  58. data/spec/models/blog.rb +2 -0
  59. data/spec/models/comment.rb +1 -1
  60. data/spec/models/photo.rb +9 -0
  61. data/spec/models/post.rb +3 -0
  62. metadata +97 -78
  63. data/lib/elastictastic/resource.rb +0 -4
  64. data/spec/examples/active_model_lint_spec.rb +0 -20
@@ -0,0 +1,417 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'ruby-debug'
3
+
4
+ describe Elastictastic::OptimisticLocking do
5
+ include Elastictastic::TestHelpers
6
+
7
+ shared_examples_for 'updatable scope' do
8
+ let :post do
9
+ scope.new.tap do |post|
10
+ post.id = '123abc'
11
+ post.version = 1
12
+ post.persisted!
13
+ end
14
+ end
15
+
16
+ context 'when save is cancelled' do
17
+ before do
18
+ stub_request_json(
19
+ :get,
20
+ match_es_resource(index, 'post', '123abc'),
21
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 1).merge('exists' => true),
22
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 2, :source => { :title => 'Hey' }).merge('exists' => true)
23
+ )
24
+ end
25
+
26
+ describe '::update' do
27
+ before do
28
+ scope.update('123abc') do
29
+ raise Elastictastic::CancelSave
30
+ end
31
+ end
32
+
33
+ it 'should have 1 request for fetch' do
34
+ FakeWeb.should have(1).requests # 1 for fetch
35
+ end
36
+ end
37
+
38
+ describe '::create_or_update' do
39
+ before do
40
+ scope.create_or_update('123abc') do
41
+ raise Elastictastic::CancelSave
42
+ end
43
+ end
44
+
45
+ it 'should make no requests' do
46
+ FakeWeb.should have(0).requests
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'when version conflict raised from discrete persistence' do
52
+ describe '#save' do
53
+ before do
54
+ stub_request_json(
55
+ :put,
56
+ match_es_resource(index, 'post', '123abc'),
57
+ version_conflict
58
+ )
59
+ end
60
+
61
+ it 'should raise VersionConflict' do
62
+ expect { post.save }.to raise_error(Elastictastic::ServerError::VersionConflictEngineException)
63
+ end
64
+
65
+ it 'should yield VersionConflict when called with block' do
66
+ ex = nil
67
+ post.save { |e| ex = e }
68
+ ex.should be_a(Elastictastic::ServerError::VersionConflictEngineException)
69
+ end
70
+ end # describe '#save'
71
+
72
+ describe '::update' do
73
+ before do
74
+ stub_request_json(
75
+ :get,
76
+ match_es_resource(index, 'post', '123abc'),
77
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 1).merge('exists' => true),
78
+ generate_es_hit('post', :id => '123abc', :index => index, :version => 2, :source => { :title => 'Hey' }).merge('exists' => true)
79
+ )
80
+ stub_request_json(
81
+ :put,
82
+ match_es_resource(index, 'post', '123abc'),
83
+ version_conflict,
84
+ generate_es_hit('post', :id => '123abc', :version => 3, :index => index)
85
+ )
86
+ scope.update('123abc') do |post|
87
+ post.comments_count = 3
88
+ end
89
+ end
90
+
91
+ it 'should make four requests' do
92
+ FakeWeb.should have(4).requests
93
+ end
94
+
95
+ it 'should add preference=_primary to get request' do
96
+ URI.parse(FakeWeb.requests.first.path).query.split('&').
97
+ should include('preference=_primary_first')
98
+ end
99
+
100
+ it 'should make final update with modification in update block' do
101
+ last_request_json['comments_count'].should == 3
102
+ end
103
+
104
+ it 'should make final update with data from latest version of doc' do
105
+ last_request_json['title'].should == 'Hey'
106
+ end
107
+
108
+ it 'should make final update with correct version' do
109
+ last_request_uri.query.split('&').should include('version=2')
110
+ end
111
+
112
+ it 'should send final update to correct index' do
113
+ last_request_uri.path.split('/')[1].should == index
114
+ end
115
+ end # describe '::update'
116
+
117
+ describe '::update_each' do
118
+ let(:last_update_request) do
119
+ FakeWeb.requests.reverse.find { |req| req.method == 'PUT' }
120
+ end
121
+
122
+ before do
123
+ stub_es_scan(
124
+ index, 'post', 100,
125
+ generate_es_hit('post', :index => index, :id => '1'),
126
+ generate_es_hit('post', :index => index, :id => '2')
127
+ )
128
+ stub_es_get(index, 'post', '2', { :title => 'Hey' }, 2)
129
+ stub_es_update(index, 'post', '1')
130
+ stub_request_json(
131
+ :put,
132
+ match_es_resource(index, 'post', '2'),
133
+ version_conflict,
134
+ generate_es_hit('post', :id => '2', :index => index, :version => 3)
135
+ )
136
+ scope.update_each do |post|
137
+ post.comments_count = 2
138
+ end
139
+ end
140
+
141
+ it 'should retry unsuccessful updates' do
142
+ FakeWeb.should have(7).requests # initiate scan, 2 cursors, update '1', update '2' (fail), get '2', update '2'
143
+ end
144
+
145
+ it 'should re-perform update on failed document' do
146
+ URI.parse(last_update_request.path).path.should == "/#{index}/post/2"
147
+ end
148
+
149
+ it 'should send data from latest version in persistence' do
150
+ Elastictastic.json_decode(last_update_request.body)['title'].should == 'Hey'
151
+ end
152
+
153
+ it 'should send data from update block' do
154
+ Elastictastic.json_decode(last_update_request.body)['comments_count'].should == 2
155
+ end
156
+
157
+ it 'should update with latest version' do
158
+ URI.parse(last_update_request.path).query.split('&').should include('version=2')
159
+ end
160
+ end # describe '::update_each'
161
+ end # context 'when version conflict raised from discrete persistence'
162
+
163
+ context 'when version conflict raised from bulk persistence' do
164
+ describe '#save' do
165
+ before do
166
+ stub_es_bulk(
167
+ 'index' => {
168
+ '_index' => index, '_type' => 'post', '_id' => '123abc',
169
+ 'error' => version_conflict['error']
170
+ }
171
+ )
172
+ end
173
+
174
+ it 'should raise error' do
175
+ expect { Elastictastic.bulk { post.save }}.to raise_error(Elastictastic::ServerError::VersionConflictEngineException)
176
+ end
177
+
178
+ it 'should yield an error when called with block' do
179
+ ex = nil
180
+ Elastictastic.bulk { post.save { |e| ex = e }}
181
+ ex.should be_a(Elastictastic::ServerError::VersionConflictEngineException)
182
+ end
183
+ end
184
+
185
+ describe '::update' do
186
+ before do
187
+ stub_request_json(
188
+ :get,
189
+ match_es_resource(index, 'post', 'abc123'),
190
+ generate_es_hit('post', :id => 'abc123', :index => index, :version => 1),
191
+ generate_es_hit('post', :id => 'abc123', :index => index, :version => 2, :source => { :title => 'Hey' })
192
+ )
193
+ stub_es_bulk(
194
+ 'index' => {
195
+ '_index' => index, '_type' => 'post', '_id' => '123abc',
196
+ 'error' => version_conflict['error']
197
+ }
198
+ )
199
+ stub_es_update(index, 'post', 'abc123')
200
+
201
+ Elastictastic.bulk do
202
+ scope.update('abc123') do |post|
203
+ post.comments_count = 2
204
+ end
205
+ end
206
+ end
207
+
208
+ it 'should make 4 requests' do
209
+ FakeWeb.should have(4).requests
210
+ end
211
+
212
+ it 'should send data from block in last request' do
213
+ last_request_json['comments_count'].should == 2
214
+ end
215
+
216
+ it 'should send data from most recent version in last request' do
217
+ last_request_json['title'].should == 'Hey'
218
+ end
219
+
220
+ it 'should send correct version in last request' do
221
+ last_request_uri.query.split('&').should include('version=2')
222
+ end
223
+
224
+ it 'should send last request to correct index' do
225
+ last_request_uri.path.split('/')[1].should == index
226
+ end
227
+ end # describe '::update'
228
+
229
+ describe '::update_each' do
230
+ before do
231
+ stub_es_scan(
232
+ index, 'post', 100,
233
+ generate_es_hit('post', :index => index, :id => '1'),
234
+ generate_es_hit('post', :index => index, :id => '2')
235
+ )
236
+ stub_request_json(
237
+ :post,
238
+ match_es_path('/_bulk'),
239
+ 'items' => [
240
+ { 'index' => generate_es_hit('post', :index => index, :id => '1').except('_source').merge('ok' => true) },
241
+ { 'index' => generate_es_hit('post', :index => index, :id => '2').except('_source').merge(version_conflict.slice('error')) }
242
+ ]
243
+ )
244
+ stub_es_get(index, 'post', '2', { 'title' => 'Hey' }, 2)
245
+ stub_es_update(index, 'post', '2', 3)
246
+ Elastictastic.bulk do
247
+ scope.update_each { |post| post.comments_count = 2 }
248
+ end
249
+ end
250
+
251
+ it 'should retry failed update' do
252
+ FakeWeb.should have(6).requests # start scan, 2 cursor reads, bulk update, reload '2', update '2'
253
+ end
254
+
255
+ it 'should update failed document' do
256
+ last_request_uri.path.should == "/#{index}/post/2"
257
+ end
258
+
259
+ it 'should update conflicted document with reloaded data' do
260
+ last_request_json['title'].should == 'Hey'
261
+ end
262
+
263
+ it 'should update conflicted document with data from block' do
264
+ last_request_json['comments_count'].should == 2
265
+ end
266
+
267
+ it 'should update conflicted document with proper version' do
268
+ last_request_uri.query.split('&').should include('version=2')
269
+ end
270
+ end
271
+ end # context 'when version conflict raised from bulk persistence'
272
+ end # shared_examples_for 'updatable scope'
273
+
274
+ describe 'default scope' do
275
+ let(:scope) { Post }
276
+ let(:index) { 'default' }
277
+ let :version_conflict do
278
+ {
279
+ 'error' => "VersionConflictEngineException: [[#{index}][3] [post][abc123]: version conflict, current[2], required[1]]",
280
+ 'status' => 409
281
+ }
282
+ end
283
+ it_should_behave_like 'updatable scope'
284
+ end
285
+
286
+ describe 'scoped in index' do
287
+ let(:scope) { Post.in_index('my_index') }
288
+ let(:index) { 'my_index' }
289
+ let :version_conflict do
290
+ {
291
+ 'error' => "VersionConflictEngineException: [[#{index}][3] [post][abc123]: version conflict, current[2], required[1]]",
292
+ 'status' => 409
293
+ }
294
+ end
295
+ it_should_behave_like 'updatable scope'
296
+ end
297
+
298
+ describe 'default scope with nested exception' do
299
+ let(:scope) { Post }
300
+ let(:index) { 'default' }
301
+ let(:version_conflict) do
302
+ {
303
+ 'error' => "RemoteTransportException: [[server][inet[/ip]][/index]]; nested: VersionConflictEngineException[[#{index}][0] [[post][abc123]: version conflict, current [2], required [1]]",
304
+ 'status' => 409
305
+ }
306
+ end
307
+ it_should_behave_like 'updatable scope'
308
+ end
309
+
310
+ describe 'scoped in index with nested exception' do
311
+ let(:scope) { Post.in_index('my_index') }
312
+ let(:index) { 'my_index' }
313
+ let(:version_conflict) do
314
+ {
315
+ 'error' => "RemoteTransportException: [[server][inet[/ip]][/index]]; nested: VersionConflictEngineException[[#{index}][0] [[post][abc123]: version conflict, current [2], required [1]]",
316
+ 'status' => 409
317
+ }
318
+ end
319
+ it_should_behave_like 'updatable scope'
320
+ end
321
+
322
+ context '::update called on nonexistent document' do
323
+ before do
324
+ stub_es_get('default', 'post', '1', nil)
325
+ end
326
+
327
+ it 'should not do anything' do
328
+ expect { Post.update('1') { |post| post.title = 'bogus' }}.to_not raise_error
329
+ end
330
+ end
331
+
332
+ describe '::create_or_update' do
333
+ let :already_exists do
334
+ {
335
+ 'error' => 'DocumentAlreadyExistsEngineException: [[my_index][2] [post][1]: document already exists]',
336
+ 'status' => 409
337
+ }
338
+ end
339
+
340
+ let :version_conflict do
341
+ {
342
+ 'error' => "VersionConflictEngineException: [[my_index][3] [post][1]: version conflict, current[2], required[1]]",
343
+ 'status' => 409
344
+ }
345
+ end
346
+
347
+ context 'with discrete persistence' do
348
+ context "when document doesn't already exist" do
349
+ before do
350
+ stub_es_create('my_index', 'post', '1')
351
+ Post.in_index('my_index').create_or_update('1') { |post| post.title = 'hey' }
352
+ end
353
+
354
+ it 'should post to create endpoint' do
355
+ last_request.path.should == '/my_index/post/1/_create'
356
+ end
357
+
358
+ it 'should yield before saving' do
359
+ last_request_json['title'].should == 'hey'
360
+ end
361
+ end
362
+
363
+ context "when document already exists" do
364
+ before do
365
+ stub_request_json(
366
+ :put,
367
+ match_es_path('/my_index/post/1/_create'),
368
+ already_exists
369
+ )
370
+ stub_es_get('my_index', 'post', '1', :comments_count => 2)
371
+ stub_es_update('my_index', 'post', '1')
372
+ Post.in_index('my_index').create_or_update('1') { |post| post.title = 'hey' }
373
+ end
374
+
375
+ it 'should re-update data with correct version' do
376
+ last_request.path.should == '/my_index/post/1?version=1'
377
+ end
378
+
379
+ it 'should include data from storage' do
380
+ last_request_json['comments_count'].should == 2
381
+ end
382
+
383
+ it 'should include updated data from block' do
384
+ last_request_json['title'].should == 'hey'
385
+ end
386
+ end
387
+ end
388
+
389
+ context 'with bulk persistence' do
390
+ context 'when document already exists' do
391
+ before do
392
+ stub_es_bulk(
393
+ 'create' => {
394
+ '_index' => 'my_index', '_type' => 'post', '_id' => '1',
395
+ 'error' => already_exists['error']
396
+ }
397
+ )
398
+ stub_es_get('my_index', 'post', '1', 'comments_count' => 2)
399
+ stub_es_update('my_index', 'post', '1')
400
+ Elastictastic.bulk { Post.in_index('my_index').create_or_update('1') { |post| post.title = 'hey' } }
401
+ end
402
+
403
+ it 'should send update' do
404
+ last_request.path.should == '/my_index/post/1?version=1'
405
+ end
406
+
407
+ it 'should send data from existing document' do
408
+ last_request_json['comments_count'].should == 2
409
+ end
410
+
411
+ it 'should send data from block' do
412
+ last_request_json['title'].should == 'hey'
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end
@@ -12,7 +12,7 @@ describe Elastictastic::ParentChild do
12
12
  describe 'child instance' do
13
13
  let(:post) { blog.posts.new }
14
14
  let(:blog) do
15
- stub_elasticsearch_create('default', 'blog')
15
+ stub_es_create('default', 'blog')
16
16
  Blog.new.tap { |blog| blog.save }
17
17
  end
18
18
 
@@ -24,40 +24,74 @@ describe Elastictastic::ParentChild do
24
24
  blog.posts.from_hash('title' => 'hey').blog.should == blog
25
25
  end
26
26
 
27
+ describe 'when retrieved directly' do
28
+ let(:post) { Post.in_index('my_index').fields('_source', '_parent').first }
29
+
30
+ before do
31
+ stub_es_search(
32
+ 'my_index', 'post',
33
+ 'hits' => {
34
+ 'total' => 1,
35
+ 'hits' => [{
36
+ '_id' => '2',
37
+ '_type' => 'post',
38
+ '_index' => 'my_index',
39
+ '_source' => {},
40
+ 'fields' => { '_parent' => '1' }
41
+ }]
42
+ }
43
+ )
44
+
45
+ stub_es_get(
46
+ 'my_index', 'blog', '1',
47
+ { 'name' => 'Awesome Blog' }
48
+ )
49
+ end
50
+
51
+ it 'should retrieve parent via GET' do
52
+ post.blog.name.should == 'Awesome Blog'
53
+ end
54
+
55
+ it 'should lookup in correct index' do
56
+ post.blog
57
+ FakeWeb.last_request.path.split('?').first.should == '/my_index/blog/1'
58
+ end
59
+ end
60
+
27
61
  describe 'discrete persistence' do
28
62
  it 'should pass parent param on create' do
29
- stub_elasticsearch_create('default', 'post')
63
+ stub_es_create('default', 'post')
30
64
  post.save
31
65
  URI.parse(FakeWeb.last_request.path).query.should == "parent=#{blog.id}"
32
66
  end
33
67
 
34
68
  it 'should pass parent param on update' do
35
- stub_elasticsearch_create('default', 'post')
69
+ stub_es_create('default', 'post')
36
70
  post.save
37
- stub_elasticsearch_update('default', 'post', post.id)
71
+ stub_es_update('default', 'post', post.id)
38
72
  post.save
39
- URI.parse(FakeWeb.last_request.path).query.should == "parent=#{blog.id}"
73
+ URI.parse(FakeWeb.last_request.path).query.split('&').should include("parent=#{blog.id}")
40
74
  end
41
75
 
42
76
  it 'should pass parent on delete' do
43
- stub_elasticsearch_create('default', 'post')
77
+ stub_es_create('default', 'post')
44
78
  post = blog.posts.new
45
79
  post.save
46
- stub_elasticsearch_destroy('default', 'post', post.id)
80
+ stub_es_destroy('default', 'post', post.id)
47
81
  post.destroy
48
- URI.parse(FakeWeb.last_request.path).query.should == "parent=#{blog.id}"
82
+ URI.parse(FakeWeb.last_request.path).query.split('&').should include("parent=#{blog.id}")
49
83
  end
50
84
  end
51
85
 
52
86
  describe 'bulk persistence' do
53
87
  let(:bulk_requests) do
54
88
  FakeWeb.last_request.body.split("\n").map do |line|
55
- JSON.parse(line)
89
+ Elastictastic.json_decode(line)
56
90
  end
57
91
  end
58
92
 
59
93
  before do
60
- stub_elasticsearch_bulk
94
+ stub_es_bulk
61
95
  end
62
96
 
63
97
  it 'should pass parent param on create' do
@@ -102,7 +136,7 @@ describe Elastictastic::ParentChild do
102
136
  end
103
137
 
104
138
  it 'should set index' do
105
- stub_elasticsearch_create('my_index', 'blog')
139
+ stub_es_create('my_index', 'blog')
106
140
  blog = Blog.in_index('my_index').new
107
141
  blog.save
108
142
  post = blog.posts.new
@@ -112,7 +146,7 @@ describe Elastictastic::ParentChild do
112
146
 
113
147
  describe 'collection proxies' do
114
148
  let(:blog) do
115
- stub_elasticsearch_create('my_index', 'blog')
149
+ stub_es_create('my_index', 'blog')
116
150
  Blog.in_index('my_index').new.tap { |blog| blog.save }
117
151
  end
118
152
  let(:posts) { blog.posts }
@@ -137,20 +171,20 @@ describe Elastictastic::ParentChild do
137
171
  end
138
172
 
139
173
  it 'should search correct index' do
140
- stub_elasticsearch_scan('my_index', 'post', 100, { '_id' => '1' })
174
+ stub_es_scan('my_index', 'post', 100, { '_id' => '1' })
141
175
  posts.to_a.first.id.should == '1'
142
176
  end
143
177
 
144
178
  it 'should set routing to parent ID on get' do
145
- stub_elasticsearch_get('my_index', 'post', 1)
179
+ stub_es_get('my_index', 'post', 1)
146
180
  blog.posts.find(1)
147
181
  URI.parse(FakeWeb.last_request.path).query.should == "routing=#{blog.id}"
148
182
  end
149
183
 
150
184
  it 'should set routing to parent ID on multiget' do
151
- stub_elasticsearch_mget('my_index', 'post')
185
+ stub_es_mget('my_index', 'post')
152
186
  blog.posts.find(1, 2)
153
- JSON.parse(FakeWeb.last_request.body).should == {
187
+ Elastictastic.json_decode(FakeWeb.last_request.body).should == {
154
188
  'docs' => [
155
189
  { '_id' => 1, 'routing' => blog.id },
156
190
  { '_id' => 2, 'routing' => blog.id }
@@ -160,14 +194,14 @@ describe Elastictastic::ParentChild do
160
194
 
161
195
  it 'should save transient instances when parent is saved' do
162
196
  post = posts.new
163
- stub_elasticsearch_update('my_index', 'blog', blog.id)
164
- stub_elasticsearch_create('my_index', 'post')
197
+ stub_es_update('my_index', 'blog', blog.id)
198
+ stub_es_create('my_index', 'post')
165
199
  blog.save
166
200
  post.should be_persisted
167
201
  end
168
202
 
169
203
  it 'should not attempt to save transient instances that are pending for save in a bulk block' do
170
- stub_elasticsearch_bulk(
204
+ stub_es_bulk(
171
205
  { 'create' => { '_index' => 'my_index', '_type' => 'post', '_id' => '123', '_version' => 1, 'ok' => true }},
172
206
  { 'index' => { '_index' => 'my_index', '_type' => 'blog', '_id' => blog.id, '_version' => 2, 'ok' => true }}
173
207
  )
@@ -177,34 +211,34 @@ describe Elastictastic::ParentChild do
177
211
  blog.save
178
212
  end
179
213
  request_statements =
180
- FakeWeb.last_request.body.each_line.map { |line| JSON.parse(line) }
214
+ FakeWeb.last_request.body.each_line.map { |line| Elastictastic.json_decode(line) }
181
215
  request_statements.length.should == 4
182
216
  end
183
217
 
184
218
  it 'should not save transient instances again' do
185
219
  post = posts.new
186
- stub_elasticsearch_update('my_index', 'blog', blog.id)
187
- stub_elasticsearch_create('my_index', 'post')
220
+ stub_es_update('my_index', 'blog', blog.id)
221
+ stub_es_create('my_index', 'post')
188
222
  blog.save
189
223
  FakeWeb.clean_registry
190
- stub_elasticsearch_update('my_index', 'blog', blog.id)
224
+ stub_es_update('my_index', 'blog', blog.id)
191
225
  expect { blog.save }.to_not raise_error
192
226
  end
193
227
 
194
228
  it 'should populate parent when finding one' do
195
- stub_elasticsearch_get('my_index', 'post', '1')
229
+ stub_es_get('my_index', 'post', '1')
196
230
  blog.posts.find('1').blog.should == blog
197
231
  end
198
232
 
199
233
  it 'should populate parent when finding many' do
200
- stub_elasticsearch_mget('my_index', 'post', '1', '2')
234
+ stub_es_mget('my_index', 'post', '1', '2')
201
235
  blog.posts.find('1', '2').each do |post|
202
236
  post.blog.should == blog
203
237
  end
204
238
  end
205
239
 
206
240
  it 'should populate parent when retrieving first' do
207
- stub_elasticsearch_search(
241
+ stub_es_search(
208
242
  'my_index', 'post',
209
243
  'total' => 1,
210
244
  'hits' => { 'hits' => [{ '_id' => '2', 'index' => 'my_index', '_type' => 'post' }]}
@@ -213,7 +247,7 @@ describe Elastictastic::ParentChild do
213
247
  end
214
248
 
215
249
  it 'should populate parent when iterating over cursor' do
216
- stub_elasticsearch_scan(
250
+ stub_es_scan(
217
251
  'my_index', 'post', 100,
218
252
  '_id' => '1'
219
253
  )
@@ -221,7 +255,7 @@ describe Elastictastic::ParentChild do
221
255
  end
222
256
 
223
257
  it 'should populate parent when paginating' do
224
- stub_elasticsearch_search(
258
+ stub_es_search(
225
259
  'my_index', 'post',
226
260
  'hits' => {
227
261
  'total' => 1,
@@ -232,15 +266,21 @@ describe Elastictastic::ParentChild do
232
266
  end
233
267
 
234
268
  it 'should iterate over transient instances along with retrieved results' do
235
- stub_elasticsearch_scan('my_index', 'post', 100, '_id' => '1')
269
+ stub_es_scan('my_index', 'post', 100, '_id' => '1')
236
270
  post = blog.posts.new
237
271
  posts = blog.posts.to_a
238
272
  posts[0].id.should == '1'
239
273
  posts[1].should == post
240
274
  end
241
275
 
276
+ it 'should iterate over only transient instances if parent is transient' do
277
+ blog = Blog.new
278
+ post = blog.posts.new
279
+ blog.posts.to_a.should == [post]
280
+ end
281
+
242
282
  it 'should return transient instance as #first if no persisted results' do
243
- stub_elasticsearch_search(
283
+ stub_es_search(
244
284
  'my_index', 'post', 'hits' => { 'total' => 0, 'hits' => [] })
245
285
  post = blog.posts.new
246
286
  blog.posts.first.should == post
@@ -274,7 +314,7 @@ describe Elastictastic::ParentChild do
274
314
 
275
315
  describe 'searching children directly' do
276
316
  before do
277
- stub_elasticsearch_search(
317
+ stub_es_search(
278
318
  'default', 'post',
279
319
  'hits' => {
280
320
  'hits' => [
@@ -291,7 +331,7 @@ describe Elastictastic::ParentChild do
291
331
  let(:post) { Post.first }
292
332
 
293
333
  it 'should provide access to parent' do
294
- stub_elasticsearch_get('default', 'blog', '3')
334
+ stub_es_get('default', 'blog', '3')
295
335
  post.blog.id.should == '3'
296
336
  end
297
337
 
@@ -300,7 +340,7 @@ describe Elastictastic::ParentChild do
300
340
  end
301
341
 
302
342
  it 'should save post without dereferencing parent' do
303
- stub_elasticsearch_update('default', 'post', post.id)
343
+ stub_es_update('default', 'post', post.id)
304
344
  post.save
305
345
  URI.parse(FakeWeb.last_request.path).query.should == 'parent=3'
306
346
  end