chewy 0.10.1 → 5.0.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 (73) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +25 -25
  3. data/.travis.yml +21 -29
  4. data/Appraisals +27 -9
  5. data/CHANGELOG.md +36 -0
  6. data/Gemfile +3 -1
  7. data/README.md +73 -23
  8. data/chewy.gemspec +6 -8
  9. data/gemfiles/rails.4.0.activerecord.gemfile +1 -0
  10. data/gemfiles/rails.4.1.activerecord.gemfile +1 -0
  11. data/gemfiles/rails.4.2.activerecord.gemfile +1 -0
  12. data/gemfiles/{rails.4.2.mongoid.5.1.gemfile → rails.4.2.mongoid.5.2.gemfile} +2 -1
  13. data/gemfiles/rails.5.0.activerecord.gemfile +2 -1
  14. data/gemfiles/{rails.5.0.mongoid.6.0.gemfile → rails.5.0.mongoid.6.1.gemfile} +3 -2
  15. data/gemfiles/rails.5.1.activerecord.gemfile +2 -1
  16. data/gemfiles/{rails.5.1.mongoid.6.1.gemfile → rails.5.1.mongoid.6.3.gemfile} +3 -2
  17. data/gemfiles/rails.5.2.activerecord.gemfile +16 -0
  18. data/gemfiles/sequel.4.45.gemfile +2 -2
  19. data/lib/chewy.rb +1 -0
  20. data/lib/chewy/config.rb +8 -19
  21. data/lib/chewy/fields/base.rb +15 -3
  22. data/lib/chewy/fields/root.rb +13 -9
  23. data/lib/chewy/index.rb +1 -1
  24. data/lib/chewy/index/actions.rb +14 -12
  25. data/lib/chewy/index/settings.rb +2 -0
  26. data/lib/chewy/index/specification.rb +12 -10
  27. data/lib/chewy/minitest/helpers.rb +6 -6
  28. data/lib/chewy/minitest/search_index_receiver.rb +17 -17
  29. data/lib/chewy/query.rb +135 -96
  30. data/lib/chewy/query/filters.rb +20 -3
  31. data/lib/chewy/query/loading.rb +0 -1
  32. data/lib/chewy/railtie.rb +2 -4
  33. data/lib/chewy/rake_helper.rb +5 -5
  34. data/lib/chewy/rspec/update_index.rb +3 -5
  35. data/lib/chewy/search.rb +2 -2
  36. data/lib/chewy/search/parameters/concerns/query_storage.rb +4 -3
  37. data/lib/chewy/stash.rb +30 -21
  38. data/lib/chewy/strategy/atomic.rb +1 -1
  39. data/lib/chewy/type.rb +2 -2
  40. data/lib/chewy/type/adapter/base.rb +9 -9
  41. data/lib/chewy/type/adapter/mongoid.rb +1 -3
  42. data/lib/chewy/type/adapter/sequel.rb +4 -6
  43. data/lib/chewy/type/crutch.rb +1 -1
  44. data/lib/chewy/type/import.rb +3 -2
  45. data/lib/chewy/type/import/bulk_builder.rb +1 -1
  46. data/lib/chewy/type/import/journal_builder.rb +3 -3
  47. data/lib/chewy/type/import/routine.rb +2 -2
  48. data/lib/chewy/type/mapping.rb +40 -34
  49. data/lib/chewy/type/observe.rb +13 -9
  50. data/lib/chewy/type/syncer.rb +2 -2
  51. data/lib/chewy/type/witchcraft.rb +2 -2
  52. data/lib/chewy/type/wrapper.rb +2 -2
  53. data/lib/chewy/version.rb +1 -1
  54. data/lib/sequel/plugins/chewy_observe.rb +4 -19
  55. data/spec/chewy/config_spec.rb +16 -0
  56. data/spec/chewy/fields/base_spec.rb +61 -65
  57. data/spec/chewy/fields/root_spec.rb +13 -13
  58. data/spec/chewy/index/actions_spec.rb +37 -5
  59. data/spec/chewy/index/specification_spec.rb +25 -16
  60. data/spec/chewy/index_spec.rb +61 -8
  61. data/spec/chewy/journal_spec.rb +11 -11
  62. data/spec/chewy/query/filters_spec.rb +1 -1
  63. data/spec/chewy/query/nodes/not_spec.rb +1 -0
  64. data/spec/chewy/query_spec.rb +3 -2
  65. data/spec/chewy/rake_helper_spec.rb +46 -33
  66. data/spec/chewy/search_spec.rb +20 -10
  67. data/spec/chewy/stash_spec.rb +1 -1
  68. data/spec/chewy/strategy/shoryuken_spec.rb +2 -0
  69. data/spec/chewy/type/import/journal_builder_spec.rb +8 -8
  70. data/spec/chewy/type/import_spec.rb +6 -0
  71. data/spec/chewy/type/mapping_spec.rb +48 -17
  72. data/spec/spec_helper.rb +8 -0
  73. metadata +26 -25
@@ -12,7 +12,7 @@ module Chewy
12
12
  def initialize(type, collection)
13
13
  @type = type
14
14
  @collection = collection
15
- @type._crutches.keys.each do |name|
15
+ @type._crutches.each_key do |name|
16
16
  singleton_class.class_eval <<-METHOD, __FILE__, __LINE__ + 1
17
17
  def #{name}
18
18
  @#{name} ||= @type._crutches[:#{name}].call @collection
@@ -116,16 +116,17 @@ module Chewy
116
116
  def compose(object, crutches = nil, fields: [])
117
117
  crutches ||= Chewy::Type::Crutch::Crutches.new self, [object]
118
118
 
119
- if witchcraft? && build_root.children.present?
119
+ if witchcraft? && root.children.present?
120
120
  cauldron(fields: fields).brew(object, crutches)
121
121
  else
122
- build_root.compose(object, crutches, fields: fields)
122
+ root.compose(object, crutches, fields: fields)
123
123
  end
124
124
  end
125
125
 
126
126
  private
127
127
 
128
128
  def import_routine(*args)
129
+ return if args.first.blank? && !args.first.nil?
129
130
  routine = Routine.new(self, args.extract_options!)
130
131
  routine.create_indexes!
131
132
 
@@ -114,7 +114,7 @@ module Chewy
114
114
  end
115
115
 
116
116
  def type_root
117
- @type_root = @type.send(:build_root)
117
+ @type_root ||= @type.root
118
118
  end
119
119
  end
120
120
  end
@@ -10,7 +10,7 @@ module Chewy
10
10
 
11
11
  def bulk_body
12
12
  Chewy::Type::Import::BulkBuilder.new(
13
- Chewy::Stash::Journal,
13
+ Chewy::Stash::Journal::Journal,
14
14
  index: [
15
15
  entries(:index, @index),
16
16
  entries(:delete, @delete)
@@ -18,7 +18,7 @@ module Chewy
18
18
  ).bulk_body.each do |item|
19
19
  item.values.first.merge!(
20
20
  _index: Chewy::Stash::Journal.index_name,
21
- _type: Chewy::Stash::Journal.type_name
21
+ _type: Chewy::Stash::Journal::Journal.type_name
22
22
  )
23
23
  end
24
24
  end
@@ -31,7 +31,7 @@ module Chewy
31
31
  index_name: @type.index.derivable_name,
32
32
  type_name: @type.type_name,
33
33
  action: action,
34
- references: identify(objects).map(&:to_json),
34
+ references: identify(objects).map(&:to_json).map(&Base64.method(:encode64)),
35
35
  created_at: Time.now.utc
36
36
  }
37
37
  end
@@ -13,7 +13,7 @@ module Chewy
13
13
  # * performs the bulk request;
14
14
  # * composes new leftovers bulk for the next iteration basing on the response errors if `update_failover` is true;
15
15
  # * appends the rest of unfixable errors to the instance level errors array.
16
- # 4. Perform the request for the last leftovers bulk if present using {#process_leftovers}.
16
+ # 4. Perform the request for the last leftovers bulk if present using {#extract_leftovers}.
17
17
  # 3. Return the result errors array.
18
18
  #
19
19
  # At the moment, it tries to restore only from the partial document update errors in cases
@@ -64,7 +64,7 @@ module Chewy
64
64
  # Creates the journal index and the type corresponding index if necessary.
65
65
  # @return [Object] whatever
66
66
  def create_indexes!
67
- Chewy::Stash.create if @options[:journal]
67
+ Chewy::Stash::Journal.create if @options[:journal]
68
68
  return if Chewy.configuration[:skip_index_creation_on_import]
69
69
  @type.index.create!(@bulk_options.slice(:suffix)) unless @type.index.exists?
70
70
  end
@@ -17,10 +17,11 @@ module Chewy
17
17
  # definition. Use it only if you need to pass options for root
18
18
  # object mapping, such as `date_detection` or `dynamic_date_formats`
19
19
  #
20
+ # @example
20
21
  # class UsersIndex < Chewy::Index
21
22
  # define_type User do
22
23
  # # root object defined implicitly and optionless for current type
23
- # field :full_name, type: 'string'
24
+ # field :full_name, type: 'keyword'
24
25
  # end
25
26
  # end
26
27
  #
@@ -28,32 +29,37 @@ module Chewy
28
29
  # define_type Car do
29
30
  # # explicit root definition with additional options
30
31
  # root dynamic_date_formats: ['yyyy-MM-dd'] do
31
- # field :model_name, type: 'string'
32
+ # field :model_name, type: 'keyword'
32
33
  # end
33
34
  # end
34
35
  # end
35
36
  #
36
- def root(options = {}, &block)
37
- raise 'Root is already defined' if root_object
38
- build_root(options, &block)
37
+ def root(**options)
38
+ self.root_object ||= Chewy::Fields::Root.new(type_name, Chewy.default_root_options.merge(options))
39
+ root_object.update_options!(options)
40
+ yield if block_given?
41
+ root_object
39
42
  end
40
43
 
41
44
  # Defines mapping field for current type
42
45
  #
46
+ # @example
43
47
  # class UsersIndex < Chewy::Index
44
48
  # define_type User do
45
49
  # # passing all the options to field definition:
46
- # field :full_name, type: 'string', analyzer: 'special'
50
+ # field :full_name, analyzer: 'special'
47
51
  # end
48
52
  # end
49
53
  #
50
54
  # The `type` is optional and defaults to `string` if not defined:
51
55
  #
56
+ # @example
52
57
  # field :full_name
53
58
  #
54
59
  # Also, multiple fields might be defined with one call and
55
60
  # with the same options:
56
61
  #
62
+ # @example
57
63
  # field :first_name, :last_name, analyzer: 'special'
58
64
  #
59
65
  # The only special option in the field definition
@@ -61,31 +67,35 @@ module Chewy
61
67
  # method will be called for the indexed object. Also
62
68
  # `:value` might be a proc or indexed object method name:
63
69
  #
70
+ # @example
64
71
  # class User < ActiveRecord::Base
65
72
  # def user_full_name
66
73
  # [first_name, last_name].join(' ')
67
74
  # end
68
75
  # end
69
76
  #
70
- # field :full_name, type: 'string', value: :user_full_name
77
+ # field :full_name, type: 'keyword', value: :user_full_name
71
78
  #
72
79
  # The proc evaluates inside the indexed object context if
73
80
  # its arity is 0 and in present contexts if there is an argument:
74
81
  #
75
- # field :full_name, type: 'string', value: -> { [first_name, last_name].join(' ') }
82
+ # @example
83
+ # field :full_name, type: 'keyword', value: -> { [first_name, last_name].join(' ') }
76
84
  #
77
85
  # separator = ' '
78
- # field :full_name, type: 'string', value: ->(user) { [user.first_name, user.last_name].join(separator) }
86
+ # field :full_name, type: 'keyword', value: ->(user) { [user.first_name, user.last_name].join(separator) }
79
87
  #
80
88
  # If array was returned as value - it will be put in index as well.
81
89
  #
82
- # field :tags, type: 'string', value: -> { tags.map(&:name) }
90
+ # @example
91
+ # field :tags, type: 'keyword', value: -> { tags.map(&:name) }
83
92
  #
84
93
  # Fields supports nesting in case of `object` field type. If
85
94
  # `user.quiz` will return an array of objects, then result index content
86
95
  # will be an array of hashes, if `user.quiz` is not a collection association
87
96
  # then just values hash will be put in the index.
88
97
  #
98
+ # @example
89
99
  # field :quiz do
90
100
  # field :question, :answer
91
101
  # field :score, type: 'integer'
@@ -93,6 +103,7 @@ module Chewy
93
103
  #
94
104
  # Nested fields are composed from nested objects:
95
105
  #
106
+ # @example
96
107
  # field :name, value: -> { name_translations } do
97
108
  # field :ru, value: ->(name) { name['ru'] }
98
109
  # field :en, value: ->(name) { name['en'] }
@@ -101,20 +112,19 @@ module Chewy
101
112
  # Of course it is possible to define object fields contents dynamically
102
113
  # but make sure evaluation proc returns hash:
103
114
  #
115
+ # @example
104
116
  # field :name, type: 'object', value: -> { name_translations }
105
117
  #
106
118
  # The special case is multi_field. If type options and block are
107
119
  # both present field is treated as a multi-field. In that case field
108
120
  # composition changes satisfy elasticsearch rules:
109
121
  #
110
- # field :full_name, type: 'string', analyzer: 'name', value: ->{ full_name.try(:strip) } do
122
+ # @example
123
+ # field :full_name, type: 'text', analyzer: 'name', value: ->{ full_name.try(:strip) } do
111
124
  # field :sorted, analyzer: 'sorted'
112
125
  # end
113
126
  #
114
- def field(*args, &block)
115
- options = args.extract_options!
116
- build_root
117
-
127
+ def field(*args, **options, &block)
118
128
  if args.size > 1
119
129
  args.map { |name| field(name, options) }
120
130
  else
@@ -124,9 +134,9 @@ module Chewy
124
134
 
125
135
  # Defines an aggregation that can be bound to a query or filter
126
136
  #
127
- # Suppose that a user has posts and each post has ratings
128
- # avg_post_rating is the mean of all ratings
129
- #
137
+ # @example
138
+ # # Suppose that a user has posts and each post has ratings
139
+ # # avg_post_rating is the mean of all ratings
130
140
  # class UsersIndex < Chewy::Index
131
141
  # define_type User do
132
142
  # field :posts do
@@ -139,72 +149,68 @@ module Chewy
139
149
  # end
140
150
  # end
141
151
  def agg(name, &block)
142
- build_root
143
152
  self._agg_defs = _agg_defs.merge(name => block)
144
153
  end
145
154
  alias_method :aggregation, :agg
146
155
 
147
156
  # Defines dynamic template in mapping root objects
148
157
  #
158
+ # @example
149
159
  # class CarsIndex < Chewy::Index
150
160
  # define_type Car do
151
- # template 'model.*', type: 'string', analyzer: 'special'
161
+ # template 'model.*', type: 'text', analyzer: 'special'
152
162
  # field 'model', type: 'object' # here we can put { de: 'Der Mercedes', en: 'Mercedes' }
153
163
  # # and template will be applyed to this field
154
164
  # end
155
165
  # end
156
166
  #
157
167
  # Name for each template is generated with the following
158
- # rule: "template_#{dynamic_templates.size + 1}".
168
+ # rule: `template_#!{dynamic_templates.size + 1}`.
159
169
  #
170
+ # @example Templates
160
171
  # template 'tit*', mapping_hash
161
172
  # template 'title.*', mapping_hash # dot in template causes "path_match" using
162
173
  # template /tit.+/, mapping_hash # using "match_pattern": "regexp"
163
174
  # template /title\..+/, mapping_hash # "\." - escaped dot causes "path_match" using
164
- # template /tit.+/, 'string', mapping_hash # "match_mapping_type" as the optionsl second argument
175
+ # template /tit.+/, type: 'text', mapping_hash # "match_mapping_type" as the optionsl second argument
165
176
  # template template42: {match: 'hello*', mapping: {type: 'object'}} # or even pass a template as is
166
177
  #
167
178
  def template(*args)
168
- build_root.dynamic_template(*args)
179
+ root.dynamic_template(*args)
169
180
  end
170
181
  alias_method :dynamic_template, :template
171
182
 
172
183
  # Returns compiled mappings hash for current type
173
184
  #
174
185
  def mappings_hash
175
- root_object ? root_object.mappings_hash : {}
186
+ root.mappings_hash[type_name.to_sym].present? ? root.mappings_hash : {}
176
187
  end
177
188
 
178
189
  # Check whether the type has outdated_sync_field defined with a simple value.
179
190
  #
180
191
  # @return [true, false]
181
192
  def supports_outdated_sync?
182
- updated_at_field = root_object.child_hash[outdated_sync_field] if root_object && outdated_sync_field
193
+ updated_at_field = root.child_hash[outdated_sync_field] if outdated_sync_field
183
194
  !!updated_at_field && updated_at_field.value.nil?
184
195
  end
185
196
 
186
197
  private
187
198
 
188
- def expand_nested(field, &block)
199
+ def expand_nested(field)
200
+ @_current_field ||= root
201
+
189
202
  if @_current_field
190
203
  field.parent = @_current_field
191
204
  @_current_field.children.push(field)
192
205
  end
193
206
 
194
- return unless block
207
+ return unless block_given?
195
208
 
196
209
  previous_field = @_current_field
197
210
  @_current_field = field
198
211
  yield
199
212
  @_current_field = previous_field
200
213
  end
201
-
202
- def build_root(options = {}, &block)
203
- return root_object if root_object
204
- self.root_object = Chewy::Fields::Root.new(type_name, Chewy.default_root_options.merge(options))
205
- expand_nested(root_object, &block)
206
- @_current_field = root_object
207
- end
208
214
  end
209
215
  end
210
216
  end
@@ -9,14 +9,6 @@ module Chewy
9
9
  method = args.first
10
10
 
11
11
  proc do
12
- backreference = if method && method.to_s == 'self'
13
- self
14
- elsif method
15
- send(method)
16
- else
17
- instance_eval(&block)
18
- end
19
-
20
12
  reference = if type_name.is_a?(Proc)
21
13
  if type_name.arity.zero?
22
14
  instance_exec(&type_name)
@@ -27,7 +19,19 @@ module Chewy
27
19
  type_name
28
20
  end
29
21
 
30
- Chewy.derive_type(reference).update_index(backreference, options)
22
+ type = Chewy.derive_type(reference)
23
+
24
+ next if Chewy.strategy.current.name == :bypass
25
+
26
+ backreference = if method && method.to_s == 'self'
27
+ self
28
+ elsif method
29
+ send(method)
30
+ else
31
+ instance_eval(&block)
32
+ end
33
+
34
+ type.update_index(backreference, options)
31
35
  end
32
36
  end
33
37
 
@@ -34,7 +34,7 @@ module Chewy
34
34
  next unless source_data_hash[id]
35
35
 
36
36
  outdated = if outdated_sync_field_type == 'date'
37
- !Chewy::Type::Syncer.dates_equal(typecast_date(source_data_hash[id]), DateTime.iso8601(index_sync_value))
37
+ !Chewy::Type::Syncer.dates_equal(typecast_date(source_data_hash[id]), Time.iso8601(index_sync_value))
38
38
  else
39
39
  source_data_hash[id] != index_sync_value
40
40
  end
@@ -57,7 +57,7 @@ module Chewy
57
57
  if string.is_a?(String) && (match = ISO_DATETIME.match(string))
58
58
  microsec = (match[7].to_r * 1_000_000).to_i
59
59
  date = "#{match[1]}-#{match[2]}-#{match[3]}T#{match[4]}:#{match[5]}:#{match[6]}.#{format('%06d', microsec)}+00:00"
60
- DateTime.iso8601(date)
60
+ Time.iso8601(date)
61
61
  else
62
62
  string
63
63
  end
@@ -58,9 +58,9 @@ module Chewy
58
58
  private
59
59
 
60
60
  def alicorn
61
- @alicorn ||= class_eval <<-RUBY
61
+ @alicorn ||= class_eval <<-RUBY, __FILE__, __LINE__ + 1
62
62
  -> (locals, object0, crutches) do
63
- #{composed_values(@type.root_object, 0)}
63
+ #{composed_values(@type.root, 0)}
64
64
  end
65
65
  RUBY
66
66
  end
@@ -70,11 +70,11 @@ module Chewy
70
70
  end
71
71
 
72
72
  def attribute_defined?(attribute)
73
- self.class.root_object && self.class.root_object.children.find { |a| a.name.to_s == attribute }.present?
73
+ self.class.root && self.class.root.children.find { |a| a.name.to_s == attribute }.present?
74
74
  end
75
75
 
76
76
  def highlight(attribute)
77
- _data['highlight'][attribute].first
77
+ _data['highlight'][attribute].first if highlight?(attribute)
78
78
  end
79
79
 
80
80
  def highlight?(attribute)
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '0.10.1'.freeze
2
+ VERSION = '5.0.0'.freeze
3
3
  end
@@ -36,40 +36,25 @@ module Sequel
36
36
  callback_options = ChewyObserve.extract_callback_options!(args)
37
37
  update_proc = ChewyObserve.update_proc(type_name, *args, &block)
38
38
 
39
- if Chewy.use_after_commit_callbacks
40
- set_callback(:commit, callback_options, &update_proc)
41
- set_callback(:destroy_commit, callback_options, &update_proc)
42
- else
43
- set_callback(:save, callback_options, &update_proc)
44
- set_callback(:destroy, callback_options, &update_proc)
45
- end
39
+ set_callback(:save, callback_options, &update_proc)
40
+ set_callback(:destroy, callback_options, &update_proc)
46
41
  end
47
42
  end
48
43
 
49
44
  # Instance level methods for Sequel::Model
50
45
  #
51
46
  module InstanceMethods
52
- def after_commit
53
- run_callbacks(:commit) do
54
- super
55
- end
56
- end
57
-
58
- def after_destroy_commit
59
- run_callbacks(:destroy_commit) do
60
- super
61
- end
62
- end
63
-
64
47
  def after_save
65
48
  run_callbacks(:save) do
66
49
  super
50
+ db.after_commit {} if Chewy.use_after_commit_callbacks
67
51
  end
68
52
  end
69
53
 
70
54
  def after_destroy
71
55
  run_callbacks(:destroy) do
72
56
  super
57
+ db.after_commit {} if Chewy.use_after_commit_callbacks
73
58
  end
74
59
  end
75
60
  end
@@ -137,5 +137,21 @@ describe Chewy::Config do
137
137
  specify do
138
138
  expect(subject.configuration).to include(indices_path: 'app/custom_indices_path')
139
139
  end
140
+
141
+ context 'when Rails::VERSION constant is defined' do
142
+ it 'looks for configuration in "config/chewy.yml"' do
143
+ module Rails
144
+ VERSION = '5.1.0'.freeze
145
+
146
+ def self.root
147
+ Pathname.new(__dir__)
148
+ end
149
+ end
150
+
151
+ expect(File).to receive(:exist?)
152
+ .with(Pathname.new(__dir__).join('config', 'chewy.yml'))
153
+ subject.configuration
154
+ end
155
+ end
140
156
  end
141
157
  end