chewy 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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 }