chewy 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +35 -29
- data/Appraisals +37 -0
- data/CHANGELOG.md +115 -4
- data/Gemfile +2 -3
- data/README.md +135 -40
- data/chewy.gemspec +4 -3
- data/gemfiles/rails.3.2.activerecord.gemfile +13 -0
- data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.0.activerecord.gemfile +13 -0
- data/gemfiles/rails.4.0.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.0.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.0.mongoid.gemfile +13 -0
- data/gemfiles/rails.4.0.mongoid.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.0.mongoid.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.1.activerecord.gemfile +13 -0
- data/gemfiles/rails.4.1.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.1.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.1.mongoid.gemfile +13 -0
- data/gemfiles/rails.4.1.mongoid.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.1.mongoid.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.2.activerecord.gemfile +13 -0
- data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +14 -0
- data/gemfiles/rails.4.2.mongoid.gemfile +13 -0
- data/gemfiles/rails.4.2.mongoid.kaminari.gemfile +14 -0
- data/gemfiles/rails.4.2.mongoid.will_paginate.gemfile +14 -0
- data/lib/chewy.rb +65 -0
- data/lib/chewy/config.rb +44 -93
- data/lib/chewy/errors.rb +14 -5
- data/lib/chewy/fields/base.rb +8 -7
- data/lib/chewy/fields/root.rb +2 -2
- data/lib/chewy/index.rb +7 -9
- data/lib/chewy/log_subscriber.rb +34 -0
- data/lib/chewy/query.rb +41 -27
- data/lib/chewy/query/criteria.rb +28 -23
- data/lib/chewy/query/scoping.rb +20 -0
- data/lib/chewy/railtie.rb +51 -13
- data/lib/chewy/repository.rb +61 -0
- data/lib/chewy/rspec/update_index.rb +3 -6
- data/lib/chewy/search.rb +28 -7
- data/lib/chewy/strategy.rb +60 -0
- data/lib/chewy/strategy/atomic.rb +31 -0
- data/lib/chewy/strategy/base.rb +27 -0
- data/lib/chewy/strategy/bypass.rb +15 -0
- data/lib/chewy/strategy/urgent.rb +17 -0
- data/lib/chewy/type.rb +19 -5
- data/lib/chewy/type/adapter/active_record.rb +28 -117
- data/lib/chewy/type/adapter/base.rb +35 -0
- data/lib/chewy/type/adapter/mongoid.rb +23 -123
- data/lib/chewy/type/adapter/object.rb +41 -19
- data/lib/chewy/type/adapter/orm.rb +142 -0
- data/lib/chewy/type/import.rb +43 -16
- data/lib/chewy/type/observe.rb +8 -21
- data/lib/chewy/version.rb +1 -1
- data/lib/tasks/chewy.rake +8 -4
- data/spec/chewy/config_spec.rb +20 -97
- data/spec/chewy/fields/base_spec.rb +24 -11
- data/spec/chewy/fields/time_fields_spec.rb +27 -0
- data/spec/chewy/index/settings_spec.rb +2 -1
- data/spec/chewy/index_spec.rb +98 -79
- data/spec/chewy/query/criteria_spec.rb +14 -0
- data/spec/chewy/query_spec.rb +1 -1
- data/spec/chewy/repository_spec.rb +50 -0
- data/spec/chewy/search_spec.rb +100 -0
- data/spec/chewy/strategy_spec.rb +109 -0
- data/spec/chewy/type/adapter/active_record_spec.rb +110 -46
- data/spec/chewy/type/adapter/mongoid_spec.rb +123 -74
- data/spec/chewy/type/adapter/object_spec.rb +51 -34
- data/spec/chewy/type/import_spec.rb +21 -21
- data/spec/chewy/type/observe_spec.rb +26 -29
- data/spec/chewy/type_spec.rb +19 -0
- data/spec/chewy_spec.rb +19 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/support/active_record.rb +2 -1
- data/spec/support/mongoid.rb +29 -38
- metadata +85 -55
- data/gemfiles/Gemfile.rails-3.2.active_record +0 -6
- data/gemfiles/Gemfile.rails-3.2.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-3.2.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.0.active_record +0 -6
- data/gemfiles/Gemfile.rails-4.0.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.0.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.0.mongoid +0 -6
- data/gemfiles/Gemfile.rails-4.0.mongoid.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.0.mongoid.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.1.active_record +0 -6
- data/gemfiles/Gemfile.rails-4.1.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.1.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.1.mongoid +0 -6
- data/gemfiles/Gemfile.rails-4.1.mongoid.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.1.mongoid.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.2.active_record +0 -6
- data/gemfiles/Gemfile.rails-4.2.active_record.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.2.active_record.will_paginate +0 -7
- data/gemfiles/Gemfile.rails-4.2.mongoid +0 -6
- data/gemfiles/Gemfile.rails-4.2.mongoid.kaminari +0 -7
- data/gemfiles/Gemfile.rails-4.2.mongoid.will_paginate +0 -7
- data/spec/chewy/index/search_spec.rb +0 -46
@@ -8,6 +8,7 @@ describe Chewy::Query::Criteria do
|
|
8
8
|
its(:facets) { should == {} }
|
9
9
|
its(:scores) { should == [] }
|
10
10
|
its(:aggregations) { should == {} }
|
11
|
+
its(:script_fields) { should == {} }
|
11
12
|
its(:queries) { should == [] }
|
12
13
|
its(:filters) { should == [] }
|
13
14
|
its(:post_filters) { should == [] }
|
@@ -19,6 +20,7 @@ describe Chewy::Query::Criteria do
|
|
19
20
|
its(:facets?) { should eq(false) }
|
20
21
|
its(:scores?) { should eq(false) }
|
21
22
|
its(:aggregations?) { should eq(false) }
|
23
|
+
its(:script_fields?) { should eq(false) }
|
22
24
|
its(:queries?) { should eq(false) }
|
23
25
|
its(:filters?) { should eq(false) }
|
24
26
|
its(:post_filters?) { should eq(false) }
|
@@ -54,6 +56,11 @@ describe Chewy::Query::Criteria do
|
|
54
56
|
specify { expect { subject.update_aggregations(field: 'hello') }.to change { subject.aggregations }.to(field: 'hello') }
|
55
57
|
end
|
56
58
|
|
59
|
+
describe '#update_script_fields' do
|
60
|
+
specify { expect { subject.update_script_fields(distance: {script: "doc['coordinates'].distanceInMiles(lat, lon)"}) }.to change { subject.script_fields? }.to(true) }
|
61
|
+
specify { expect { subject.update_script_fields(distance_km: {script: "doc['coordinates'].distanceInKm(lat, lon)"}) }.to change { subject.script_fields }.to(distance_km: {script: "doc['coordinates'].distanceInKm(lat, lon)"}) }
|
62
|
+
end
|
63
|
+
|
57
64
|
describe '#update_queries' do
|
58
65
|
specify { expect { subject.update_queries(field: 'hello') }.to change { subject.queries? }.to(true) }
|
59
66
|
specify { expect { subject.update_queries(field: 'hello') }.to change { subject.queries }.to([field: 'hello']) }
|
@@ -132,6 +139,8 @@ describe Chewy::Query::Criteria do
|
|
132
139
|
.merge(criteria.tap { |c| c.update_request_options(opt2: 'hello') }).request_options).to include(opt1: 'hello', opt2: 'hello') }
|
133
140
|
specify { expect(subject.tap { |c| c.update_facets(field1: 'hello') }
|
134
141
|
.merge(criteria.tap { |c| c.update_facets(field1: 'hello') }).facets).to eq({field1: 'hello', field1: 'hello'}) }
|
142
|
+
specify { expect(subject.tap { |c| c.update_script_fields(distance_m: {script: "doc['coordinates'].distanceInMiles(lat, lon)"}) }
|
143
|
+
.merge(criteria.tap { |c| c.update_script_fields(distance_km: {script: "doc['coordinates'].distanceInKm(lat, lon)"}) }).script_fields).to eq({distance_m: {script: "doc['coordinates'].distanceInMiles(lat, lon)"}, distance_km: {script: "doc['coordinates'].distanceInKm(lat, lon)"}}) }
|
135
144
|
specify { expect(subject.tap { |c| c.update_scores(script: 'hello') }
|
136
145
|
.merge(criteria.tap { |c| c.update_scores(script: 'foobar') }).scores).to eq([{script: 'hello'}, { script: 'foobar' } ]) }
|
137
146
|
specify { expect(subject.tap { |c| c.update_aggregations(field1: 'hello') }
|
@@ -162,6 +171,8 @@ describe Chewy::Query::Criteria do
|
|
162
171
|
.merge!(criteria.tap { |c| c.update_request_options(opt2: 'hello') }).request_options).to include(opt1: 'hello', opt2: 'hello') }
|
163
172
|
specify { expect(subject.tap { |c| c.update_facets(field1: 'hello') }
|
164
173
|
.merge!(criteria.tap { |c| c.update_facets(field1: 'hello') }).facets).to eq({field1: 'hello', field1: 'hello'}) }
|
174
|
+
specify { expect(subject.tap { |c| c.update_script_fields(distance_m: {script: "doc['coordinates'].distanceInMiles(lat, lon)"}) }
|
175
|
+
.merge(criteria.tap { |c| c.update_script_fields(distance_km: {script: "doc['coordinates'].distanceInKm(lat, lon)"}) }).script_fields).to eq({distance_m: {script: "doc['coordinates'].distanceInMiles(lat, lon)"}, distance_km: {script: "doc['coordinates'].distanceInKm(lat, lon)"}}) }
|
165
176
|
specify { expect(subject.tap { |c| c.update_aggregations(field1: 'hello') }
|
166
177
|
.merge!(criteria.tap { |c| c.update_aggregations(field1: 'hello') }).aggregations).to eq({field1: 'hello', field1: 'hello'}) }
|
167
178
|
specify { expect(subject.tap { |c| c.update_queries(field1: 'hello') }
|
@@ -217,6 +228,9 @@ describe Chewy::Query::Criteria do
|
|
217
228
|
specify { expect(request_body {
|
218
229
|
update_queries(:query); update_post_filters(:post_filter);
|
219
230
|
}).to eq({body: {query: :query, post_filter: :post_filter}}) }
|
231
|
+
specify { expect(request_body {
|
232
|
+
update_script_fields(distance_m: {script: "doc['coordinates'].distanceInMiles(lat, lon)"})
|
233
|
+
}).to eq({:body=>{:script_fields=>{:distance_m=>{:script=>"doc['coordinates'].distanceInMiles(lat, lon)"}}}}) }
|
220
234
|
end
|
221
235
|
|
222
236
|
describe '#_filtered_query' do
|
data/spec/chewy/query_spec.rb
CHANGED
@@ -106,7 +106,7 @@ describe Chewy::Query do
|
|
106
106
|
specify { expect(subject.script_score('23')).not_to eq(subject) }
|
107
107
|
specify { expect(subject.script_score('23').criteria.scores).to eq([ { script_score: { script: '23' } } ]) }
|
108
108
|
specify { expect { subject.script_score('23') }.not_to change { subject.criteria.scores } }
|
109
|
-
specify { expect(subject.script_score('23',
|
109
|
+
specify { expect(subject.script_score('23 * factor', params: { factor: 0.5}).criteria.scores).to eq([{ script_score: { script: '23 * factor', params: { factor: 0.5} } }]) }
|
110
110
|
end
|
111
111
|
|
112
112
|
describe '#boost_factor' do
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chewy::Repository do
|
4
|
+
subject { described_class.send(:new) }
|
5
|
+
|
6
|
+
its(:analyzers) { should == {} }
|
7
|
+
its(:tokenizers) { should == {} }
|
8
|
+
its(:filters) { should == {} }
|
9
|
+
its(:char_filters) { should == {} }
|
10
|
+
|
11
|
+
describe '#analyzer' do
|
12
|
+
specify { expect(subject.analyzer(:name)).to be_nil }
|
13
|
+
|
14
|
+
context do
|
15
|
+
before { subject.analyzer(:name, option: :foo) }
|
16
|
+
specify { expect(subject.analyzer(:name)).to eq({option: :foo}) }
|
17
|
+
specify { expect(subject.analyzers).to eq({name: {option: :foo}}) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#tokenizer' do
|
22
|
+
specify { expect(subject.tokenizer(:name)).to be_nil }
|
23
|
+
|
24
|
+
context do
|
25
|
+
before { subject.tokenizer(:name, option: :foo) }
|
26
|
+
specify { expect(subject.tokenizer(:name)).to eq({option: :foo}) }
|
27
|
+
specify { expect(subject.tokenizers).to eq({name: {option: :foo}}) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#filter' do
|
32
|
+
specify { expect(subject.filter(:name)).to be_nil }
|
33
|
+
|
34
|
+
context do
|
35
|
+
before { subject.filter(:name, option: :foo) }
|
36
|
+
specify { expect(subject.filter(:name)).to eq({option: :foo}) }
|
37
|
+
specify { expect(subject.filters).to eq({name: {option: :foo}}) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#char_filter' do
|
42
|
+
specify { expect(subject.char_filter(:name)).to be_nil }
|
43
|
+
|
44
|
+
context do
|
45
|
+
before { subject.char_filter(:name, option: :foo) }
|
46
|
+
specify { expect(subject.char_filter(:name)).to eq({option: :foo}) }
|
47
|
+
specify { expect(subject.char_filters).to eq({name: {option: :foo}}) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chewy::Search do
|
4
|
+
before { Chewy.massacre }
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_index(:products) do
|
8
|
+
define_type :product
|
9
|
+
define_type :product2
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:product) { ProductsIndex::Product }
|
14
|
+
|
15
|
+
describe '.all' do
|
16
|
+
specify { expect(ProductsIndex.all).to be_a(Chewy::Query) }
|
17
|
+
specify { expect(product.all).to be_a(Chewy::Query) }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.search_string' do
|
21
|
+
specify do
|
22
|
+
expect(ProductsIndex.client).to receive(:search).with(hash_including(q: 'hello')).twice
|
23
|
+
ProductsIndex.search_string('hello')
|
24
|
+
product.search_string('hello')
|
25
|
+
end
|
26
|
+
|
27
|
+
specify do
|
28
|
+
expect(ProductsIndex.client).to receive(:search).with(hash_including(explain: true)).twice
|
29
|
+
ProductsIndex.search_string('hello', explain: true)
|
30
|
+
product.search_string('hello', explain: true)
|
31
|
+
end
|
32
|
+
|
33
|
+
specify do
|
34
|
+
expect(ProductsIndex.client).to receive(:search).with(hash_including(index: ['products'], type: []))
|
35
|
+
ProductsIndex.search_string('hello')
|
36
|
+
end
|
37
|
+
|
38
|
+
specify do
|
39
|
+
expect(ProductsIndex.client).to receive(:search).with(hash_including(index: ['products'], type: ['product']))
|
40
|
+
product.search_string('hello')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'named scopes' do
|
45
|
+
before do
|
46
|
+
stub_model(:city)
|
47
|
+
stub_model(:country)
|
48
|
+
|
49
|
+
stub_index(:places) do
|
50
|
+
def self.by_rating(value)
|
51
|
+
filter { rating == value }
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.by_name(index)
|
55
|
+
filter { name == "Name#{index}" }
|
56
|
+
end
|
57
|
+
|
58
|
+
define_type City do
|
59
|
+
def self.by_rating
|
60
|
+
filter { rating == yield }
|
61
|
+
end
|
62
|
+
|
63
|
+
field :name, index: 'not_analyzed'
|
64
|
+
field :rating, type: :integer
|
65
|
+
end
|
66
|
+
|
67
|
+
define_type Country do
|
68
|
+
field :name, index: 'not_analyzed'
|
69
|
+
field :rating, type: :integer
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
let!(:cities) { 3.times.map { |i| City.create! rating: i + 1, name: "Name#{i+1}" } }
|
75
|
+
let!(:countries) { 3.times.map { |i| Country.create! rating: i + 1, name: "Name#{i+4}" } }
|
76
|
+
|
77
|
+
before { PlacesIndex.import! city: cities, country: countries }
|
78
|
+
|
79
|
+
specify { expect(PlacesIndex.by_rating(1).map(&:rating)).to eq([1, 1]) }
|
80
|
+
specify { expect(PlacesIndex.by_rating(1).map(&:class)).to match_array([PlacesIndex::City, PlacesIndex::Country]) }
|
81
|
+
specify { expect(PlacesIndex.by_rating(1).by_name(1).map(&:rating)).to eq([1]) }
|
82
|
+
specify { expect(PlacesIndex.by_rating(1).by_name(1).map(&:class)).to eq([PlacesIndex::City]) }
|
83
|
+
specify { expect(PlacesIndex.order(:name).by_rating(1).map(&:rating)).to eq([1, 1]) }
|
84
|
+
specify { expect(PlacesIndex.order(:name).by_rating(1).map(&:class)).to match_array([PlacesIndex::City, PlacesIndex::Country]) }
|
85
|
+
|
86
|
+
specify { expect(PlacesIndex::City.by_rating {2}.map(&:rating)).to eq([2]) }
|
87
|
+
specify { expect(PlacesIndex::City.by_rating {2}.map(&:class)).to eq([PlacesIndex::City]) }
|
88
|
+
specify { expect(PlacesIndex::City.by_rating {2}.by_name(2).map(&:rating)).to eq([2]) }
|
89
|
+
specify { expect(PlacesIndex::City.by_rating {2}.by_name(2).map(&:class)).to eq([PlacesIndex::City]) }
|
90
|
+
specify { expect(PlacesIndex::City.order(:name).by_rating {2}.map(&:rating)).to eq([2]) }
|
91
|
+
specify { expect(PlacesIndex::City.order(:name).by_rating {2}.map(&:class)).to eq([PlacesIndex::City]) }
|
92
|
+
|
93
|
+
specify { expect(PlacesIndex::Country.by_rating(3).map(&:rating)).to eq([3]) }
|
94
|
+
specify { expect(PlacesIndex::Country.by_rating(3).map(&:class)).to eq([PlacesIndex::Country]) }
|
95
|
+
specify { expect(PlacesIndex::Country.by_rating(3).by_name(6).map(&:rating)).to eq([3]) }
|
96
|
+
specify { expect(PlacesIndex::Country.by_rating(3).by_name(6).map(&:class)).to eq([PlacesIndex::Country]) }
|
97
|
+
specify { expect(PlacesIndex::Country.order(:name).by_rating(3).map(&:rating)).to eq([3]) }
|
98
|
+
specify { expect(PlacesIndex::Country.order(:name).by_rating(3).map(&:class)).to eq([PlacesIndex::Country]) }
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chewy::Strategy do
|
4
|
+
before { Chewy.massacre }
|
5
|
+
subject(:strategy) { Chewy::Strategy.new }
|
6
|
+
|
7
|
+
describe '#current' do
|
8
|
+
specify { expect(strategy.current).to be_a(Chewy::Strategy::Base) }
|
9
|
+
|
10
|
+
context do
|
11
|
+
before { allow(Chewy).to receive_messages(root_strategy: :bypass) }
|
12
|
+
specify { expect(strategy.current).to be_a(Chewy::Strategy::Bypass) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#push' do
|
17
|
+
specify { expect { strategy.push(:unexistant) }.to raise_error }
|
18
|
+
|
19
|
+
specify do
|
20
|
+
expect { strategy.push(:atomic) }
|
21
|
+
.to change { strategy.current }
|
22
|
+
.to(an_instance_of(Chewy::Strategy::Atomic))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#pop' do
|
27
|
+
specify { expect { strategy.pop }.to raise_error }
|
28
|
+
|
29
|
+
specify do
|
30
|
+
strategy.push(:urgent)
|
31
|
+
expect { strategy.pop }
|
32
|
+
.to change { strategy.current }
|
33
|
+
.to(an_instance_of(Chewy::Strategy::Base))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'nesting', :orm do
|
38
|
+
before do
|
39
|
+
stub_model(:city) do
|
40
|
+
update_index('cities#city') { self }
|
41
|
+
end
|
42
|
+
|
43
|
+
stub_index(:cities) do
|
44
|
+
define_type City
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:city) { City.create!(name: 'hello') }
|
49
|
+
let(:other_city) { City.create!(name: 'world') }
|
50
|
+
|
51
|
+
context do
|
52
|
+
around { |example| Chewy.strategy(:bypass) { example.run } }
|
53
|
+
|
54
|
+
specify do
|
55
|
+
expect(CitiesIndex::City).not_to receive(:import)
|
56
|
+
[city, other_city].map(&:save!)
|
57
|
+
end
|
58
|
+
|
59
|
+
specify do
|
60
|
+
expect(CitiesIndex::City).to receive(:import).with([city.id, other_city.id]).once
|
61
|
+
Chewy.strategy(:atomic) { [city, other_city].map(&:save!) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context do
|
66
|
+
around { |example| Chewy.strategy(:urgent) { example.run } }
|
67
|
+
|
68
|
+
specify do
|
69
|
+
expect(CitiesIndex::City).to receive(:import).at_least(2).times
|
70
|
+
[city, other_city].map(&:save!)
|
71
|
+
end
|
72
|
+
|
73
|
+
specify do
|
74
|
+
expect(CitiesIndex::City).to receive(:import).with([city.id, other_city.id]).once
|
75
|
+
Chewy.strategy(:atomic) { [city, other_city].map(&:save!) }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'hash passed to urgent' do
|
79
|
+
before do
|
80
|
+
stub_index(:cities) do
|
81
|
+
define_type :city
|
82
|
+
end
|
83
|
+
|
84
|
+
stub_model(:city) do
|
85
|
+
update_index('cities#city') { { name: name } }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
specify do
|
90
|
+
[city, other_city].map(&:save!)
|
91
|
+
expect(CitiesIndex::City.total_count).to eq(4)
|
92
|
+
end
|
93
|
+
|
94
|
+
context do
|
95
|
+
before do
|
96
|
+
stub_model(:city) do
|
97
|
+
update_index('cities#city') { { id: id.to_s, name: name } }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
specify do
|
102
|
+
[city, other_city].map(&:save!)
|
103
|
+
expect(CitiesIndex::City.total_count).to eq(2)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -16,6 +16,14 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
describe '#default_scope' do
|
20
|
+
specify { expect(described_class.new(City).default_scope).to eq(City.where(nil)) }
|
21
|
+
specify { expect(described_class.new(City.order(:id)).default_scope).to eq(City.where(nil)) }
|
22
|
+
specify { expect(described_class.new(City.limit(10)).default_scope).to eq(City.where(nil)) }
|
23
|
+
specify { expect(described_class.new(City.offset(10)).default_scope).to eq(City.where(nil)) }
|
24
|
+
specify { expect(described_class.new(City.where(rating: 10)).default_scope).to eq(City.where(rating: 10)) }
|
25
|
+
end
|
26
|
+
|
19
27
|
describe '#type_name' do
|
20
28
|
specify { expect(described_class.new(City).type_name).to eq('city') }
|
21
29
|
specify { expect(described_class.new(City.order(:id)).type_name).to eq('city') }
|
@@ -29,6 +37,29 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
29
37
|
end
|
30
38
|
end
|
31
39
|
|
40
|
+
describe '#identify' do
|
41
|
+
context do
|
42
|
+
subject { described_class.new(City) }
|
43
|
+
let!(:cities) { 3.times.map { City.create! } }
|
44
|
+
|
45
|
+
specify { expect(subject.identify(City.where(nil))).to match_array(cities.map(&:id)) }
|
46
|
+
specify { expect(subject.identify(cities)).to eq(cities.map(&:id)) }
|
47
|
+
specify { expect(subject.identify(cities.first)).to eq([cities.first.id]) }
|
48
|
+
specify { expect(subject.identify(cities.first(2).map(&:id))).to eq(cities.first(2).map(&:id)) }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'custom primary_key' do
|
52
|
+
before { stub_model(:city) { self.primary_key = 'rating' } }
|
53
|
+
subject { described_class.new(City) }
|
54
|
+
let!(:cities) { 3.times.map { |i| City.create! { |c| c.rating = i } } }
|
55
|
+
|
56
|
+
specify { expect(subject.identify(City.where(nil))).to match_array([0, 1, 2]) }
|
57
|
+
specify { expect(subject.identify(cities)).to eq([0, 1, 2]) }
|
58
|
+
specify { expect(subject.identify(cities.first)).to eq([0]) }
|
59
|
+
specify { expect(subject.identify(cities.first(2).map(&:id))).to eq([0, 1]) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
32
63
|
describe '#import' do
|
33
64
|
def import(*args)
|
34
65
|
result = []
|
@@ -37,11 +68,12 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
37
68
|
end
|
38
69
|
|
39
70
|
context do
|
40
|
-
let!(:cities) { 3.times.map {
|
41
|
-
let!(:deleted) {
|
71
|
+
let!(:cities) { 3.times.map { City.create! } }
|
72
|
+
let!(:deleted) { 4.times.map { City.create!.tap(&:destroy) } }
|
42
73
|
subject { described_class.new(City) }
|
43
74
|
|
44
75
|
specify { expect(import).to eq([{index: cities}]) }
|
76
|
+
specify { expect(import nil).to eq([]) }
|
45
77
|
|
46
78
|
specify { expect(import(City.order(:id))).to eq([{index: cities}]) }
|
47
79
|
specify { expect(import(City.order(:id), batch_size: 2))
|
@@ -50,11 +82,13 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
50
82
|
specify { expect(import(cities)).to eq([{index: cities}]) }
|
51
83
|
specify { expect(import(cities, batch_size: 2))
|
52
84
|
.to eq([{index: cities.first(2)}, {index: cities.last(1)}]) }
|
53
|
-
specify { expect(import(cities, deleted))
|
85
|
+
specify { expect(import(cities, deleted))
|
86
|
+
.to eq([{index: cities}, {delete: deleted}]) }
|
54
87
|
specify { expect(import(cities, deleted, batch_size: 2)).to eq([
|
55
|
-
|
56
|
-
|
57
|
-
|
88
|
+
{index: cities.first(2)},
|
89
|
+
{index: cities.last(1)},
|
90
|
+
{delete: deleted.first(2)},
|
91
|
+
{delete: deleted.last(2)}]) }
|
58
92
|
|
59
93
|
specify { expect(import(cities.map(&:id))).to eq([{index: cities}]) }
|
60
94
|
specify { expect(import(deleted.map(&:id))).to eq([{delete: deleted.map(&:id)}]) }
|
@@ -66,48 +100,61 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
66
100
|
{index: cities.first(2)},
|
67
101
|
{index: cities.last(1)},
|
68
102
|
{delete: deleted.first(2).map(&:id)},
|
69
|
-
{delete: deleted.last(
|
103
|
+
{delete: deleted.last(2).map(&:id)}]) }
|
70
104
|
|
71
105
|
specify { expect(import(cities.first, nil)).to eq([{index: [cities.first]}]) }
|
72
106
|
specify { expect(import(cities.first.id, nil)).to eq([{index: [cities.first]}]) }
|
107
|
+
end
|
73
108
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
109
|
+
context 'additional delete conitions' do
|
110
|
+
let!(:cities) { 4.times.map { |i| City.create! rating: i } }
|
111
|
+
before { cities.last(2).map(&:destroy) }
|
112
|
+
subject { described_class.new(City) }
|
78
113
|
|
79
114
|
context do
|
80
|
-
before
|
81
|
-
|
82
|
-
|
115
|
+
before do
|
116
|
+
City.class_eval do
|
117
|
+
def delete_from_index?
|
118
|
+
rating.in?([1, 3])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
83
122
|
|
84
|
-
|
85
|
-
|
86
|
-
|
123
|
+
specify { expect(import(City.where(nil))).to eq([
|
124
|
+
{ index: [cities[0]], delete: [cities[1]] }
|
125
|
+
]) }
|
126
|
+
specify { expect(import(cities)).to eq([
|
127
|
+
{ index: [cities[0]], delete: [cities[1]] },
|
128
|
+
{ delete: cities.last(2) }
|
129
|
+
]) }
|
130
|
+
specify { expect(import(cities.map(&:id))).to eq([
|
131
|
+
{ index: [cities[0]], delete: [cities[1]] },
|
132
|
+
{ delete: cities.last(2).map(&:id) }
|
133
|
+
]) }
|
87
134
|
end
|
88
135
|
|
89
136
|
context do
|
90
|
-
before
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
describe '#delete_from_index?' do
|
96
|
-
before do
|
97
|
-
stub_model(:city) do
|
98
|
-
def delete_from_index?
|
99
|
-
rating == 42
|
137
|
+
before do
|
138
|
+
City.class_eval do
|
139
|
+
def delete_already?
|
140
|
+
rating.in?([1, 3])
|
141
|
+
end
|
100
142
|
end
|
101
143
|
end
|
144
|
+
subject { described_class.new(City, delete_if: ->{ delete_already? }) }
|
145
|
+
|
146
|
+
specify { expect(import(City.where(nil))).to eq([
|
147
|
+
{ index: [cities[0]], delete: [cities[1]] }
|
148
|
+
]) }
|
149
|
+
specify { expect(import(cities)).to eq([
|
150
|
+
{ index: [cities[0]], delete: [cities[1]] },
|
151
|
+
{ delete: cities.last(2) }
|
152
|
+
]) }
|
153
|
+
specify { expect(import(cities.map(&:id))).to eq([
|
154
|
+
{ index: [cities[0]], delete: [cities[1]] },
|
155
|
+
{ delete: cities.last(2).map(&:id) }
|
156
|
+
]) }
|
102
157
|
end
|
103
|
-
let!(:cities) { 3.times.map { |i| City.create! } }
|
104
|
-
let!(:deleted) { 3.times.map { |i| City.create!(rating: 42) } }
|
105
|
-
subject { described_class.new(City) }
|
106
|
-
|
107
|
-
specify { expect(import(cities, deleted)).to eq([{index: cities, delete: deleted}]) }
|
108
|
-
specify { expect(import(cities.map(&:id), deleted.map(&:id)))
|
109
|
-
.to eq([{index: cities, delete: deleted}]) }
|
110
|
-
specify { expect(import(City.order(:id))).to eq([{index: cities, delete: deleted}]) }
|
111
158
|
end
|
112
159
|
|
113
160
|
context 'custom primary_key' do
|
@@ -125,11 +172,13 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
125
172
|
specify { expect(import(cities)).to eq([{index: cities}]) }
|
126
173
|
specify { expect(import(cities, batch_size: 2))
|
127
174
|
.to eq([{index: cities.first(2)}, {index: cities.last(1)}]) }
|
128
|
-
specify { expect(import(cities, deleted))
|
175
|
+
specify { expect(import(cities, deleted))
|
176
|
+
.to eq([{index: cities}, {delete: deleted}]) }
|
129
177
|
specify { expect(import(cities, deleted, batch_size: 2)).to eq([
|
130
|
-
|
131
|
-
|
132
|
-
|
178
|
+
{index: cities.first(2)},
|
179
|
+
{index: cities.last(1)},
|
180
|
+
{delete: deleted.first(2)},
|
181
|
+
{delete: deleted.last(1)}]) }
|
133
182
|
|
134
183
|
specify { expect(import(cities.map(&:id))).to eq([{index: cities}]) }
|
135
184
|
specify { expect(import(cities.map(&:id), batch_size: 2))
|
@@ -150,14 +199,29 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
150
199
|
|
151
200
|
specify { expect(import).to eq([{index: cities.first(3)}]) }
|
152
201
|
|
153
|
-
specify { expect(import(City.
|
154
|
-
|
202
|
+
specify { expect(import(City.where('rating < 2')))
|
203
|
+
.to eq([{index: cities.first(3)}]) }
|
204
|
+
specify { expect(import(City.where('rating < 2'), batch_size: 2))
|
155
205
|
.to eq([{index: cities.first(2)}, {index: [cities[2]]}]) }
|
206
|
+
specify { expect(import(City.where('rating < 1')))
|
207
|
+
.to eq([{index: cities.first(3)}]) }
|
208
|
+
specify { expect(import(City.where('rating > 1'))).to eq([]) }
|
209
|
+
|
210
|
+
specify { expect(import(cities.first(2)))
|
211
|
+
.to eq([{index: cities.first(2)}]) }
|
212
|
+
specify { expect(import(cities))
|
213
|
+
.to eq([{index: cities.first(3)}, {delete: cities.last(1)}]) }
|
214
|
+
specify { expect(import(cities, batch_size: 2))
|
215
|
+
.to eq([{index: cities.first(2)}, {index: [cities[2]]}, {delete: cities.last(1)}]) }
|
216
|
+
specify { expect(import(cities, deleted))
|
217
|
+
.to eq([{index: cities.first(3)}, {delete: cities.last(1) + deleted}]) }
|
218
|
+
specify { expect(import(cities, deleted, batch_size: 3)).to eq([
|
219
|
+
{index: cities.first(3)},
|
220
|
+
{delete: cities.last(1) + deleted.first(2)},
|
221
|
+
{delete: deleted.last(1)}]) }
|
156
222
|
|
157
|
-
specify { expect(import(cities)
|
158
|
-
|
159
|
-
.to eq([{index: cities.first(3)}, {index: cities.last(1)}]) }
|
160
|
-
|
223
|
+
specify { expect(import(cities.first(2).map(&:id)))
|
224
|
+
.to eq([{index: cities.first(2)}]) }
|
161
225
|
specify { expect(import(cities.map(&:id)))
|
162
226
|
.to eq([{index: cities.first(3)}, {delete: [cities.last.id]}]) }
|
163
227
|
specify { expect(import(cities.map(&:id), batch_size: 2))
|
@@ -177,7 +241,7 @@ describe Chewy::Type::Adapter::ActiveRecord, :active_record do
|
|
177
241
|
subject { described_class.new(City) }
|
178
242
|
|
179
243
|
let(:data_comparer) do
|
180
|
-
->(id, data) {
|
244
|
+
->(id, data) { objects = data[:index] || data[:delete]; !objects.map { |o| o.respond_to?(:id) ? o.id : o }.include?(id) }
|
181
245
|
end
|
182
246
|
|
183
247
|
context 'implicit scope' do
|