elastictastic 0.10.9 → 0.11.0

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.
@@ -6,28 +6,54 @@ module Elastictastic
6
6
 
7
7
  module ClassMethods
8
8
 
9
- def create_or_update(id, &block)
9
+ def create_or_update(*ids, &block)
10
10
  scope = current_scope
11
- new.tap do |instance|
12
- instance.id = id
13
- yield instance
14
- end.create do |e|
15
- case e
16
- when nil # chill
17
- when Elastictastic::ServerError::DocumentAlreadyExistsEngineException,
18
- Elastictastic::ServerError::DocumentAlreadyExistsException # 0.19+
19
- scope.update(id, &block)
11
+ ids.each do |id|
12
+ begin
13
+ new.tap do |instance|
14
+ instance.id = id
15
+ yield instance
16
+ end.create do |e|
17
+ case e
18
+ when nil # chill
19
+ when Elastictastic::ServerError::DocumentAlreadyExistsEngineException,
20
+ Elastictastic::ServerError::DocumentAlreadyExistsException # 0.19+
21
+ scope.update(id, &block)
22
+ else
23
+ raise e
24
+ end
25
+ end
26
+ rescue Elastictastic::CancelSave
27
+ # Do Nothing
28
+ end
29
+ end
30
+ end
31
+
32
+ def update(*ids, &block)
33
+ [].tap do |found|
34
+ case ids.length
35
+ when 0 then return
36
+ when 1
37
+ id = ids.first
38
+ instance = scoped({}).find_one(id, :preference => '_primary_first')
39
+ return unless instance
40
+ found << id
41
+ instances = [instance]
20
42
  else
21
- raise e
43
+ instances = scoped({}).
44
+ find_many(ids, :preference => '_primary_first')
45
+ found.concat(instances.map { |instance| instance.id })
46
+ end
47
+ instances.each do |instance|
48
+ instance.try_update(current_scope, &block)
22
49
  end
23
50
  end
24
- rescue Elastictastic::CancelSave
25
- # Do Nothing
26
51
  end
27
52
 
28
- def update(id, &block)
29
- instance = scoped({}).find_one(id, :preference => '_primary_first')
30
- instance.try_update(current_scope, &block) if instance
53
+ def update_or_create(*ids, &block)
54
+ updated_ids = update(*ids, &block)
55
+ create_ids = ids - updated_ids
56
+ create_or_update(*create_ids, &block) if create_ids.any?
31
57
  end
32
58
 
33
59
  def update_each(&block)
@@ -282,6 +282,19 @@ module Elastictastic
282
282
  end
283
283
  end
284
284
 
285
+ #
286
+ # @private
287
+ #
288
+ def find_many(ids, params = {})
289
+ docspec = ids.map do |id|
290
+ { '_id' => id }.merge!(params_for_find_many).
291
+ merge!(params.stringify_keys)
292
+ end
293
+ materialize_hits(
294
+ ::Elastictastic.client.mget(docspec, index, type)['docs']
295
+ ).map { |result, hit| result }
296
+ end
297
+
285
298
  def multi_get_params
286
299
  {
287
300
  '_type' => type,
@@ -380,16 +393,6 @@ module Elastictastic
380
393
  self.counts = search(params)
381
394
  end
382
395
 
383
- def find_many(ids, params = {})
384
- docspec = ids.map do |id|
385
- { '_id' => id }.merge!(params_for_find_many).
386
- merge!(params.stringify_keys)
387
- end
388
- materialize_hits(
389
- ::Elastictastic.client.mget(docspec, index, type)['docs']
390
- ).map { |result, hit| result }
391
- end
392
-
393
396
  def params_for_find_one
394
397
  params_for_find.tap do |params|
395
398
  params['fields'] &&= params['fields'].join(',')
@@ -1,3 +1,3 @@
1
1
  module Elastictastic
2
- VERSION = '0.10.9'
2
+ VERSION = '0.11.0'
3
3
  end
@@ -113,6 +113,52 @@ describe Elastictastic::OptimisticLocking do
113
113
  end
114
114
  end # describe '::update'
115
115
 
116
+ describe '::update with multiple arguments' do
117
+ let(:last_update_request) do
118
+ FakeWeb.requests.reverse.find { |req| req.method == 'PUT' }
119
+ end
120
+
121
+ before do
122
+ stub_es_mget(
123
+ index,
124
+ 'post',
125
+ '1' => {},
126
+ '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('1', '2') do |post|
137
+ post.comments_count = 2
138
+ end
139
+ end
140
+
141
+ it 'should retry unsuccessful updates' do
142
+ FakeWeb.should have(5).requests # mget, 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 with multiple requests'
161
+
116
162
  describe '::update_each' do
117
163
  let(:last_update_request) do
118
164
  FakeWeb.requests.reverse.find { |req| req.method == 'PUT' }
@@ -383,6 +429,46 @@ describe Elastictastic::OptimisticLocking do
383
429
  last_request_json['title'].should == 'hey'
384
430
  end
385
431
  end
432
+
433
+ context "with multiple arguments some of which exist" do
434
+ before do
435
+ stub_es_create('my_index', 'post', '1')
436
+ stub_request_json(
437
+ :put,
438
+ match_es_path('/my_index/post/2/_create'),
439
+ already_exists
440
+ )
441
+ stub_es_get('my_index', 'post', '2', :comments_count => 2)
442
+ stub_es_update('my_index', 'post', '2')
443
+ Post.in_index('my_index').create_or_update('1', '2') do |post|
444
+ post.title = "hey #{post.id}"
445
+ end
446
+ end
447
+
448
+ it 'should post to create endpoint for both documents' do
449
+ FakeWeb.requests.
450
+ should be_any { |request| request.path == '/my_index/post/1/_create' }
451
+ FakeWeb.requests.
452
+ should be_any { |request| request.path == '/my_index/post/2/_create' }
453
+ end
454
+
455
+ it 'should not send update request for successful create' do
456
+ FakeWeb.requests.
457
+ should_not be_any { |request| request.path == '/my_index/post/1?version=1' }
458
+ end
459
+
460
+ it 'should re-update existing data with correct version' do
461
+ last_request.path.should == '/my_index/post/2?version=1'
462
+ end
463
+
464
+ it 'should include data from storage' do
465
+ last_request_json['comments_count'].should == 2
466
+ end
467
+
468
+ it 'should include updated data from block' do
469
+ last_request_json['title'].should == 'hey 2'
470
+ end
471
+ end
386
472
  end
387
473
 
388
474
  context 'with bulk persistence' do
@@ -413,4 +499,44 @@ describe Elastictastic::OptimisticLocking do
413
499
  end
414
500
  end
415
501
  end
502
+
503
+ describe '::update_or_create' do
504
+ let(:scope) { Post }
505
+
506
+ before do
507
+ stub_es_mget(
508
+ 'default', 'post',
509
+ '1' => {title: 'Post 1'},
510
+ '2' => nil
511
+ )
512
+ stub_es_update('default', 'post', '1')
513
+ stub_es_create('default', 'post', '2')
514
+ scope.update_or_create('1', '2') do |post|
515
+ post.comments_count = 1
516
+ end
517
+ end
518
+
519
+ let :create_request do
520
+ FakeWeb.requests.
521
+ find { |request| request.path == "/default/post/2/_create" }
522
+ end
523
+
524
+ let :update_request do
525
+ FakeWeb.requests.
526
+ find { |request| request.path =~ %r(^/default/post/1\??) }
527
+ end
528
+
529
+ it 'should not send any extraneous requests' do
530
+ FakeWeb.should have(3).requests # multiget, create, update
531
+ end
532
+
533
+ it 'should send create request for nonexistent document' do
534
+ create_request.should be
535
+ end
536
+
537
+ it 'should send update request for existent document' do
538
+ update_request.should be
539
+ end
540
+ end
541
+
416
542
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastictastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.9
4
+ version: 0.11.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-01-09 00:00:00.000000000 Z
14
+ date: 2013-03-28 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -250,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
250
  requirements:
251
251
  - ElasticSearch
252
252
  rubyforge_project:
253
- rubygems_version: 1.8.24
253
+ rubygems_version: 1.8.25
254
254
  signing_key:
255
255
  specification_version: 3
256
256
  summary: Object-document mapper for ElasticSearch