chewy 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ describe Chewy::Query::Criteria do
7
7
 
8
8
  its(:options) { should be_a Hash }
9
9
  its(:facets) { should == {} }
10
+ its(:aggregations) { should == {} }
10
11
  its(:queries) { should == [] }
11
12
  its(:filters) { should == [] }
12
13
  its(:sort) { should == [] }
@@ -15,6 +16,7 @@ describe Chewy::Query::Criteria do
15
16
 
16
17
  its(:none?){ should be_false }
17
18
  its(:facets?) { should be_false }
19
+ its(:aggregations?) { should be_false }
18
20
  its(:queries?) { should be_false }
19
21
  its(:filters?) { should be_false }
20
22
  its(:sort?) { should be_false }
@@ -30,6 +32,11 @@ describe Chewy::Query::Criteria do
30
32
  specify { expect { subject.update_facets(field: 'hello') }.to change { subject.facets }.to(field: 'hello') }
31
33
  end
32
34
 
35
+ describe '#update_aggregations' do
36
+ specify { expect { subject.update_aggregations(field: 'hello') }.to change { subject.aggregations? }.to(true) }
37
+ specify { expect { subject.update_aggregations(field: 'hello') }.to change { subject.aggregations }.to(field: 'hello') }
38
+ end
39
+
33
40
  describe '#update_queries' do
34
41
  specify { expect { subject.update_queries(field: 'hello') }.to change { subject.queries? }.to(true) }
35
42
  specify { expect { subject.update_queries(field: 'hello') }.to change { subject.queries }.to([field: 'hello']) }
@@ -97,6 +104,8 @@ describe Chewy::Query::Criteria do
97
104
  .merge(criteria.tap { |c| c.update_options(opt2: 'hello') }).options.should include(opt1: 'hello', opt2: 'hello') }
98
105
  specify { subject.tap { |c| c.update_facets(field1: 'hello') }
99
106
  .merge(criteria.tap { |c| c.update_facets(field1: 'hello') }).facets.should == {field1: 'hello', field1: 'hello'} }
107
+ specify { subject.tap { |c| c.update_aggregations(field1: 'hello') }
108
+ .merge(criteria.tap { |c| c.update_aggregations(field1: 'hello') }).aggregations.should == {field1: 'hello', field1: 'hello'} }
100
109
  specify { subject.tap { |c| c.update_queries(field1: 'hello') }
101
110
  .merge(criteria.tap { |c| c.update_queries(field2: 'hello') }).queries.should == [{field1: 'hello'}, {field2: 'hello'}] }
102
111
  specify { subject.tap { |c| c.update_filters(field1: 'hello') }
@@ -119,6 +128,8 @@ describe Chewy::Query::Criteria do
119
128
  .merge!(criteria.tap { |c| c.update_options(opt2: 'hello') }).options.should include(opt1: 'hello', opt2: 'hello') }
120
129
  specify { subject.tap { |c| c.update_facets(field1: 'hello') }
121
130
  .merge!(criteria.tap { |c| c.update_facets(field1: 'hello') }).facets.should == {field1: 'hello', field1: 'hello'} }
131
+ specify { subject.tap { |c| c.update_aggregations(field1: 'hello') }
132
+ .merge!(criteria.tap { |c| c.update_aggregations(field1: 'hello') }).aggregations.should == {field1: 'hello', field1: 'hello'} }
122
133
  specify { subject.tap { |c| c.update_queries(field1: 'hello') }
123
134
  .merge!(criteria.tap { |c| c.update_queries(field2: 'hello') }).queries.should == [{field1: 'hello'}, {field2: 'hello'}] }
124
135
  specify { subject.tap { |c| c.update_filters(field1: 'hello') }
@@ -16,10 +16,10 @@ describe Chewy::Query::Loading do
16
16
  before do
17
17
  stub_index(:places) do
18
18
  define_type City do
19
- field :rating, type: 'number', value: ->(o){ o.rating }
19
+ field :rating, type: 'integer', value: ->(o){ o.rating }
20
20
  end
21
21
  define_type Country do
22
- field :rating, type: 'number', value: ->(o){ o.rating }
22
+ field :rating, type: 'integer', value: ->(o){ o.rating }
23
23
  end
24
24
  end
25
25
  end
@@ -7,7 +7,8 @@ describe Chewy::Query::Pagination do
7
7
  before do
8
8
  stub_index(:products) do
9
9
  define_type(:product) do
10
- field :name, :age
10
+ field :name
11
+ field :age, type: 'integer'
11
12
  end
12
13
  end
13
14
  end
@@ -19,7 +20,7 @@ describe Chewy::Query::Pagination do
19
20
  let(:search) { ProductsIndex.order(:age) }
20
21
 
21
22
  describe '#per, #page' do
22
- specify { search.map { |e| e.attributes.except('_score', '_explanation') }.should == data }
23
+ specify { search.map { |e| e.attributes.except('_score', '_explanation') }.should =~ data }
23
24
  specify { search.page(1).map { |e| e.attributes.except('_score', '_explanation') }.should == data[0..2] }
24
25
  specify { search.page(2).map { |e| e.attributes.except('_score', '_explanation') }.should == data[3..5] }
25
26
  specify { search.page(2).per(4).map { |e| e.attributes.except('_score', '_explanation') }.should == data[4..7] }
@@ -100,6 +100,70 @@ describe Chewy::Query do
100
100
  specify { subject.facets(term: {field: 'hello'}).should_not == subject }
101
101
  specify { subject.facets(term: {field: 'hello'}).criteria.facets.should include(term: {field: 'hello'}) }
102
102
  specify { expect { subject.facets(term: {field: 'hello'}) }.not_to change { subject.criteria.facets } }
103
+
104
+ context 'results' do
105
+ before { stub_model(:city) }
106
+ let(:cities) { 10.times.map { |i| City.create! name: "name#{i}", rating: i % 3 } }
107
+
108
+ context do
109
+ before do
110
+ stub_index(:cities) do
111
+ define_type :city do
112
+ field :rating, type: 'integer'
113
+ end
114
+ end
115
+ end
116
+
117
+ before { CitiesIndex::City.import! cities }
118
+
119
+ specify { CitiesIndex.facets.should == {} }
120
+ specify { CitiesIndex.facets(ratings: {terms: {field: 'rating'}}).facets.should == {
121
+ 'ratings' => {
122
+ '_type' => 'terms', 'missing' => 0, 'total' => 10, 'other' => 0,
123
+ 'terms' => [
124
+ {'term' => 0, 'count' => 4},
125
+ {'term' => 2, 'count' => 3},
126
+ {'term' => 1, 'count' => 3}
127
+ ]
128
+ }
129
+ } }
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '#aggregations' do
135
+ specify { subject.aggregations(aggregation1: {field: 'hello'}).should be_a described_class }
136
+ specify { subject.aggregations(aggregation1: {field: 'hello'}).should_not == subject }
137
+ specify { subject.aggregations(aggregation1: {field: 'hello'}).criteria.aggregations.should include(aggregation1: {field: 'hello'}) }
138
+ specify { expect { subject.aggregations(aggregation1: {field: 'hello'}) }.not_to change { subject.criteria.aggregations } }
139
+
140
+ context 'results' do
141
+ before { stub_model(:city) }
142
+ let(:cities) { 10.times.map { |i| City.create! name: "name#{i}", rating: i % 3 } }
143
+
144
+ context do
145
+ before do
146
+ stub_index(:cities) do
147
+ define_type :city do
148
+ field :rating, type: 'integer'
149
+ end
150
+ end
151
+ end
152
+
153
+ before { CitiesIndex::City.import! cities }
154
+
155
+ specify { CitiesIndex.aggregations.should == {} }
156
+ specify { CitiesIndex.aggregations(ratings: {terms: {field: 'rating'}}).aggregations.should == {
157
+ 'ratings' => {
158
+ 'buckets' => [
159
+ {'key' => 0, 'doc_count' => 4},
160
+ {'key' => 1, 'doc_count' => 3},
161
+ {'key' => 2, 'doc_count' => 3}
162
+ ]
163
+ }
164
+ } }
165
+ end
166
+ end
103
167
  end
104
168
 
105
169
  describe '#filter' do
@@ -174,6 +238,12 @@ describe Chewy::Query do
174
238
  specify { subject.types([:product, :city]).types!(:country).criteria.types.should =~ ['country'] }
175
239
  end
176
240
 
241
+ describe '#aggregations' do
242
+ specify { subject.aggregations(attribute: {terms: {field: 'attribute'}}).should be_a described_class }
243
+ specify { subject.aggregations(attribute: {terms: {field: 'attribute'}}).should_not == subject }
244
+ specify { subject.aggregations(attribute: {terms: {field: 'attribute'}}).criteria.request_body[:body].should include(aggregations: {attribute: {terms: {field: 'attribute'}}}) }
245
+ end
246
+
177
247
  describe '#merge' do
178
248
  let(:query) { described_class.new(ProductsIndex) }
179
249
 
@@ -193,7 +263,7 @@ describe Chewy::Query do
193
263
  field :rating, type: 'integer'
194
264
  field :nested, type: 'object', value: ->{ {name: name} }
195
265
  end
196
- end.tap(&:create!)
266
+ end
197
267
  end
198
268
 
199
269
  before { CitiesIndex::City.import! cities }
@@ -226,7 +296,7 @@ describe Chewy::Query do
226
296
  field :nested, type: 'object', value: ->{ {name: name} }
227
297
  end
228
298
  end
229
- end.tap(&:create!)
299
+ end
230
300
  end
231
301
  before { CitiesIndex::City.import! cities }
232
302
 
@@ -121,6 +121,32 @@ describe :update_index do
121
121
  }.to update_index(DummiesIndex.dummy).and_reindex(42, with: {a: 1})
122
122
  }.to fail_matching('Expected document with id `42` to be reindexed, but it was not') }
123
123
 
124
+ [
125
+ [{a: ['one', 'two']}, {a: ['one', 'two']}],
126
+ [{a: ['one', 'two']}, {a: ['two', 'one']}],
127
+ [{a: ['one', 'one', 'two']}, {a: ['one', 'two', 'one']}],
128
+ [{a: {b: ['one', 'one', 'two']}}, {a: {b: ['one', 'two', 'one']}}],
129
+ [{a: 1, b: 1}, {a: 1}]
130
+ ].each do |(data, with)|
131
+ specify { expect {
132
+ DummiesIndex.dummy.bulk body: [{index: {_id: 42, data: data}}]
133
+ }.to update_index(DummiesIndex.dummy).and_reindex(42, with: with) }
134
+ end
135
+
136
+ [
137
+ [{a: ['one', 'two']}, {a: ['one', 'one', 'two']}],
138
+ [{a: ['one', 'one', 'two']}, {a: ['one', 'two']}],
139
+ [{a: ['one', 'two']}, {a: 1}],
140
+ [{a: 1}, {a: ['one', 'two']}],
141
+ [{a: 1}, {a: 1, b: 1}]
142
+ ].each do |(data, with)|
143
+ specify { expect {
144
+ expect {
145
+ DummiesIndex.dummy.bulk body: [{index: {_id: 42, data: data}}]
146
+ }.to update_index(DummiesIndex.dummy).and_reindex(42, with: with)
147
+ }.to fail_matching('Expected document with id `42` to be reindexed') }
148
+ end
149
+
124
150
  context do
125
151
  let(:expectation) do
126
152
  expect { expect {
@@ -24,7 +24,7 @@ describe Chewy::Type::Adapter::ActiveRecord do
24
24
 
25
25
  context do
26
26
  let!(:cities) { 3.times.map { |i| City.create! } }
27
- let!(:deleted) { 3.times.map { |i| City.create!.tap(&:destroy!) } }
27
+ let!(:deleted) { 3.times.map { |i| City.create!.tap(&:destroy) } }
28
28
  subject { described_class.new(City) }
29
29
 
30
30
  specify { import.should == [{index: cities}] }
@@ -54,9 +54,42 @@ describe Chewy::Type::Adapter::ActiveRecord do
54
54
  {delete: deleted.last(1).map(&:id)}] }
55
55
  end
56
56
 
57
- context do
57
+ context 'custom primary_key' do
58
+ before { stub_model(:city) { self.primary_key = 'rating' } }
59
+ let!(:cities) { 3.times.map { |i| City.create! { |c| c.rating = i + 7 } } }
60
+ let!(:deleted) { 3.times.map { |i| City.create! { |c| c.rating = i + 10 }.tap(&:destroy) } }
61
+ subject { described_class.new(City) }
62
+
63
+ specify { import.should == [{index: cities}] }
64
+
65
+ specify { import(City.order(:rating)).should == [{index: cities}] }
66
+ specify { import(City.order(:rating), batch_size: 2)
67
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
68
+
69
+ specify { import(cities).should == [{index: cities}] }
70
+ specify { import(cities, batch_size: 2)
71
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
72
+ specify { import(cities, deleted).should == [{index: cities, delete: deleted}] }
73
+ specify { import(cities, deleted, batch_size: 2).should == [
74
+ {index: cities.first(2)},
75
+ {index: cities.last(1), delete: deleted.first(1)},
76
+ {delete: deleted.last(2)}] }
77
+
78
+ specify { import(cities.map(&:id)).should == [{index: cities}] }
79
+ specify { import(cities.map(&:id), batch_size: 2)
80
+ .should == [{index: cities.first(2)}, {index: cities.last(1)}] }
81
+ specify { import(cities.map(&:id), deleted.map(&:id))
82
+ .should == [{index: cities}, {delete: deleted.map(&:id)}] }
83
+ specify { import(cities.map(&:id), deleted.map(&:id), batch_size: 2).should == [
84
+ {index: cities.first(2)},
85
+ {index: cities.last(1)},
86
+ {delete: deleted.first(2).map(&:id)},
87
+ {delete: deleted.last(1).map(&:id)}] }
88
+ end
89
+
90
+ context 'default scope' do
58
91
  let!(:cities) { 3.times.map { |i| City.create!(country_id: i/2) } }
59
- let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy!) } }
92
+ let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy) } }
60
93
  subject { described_class.new(City.where(country_id: 0)) }
61
94
 
62
95
  specify { import.should == [{index: cities.first(2)}] }
@@ -83,12 +116,12 @@ describe Chewy::Type::Adapter::ActiveRecord do
83
116
 
84
117
  context 'error handling' do
85
118
  let!(:cities) { 3.times.map { |i| City.create! } }
86
- let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy!) } }
119
+ let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy) } }
87
120
  let(:ids) { (cities + deleted).map(&:id) }
88
121
  subject { described_class.new(City) }
89
122
 
90
123
  let(:data_comparer) do
91
- ->(id, data) { object = (data[:index] || data[:delete]).first; (object.try(:id) || object) != id }
124
+ ->(id, data) { object = (data[:index] || data[:delete]).first; (object.respond_to?(:id) ? object.id : object) != id }
92
125
  end
93
126
 
94
127
  context 'implicit scope' do
@@ -136,26 +169,53 @@ describe Chewy::Type::Adapter::ActiveRecord do
136
169
  end
137
170
 
138
171
  describe '#load' do
139
- let!(:cities) { 3.times.map { |i| City.create!(country_id: i/2) } }
140
- let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy!) } }
141
-
142
- let(:type) { double(type_name: 'user') }
143
-
144
- subject { described_class.new(City) }
145
-
146
- specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type).should == cities }
147
- specify { subject.load(cities.map { |c| double(id: c.id) }.reverse, _type: type).should == cities.reverse }
148
- specify { subject.load(deleted.map { |c| double(id: c.id) }, _type: type).should == [nil, nil] }
149
- specify { subject.load((cities + deleted).map { |c| double(id: c.id) }, _type: type).should == [*cities, nil, nil] }
150
- specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: ->{ where(country_id: 0) })
151
- .should == cities.first(2) + [nil] }
152
- specify { subject.load(cities.map { |c| double(id: c.id) },
153
- _type: type, scope: ->{ where(country_id: 0) }, user: {scope: ->{ where(country_id: 1)}})
154
- .should == [nil, nil] + cities.last(1) }
155
- specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: City.where(country_id: 1))
156
- .should == [nil, nil] + cities.last(1) }
157
- specify { subject.load(cities.map { |c| double(id: c.id) },
158
- _type: type, scope: City.where(country_id: 1), user: {scope: ->{ where(country_id: 0)}})
159
- .should == cities.first(2) + [nil] }
172
+ context do
173
+ let!(:cities) { 3.times.map { |i| City.create!(country_id: i/2) } }
174
+ let!(:deleted) { 2.times.map { |i| City.create!.tap(&:destroy) } }
175
+
176
+ let(:type) { double(type_name: 'user') }
177
+
178
+ subject { described_class.new(City) }
179
+
180
+ specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type).should == cities }
181
+ specify { subject.load(cities.map { |c| double(id: c.id) }.reverse, _type: type).should == cities.reverse }
182
+ specify { subject.load(deleted.map { |c| double(id: c.id) }, _type: type).should == [nil, nil] }
183
+ specify { subject.load((cities + deleted).map { |c| double(id: c.id) }, _type: type).should == [*cities, nil, nil] }
184
+ specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: ->{ where(country_id: 0) })
185
+ .should == cities.first(2) + [nil] }
186
+ specify { subject.load(cities.map { |c| double(id: c.id) },
187
+ _type: type, scope: ->{ where(country_id: 0) }, user: {scope: ->{ where(country_id: 1)}})
188
+ .should == [nil, nil] + cities.last(1) }
189
+ specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: City.where(country_id: 1))
190
+ .should == [nil, nil] + cities.last(1) }
191
+ specify { subject.load(cities.map { |c| double(id: c.id) },
192
+ _type: type, scope: City.where(country_id: 1), user: {scope: ->{ where(country_id: 0)}})
193
+ .should == cities.first(2) + [nil] }
194
+ end
195
+
196
+ context 'custom primary_key' do
197
+ before { stub_model(:city) { self.primary_key = 'rating' } }
198
+ let!(:cities) { 3.times.map { |i| City.create!(country_id: i/2) { |c| c.rating = i + 7 } } }
199
+ let!(:deleted) { 2.times.map { |i| City.create! { |c| c.rating = i + 10 }.tap(&:destroy) } }
200
+
201
+ let(:type) { double(type_name: 'user') }
202
+
203
+ subject { described_class.new(City) }
204
+
205
+ specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type).should == cities }
206
+ specify { subject.load(cities.map { |c| double(id: c.id) }.reverse, _type: type).should == cities.reverse }
207
+ specify { subject.load(deleted.map { |c| double(id: c.id) }, _type: type).should == [nil, nil] }
208
+ specify { subject.load((cities + deleted).map { |c| double(id: c.id) }, _type: type).should == [*cities, nil, nil] }
209
+ specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: ->{ where(country_id: 0) })
210
+ .should == cities.first(2) + [nil] }
211
+ specify { subject.load(cities.map { |c| double(id: c.id) },
212
+ _type: type, scope: ->{ where(country_id: 0) }, user: {scope: ->{ where(country_id: 1)}})
213
+ .should == [nil, nil] + cities.last(1) }
214
+ specify { subject.load(cities.map { |c| double(id: c.id) }, _type: type, scope: City.where(country_id: 1))
215
+ .should == [nil, nil] + cities.last(1) }
216
+ specify { subject.load(cities.map { |c| double(id: c.id) },
217
+ _type: type, scope: City.where(country_id: 1), user: {scope: ->{ where(country_id: 0)}})
218
+ .should == cities.first(2) + [nil] }
219
+ end
160
220
  end
161
221
  end
@@ -122,7 +122,7 @@ describe Chewy::Type::Import do
122
122
  define_type City do
123
123
  field :name, type: 'object'
124
124
  end
125
- end.tap(&:create!)
125
+ end
126
126
  end
127
127
 
128
128
  specify do
@@ -152,7 +152,7 @@ describe Chewy::Type::Import do
152
152
  define_type City do
153
153
  field :name, type: 'object'
154
154
  end
155
- end.tap(&:create!)
155
+ end
156
156
  end
157
157
 
158
158
  specify { city.import(dummy_cities).should be_false }
@@ -166,7 +166,7 @@ describe Chewy::Type::Import do
166
166
  define_type City do
167
167
  field :name, type: 'object', value: ->{ name == 'name1' ? name : {name: name} }
168
168
  end
169
- end.tap(&:create!)
169
+ end
170
170
  end
171
171
 
172
172
  specify { city.import(dummy_cities).should be_false }
@@ -185,7 +185,7 @@ describe Chewy::Type::Import do
185
185
  define_type City do
186
186
  field :name, type: 'object'
187
187
  end
188
- end.tap(&:create!)
188
+ end
189
189
  end
190
190
 
191
191
  specify { expect { city.import!(dummy_cities) }.to raise_error Chewy::FailedImport }
data/spec/spec_helper.rb CHANGED
@@ -27,7 +27,14 @@ ActiveRecord::Schema.define do
27
27
  end
28
28
  end
29
29
 
30
- Chewy.configuration = {host: 'localhost:9250', index: {number_of_shards: 1, number_of_replicas: 0}}
30
+ Chewy.configuration = {
31
+ host: 'localhost:9250',
32
+ wait_for_status: 'green',
33
+ index: {
34
+ number_of_shards: 1,
35
+ number_of_replicas: 0
36
+ }
37
+ }
31
38
 
32
39
  RSpec.configure do |config|
33
40
  config.mock_with :rspec
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chewy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pyromaniac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-04 00:00:00.000000000 Z
11
+ date: 2014-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - '>='
130
130
  - !ruby/object:Gem::Version
131
- version: '0'
131
+ version: 1.0.0
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - '>='
137
137
  - !ruby/object:Gem::Version
138
- version: '0'
138
+ version: 1.0.0
139
139
  description: Chewy provides functionality for Elasticsearch index handling, documents
140
140
  import mappings and chainable query DSL
141
141
  email:
@@ -155,6 +155,8 @@ files:
155
155
  - Rakefile
156
156
  - chewy.gemspec
157
157
  - filters
158
+ - gemfiles/Gemfile.rails-3.2
159
+ - gemfiles/Gemfile.rails-4.0
158
160
  - lib/chewy.rb
159
161
  - lib/chewy/config.rb
160
162
  - lib/chewy/errors.rb
@@ -268,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
268
270
  version: '0'
269
271
  requirements: []
270
272
  rubyforge_project:
271
- rubygems_version: 2.2.1
273
+ rubygems_version: 2.2.2
272
274
  signing_key:
273
275
  specification_version: 4
274
276
  summary: Elasticsearch ODM client wrapper