chewy 0.4.1 → 0.5.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGELOG.md +43 -1
  4. data/Gemfile +3 -0
  5. data/README.md +49 -11
  6. data/chewy.gemspec +1 -2
  7. data/gemfiles/Gemfile.rails-3.2 +1 -0
  8. data/gemfiles/Gemfile.rails-4.0 +1 -0
  9. data/lib/chewy.rb +8 -2
  10. data/lib/chewy/backports/deep_dup.rb +46 -0
  11. data/lib/chewy/backports/duplicable.rb +90 -0
  12. data/lib/chewy/config.rb +33 -6
  13. data/lib/chewy/errors.rb +1 -1
  14. data/lib/chewy/fields/base.rb +19 -7
  15. data/lib/chewy/fields/root.rb +13 -0
  16. data/lib/chewy/index/actions.rb +14 -6
  17. data/lib/chewy/index/search.rb +3 -2
  18. data/lib/chewy/query.rb +131 -17
  19. data/lib/chewy/query/compose.rb +27 -17
  20. data/lib/chewy/query/criteria.rb +34 -22
  21. data/lib/chewy/query/loading.rb +94 -10
  22. data/lib/chewy/query/nodes/exists.rb +1 -1
  23. data/lib/chewy/query/nodes/has_relation.rb +1 -1
  24. data/lib/chewy/query/nodes/missing.rb +1 -1
  25. data/lib/chewy/query/pagination.rb +8 -38
  26. data/lib/chewy/query/pagination/kaminari.rb +37 -0
  27. data/lib/chewy/runtime.rb +9 -0
  28. data/lib/chewy/runtime/version.rb +25 -0
  29. data/lib/chewy/type/adapter/active_record.rb +21 -7
  30. data/lib/chewy/type/adapter/base.rb +1 -1
  31. data/lib/chewy/type/adapter/object.rb +9 -6
  32. data/lib/chewy/type/import.rb +7 -4
  33. data/lib/chewy/type/mapping.rb +9 -9
  34. data/lib/chewy/type/wrapper.rb +1 -1
  35. data/lib/chewy/version.rb +1 -1
  36. data/lib/tasks/chewy.rake +40 -21
  37. data/spec/chewy/config_spec.rb +1 -1
  38. data/spec/chewy/fields/base_spec.rb +273 -8
  39. data/spec/chewy/index/actions_spec.rb +1 -2
  40. data/spec/chewy/index/aliases_spec.rb +0 -1
  41. data/spec/chewy/index/search_spec.rb +0 -8
  42. data/spec/chewy/index/settings_spec.rb +0 -2
  43. data/spec/chewy/index_spec.rb +0 -2
  44. data/spec/chewy/query/criteria_spec.rb +85 -18
  45. data/spec/chewy/query/loading_spec.rb +26 -9
  46. data/spec/chewy/query/nodes/and_spec.rb +2 -2
  47. data/spec/chewy/query/nodes/exists_spec.rb +6 -6
  48. data/spec/chewy/query/nodes/missing_spec.rb +4 -4
  49. data/spec/chewy/query/nodes/or_spec.rb +2 -2
  50. data/spec/chewy/query/pagination/kaminari_spec.rb +55 -0
  51. data/spec/chewy/query/pagination_spec.rb +15 -22
  52. data/spec/chewy/query_spec.rb +121 -52
  53. data/spec/chewy/rspec/update_index_spec.rb +0 -1
  54. data/spec/chewy/runtime/version_spec.rb +48 -0
  55. data/spec/chewy/runtime_spec.rb +9 -0
  56. data/spec/chewy/type/adapter/active_record_spec.rb +52 -0
  57. data/spec/chewy/type/adapter/object_spec.rb +33 -0
  58. data/spec/chewy/type/import_spec.rb +1 -3
  59. data/spec/chewy/type/mapping_spec.rb +4 -6
  60. data/spec/chewy/type/observe_spec.rb +0 -2
  61. data/spec/chewy/type/wrapper_spec.rb +0 -2
  62. data/spec/chewy/type_spec.rb +26 -5
  63. data/spec/chewy_spec.rb +0 -2
  64. data/spec/spec_helper.rb +2 -2
  65. metadata +15 -21
  66. data/lib/chewy/fields/default.rb +0 -10
  67. data/spec/chewy/fields/default_spec.rb +0 -6
@@ -3,7 +3,7 @@ module Chewy
3
3
  module Wrapper
4
4
  extend ActiveSupport::Concern
5
5
 
6
- attr_accessor :attributes
6
+ attr_accessor :attributes, :_data, :_object
7
7
 
8
8
  def initialize(attributes = {})
9
9
  @attributes = attributes.stringify_keys
data/lib/chewy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '0.4.1'
2
+ VERSION = '0.5.0'
3
3
  end
data/lib/tasks/chewy.rake CHANGED
@@ -12,49 +12,68 @@ def subscribe_task_stats!
12
12
  end
13
13
  end
14
14
 
15
+ def eager_load_chewy!
16
+ Rails.application.config.paths['app/chewy'].existent.each do |dir|
17
+ Dir.glob(File.join(dir, '**/*.rb')).each { |file| require file }
18
+ end
19
+ end
20
+
21
+ def normalize_index index
22
+ "#{index.to_s.gsub(/index$/i, '').camelize}Index".constantize
23
+ end
24
+
25
+ def reset_index index
26
+ index = normalize_index(index)
27
+ puts "Resetting #{index}"
28
+ index.reset! (Time.now.to_f * 1000).round
29
+ end
30
+
31
+ def reset_all
32
+ eager_load_chewy!
33
+ Chewy::Index.descendants.each { |index| reset_index index }
34
+ end
35
+
36
+ def update_index index
37
+ index = normalize_index(index)
38
+ puts "Updating #{index}"
39
+ if index.exists?
40
+ index.import
41
+ else
42
+ puts "Index `#{index.index_name}` does not exists. Use rake chewy:reset[#{index.index_name}] to create and update it."
43
+ end
44
+ end
45
+
46
+ def update_all
47
+ eager_load_chewy!
48
+ Chewy::Index.descendants.each { |index| update_index index }
49
+ end
50
+
15
51
  namespace :chewy do
16
52
  desc 'Destroy, recreate and import data to specified index'
17
53
  task :reset, [:index] => :environment do |task, args|
18
54
  subscribe_task_stats!
19
- "#{args[:index].camelize}Index".constantize.reset! (Time.now.to_f * 1000).round
55
+ args[:index].present? ? reset_index(args[:index]) : reset_all
20
56
  end
21
57
 
22
58
  namespace :reset do
23
59
  desc 'Destroy, recreate and import data for all found indexes'
24
60
  task all: :environment do
25
61
  subscribe_task_stats!
26
- Rails.application.config.paths['app/chewy'].existent.each do |dir|
27
- Dir.glob(File.join(dir, '**/*.rb')).each { |file| require file }
28
- end
29
-
30
- Chewy::Index.descendants.each do |index|
31
- puts "Resetting #{index}"
32
- index.reset! (Time.now.to_f * 1000).round
33
- end
62
+ reset_all
34
63
  end
35
64
  end
36
65
 
37
66
  desc 'Updates data specified index'
38
67
  task :update, [:index] => :environment do |task, args|
39
68
  subscribe_task_stats!
40
- index = "#{args[:index].camelize}Index".constantize
41
- raise "Index `#{index.index_name}` does not exists. Use rake chewy:reset[#{index.index_name}] to create and update it." unless index.exists?
42
- index.import
69
+ args[:index].present? ? update_index(args[:index]) : update_all
43
70
  end
44
71
 
45
72
  namespace :update do
46
73
  desc 'Updates data for all found indexes'
47
74
  task all: :environment do
48
75
  subscribe_task_stats!
49
- Rails.application.config.paths['app/chewy'].existent.each do |dir|
50
- Dir.glob(File.join(dir, '**/*.rb')).each { |file| require file }
51
- end
52
-
53
- Chewy::Index.descendants.each do |index|
54
- puts "Updating #{index}"
55
- puts "Index `#{index.index_name}` does not exists. Use rake chewy:reset[#{index.index_name}] to create and update it." unless index.exists?
56
- index.import
57
- end
76
+ update_all
58
77
  end
59
78
  end
60
79
  end
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Chewy::Config do
4
- include ClassHelpers
5
4
  subject { described_class.send(:new) }
6
5
 
7
6
  its(:query_mode) { should == :must }
8
7
  its(:filter_mode) { should == :and }
8
+ its(:post_filter_mode) { should be_nil }
9
9
  its(:logger) { should be_nil }
10
10
  its(:configuration) { should_not have_key :logger }
11
11
  its(:analyzers) { should == {} }
@@ -31,7 +31,7 @@ describe Chewy::Fields::Base do
31
31
  end
32
32
 
33
33
  context do
34
- let(:field) { described_class.new(:name, type: 'multi_field') }
34
+ let(:field) { described_class.new(:name, type: 'string') }
35
35
  before do
36
36
  field.nested(described_class.new(:name))
37
37
  field.nested(described_class.new(:untouched))
@@ -61,7 +61,7 @@ describe Chewy::Fields::Base do
61
61
  end
62
62
 
63
63
  describe '#mappings_hash' do
64
- let(:field) { described_class.new(:name, type: 'string') }
64
+ let(:field) { described_class.new(:name, type: :object) }
65
65
  let(:fields1) { 2.times.map { |i| described_class.new("name#{i+1}", type: "string#{i+1}") } }
66
66
  let(:fields2) { 2.times.map { |i| described_class.new("name#{i+3}", type: "string#{i+3}") } }
67
67
  before do
@@ -69,20 +69,285 @@ describe Chewy::Fields::Base do
69
69
  fields2.each { |m| fields1[0].nested(m) }
70
70
  end
71
71
 
72
- specify { field.mappings_hash.should == {name: {type: 'string', properties: {
73
- name1: {type: 'string1', properties: {
72
+ specify { field.mappings_hash.should == {name: {type: :object, properties: {
73
+ name1: {type: 'string1', fields: {
74
74
  name3: {type: 'string3'}, name4: {type: 'string4'}
75
75
  }}, name2: {type: 'string2'}
76
76
  }}} }
77
77
 
78
78
  context do
79
- let(:field) { described_class.new(:name, type: 'multi_field') }
79
+ let(:field) { described_class.new(:name, type: :string) }
80
+ let(:fields1) { 2.times.map { |i| described_class.new("name#{i+1}") } }
80
81
 
81
- specify { field.mappings_hash.should == {name: {type: 'multi_field', fields: {
82
- name1: {type: 'string1', properties: {
82
+ specify { field.mappings_hash.should == {name: {type: :string, fields: {
83
+ name1: {type: 'object', properties: {
83
84
  name3: {type: 'string3'}, name4: {type: 'string4'}
84
- }}, name2: {type: 'string2'}
85
+ }}, name2: {type: 'string'}
85
86
  }}} }
86
87
  end
87
88
  end
89
+
90
+ context 'integration' do
91
+ context 'objects, hashes and arrays' do
92
+ before do
93
+ stub_index(:events) do
94
+ define_type :event do
95
+ field :id
96
+ field :category do
97
+ field :id
98
+ field :licenses do
99
+ field :id
100
+ field :name
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ specify do
108
+ EventsIndex::Event.mappings_hash.should == { event: {
109
+ properties: {
110
+ id: { type: 'string' },
111
+ category: {
112
+ type: 'object',
113
+ properties: {
114
+ id: { type: 'string' },
115
+ licenses: {
116
+ type: 'object',
117
+ properties: {
118
+ id: { type: 'string' },
119
+ name: { type: 'string' } } } } } } } }
120
+ end
121
+
122
+ specify do
123
+ EventsIndex::Event.root_object.compose(
124
+ id: 1, category: { id: 2, licenses: { id: 3, name: 'Name' } }
125
+ ).should == {
126
+ event: { 'id' => 1, 'category' => { 'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}}}
127
+ }
128
+ end
129
+
130
+ specify do
131
+ EventsIndex::Event.root_object.compose(id: 1, category: [
132
+ { id: 2, 'licenses' => { id: 3, name: 'Name1' } },
133
+ { id: 4, licenses: nil}
134
+ ]).should == {
135
+ event: { 'id' => 1, 'category' => [
136
+ { 'id' => 2, 'licenses' => { 'id' => 3, 'name' => 'Name1' } },
137
+ {'id' => 4, 'licenses' => nil }
138
+ ] }
139
+ }
140
+ end
141
+
142
+ specify do
143
+ EventsIndex::Event.root_object.compose('id' => 1, category: { id: 2, licenses: [
144
+ { id: 3, name: 'Name1' }, { id: 4, name: 'Name2' }
145
+ ] }).should == {
146
+ event: { 'id' => 1, 'category' => { 'id' => 2, 'licenses' => [
147
+ {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
148
+ ] } }
149
+ }
150
+ end
151
+
152
+ specify do
153
+ EventsIndex::Event.root_object.compose(id: 1, category: [
154
+ { id: 2, licenses: [
155
+ { id: 3, 'name' => 'Name1' }, { id: 4, name: 'Name2' }
156
+ ] },
157
+ { id: 5, licenses: [] }
158
+ ]).should == {
159
+ event: { 'id' => 1, 'category' => [
160
+ { 'id' => 2, 'licenses' => [
161
+ { 'id' => 3, 'name' => 'Name1' }, { 'id' => 4, 'name' => 'Name2' }
162
+ ] },
163
+ {'id' => 5, 'licenses' => [] }
164
+ ] }
165
+ }
166
+ end
167
+
168
+ specify do
169
+ EventsIndex::Event.root_object.compose(
170
+ double(id: 1, category: double(id: 2, licenses: double(id: 3, name: 'Name')))
171
+ ).should == {
172
+ event: { 'id' => 1, 'category' => { 'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}}}
173
+ }
174
+ end
175
+
176
+ specify do
177
+ EventsIndex::Event.root_object.compose(double(id: 1, category: [
178
+ double(id: 2, licenses: double(id: 3, name: 'Name1')),
179
+ double(id: 4, licenses: nil)
180
+ ])).should == {
181
+ event: { 'id' => 1, 'category' => [
182
+ { 'id' => 2, 'licenses' => { 'id' => 3, 'name' => 'Name1' } },
183
+ {'id' => 4, 'licenses' => nil }
184
+ ] }
185
+ }
186
+ end
187
+
188
+ specify do
189
+ EventsIndex::Event.root_object.compose(double(id: 1, category: double(id: 2, licenses: [
190
+ double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2')
191
+ ]))).should == {
192
+ event: { 'id' => 1, 'category' => { 'id' => 2, 'licenses' => [
193
+ {'id' => 3, 'name' => 'Name1'}, {'id' => 4, 'name' => 'Name2'}
194
+ ] } }
195
+ }
196
+ end
197
+
198
+ specify do
199
+ EventsIndex::Event.root_object.compose(double(id: 1, category: [
200
+ double(id: 2, licenses: [
201
+ double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2')
202
+ ]),
203
+ double(id: 5, licenses: [])
204
+ ])).should == {
205
+ event: { 'id' => 1, 'category' => [
206
+ { 'id' => 2, 'licenses' => [
207
+ { 'id' => 3, 'name' => 'Name1' }, { 'id' => 4, 'name' => 'Name2' }
208
+ ] },
209
+ {'id' => 5, 'licenses' => [] }
210
+ ] }
211
+ }
212
+ end
213
+ end
214
+
215
+ context 'custom methods' do
216
+ before do
217
+ stub_index(:events) do
218
+ define_type :event do
219
+ field :id
220
+ field :category, value: ->{ categories } do
221
+ field :id
222
+ field :licenses, value: ->{ license } do
223
+ field :id
224
+ field :name
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ specify do
232
+ EventsIndex::Event.root_object.compose(
233
+ double(id: 1, categories: double(id: 2, license: double(id: 3, name: 'Name')))
234
+ ).should == {
235
+ event: { 'id' => 1, 'category' => { 'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}}}
236
+ }
237
+ end
238
+ end
239
+
240
+ context 'objects and multi_fields' do
241
+ before do
242
+ stub_index(:events) do
243
+ define_type :event do
244
+ field :id
245
+ field :name, type: 'string' do
246
+ field :raw, analyzer: 'my_own'
247
+ end
248
+ field :category, type: 'object'
249
+ end
250
+ end
251
+ end
252
+
253
+ specify do
254
+ EventsIndex::Event.mappings_hash.should == { event: {
255
+ properties: {
256
+ id: { type: 'string' },
257
+ name: {
258
+ type: 'string',
259
+ fields: {
260
+ raw: { analyzer: 'my_own', type: 'string' }
261
+ }
262
+ },
263
+ category: { type: 'object' }
264
+ }
265
+ } }
266
+ end
267
+
268
+ specify do
269
+ EventsIndex::Event.root_object.compose(
270
+ double(id: 1, name: 'Jonny', category: double(id: 2, as_json: {name: 'Borogoves'}))
271
+ ).should == {
272
+ event: {
273
+ 'id' => 1,
274
+ 'name' => 'Jonny',
275
+ 'category' => { 'name' => 'Borogoves' }
276
+ }
277
+ }
278
+ end
279
+
280
+ specify do
281
+ EventsIndex::Event.root_object.compose(
282
+ double(id: 1, name: 'Jonny', category: [
283
+ double(id: 2, as_json: { name: 'Borogoves1' }),
284
+ double(id: 3, as_json: { name: 'Borogoves2' })
285
+ ])
286
+ ).should == {
287
+ event: {
288
+ 'id' => 1,
289
+ 'name' => 'Jonny',
290
+ 'category' => [
291
+ { 'name' => 'Borogoves1' },
292
+ { 'name' => 'Borogoves2' }
293
+ ]
294
+ }
295
+ }
296
+ end
297
+ end
298
+
299
+ context 'objects and scopes' do
300
+ before do
301
+ stub_model(:city) do
302
+ belongs_to :country
303
+ end
304
+
305
+ stub_model(:country) do
306
+ has_many :cities
307
+ end
308
+
309
+ stub_index(:countries) do
310
+ define_type Country do
311
+ field :id
312
+ field :cities do
313
+ field :id
314
+ field :name
315
+ end
316
+ end
317
+ end
318
+ end
319
+
320
+ specify do
321
+ CountriesIndex::Country.root_object.compose(
322
+ Country.create!(cities: [City.create!(name: 'City1'), City.create!(name: 'City2')])
323
+ ).should == {
324
+ country: { 'id' => 1, 'cities' => [
325
+ { 'id' => 1, 'name' => 'City1' }, { 'id' => 2, 'name' => 'City2' }
326
+ ] }
327
+ }
328
+ end
329
+
330
+ context 'nested object' do
331
+ before do
332
+ stub_index(:cities) do
333
+ define_type City do
334
+ field :id
335
+ field :country do
336
+ field :id
337
+ field :name
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ specify do
344
+ CitiesIndex::City.root_object.compose(
345
+ City.create!(country: Country.create!(name: 'Country'))
346
+ ).should == {
347
+ city: { 'id' => 1, 'country' => { 'id' => 1, 'name' => 'Country' } }
348
+ }
349
+ end
350
+ end
351
+ end
352
+ end
88
353
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Chewy::Index::Actions do
4
- include ClassHelpers
5
4
  before { Chewy.client.indices.delete index: '*' }
6
5
 
7
6
  before { stub_index :dummies }
@@ -311,7 +310,7 @@ describe Chewy::Index::Actions do
311
310
  end
312
311
  end
313
312
 
314
- specify { expect { CitiesIndex.import!(city: dummy_cities) }.to raise_error Chewy::FailedImport }
313
+ specify { expect { CitiesIndex.import!(city: dummy_cities) }.to raise_error Chewy::ImportFailed }
315
314
  end
316
315
  end
317
316
 
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Chewy::Index::Aliases do
4
- include ClassHelpers
5
4
  before { Chewy.client.indices.delete index: '*' }
6
5
 
7
6
  before { stub_index :dummies }