chewy 0.3.0 → 0.4.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.
@@ -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