chewy 0.10.1 → 7.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
  6. data/.github/workflows/ruby.yml +74 -0
  7. data/.rubocop.yml +28 -23
  8. data/.rubocop_todo.yml +110 -22
  9. data/CHANGELOG.md +480 -298
  10. data/CODE_OF_CONDUCT.md +14 -0
  11. data/CONTRIBUTING.md +63 -0
  12. data/Gemfile +3 -5
  13. data/Guardfile +3 -1
  14. data/LICENSE.txt +1 -1
  15. data/README.md +571 -333
  16. data/chewy.gemspec +12 -15
  17. data/gemfiles/rails.5.2.activerecord.gemfile +11 -0
  18. data/gemfiles/rails.6.0.activerecord.gemfile +11 -0
  19. data/gemfiles/rails.6.1.activerecord.gemfile +13 -0
  20. data/gemfiles/rails.7.0.activerecord.gemfile +13 -0
  21. data/lib/chewy/config.rb +48 -77
  22. data/lib/chewy/errors.rb +4 -10
  23. data/lib/chewy/fields/base.rb +88 -16
  24. data/lib/chewy/fields/root.rb +15 -21
  25. data/lib/chewy/index/actions.rb +67 -38
  26. data/lib/chewy/{type → index}/adapter/active_record.rb +18 -4
  27. data/lib/chewy/{type → index}/adapter/base.rb +11 -12
  28. data/lib/chewy/{type → index}/adapter/object.rb +28 -32
  29. data/lib/chewy/{type → index}/adapter/orm.rb +26 -24
  30. data/lib/chewy/index/aliases.rb +14 -5
  31. data/lib/chewy/index/crutch.rb +40 -0
  32. data/lib/chewy/index/import/bulk_builder.rb +311 -0
  33. data/lib/chewy/{type → index}/import/bulk_request.rb +10 -9
  34. data/lib/chewy/{type → index}/import/journal_builder.rb +11 -12
  35. data/lib/chewy/{type → index}/import/routine.rb +19 -18
  36. data/lib/chewy/{type → index}/import.rb +82 -36
  37. data/lib/chewy/{type → index}/mapping.rb +63 -62
  38. data/lib/chewy/index/observe/active_record_methods.rb +87 -0
  39. data/lib/chewy/index/observe/callback.rb +34 -0
  40. data/lib/chewy/index/observe.rb +17 -0
  41. data/lib/chewy/index/settings.rb +2 -0
  42. data/lib/chewy/index/specification.rb +13 -10
  43. data/lib/chewy/{type → index}/syncer.rb +62 -63
  44. data/lib/chewy/{type → index}/witchcraft.rb +15 -9
  45. data/lib/chewy/{type → index}/wrapper.rb +16 -6
  46. data/lib/chewy/index.rb +68 -93
  47. data/lib/chewy/journal.rb +25 -14
  48. data/lib/chewy/minitest/helpers.rb +91 -18
  49. data/lib/chewy/minitest/search_index_receiver.rb +29 -33
  50. data/lib/chewy/multi_search.rb +62 -0
  51. data/lib/chewy/railtie.rb +8 -24
  52. data/lib/chewy/rake_helper.rb +141 -112
  53. data/lib/chewy/rspec/build_query.rb +12 -0
  54. data/lib/chewy/rspec/helpers.rb +55 -0
  55. data/lib/chewy/rspec/update_index.rb +58 -49
  56. data/lib/chewy/rspec.rb +2 -0
  57. data/lib/chewy/runtime.rb +1 -1
  58. data/lib/chewy/search/loader.rb +19 -41
  59. data/lib/chewy/search/parameters/allow_partial_search_results.rb +27 -0
  60. data/lib/chewy/search/parameters/collapse.rb +16 -0
  61. data/lib/chewy/search/parameters/concerns/query_storage.rb +6 -5
  62. data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
  63. data/lib/chewy/search/parameters/indices.rb +78 -0
  64. data/lib/chewy/search/parameters/none.rb +1 -3
  65. data/lib/chewy/search/parameters/order.rb +6 -19
  66. data/lib/chewy/search/parameters/source.rb +5 -1
  67. data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
  68. data/lib/chewy/search/parameters.rb +28 -8
  69. data/lib/chewy/search/query_proxy.rb +9 -2
  70. data/lib/chewy/search/request.rb +207 -157
  71. data/lib/chewy/search/response.rb +5 -5
  72. data/lib/chewy/search/scoping.rb +7 -8
  73. data/lib/chewy/search/scrolling.rb +14 -13
  74. data/lib/chewy/search.rb +7 -26
  75. data/lib/chewy/stash.rb +27 -29
  76. data/lib/chewy/strategy/active_job.rb +2 -2
  77. data/lib/chewy/strategy/atomic.rb +1 -1
  78. data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
  79. data/lib/chewy/strategy/base.rb +10 -0
  80. data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +148 -0
  81. data/lib/chewy/strategy/delayed_sidekiq/worker.rb +52 -0
  82. data/lib/chewy/strategy/delayed_sidekiq.rb +17 -0
  83. data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
  84. data/lib/chewy/strategy/sidekiq.rb +3 -2
  85. data/lib/chewy/strategy.rb +6 -19
  86. data/lib/chewy/version.rb +1 -1
  87. data/lib/chewy.rb +37 -80
  88. data/lib/generators/chewy/install_generator.rb +1 -1
  89. data/lib/tasks/chewy.rake +26 -32
  90. data/migration_guide.md +56 -0
  91. data/spec/chewy/config_spec.rb +27 -57
  92. data/spec/chewy/fields/base_spec.rb +457 -174
  93. data/spec/chewy/fields/root_spec.rb +24 -32
  94. data/spec/chewy/fields/time_fields_spec.rb +5 -5
  95. data/spec/chewy/index/actions_spec.rb +425 -60
  96. data/spec/chewy/{type → index}/adapter/active_record_spec.rb +110 -44
  97. data/spec/chewy/{type → index}/adapter/object_spec.rb +21 -6
  98. data/spec/chewy/index/aliases_spec.rb +3 -3
  99. data/spec/chewy/index/import/bulk_builder_spec.rb +494 -0
  100. data/spec/chewy/{type → index}/import/bulk_request_spec.rb +5 -12
  101. data/spec/chewy/{type → index}/import/journal_builder_spec.rb +22 -30
  102. data/spec/chewy/{type → index}/import/routine_spec.rb +19 -19
  103. data/spec/chewy/{type → index}/import_spec.rb +154 -95
  104. data/spec/chewy/index/mapping_spec.rb +135 -0
  105. data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
  106. data/spec/chewy/index/observe/callback_spec.rb +139 -0
  107. data/spec/chewy/index/observe_spec.rb +143 -0
  108. data/spec/chewy/index/settings_spec.rb +3 -1
  109. data/spec/chewy/index/specification_spec.rb +32 -33
  110. data/spec/chewy/{type → index}/syncer_spec.rb +14 -19
  111. data/spec/chewy/{type → index}/witchcraft_spec.rb +34 -21
  112. data/spec/chewy/index/wrapper_spec.rb +100 -0
  113. data/spec/chewy/index_spec.rb +99 -114
  114. data/spec/chewy/journal_spec.rb +56 -101
  115. data/spec/chewy/minitest/helpers_spec.rb +122 -14
  116. data/spec/chewy/minitest/search_index_receiver_spec.rb +24 -26
  117. data/spec/chewy/multi_search_spec.rb +84 -0
  118. data/spec/chewy/rake_helper_spec.rb +325 -101
  119. data/spec/chewy/rspec/build_query_spec.rb +34 -0
  120. data/spec/chewy/rspec/helpers_spec.rb +61 -0
  121. data/spec/chewy/rspec/update_index_spec.rb +106 -102
  122. data/spec/chewy/runtime_spec.rb +2 -2
  123. data/spec/chewy/search/loader_spec.rb +19 -53
  124. data/spec/chewy/search/pagination/kaminari_examples.rb +3 -5
  125. data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
  126. data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
  127. data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
  128. data/spec/chewy/search/parameters/indices_spec.rb +99 -0
  129. data/spec/chewy/search/parameters/none_spec.rb +1 -1
  130. data/spec/chewy/search/parameters/order_spec.rb +18 -11
  131. data/spec/chewy/search/parameters/query_storage_examples.rb +67 -21
  132. data/spec/chewy/search/parameters/search_after_spec.rb +4 -1
  133. data/spec/chewy/search/parameters/source_spec.rb +8 -2
  134. data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
  135. data/spec/chewy/search/parameters_spec.rb +39 -8
  136. data/spec/chewy/search/query_proxy_spec.rb +68 -17
  137. data/spec/chewy/search/request_spec.rb +360 -149
  138. data/spec/chewy/search/response_spec.rb +35 -25
  139. data/spec/chewy/search/scrolling_spec.rb +28 -26
  140. data/spec/chewy/search_spec.rb +73 -53
  141. data/spec/chewy/stash_spec.rb +16 -26
  142. data/spec/chewy/strategy/active_job_spec.rb +23 -10
  143. data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
  144. data/spec/chewy/strategy/atomic_spec.rb +9 -10
  145. data/spec/chewy/strategy/delayed_sidekiq_spec.rb +190 -0
  146. data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
  147. data/spec/chewy/strategy/sidekiq_spec.rb +14 -10
  148. data/spec/chewy/strategy_spec.rb +19 -15
  149. data/spec/chewy_spec.rb +17 -110
  150. data/spec/spec_helper.rb +7 -22
  151. data/spec/support/active_record.rb +43 -5
  152. metadata +123 -198
  153. data/.travis.yml +0 -53
  154. data/Appraisals +0 -79
  155. data/LEGACY_DSL.md +0 -497
  156. data/gemfiles/rails.4.0.activerecord.gemfile +0 -14
  157. data/gemfiles/rails.4.1.activerecord.gemfile +0 -14
  158. data/gemfiles/rails.4.2.activerecord.gemfile +0 -15
  159. data/gemfiles/rails.4.2.mongoid.5.1.gemfile +0 -15
  160. data/gemfiles/rails.5.0.activerecord.gemfile +0 -15
  161. data/gemfiles/rails.5.0.mongoid.6.0.gemfile +0 -15
  162. data/gemfiles/rails.5.1.activerecord.gemfile +0 -15
  163. data/gemfiles/rails.5.1.mongoid.6.1.gemfile +0 -15
  164. data/gemfiles/sequel.4.45.gemfile +0 -11
  165. data/lib/chewy/backports/deep_dup.rb +0 -46
  166. data/lib/chewy/backports/duplicable.rb +0 -91
  167. data/lib/chewy/query/compose.rb +0 -68
  168. data/lib/chewy/query/criteria.rb +0 -191
  169. data/lib/chewy/query/filters.rb +0 -227
  170. data/lib/chewy/query/loading.rb +0 -111
  171. data/lib/chewy/query/nodes/and.rb +0 -25
  172. data/lib/chewy/query/nodes/base.rb +0 -17
  173. data/lib/chewy/query/nodes/bool.rb +0 -34
  174. data/lib/chewy/query/nodes/equal.rb +0 -34
  175. data/lib/chewy/query/nodes/exists.rb +0 -20
  176. data/lib/chewy/query/nodes/expr.rb +0 -28
  177. data/lib/chewy/query/nodes/field.rb +0 -110
  178. data/lib/chewy/query/nodes/has_child.rb +0 -15
  179. data/lib/chewy/query/nodes/has_parent.rb +0 -15
  180. data/lib/chewy/query/nodes/has_relation.rb +0 -59
  181. data/lib/chewy/query/nodes/match_all.rb +0 -11
  182. data/lib/chewy/query/nodes/missing.rb +0 -20
  183. data/lib/chewy/query/nodes/not.rb +0 -25
  184. data/lib/chewy/query/nodes/or.rb +0 -25
  185. data/lib/chewy/query/nodes/prefix.rb +0 -19
  186. data/lib/chewy/query/nodes/query.rb +0 -20
  187. data/lib/chewy/query/nodes/range.rb +0 -63
  188. data/lib/chewy/query/nodes/raw.rb +0 -15
  189. data/lib/chewy/query/nodes/regexp.rb +0 -35
  190. data/lib/chewy/query/nodes/script.rb +0 -20
  191. data/lib/chewy/query/pagination.rb +0 -25
  192. data/lib/chewy/query.rb +0 -1098
  193. data/lib/chewy/search/pagination/will_paginate.rb +0 -43
  194. data/lib/chewy/search/parameters/types.rb +0 -20
  195. data/lib/chewy/strategy/resque.rb +0 -27
  196. data/lib/chewy/strategy/shoryuken.rb +0 -40
  197. data/lib/chewy/type/actions.rb +0 -43
  198. data/lib/chewy/type/adapter/mongoid.rb +0 -69
  199. data/lib/chewy/type/adapter/sequel.rb +0 -95
  200. data/lib/chewy/type/crutch.rb +0 -32
  201. data/lib/chewy/type/import/bulk_builder.rb +0 -122
  202. data/lib/chewy/type/observe.rb +0 -78
  203. data/lib/chewy/type.rb +0 -117
  204. data/lib/sequel/plugins/chewy_observe.rb +0 -78
  205. data/spec/chewy/query/criteria_spec.rb +0 -700
  206. data/spec/chewy/query/filters_spec.rb +0 -201
  207. data/spec/chewy/query/loading_spec.rb +0 -124
  208. data/spec/chewy/query/nodes/and_spec.rb +0 -12
  209. data/spec/chewy/query/nodes/bool_spec.rb +0 -14
  210. data/spec/chewy/query/nodes/equal_spec.rb +0 -32
  211. data/spec/chewy/query/nodes/exists_spec.rb +0 -18
  212. data/spec/chewy/query/nodes/has_child_spec.rb +0 -59
  213. data/spec/chewy/query/nodes/has_parent_spec.rb +0 -59
  214. data/spec/chewy/query/nodes/match_all_spec.rb +0 -11
  215. data/spec/chewy/query/nodes/missing_spec.rb +0 -16
  216. data/spec/chewy/query/nodes/not_spec.rb +0 -13
  217. data/spec/chewy/query/nodes/or_spec.rb +0 -12
  218. data/spec/chewy/query/nodes/prefix_spec.rb +0 -16
  219. data/spec/chewy/query/nodes/query_spec.rb +0 -12
  220. data/spec/chewy/query/nodes/range_spec.rb +0 -32
  221. data/spec/chewy/query/nodes/raw_spec.rb +0 -11
  222. data/spec/chewy/query/nodes/regexp_spec.rb +0 -43
  223. data/spec/chewy/query/nodes/script_spec.rb +0 -15
  224. data/spec/chewy/query/pagination/kaminari_spec.rb +0 -5
  225. data/spec/chewy/query/pagination/will_paginate_spec.rb +0 -5
  226. data/spec/chewy/query/pagination_spec.rb +0 -39
  227. data/spec/chewy/query_spec.rb +0 -636
  228. data/spec/chewy/search/pagination/will_paginate_examples.rb +0 -63
  229. data/spec/chewy/search/pagination/will_paginate_spec.rb +0 -23
  230. data/spec/chewy/search/parameters/indices_boost_spec.rb +0 -83
  231. data/spec/chewy/search/parameters/types_spec.rb +0 -5
  232. data/spec/chewy/strategy/resque_spec.rb +0 -46
  233. data/spec/chewy/strategy/shoryuken_spec.rb +0 -64
  234. data/spec/chewy/type/actions_spec.rb +0 -50
  235. data/spec/chewy/type/adapter/mongoid_spec.rb +0 -372
  236. data/spec/chewy/type/adapter/sequel_spec.rb +0 -472
  237. data/spec/chewy/type/import/bulk_builder_spec.rb +0 -279
  238. data/spec/chewy/type/mapping_spec.rb +0 -142
  239. data/spec/chewy/type/observe_spec.rb +0 -137
  240. data/spec/chewy/type/wrapper_spec.rb +0 -98
  241. data/spec/chewy/type_spec.rb +0 -55
  242. data/spec/support/mongoid.rb +0 -93
  243. data/spec/support/sequel.rb +0 -80
data/lib/chewy/index.rb CHANGED
@@ -1,23 +1,52 @@
1
1
  require 'chewy/search'
2
2
  require 'chewy/index/actions'
3
+ require 'chewy/index/adapter/active_record'
4
+ require 'chewy/index/adapter/object'
3
5
  require 'chewy/index/aliases'
6
+ require 'chewy/index/crutch'
7
+ require 'chewy/index/import'
8
+ require 'chewy/index/mapping'
9
+ require 'chewy/index/observe'
4
10
  require 'chewy/index/settings'
5
11
  require 'chewy/index/specification'
12
+ require 'chewy/index/syncer'
13
+ require 'chewy/index/witchcraft'
14
+ require 'chewy/index/wrapper'
6
15
 
7
16
  module Chewy
8
17
  class Index
18
+ IMPORT_OPTIONS_KEYS = %i[
19
+ batch_size bulk_size consistency direct_import journal
20
+ pipeline raw_import refresh replication
21
+ ].freeze
22
+
23
+ STRATEGY_OPTIONS = {
24
+ delayed_sidekiq: %i[latency margin ttl reindex_wrapper]
25
+ }.freeze
26
+
9
27
  include Search
10
28
  include Actions
11
29
  include Aliases
30
+ include Import
31
+ include Mapping
32
+ include Observe
33
+ include Crutch
34
+ include Witchcraft
35
+ include Wrapper
12
36
 
13
37
  singleton_class.delegate :client, to: 'Chewy'
14
38
 
15
- class_attribute :type_hash
16
- self.type_hash = {}
39
+ class_attribute :adapter
40
+ self.adapter = Chewy::Index::Adapter::Object.new(:default)
41
+
42
+ class_attribute :index_scope_defined
17
43
 
18
44
  class_attribute :_settings
19
45
  self._settings = Chewy::Index::Settings.new
20
46
 
47
+ class_attribute :_default_import_options
48
+ self._default_import_options = {}
49
+
21
50
  class << self
22
51
  # @overload index_name(suggest)
23
52
  # If suggested name is passed, it is set up as the new base name for
@@ -47,15 +76,16 @@ module Chewy
47
76
  # UsersIndex.index_name(prefix: '', suffix: '2017') # => 'users_2017'
48
77
  #
49
78
  # @param prefix [String] index name prefix, uses {.prefix} method by default
50
- # @param suffix [String] index name suffix, used for creating several indexes for the same alias during the zero-downtime reset
79
+ # @param suffix [String] index name suffix, used for creating several indexes for the same
80
+ # alias during the zero-downtime reset
51
81
  # @raise [UndefinedIndex] if the base name is blank
52
82
  # @return [String] result index name
53
83
  def index_name(suggest = nil, prefix: nil, suffix: nil)
54
84
  if suggest
55
- @base_name = suggest.to_s.underscore.presence
85
+ @base_name = suggest.to_s.presence
56
86
  else
57
87
  [
58
- prefix || prefix_with_deprecation,
88
+ prefix || self.prefix,
59
89
  base_name,
60
90
  suffix
61
91
  ].reject(&:blank?).join('_')
@@ -77,6 +107,7 @@ module Chewy
77
107
  def base_name
78
108
  @base_name ||= name.sub(/Index\z/, '').demodulize.underscore if name
79
109
  raise UndefinedIndex if @base_name.blank?
110
+
80
111
  @base_name
81
112
  end
82
113
 
@@ -111,78 +142,30 @@ module Chewy
111
142
  Chewy.configuration[:prefix]
112
143
  end
113
144
 
114
- # Defines type for the index. Arguments depends on adapter used. For
145
+ # Defines scope and options for the index. Arguments depends on adapter used. For
115
146
  # ActiveRecord you can pass model or scope and options
116
147
  #
117
148
  # class CarsIndex < Chewy::Index
118
- # define_type Car do
119
- # ...
120
- # end # defines VehiclesIndex::Car type
121
- # end
122
- #
123
- # Type name might be passed in complicated cases:
124
- #
125
- # class VehiclesIndex < Chewy::Index
126
- # define_type Vehicle.cars.includes(:manufacturer), name: 'cars' do
127
- # ...
128
- # end # defines VehiclesIndex::Cars type
129
- #
130
- # define_type Vehicle.motocycles.includes(:manufacturer), name: 'motocycles' do
131
- # ...
132
- # end # defines VehiclesIndex::Motocycles type
149
+ # index_scope Car
150
+ # ...
133
151
  # end
134
152
  #
135
- # For plain objects:
153
+ # For plain objects you can completely omit this directive, unless you need to specify some options:
136
154
  #
137
155
  # class PlanesIndex < Chewy::Index
138
- # define_type :plane do
139
- # ...
140
- # end # defines PlanesIndex::Plane type
156
+ # ...
141
157
  # end
142
158
  #
143
159
  # The main difference between using plain objects or ActiveRecord models for indexing
144
- # is import. If you will call `CarsIndex::Car.import` - it will import all the cars
145
- # automatically, while `PlanesIndex::Plane.import(my_planes)` requires import data to be
160
+ # is import. If you will call `CarsIndex.import` - it will import all the cars
161
+ # automatically, while `PlanesIndex.import(my_planes)` requires import data to be
146
162
  # passed.
147
163
  #
148
- def define_type(target, options = {}, &block)
149
- type_class = Chewy.create_type(self, target, options, &block)
150
- self.type_hash = type_hash.merge(type_class.type_name => type_class)
151
- end
164
+ def index_scope(target, options = {})
165
+ raise 'Index scope is already defined' if index_scope_defined?
152
166
 
153
- # Types method has double usage.
154
- # If no arguments are passed - it returns array of defined types:
155
- #
156
- # UsersIndex.types # => [UsersIndex::Admin, UsersIndex::Manager, UsersIndex::User]
157
- #
158
- # If arguments are passed it treats like a part of chainable query DSL and
159
- # adds types array for index to select.
160
- #
161
- # UsersIndex.filters { name =~ 'ro' }.types(:admin, :manager)
162
- # UsersIndex.types(:admin, :manager).filters { name =~ 'ro' } # the same as the first example
163
- #
164
- def types(*args)
165
- if args.present?
166
- all.types(*args)
167
- else
168
- type_hash.values
169
- end
170
- end
171
-
172
- # Returns defined types names:
173
- #
174
- # UsersIndex.type_names # => ['admin', 'manager', 'user']
175
- #
176
- def type_names
177
- type_hash.keys
178
- end
179
-
180
- # Returns named type:
181
- #
182
- # UserIndex.type('admin') # => UsersIndex::Admin
183
- #
184
- def type(type_name)
185
- type_hash.fetch(type_name) { raise UndefinedType, "Unknown type in #{name}: #{type_name}" }
167
+ self.adapter = Chewy.adapters.find { |klass| klass.accepts?(target) }.new(target, **options)
168
+ self.index_scope_defined = true
186
169
  end
187
170
 
188
171
  # Used as a part of index definition DSL. Defines settings:
@@ -219,7 +202,7 @@ module Chewy
219
202
  end
220
203
 
221
204
  def mappings_hash
222
- mappings = types.map(&:mappings_hash).inject(:merge)
205
+ mappings = root.mappings_hash
223
206
  mappings.present? ? {mappings: mappings} : {}
224
207
  end
225
208
 
@@ -232,44 +215,36 @@ module Chewy
232
215
  [settings_hash, mappings_hash].inject(:merge)
233
216
  end
234
217
 
235
- def index_params
236
- ActiveSupport::Deprecation.warn '`Chewy::Index.index_params` is deprecated and will be removed soon, use `Chewy::Index.specification_hash`'
237
- specification_hash
238
- end
239
-
240
218
  # @see Chewy::Index::Specification
241
219
  # @return [Chewy::Index::Specification] a specification object instance for this particular index
242
220
  def specification
243
221
  @specification ||= Specification.new(self)
244
222
  end
245
223
 
246
- def derivable_index_name
247
- ActiveSupport::Deprecation.warn '`Chewy::Index.derivable_index_name` is deprecated and will be removed soon, use `Chewy::Index.derivable_name` instead'
248
- derivable_name
224
+ def default_import_options(params)
225
+ params.assert_valid_keys(IMPORT_OPTIONS_KEYS)
226
+ self._default_import_options = _default_import_options.merge(params)
249
227
  end
250
228
 
251
- # Handling old default_prefix if it is not defined.
252
- def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
253
- if name == :default_prefix
254
- ActiveSupport::Deprecation.warn '`Chewy::Index.default_prefix` is deprecated and will be removed soon, use `Chewy::Index.prefix` instead'
255
- prefix
256
- else
257
- super
258
- end
259
- end
229
+ def strategy_config(params = {})
230
+ @strategy_config ||= begin
231
+ config_struct = Struct.new(*STRATEGY_OPTIONS.keys).new
260
232
 
261
- def prefix_with_deprecation
262
- if respond_to?(:default_prefix)
263
- ActiveSupport::Deprecation.warn '`Chewy::Index.default_prefix` is deprecated and will be removed soon, define `Chewy::Index.prefix` method instead'
264
- default_prefix
265
- else
266
- prefix
267
- end
268
- end
233
+ STRATEGY_OPTIONS.each_with_object(config_struct) do |(strategy, options), res|
234
+ res[strategy] = case strategy
235
+ when :delayed_sidekiq
236
+ Struct.new(*STRATEGY_OPTIONS[strategy]).new.tap do |config|
237
+ options.each do |option|
238
+ config[option] = params.dig(strategy, option) || Chewy.configuration.dig(:strategy_config, strategy, option)
239
+ end
269
240
 
270
- def build_index_name(*args)
271
- ActiveSupport::Deprecation.warn '`Chewy::Index.build_index_name` is deprecated and will be removed soon, use `Chewy::Index.index_name` instead'
272
- index_name(args.extract_options!)
241
+ config[:reindex_wrapper] ||= ->(&reindex) { reindex.call } # default wrapper
242
+ end
243
+ else
244
+ raise NotImplementedError, "Unsupported strategy: '#{strategy}'"
245
+ end
246
+ end
247
+ end
273
248
  end
274
249
  end
275
250
  end
data/lib/chewy/journal.rb CHANGED
@@ -2,33 +2,36 @@ module Chewy
2
2
  # A class to perform journal-related actions for the specified indexes/types.
3
3
  #
4
4
  # @example
5
- # journal = Chewy::Journal.new('places#city', UsersIndex)
5
+ # journal = Chewy::Journal.new('places', UsersIndex)
6
6
  # journal.apply(20.minutes.ago)
7
7
  # journal.clean
8
8
  #
9
9
  class Journal
10
- # @param only [Array<String, Chewy::Index, Chewy::Type>] indexes/types or even string references to perform actions on
10
+ # @param only [Array<String, Chewy::Index>] indexes or string references to perform actions on
11
11
  def initialize(*only)
12
12
  @only = only
13
13
  end
14
14
 
15
15
  # Applies all changes that were done since the specified time to the
16
- # specified indexes/types.
16
+ # specified indexes.
17
17
  #
18
18
  # @param since_time [Time, DateTime] timestamp from which changes will be applied
19
- # @param retries [Integer] maximum number of attempts to make journal empty, 10 by default
19
+ # @param fetch_limit [Int] amount of entries to be fetched on each cycle
20
20
  # @return [Integer] the amount of journal entries found
21
- def apply(since_time, retries: 10, **import_options)
21
+ def apply(since_time, fetch_limit: 10, **import_options)
22
22
  stage = 1
23
23
  since_time -= 1
24
24
  count = 0
25
- while stage <= retries
26
- entries = Chewy::Stash::Journal.entries(since_time, only: @only).to_a.presence or break
25
+
26
+ total_count = entries(since_time, fetch_limit).total_count
27
+
28
+ while count < total_count
29
+ entries = entries(since_time, fetch_limit).to_a.presence or break
27
30
  count += entries.size
28
31
  groups = reference_groups(entries)
29
32
  ActiveSupport::Notifications.instrument 'apply_journal.chewy', stage: stage, groups: groups
30
- groups.each do |type, references|
31
- type.import(references, import_options.merge(journal: false))
33
+ groups.each do |index, references|
34
+ index.import(references, import_options.merge(journal: false))
32
35
  end
33
36
  stage += 1
34
37
  since_time = entries.map(&:created_at).max
@@ -40,16 +43,24 @@ module Chewy
40
43
  #
41
44
  # @param until_time [Time, DateTime] time to clean up until it
42
45
  # @return [Hash] delete_by_query ES API call result
43
- def clean(until_time = nil)
44
- Chewy::Stash::Journal.clean(until_time, only: @only)
46
+ def clean(until_time = nil, delete_by_query_options: {})
47
+ Chewy::Stash::Journal.clean(
48
+ until_time,
49
+ only: @only,
50
+ delete_by_query_options: delete_by_query_options.merge(refresh: false)
51
+ )
45
52
  end
46
53
 
47
54
  private
48
55
 
56
+ def entries(since_time, fetch_limit)
57
+ Chewy::Stash::Journal.entries(since_time, only: @only).order(:created_at).limit(fetch_limit)
58
+ end
59
+
49
60
  def reference_groups(entries)
50
- entries.group_by(&:type).map do |type, grouped_entries|
51
- [type, grouped_entries.map(&:references).inject(:|)]
52
- end.to_h
61
+ entries.group_by(&:index_name)
62
+ .transform_keys { |index_name| Chewy.derive_name(index_name) }
63
+ .transform_values { |grouped_entries| grouped_entries.map(&:references).inject(:|) }
53
64
  end
54
65
  end
55
66
  end
@@ -6,19 +6,19 @@ module Chewy
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  # Assert that an index *changes* during a block.
9
- # @param (Chewy::Type) index the index / type to watch, eg EntitiesIndex::Entity.
10
- # @param (Symbol) strategy the Chewy strategy to use around the block. See Chewy docs.
11
- # @param (boolean) assert the index changes
12
- # @param (boolean) bypass_actual_index
9
+ # @param index [Chewy::Index] the index to watch, eg EntitiesIndex.
10
+ # @param strategy [Symbol] the Chewy strategy to use around the block. See Chewy docs.
11
+ # @param bypass_actual_index [true, false]
13
12
  # True to preempt the http call to Elastic, false otherwise.
14
13
  # Should be set to true unless actually testing search functionality.
15
14
  #
16
- # @return (SearchIndexReceiver) for optional further assertions on the nature of the index changes.
17
- def assert_indexes(index, strategy: :atomic, bypass_actual_index: true)
18
- type = Chewy.derive_type index
15
+ # @return [SearchIndexReceiver] for optional further assertions on the nature of the index changes.
16
+ #
17
+ def assert_indexes(index, strategy: :atomic, bypass_actual_index: true, &block)
18
+ index_class = Chewy.derive_name index
19
19
  receiver = SearchIndexReceiver.new
20
20
 
21
- bulk_method = type.method :bulk
21
+ bulk_method = index_class.method :bulk
22
22
  # Manually mocking #bulk because we need to properly capture `self`
23
23
  bulk_mock = lambda do |*bulk_args|
24
24
  receiver.catch bulk_args, self
@@ -28,13 +28,11 @@ module Chewy
28
28
  {}
29
29
  end
30
30
 
31
- type.define_singleton_method :bulk, bulk_mock
31
+ index_class.define_singleton_method :bulk, bulk_mock
32
32
 
33
- Chewy.strategy(strategy) do
34
- yield
35
- end
33
+ Chewy.strategy(strategy, &block)
36
34
 
37
- type.define_singleton_method :bulk, bulk_method
35
+ index_class.define_singleton_method :bulk, bulk_method
38
36
 
39
37
  assert_includes receiver.updated_indexes, index, "Expected #{index} to be updated but it wasn't"
40
38
 
@@ -43,11 +41,86 @@ module Chewy
43
41
 
44
42
  # Run indexing for the database changes during the block provided.
45
43
  # By default, indexing is run at the end of the block.
46
- # @param (Symbol) strategy the Chewy index update strategy see Chewy docs.
47
- def run_indexing(strategy: :atomic)
48
- Chewy.strategy strategy do
49
- yield
50
- end
44
+ # @param strategy [Symbol] the Chewy index update strategy see Chewy docs.
45
+ #
46
+ def run_indexing(strategy: :atomic, &block)
47
+ Chewy.strategy strategy, &block
48
+ end
49
+
50
+ # Mock Elasticsearch response
51
+ # Simple usage - just pass index, expected raw response
52
+ # and block with the query.
53
+ #
54
+ # @param index [Chewy::Index] the index to watch, eg EntitiesIndex.
55
+ # @param raw_response [Hash] hash with response.
56
+ #
57
+ def mock_elasticsearch_response(index, raw_response)
58
+ mocked_request = Chewy::Search::Request.new(index)
59
+
60
+ original_new = Chewy::Search::Request.method(:new)
61
+
62
+ Chewy::Search::Request.define_singleton_method(:new) { |*_args| mocked_request }
63
+
64
+ original_perform = mocked_request.method(:perform)
65
+ mocked_request.define_singleton_method(:perform) { raw_response }
66
+
67
+ yield
68
+ ensure
69
+ mocked_request.define_singleton_method(:perform, original_perform)
70
+ Chewy::Search::Request.define_singleton_method(:new, original_new)
71
+ end
72
+
73
+ # Mock Elasticsearch response with defined sources
74
+ # Simple usage - just pass index, expected sources
75
+ # and block with the query.
76
+ #
77
+ # @param index [Chewy::Index] the index to watch, eg EntitiesIndex.
78
+ # @param hits [Hash] hash with sources.
79
+ #
80
+ def mock_elasticsearch_response_sources(index, hits, &block)
81
+ raw_response = {
82
+ 'took' => 4,
83
+ 'timed_out' => false,
84
+ '_shards' => {
85
+ 'total' => 1,
86
+ 'successful' => 1,
87
+ 'skipped' => 0,
88
+ 'failed' => 0
89
+ },
90
+ 'hits' => {
91
+ 'total' => {
92
+ 'value' => hits.count,
93
+ 'relation' => 'eq'
94
+ },
95
+ 'max_score' => 1.0,
96
+ 'hits' => hits.each_with_index.map do |hit, i|
97
+ {
98
+ '_index' => index.index_name,
99
+ '_type' => '_doc',
100
+ '_id' => hit[:id] || (i + 1).to_s,
101
+ '_score' => 3.14,
102
+ '_source' => hit
103
+ }
104
+ end
105
+ }
106
+ }
107
+
108
+ mock_elasticsearch_response(index, raw_response, &block)
109
+ end
110
+
111
+ # Check the assertion that actual Elasticsearch query is rendered
112
+ # to the expected query
113
+ #
114
+ # @param query [::Query] the actual Elasticsearch query.
115
+ # @param expected_query [Hash] expected query.
116
+ #
117
+ # @return [Boolean]
118
+ # True - in the case when actual Elasticsearch query is rendered to the expected query.
119
+ # False - in the opposite case.
120
+ #
121
+ def assert_elasticsearch_query(query, expected_query)
122
+ actual_query = query.render
123
+ assert_equal expected_query, actual_query, "got #{actual_query.inspect} instead of expected query."
51
124
  end
52
125
 
53
126
  module ClassMethods
@@ -1,80 +1,76 @@
1
1
  # Test helper class to provide minitest hooks for Chewy::Index testing.
2
2
  #
3
3
  # @note Intended to be used in conjunction with a test helper which mocks over the #bulk
4
- # method on a Chewy::Type class. (See SearchTestHelper)
4
+ # method on a {Chewy::Index} class. (See {Chewy::Minitest::Helpers})
5
5
  #
6
- # The class will capture the data from the *param on the Chewy::Type#bulk method and
6
+ # The class will capture the data from the *param on the Chewy::Index.bulk method and
7
7
  # aggregate the data for test analysis.
8
8
  class SearchIndexReceiver
9
9
  def initialize
10
10
  @mutations = {}
11
11
  end
12
12
 
13
- # @param bulk_params the bulk_params that should be sent to the Chewy::Type#bulk method.
14
- # @param (Chewy::Type) type the Index::Type executing this query.
15
- def catch(bulk_params, type)
13
+ # @param bulk_params [Hash] the bulk_params that should be sent to the Chewy::Index.bulk method.
14
+ # @param index [Chewy::Index] the index executing this query.
15
+ def catch(bulk_params, index)
16
16
  Array.wrap(bulk_params).map { |y| y[:body] }.flatten.each do |update|
17
17
  if update[:delete]
18
- mutation_for(type).deletes << update[:delete][:_id]
18
+ mutation_for(index).deletes << update[:delete][:_id]
19
19
  elsif update[:index]
20
- mutation_for(type).indexes << update[:index]
20
+ mutation_for(index).indexes << update[:index]
21
21
  end
22
22
  end
23
23
  end
24
24
 
25
- # @param index return only index requests to the specified Chewy::Type index.
26
- # @return the index changes captured by the mock.
25
+ # @param index [Chewy::Index] return only index requests to the specified {Chewy::Index} index.
26
+ # @return [Hash] the index changes captured by the mock.
27
27
  def indexes_for(index = nil)
28
28
  if index
29
29
  mutation_for(index).indexes
30
30
  else
31
- Hash[
32
- @mutations.map { |a, b| [a, b.indexes] }
33
- ]
31
+ @mutations.transform_values(&:indexes)
34
32
  end
35
33
  end
36
34
  alias_method :indexes, :indexes_for
37
35
 
38
- # @param index return only delete requests to the specified Chewy::Type index.
39
- # @return the index deletes captured by the mock.
36
+ # @param index [Chewy::Index] return only delete requests to the specified {Chewy::Index} index.
37
+ # @return [Hash] the index deletes captured by the mock.
40
38
  def deletes_for(index = nil)
41
39
  if index
42
40
  mutation_for(index).deletes
43
41
  else
44
- Hash[
45
- @mutations.map { |a, b| [a, b.deletes] }
46
- ]
42
+ @mutations.transform_values(&:deletes)
47
43
  end
48
44
  end
49
45
  alias_method :deletes, :deletes_for
50
46
 
51
47
  # Check to see if a given object has been indexed.
52
- # @param (#id) obj the object to look for.
53
- # @param Chewy::Type what type the object should be indexed as.
54
- # @return bool if the object was indexed.
55
- def indexed?(obj, type)
56
- indexes_for(type).map { |i| i[:_id] }.include? obj.id
48
+ # @param obj [#id] obj the object to look for.
49
+ # @param index [Chewy::Index] what index the object should be indexed in.
50
+ # @return [true, false] if the object was indexed.
51
+ def indexed?(obj, index)
52
+ indexes_for(index).map { |i| i[:_id] }.include? obj.id
57
53
  end
58
54
 
59
55
  # Check to see if a given object has been deleted.
60
- # @param (#id) obj the object to look for.
61
- # @param Chewy::Type what type the object should have been deleted from.
62
- # @return bool if the object was deleted.
63
- def deleted?(obj, type)
64
- deletes_for(type).include? obj.id
56
+ # @param obj [#id] obj the object to look for.
57
+ # @param index [Chewy::Index] what index the object should have been deleted from.
58
+ # @return [true, false] if the object was deleted.
59
+ def deleted?(obj, index)
60
+ deletes_for(index).include? obj.id
65
61
  end
66
62
 
67
- # @return a list of Chewy::Type indexes changed.
63
+ # @return [Array<Chewy::Index>] a list of indexes changed.
68
64
  def updated_indexes
69
65
  @mutations.keys
70
66
  end
71
67
 
72
68
  private
73
69
 
74
- # Get the mutation object for a given type.
75
- # @param (Chewy::Type) type the index type to fetch.
76
- # @return (#indexes, #deletes) an object with a list of indexes and a list of deletes.
77
- def mutation_for(type)
78
- @mutations[type] ||= OpenStruct.new(indexes: [], deletes: [])
70
+ # Get the mutation object for a given index.
71
+ # @param index [Chewy::Index] the index to fetch.
72
+ # @return [#indexes, #deletes] an object with a list of indexes and a list of deletes.
73
+ def mutation_for(index)
74
+ @mutations[index] ||= OpenStruct.new(indexes: [], deletes: [])
79
75
  end
80
76
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chewy
4
+ # `Chewy::MultiSearch` provides an interface for executing multiple
5
+ # queries via the Elasticsearch Multi Search API. When a MultiSearch
6
+ # is performed it wraps the responses from Elasticsearch and assigns
7
+ # them to the appropriate queries.
8
+ class MultiSearch
9
+ attr_reader :queries
10
+
11
+ # Instantiate a new MultiSearch instance.
12
+ #
13
+ # @param queries [Array<Chewy::Search::Request>]
14
+ # @option [Elasticsearch::Transport::Client] :client (Chewy.client)
15
+ # The Elasticsearch client that should be used for issuing requests.
16
+ def initialize(queries, client: Chewy.client)
17
+ @client = client
18
+ @queries = Array(queries)
19
+ end
20
+
21
+ # Adds a query to be performed by the MultiSearch
22
+ #
23
+ # @param query [Chewy::Search::Request]
24
+ def add_query(query)
25
+ @queries << query
26
+ end
27
+
28
+ # Performs any unperformed queries and returns the responses for all queries.
29
+ #
30
+ # @return [Array<Chewy::Search::Response>]
31
+ def responses
32
+ perform
33
+ queries.map(&:response)
34
+ end
35
+
36
+ # Performs any unperformed queries.
37
+ def perform
38
+ unperformed_queries = queries.reject(&:performed?)
39
+ return if unperformed_queries.empty?
40
+
41
+ responses = msearch(unperformed_queries)['responses']
42
+ unperformed_queries.zip(responses).map { |query, response| query.response = response }
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :client
48
+
49
+ def msearch(queries_to_search)
50
+ body = queries_to_search.flat_map do |query|
51
+ rendered = query.render
52
+ [rendered.except(:body), rendered[:body]]
53
+ end
54
+
55
+ client.msearch(body: body)
56
+ end
57
+ end
58
+
59
+ def self.msearch(queries)
60
+ Chewy::MultiSearch.new(queries)
61
+ end
62
+ end