elastictastic 0.5.0 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -15,7 +15,7 @@ describe Elastictastic::Dirty do
15
15
  end
16
16
 
17
17
  before do
18
- stub_elasticsearch_update('default', 'post', '1')
18
+ stub_es_update('default', 'post', '1')
19
19
  end
20
20
 
21
21
  context 'top-level attribute' do
@@ -71,6 +71,58 @@ describe Elastictastic::Dirty do
71
71
  post.should_not be_title_changed
72
72
  end
73
73
  end
74
+
75
+ context 'after change and reload' do
76
+ before do
77
+ post.title = 'hey guy'
78
+ stub_es_get('default', 'post', '1', 'title' => 'second title')
79
+ post.reload
80
+ end
81
+
82
+ it 'should not be changed' do
83
+ post.should_not be_changed
84
+ end
85
+ end
86
+
87
+ context 'changing it to the same thing' do
88
+ before do
89
+ post.title = 'first title'
90
+ end
91
+
92
+ it 'should not be changed' do
93
+ post.should_not be_changed
94
+ end
95
+
96
+ it 'should not have any changes' do
97
+ post.changes.should be_empty
98
+ end
99
+
100
+ it 'should not have the title attribute changed' do
101
+ post.should_not be_title_changed
102
+ end
103
+ end
104
+
105
+ context 'changing it twice' do
106
+ before do
107
+ post.title = 'second title'
108
+ post.title = 'third title'
109
+ end
110
+
111
+ it 'should keep track of original value' do
112
+ post.title_change.should == ['first title', 'third title']
113
+ end
114
+ end
115
+
116
+ context 'changing it to something else and then back' do
117
+ before do
118
+ post.title = 'second title'
119
+ post.title = 'first title'
120
+ end
121
+
122
+ it 'should not be changed' do
123
+ post.should_not be_changed
124
+ end
125
+ end
74
126
  end
75
127
 
76
128
  context 'single nested document' do
@@ -113,6 +165,12 @@ describe Elastictastic::Dirty do
113
165
  post.save
114
166
  post.author.should_not be_changed
115
167
  end
168
+
169
+ it 'should not be changed when setting it to the same thing' do
170
+ post.save
171
+ post.author.name = post.author.name
172
+ post.author.should_not be_changed
173
+ end
116
174
  end
117
175
 
118
176
  context 'with nested document replaced' do
@@ -129,6 +187,37 @@ describe Elastictastic::Dirty do
129
187
  ['Mat Brown', 'Barack Obama']
130
188
  end
131
189
  end
190
+
191
+ context 'with nested document replaced by nil' do
192
+ before do
193
+ post.author = Author.new(:name => 'Barack Obama')
194
+ post.save
195
+ post.author = nil
196
+ end
197
+
198
+ it 'should be changed' do
199
+ post.should be_changed
200
+ end
201
+
202
+ it 'should expose changes' do
203
+ post.changes['author'][0].name.should == 'Barack Obama'
204
+ post.changes['author'][1].should == nil
205
+ end
206
+
207
+ it 'should save properly' do
208
+ post.save!
209
+ end
210
+ end
211
+
212
+ context 'with nested document replaced by itself' do
213
+ before do
214
+ post.author = Author.new(:name => 'Mat Brown')
215
+ end
216
+
217
+ it 'should not be changed' do
218
+ post.should_not be_changed
219
+ end
220
+ end
132
221
  end
133
222
 
134
223
  context 'nested document collection' do
@@ -168,6 +257,33 @@ describe Elastictastic::Dirty do
168
257
  post.save
169
258
  post.comments.first.should_not be_changed
170
259
  end
260
+
261
+ it 'should not be changed when setting it to the same thing' do
262
+ post.save
263
+ post.comments.first.body = post.comments.first.body
264
+ post.should_not be_changed
265
+ end
266
+ end
267
+
268
+ context 'when nested document value set to same value' do
269
+ before do
270
+ post.comments.first.body = 'I like pizza'
271
+ end
272
+
273
+ it 'should not be changed' do
274
+ post.should_not be_changed
275
+ end
276
+ end
277
+
278
+ context 'when nested document value set to a different value and then back' do
279
+ before do
280
+ post.comments.first.body = 'I like lasagne'
281
+ post.comments.first.body = 'I like pizza'
282
+ end
283
+
284
+ it 'should not be changed' do
285
+ post.should_not be_changed
286
+ end
171
287
  end
172
288
 
173
289
  context 'when document added' do
@@ -216,6 +332,17 @@ describe Elastictastic::Dirty do
216
332
  end
217
333
  end
218
334
 
335
+ context 'with document removed and then identical document added' do
336
+ before do
337
+ post.comments.delete(post.comments.first)
338
+ post.comments << Comment.new(:body => 'I like pizza')
339
+ end
340
+
341
+ it 'should not be changed' do
342
+ post.should_not be_changed
343
+ end
344
+ end
345
+
219
346
  context 'with collection replaced' do
220
347
  before do
221
348
  post.comments = [Comment.new(:body => 'I like lasagne')]
@@ -234,5 +361,37 @@ describe Elastictastic::Dirty do
234
361
  post.should_not be_changed
235
362
  end
236
363
  end
364
+
365
+ context 'with collection replaced with identical collection' do
366
+ before do
367
+ post.comments = [Comment.new(:body => 'I like pizza')]
368
+ end
369
+
370
+ it 'should not be changed' do
371
+ post.should_not be_changed
372
+ end
373
+ end
374
+
375
+ context 'with collection replaced by a different collection and then back to the original' do
376
+ before do
377
+ post.comments = [Comment.new(:body => 'I am the walrus')]
378
+ post.comments = [Comment.new(:body => 'I like pizza')]
379
+ end
380
+
381
+ it 'should not be changed' do
382
+ post.should_not be_changed
383
+ end
384
+ end
385
+
386
+ context 'with collection replaced and then populated with identical members' do
387
+ before do
388
+ post.comments = []
389
+ post.comments << Comment.new(:body => 'I like pizza')
390
+ end
391
+
392
+ it 'should not be changed' do
393
+ post.should_not be_changed
394
+ end
395
+ end
237
396
  end
238
397
  end
@@ -4,11 +4,11 @@ describe Elastictastic::Document do
4
4
  include Elastictastic::TestHelpers
5
5
 
6
6
  let(:last_request) { FakeWeb.last_request }
7
- let(:last_request_body) { JSON.parse(last_request.body) }
7
+ let(:last_request_body) { Elastictastic.json_decode(last_request.body) }
8
8
 
9
9
  describe '#save' do
10
10
  context 'new object' do
11
- let!(:id) { stub_elasticsearch_create('default', 'post') }
11
+ let!(:id) { stub_es_create('default', 'post') }
12
12
  let(:post) { Post.new }
13
13
 
14
14
  before do
@@ -25,27 +25,48 @@ describe Elastictastic::Document do
25
25
  end
26
26
 
27
27
  it 'should send document in the body' do
28
- last_request.body.should == post.elasticsearch_doc.to_json
28
+ last_request.body.should == Elastictastic.json_encode(post.elasticsearch_doc)
29
29
  end
30
30
 
31
31
  it 'should populate ID of model object' do
32
32
  post.id.should == id
33
33
  end
34
34
 
35
+ it 'should populate version' do
36
+ post.version.should == 1
37
+ end
38
+
35
39
  it 'should mark object as persisted' do
36
40
  post.should be_persisted
37
41
  end
38
42
  end # context 'new object'
39
43
 
44
+ context 'new object with routing' do
45
+ let!(:id) { stub_es_create('default', 'photo') }
46
+ let(:photo) { Photo.new(:post_id => '123') }
47
+
48
+ before do
49
+ photo.save
50
+ end
51
+
52
+ it 'should send routing param' do
53
+ last_request_uri.query.split('&').should include('routing=123')
54
+ end
55
+ end
56
+
40
57
  context 'new object with ID' do
41
58
  let(:post) { Post.new.tap { |post| post.id = '123' }}
42
59
 
43
60
  context 'with unique id' do
44
61
  before do
45
- stub_elasticsearch_create('default', 'post', post.id)
62
+ stub_es_create('default', 'post', post.id)
46
63
  post.save
47
64
  end
48
65
 
66
+ it 'should populate version' do
67
+ post.version.should == 1
68
+ end
69
+
49
70
  it 'should send PUT request' do
50
71
  last_request.method.should == 'PUT'
51
72
  end
@@ -55,18 +76,17 @@ describe Elastictastic::Document do
55
76
  end
56
77
 
57
78
  it 'should send document in body' do
58
- last_request.body.should == post.elasticsearch_doc.to_json
79
+ last_request.body.should == Elastictastic.json_encode(post.elasticsearch_doc)
59
80
  end
60
81
  end # context 'with unique ID'
61
82
 
62
83
  context 'with duplicate ID' do
63
84
  before do
64
- stub_elasticsearch_create(
65
- 'default', 'post', '123',
66
- :body => {
67
- 'error' => 'DocumentAlreadyExistsEngineException[[post][2] [post][1]]: document already exists',
68
- 'status' => 409
69
- }.to_json
85
+ stub_request_json(
86
+ :put,
87
+ match_es_resource('default', 'post', '123', '_create'),
88
+ 'error' => 'DocumentAlreadyExistsEngineException[[post][2] [post][1]]: document already exists',
89
+ 'status' => 409
70
90
  )
71
91
  end
72
92
 
@@ -90,6 +110,16 @@ describe Elastictastic::Document do
90
110
  end # context 'with duplicate ID'
91
111
  end # context 'new object with ID'
92
112
 
113
+ context 'new object with ID with routing' do
114
+ let(:photo) { Photo.new(:id => 'abc', :post_id => '123') }
115
+
116
+ it 'should include routing param when saving' do
117
+ stub_es_create('default', 'photo', 'abc')
118
+ photo.save
119
+ last_request_uri.query.split('&').should include('routing=123')
120
+ end
121
+ end
122
+
93
123
  shared_examples_for 'persisted object' do
94
124
  describe 'identity attributes' do
95
125
  it 'should not allow setting of ID' do
@@ -99,7 +129,7 @@ describe Elastictastic::Document do
99
129
 
100
130
  describe '#save' do
101
131
  before do
102
- stub_elasticsearch_update('default', 'post', post.id)
132
+ stub_es_update('default', 'post', post.id)
103
133
  post.title = 'Fun Factories for Fickle Ferrets'
104
134
  post.save
105
135
  end
@@ -108,19 +138,23 @@ describe Elastictastic::Document do
108
138
  last_request.method.should == 'PUT'
109
139
  end
110
140
 
111
- it "should send to document's resource path" do
112
- last_request.path.should == "/default/post/#{post.id}"
141
+ it "should send to document's resource path with version" do
142
+ last_request.path.should == "/default/post/#{post.id}?version=1"
113
143
  end
114
144
 
115
145
  it "should send document's body in request" do
116
- last_request.body.should == post.elasticsearch_doc.to_json
146
+ last_request.body.should == Elastictastic.json_encode(post.elasticsearch_doc)
147
+ end
148
+
149
+ it 'should populate new version' do
150
+ post.version.should == 2
117
151
  end
118
152
  end # describe '#save'
119
153
  end # shared_examples_for 'persisted object'
120
154
 
121
155
  context 'object after save' do
122
156
  let(:post) do
123
- stub_elasticsearch_create('default', 'post')
157
+ stub_es_create('default', 'post')
124
158
  Post.new.tap { |post| post.save }
125
159
  end
126
160
 
@@ -131,12 +165,33 @@ describe Elastictastic::Document do
131
165
  let(:post) do
132
166
  Post.new.tap do |post|
133
167
  post.id = '123'
168
+ post.version = 1
134
169
  post.persisted!
135
170
  end
136
171
  end
137
172
 
138
173
  it_should_behave_like 'persisted object'
139
174
  end # context 'existing persisted object'
175
+
176
+ context 'persisted object with routing' do
177
+ let(:photo) do
178
+ Photo.new.tap do |photo|
179
+ photo.id = 'abc'
180
+ photo.post_id = '123'
181
+ photo.version = 1
182
+ photo.persisted!
183
+ end
184
+ end
185
+
186
+ before do
187
+ stub_es_update('default', 'photo', 'abc')
188
+ photo.save!
189
+ end
190
+
191
+ it 'should include routing param' do
192
+ last_request_uri.query.split('&').should include('routing=123')
193
+ end
194
+ end
140
195
  end # describe '#save'
141
196
 
142
197
  describe '#destroy' do
@@ -149,7 +204,7 @@ describe Elastictastic::Document do
149
204
  end
150
205
 
151
206
  before do
152
- stub_elasticsearch_destroy('default', 'post', '123')
207
+ stub_es_destroy('default', 'post', '123')
153
208
  @result = post.destroy
154
209
  end
155
210
 
@@ -170,6 +225,26 @@ describe Elastictastic::Document do
170
225
  end
171
226
  end # context 'existing persisted object'
172
227
 
228
+ context 'persisted object with routing' do
229
+ let(:photo) do
230
+ Photo.new.tap do |photo|
231
+ photo.id = 'abc'
232
+ photo.post_id = '123'
233
+ photo.version = 1
234
+ photo.persisted!
235
+ end
236
+ end
237
+
238
+ before do
239
+ stub_es_destroy('default', 'photo', 'abc')
240
+ photo.destroy
241
+ end
242
+
243
+ it 'should include routing param' do
244
+ last_request_uri.query.split('&').should include('routing=123')
245
+ end
246
+ end
247
+
173
248
  context 'transient object' do
174
249
  let(:post) { Post.new }
175
250
 
@@ -187,16 +262,9 @@ describe Elastictastic::Document do
187
262
  end
188
263
 
189
264
  before do
190
- stub_elasticsearch_destroy(
265
+ stub_es_destroy(
191
266
  'default', 'post', '123',
192
- :body => {
193
- 'ok' => true,
194
- 'found' => false,
195
- '_index' => 'default',
196
- '_type' => 'post',
197
- '_id' => '123',
198
- '_version' => 0
199
- }.to_json
267
+ 'found' => false
200
268
  )
201
269
  @result = post.destroy
202
270
  end
@@ -207,10 +275,32 @@ describe Elastictastic::Document do
207
275
  end # describe 'non-existent persisted object'
208
276
  end # describe '#destroy'
209
277
 
278
+ describe '#reload' do
279
+ context 'with persisted object' do
280
+ let :post do
281
+ Post.new.tap do |post|
282
+ post.id = '1'
283
+ post.title = 'Title'
284
+ post.persisted!
285
+ end
286
+ end
287
+
288
+ before do
289
+ stub_es_get('default', 'post', '1', 'title' => 'Title')
290
+ post.title = 'Something'
291
+ post.reload
292
+ end
293
+
294
+ it 'should reset changed attributes' do
295
+ post.title.should == 'Title'
296
+ end
297
+ end
298
+ end
299
+
210
300
  describe '::destroy_all' do
211
301
  describe 'with default index' do
212
302
  before do
213
- stub_elasticsearch_destroy_all('default', 'post')
303
+ stub_es_destroy_all('default', 'post')
214
304
  Post.destroy_all
215
305
  end
216
306
 
@@ -225,7 +315,7 @@ describe Elastictastic::Document do
225
315
 
226
316
  describe 'with specified index' do
227
317
  before do
228
- stub_elasticsearch_destroy_all('my_index', 'post')
318
+ stub_es_destroy_all('my_index', 'post')
229
319
  Post.in_index('my_index').destroy_all
230
320
  end
231
321
 
@@ -242,13 +332,13 @@ describe Elastictastic::Document do
242
332
  end
243
333
 
244
334
  it 'should send mapping to ES' do
245
- last_request.body.should == Post.mapping.to_json
335
+ last_request.body.should == Elastictastic.json_encode(Post.mapping)
246
336
  end
247
337
  end # shared_examples_for 'put mapping'
248
338
 
249
339
  context 'with default index' do
250
340
  before do
251
- stub_elasticsearch_put_mapping('default', 'post')
341
+ stub_es_put_mapping('default', 'post')
252
342
  Post.sync_mapping
253
343
  end
254
344
 
@@ -261,7 +351,7 @@ describe Elastictastic::Document do
261
351
 
262
352
  context 'with specified index' do
263
353
  before do
264
- stub_elasticsearch_put_mapping('my_cool_index', 'post')
354
+ stub_es_put_mapping('my_cool_index', 'post')
265
355
  Post.in_index('my_cool_index').sync_mapping
266
356
  end
267
357
 
@@ -278,7 +368,7 @@ describe Elastictastic::Document do
278
368
  shared_examples_for 'single document lookup' do
279
369
  context 'when document is found' do
280
370
  before do
281
- stub_elasticsearch_get(
371
+ stub_es_get(
282
372
  index, 'post', '1',
283
373
  )
284
374
  end
@@ -287,6 +377,10 @@ describe Elastictastic::Document do
287
377
  post.should be_a(Post)
288
378
  end
289
379
 
380
+ it 'should populate version' do
381
+ post.version.should == 1
382
+ end
383
+
290
384
  it 'should request specified fields if specified' do
291
385
  scope.fields('name', 'author.name').find(1)
292
386
  last_request.path.should == "/#{index}/post/1?fields=name%2Cauthor.name"
@@ -301,7 +395,7 @@ describe Elastictastic::Document do
301
395
 
302
396
  context 'when document is not found' do
303
397
  before do
304
- stub_elasticsearch_get(index, 'post', '1', nil)
398
+ stub_es_get(index, 'post', '1', nil)
305
399
  end
306
400
 
307
401
  it 'should return nil' do
@@ -313,7 +407,7 @@ describe Elastictastic::Document do
313
407
  shared_examples_for 'multi document single index lookup' do
314
408
 
315
409
  before do
316
- stub_elasticsearch_mget(index, 'post', '1', '2')
410
+ stub_es_mget(index, 'post', '1', '2')
317
411
  posts
318
412
  end
319
413
 
@@ -370,82 +464,12 @@ describe Elastictastic::Document do
370
464
  it_should_behave_like 'single document lookup'
371
465
  it_should_behave_like 'multi document single index lookup'
372
466
 
373
- describe 'multi-index multi-get' do
374
- before do
375
- stub_elasticsearch_mget(
376
- nil,
377
- nil,
378
- ['1', 'default'], ['2', 'my_index'], ['3', 'my_index']
379
- )
380
- posts
381
- end
382
-
383
- describe 'with no options' do
384
- let(:posts) { Post.find('default' => '1', 'my_index' => %w(2 3)) }
385
-
386
- it 'should send request to base path' do
387
- last_request.path.should == '/_mget'
388
- end
389
-
390
- it 'should request ids with type and index' do
391
- last_request_body.should == {
392
- 'docs' => [{
393
- '_id' => '1',
394
- '_type' => 'post',
395
- '_index' => 'default'
396
- }, {
397
- '_id' => '2',
398
- '_type' => 'post',
399
- '_index' => 'my_index'
400
- }, {
401
- '_id' => '3',
402
- '_type' => 'post',
403
- '_index' => 'my_index'
404
- }]
405
- }
406
- end
407
-
408
- it 'should return docs with IDs' do
409
- posts.map(&:id).should == %w(1 2 3)
410
- end
411
-
412
- it 'should set proper indices' do
413
- posts.map { |post| post.index.name }.should ==
414
- %w(default my_index my_index)
415
- end
416
- end # context 'with no options'
417
-
418
- context 'with fields specified' do
419
- let(:posts) { Post.fields('title').find('default' => '1', 'my_index' => %w(2 3)) }
420
-
421
- it 'should inject fields into each identifier' do
422
- last_request_body.should == {
423
- 'docs' => [{
424
- '_id' => '1',
425
- '_type' => 'post',
426
- '_index' => 'default',
427
- 'fields' => %w(title)
428
- }, {
429
- '_id' => '2',
430
- '_type' => 'post',
431
- '_index' => 'my_index',
432
- 'fields' => %w(title)
433
- }, {
434
- '_id' => '3',
435
- '_type' => 'post',
436
- '_index' => 'my_index',
437
- 'fields' => %w(title)
438
- }]
439
- }
440
- end
441
- end
442
- end # describe 'multi-index multi-get'
443
467
 
444
468
  context 'when documents are missing' do
445
469
  let(:posts) { Post.find('1', '2') }
446
470
 
447
471
  before do
448
- stub_elasticsearch_mget('default', 'post', '1' => {}, '2' => nil)
472
+ stub_es_mget('default', 'post', '1' => {}, '2' => nil)
449
473
  end
450
474
 
451
475
  it 'should only return docs that exist' do
@@ -462,15 +486,49 @@ describe Elastictastic::Document do
462
486
  it_should_behave_like 'single document lookup'
463
487
  it_should_behave_like 'multi document single index lookup'
464
488
  end # context 'with specified index'
489
+
490
+ context 'with routing specified' do
491
+ context 'single document lookup' do
492
+ it 'should specify routing' do
493
+ stub_es_get('default', 'photo', 'abc')
494
+ Photo.routing('123').find('abc')
495
+ last_request_uri.query.split('&').should include('routing=123')
496
+ end
497
+
498
+ it 'should complain if routing required but not specified' do
499
+ expect { Photo.find('abc') }.
500
+ to raise_error(Elastictastic::MissingParameter)
501
+ end
502
+ end
503
+ end
465
504
  end # describe '::find'
466
505
 
467
- describe '::new_from_elasticsearch_hit' do
506
+ describe '::exists?' do
507
+ it 'should return true if document exists' do
508
+ stub_es_head('my_index', 'post', 1, true)
509
+ Post.in_index('my_index').exists?(1).should be_true
510
+ end
511
+
512
+ it 'should return false if document does not exist' do
513
+ stub_es_head('my_index', 'post', 1, false)
514
+ Post.in_index('my_index').exists?(1).should be_false
515
+ end
516
+
517
+ it 'should send routing when given' do
518
+ stub_es_head('my_index', 'post', 1, true)
519
+ Post.in_index('my_index').routing('my_route').exists?(1)
520
+ last_request_uri.query.split('&').should include('routing=my_route')
521
+ end
522
+ end
523
+
524
+ describe '#elasticsearch_hit=' do
468
525
  context 'with full _source' do
469
526
  let :post do
470
527
  Post.new.tap do |post|
471
528
  post.elasticsearch_hit = {
472
529
  '_id' => '1',
473
530
  '_index' => 'my_index',
531
+ '_version' => 2,
474
532
  '_source' => {
475
533
  'title' => 'Testy time',
476
534
  'tags' => %w(search lucene),
@@ -494,6 +552,10 @@ describe Elastictastic::Document do
494
552
  post.index.name.should == 'my_index'
495
553
  end
496
554
 
555
+ it 'should populate version' do
556
+ post.version.should == 2
557
+ end
558
+
497
559
  it 'should mark document perisistent' do
498
560
  post.should be_persisted
499
561
  end