meilisearch-rails 0.2.3 → 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.
@@ -1,11 +1,12 @@
1
1
  require 'meilisearch'
2
2
 
3
- require 'meilisearch/version'
4
- require 'meilisearch/utilities'
3
+ require 'meilisearch/rails/version'
4
+ require 'meilisearch/rails/utilities'
5
+ require 'meilisearch/rails/errors'
5
6
 
6
7
  if defined? Rails
7
8
  begin
8
- require 'meilisearch/railtie'
9
+ require 'meilisearch/rails/railtie'
9
10
  rescue LoadError
10
11
  end
11
12
  end
@@ -19,237 +20,227 @@ end
19
20
  require 'logger'
20
21
 
21
22
  module MeiliSearch
23
+ module Rails
24
+ autoload :Configuration, 'meilisearch/rails/configuration'
25
+ extend Configuration
22
26
 
23
- class NotConfigured < StandardError; end
24
- class BadConfiguration < StandardError; end
25
- class NoBlockGiven < StandardError; end
27
+ autoload :Pagination, 'meilisearch/rails/pagination'
26
28
 
27
- autoload :Configuration, 'meilisearch/configuration'
28
- extend Configuration
29
+ class << self
30
+ attr_reader :included_in
29
31
 
30
- autoload :Pagination, 'meilisearch/pagination'
32
+ def included(klass)
33
+ @included_in ||= []
34
+ @included_in << klass
35
+ @included_in.uniq!
31
36
 
32
- class << self
33
- attr_reader :included_in
34
-
35
- def included(klass)
36
- @included_in ||= []
37
- @included_in << klass
38
- @included_in.uniq!
39
-
40
- klass.class_eval do
41
- extend ClassMethods
42
- include InstanceMethods
37
+ klass.class_eval do
38
+ extend ClassMethods
39
+ include InstanceMethods
40
+ end
43
41
  end
44
42
  end
45
43
 
46
- end
44
+ class IndexSettings
45
+ DEFAULT_BATCH_SIZE = 1000
47
46
 
48
- class IndexSettings
49
- DEFAULT_BATCH_SIZE = 1000
47
+ DEFAULT_PRIMARY_KEY = 'id'.freeze
50
48
 
51
- DEFAULT_PRIMARY_KEY = 'id'
49
+ # Meilisearch settings
50
+ OPTIONS = %i[
51
+ searchableAttributes
52
+ filterableAttributes
53
+ sortableAttributes
54
+ displayedAttributes
55
+ distinctAttribute
56
+ synonyms
57
+ stopWords
58
+ rankingRules
59
+ attributesToHighlight
60
+ attributesToCrop
61
+ cropLength
62
+ ].freeze
52
63
 
53
- # MeiliSearch settings
54
- OPTIONS = [
55
- :searchableAttributes,
56
- :filterableAttributes,
57
- :displayedAttributes,
58
- :distinctAttribute,
59
- :synonyms,
60
- :stopWords,
61
- :rankingRules,
62
- :attributesToHighlight,
63
- :attributesToCrop,
64
- :cropLength,
65
- ]
64
+ OPTIONS.each do |option|
65
+ define_method option do |value|
66
+ instance_variable_set("@#{option}", value)
67
+ end
66
68
 
67
- OPTIONS.each do |option|
68
- define_method option do |value|
69
- instance_variable_set("@#{option}", value)
69
+ underscored_name = option.to_s.gsub(/(.)([A-Z])/, '\1_\2').downcase
70
+ alias_method underscored_name, option if underscored_name != option
70
71
  end
71
72
 
72
- underscored_name = option.to_s.gsub(/(.)([A-Z])/, '\1_\2').downcase
73
- alias_method underscored_name, option if underscored_name != option
74
- end
73
+ def initialize(options, &block)
74
+ @options = options
75
+ instance_exec(&block) if block_given?
76
+ end
75
77
 
76
- def initialize(options, &block)
77
- @options = options
78
- instance_exec(&block) if block_given?
79
- end
78
+ def use_serializer(serializer)
79
+ @serializer = serializer
80
+ # instance_variable_set("@serializer", serializer)
81
+ end
80
82
 
81
- def use_serializer(serializer)
82
- @serializer = serializer
83
- # instance_variable_set("@serializer", serializer)
84
- end
83
+ def attribute(*names, &block)
84
+ raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1)
85
85
 
86
- def attribute(*names, &block)
87
- raise ArgumentError.new('Cannot pass multiple attribute names if block given') if block_given? and names.length > 1
88
- @attributes ||= {}
89
- names.flatten.each do |name|
90
- @attributes[name.to_s] = block_given? ? Proc.new { |d| d.instance_eval(&block) } : Proc.new { |d| d.send(name) }
86
+ @attributes ||= {}
87
+ names.flatten.each do |name|
88
+ @attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) }
89
+ end
91
90
  end
92
- end
93
- alias :attributes :attribute
91
+ alias attributes attribute
94
92
 
95
- def add_attribute(*names, &block)
96
- raise ArgumentError.new('Cannot pass multiple attribute names if block given') if block_given? and names.length > 1
97
- @additional_attributes ||= {}
98
- names.each do |name|
99
- @additional_attributes[name.to_s] = block_given? ? Proc.new { |d| d.instance_eval(&block) } : Proc.new { |d| d.send(name) }
93
+ def add_attribute(*names, &block)
94
+ raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1)
95
+
96
+ @additional_attributes ||= {}
97
+ names.each do |name|
98
+ @additional_attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) }
99
+ end
100
100
  end
101
- end
102
- alias :add_attributes :add_attribute
101
+ alias add_attributes add_attribute
103
102
 
104
- def is_mongoid?(document)
105
- defined?(::Mongoid::Document) && document.class.include?(::Mongoid::Document)
106
- end
103
+ def mongoid?(document)
104
+ defined?(::Mongoid::Document) && document.class.include?(::Mongoid::Document)
105
+ end
107
106
 
108
- def is_sequel?(document)
109
- defined?(::Sequel) && document.class < ::Sequel::Model
110
- end
107
+ def sequel?(document)
108
+ defined?(::Sequel) && document.class < ::Sequel::Model
109
+ end
111
110
 
112
- def is_active_record?(document)
113
- !is_mongoid?(document) && !is_sequel?(document)
114
- end
111
+ def active_record?(document)
112
+ !mongoid?(document) && !sequel?(document)
113
+ end
115
114
 
116
- def get_default_attributes(document)
117
- if is_mongoid?(document)
118
- # work-around mongoid 2.4's unscoped method, not accepting a block
119
- document.attributes
120
- elsif is_sequel?(document)
121
- document.to_hash
122
- else
123
- document.class.unscoped do
115
+ def get_default_attributes(document)
116
+ if mongoid?(document)
117
+ # work-around mongoid 2.4's unscoped method, not accepting a block
124
118
  document.attributes
119
+ elsif sequel?(document)
120
+ document.to_hash
121
+ else
122
+ document.class.unscoped do
123
+ document.attributes
124
+ end
125
125
  end
126
126
  end
127
- end
128
127
 
129
- def get_attribute_names(document)
130
- get_attributes(document).keys
131
- end
128
+ def get_attribute_names(document)
129
+ get_attributes(document).keys
130
+ end
132
131
 
133
- def attributes_to_hash(attributes, document)
134
- if attributes
135
- Hash[attributes.map { |name, value| [name.to_s, value.call(document) ] }]
136
- else
137
- {}
132
+ def attributes_to_hash(attributes, document)
133
+ if attributes
134
+ attributes.to_h { |name, value| [name.to_s, value.call(document)] }
135
+ else
136
+ {}
137
+ end
138
138
  end
139
- end
140
139
 
141
- def get_attributes(document)
142
- # If a serializer is set, we ignore attributes
143
- # everything should be done via the serializer
144
- if not @serializer.nil?
145
- attributes = @serializer.new(document).attributes
146
- else
147
- if @attributes.nil? || @attributes.length == 0
148
- # no `attribute ...` have been configured, use the default attributes of the model
140
+ def get_attributes(document)
141
+ # If a serializer is set, we ignore attributes
142
+ # everything should be done via the serializer
143
+ if !@serializer.nil?
144
+ attributes = @serializer.new(document).attributes
145
+ elsif @attributes.blank?
149
146
  attributes = get_default_attributes(document)
150
- else
147
+ # no `attribute ...` have been configured, use the default attributes of the model
148
+ elsif active_record?(document)
151
149
  # at least 1 `attribute ...` has been configured, therefore use ONLY the one configured
152
- if is_active_record?(document)
153
- document.class.unscoped do
154
- attributes = attributes_to_hash(@attributes, document)
155
- end
156
- else
150
+ document.class.unscoped do
157
151
  attributes = attributes_to_hash(@attributes, document)
158
152
  end
153
+ else
154
+ attributes = attributes_to_hash(@attributes, document)
159
155
  end
160
- end
161
156
 
162
- attributes.merge!(attributes_to_hash(@additional_attributes, document)) if @additional_attributes
157
+ attributes.merge!(attributes_to_hash(@additional_attributes, document)) if @additional_attributes
163
158
 
164
- if @options[:sanitize]
165
- sanitizer = begin
166
- ::HTML::FullSanitizer.new
167
- rescue NameError
168
- # from rails 4.2
169
- ::Rails::Html::FullSanitizer.new
159
+ if @options[:sanitize]
160
+ attributes = sanitize_attributes(attributes)
170
161
  end
171
- attributes = sanitize_attributes(attributes, sanitizer)
162
+
163
+ attributes = encode_attributes(attributes) if @options[:force_utf8_encoding]
164
+
165
+ attributes
172
166
  end
173
167
 
174
- if @options[:force_utf8_encoding]
175
- attributes = encode_attributes(attributes)
168
+ def sanitize_attributes(value)
169
+ case value
170
+ when String
171
+ ActionView::Base.full_sanitizer.sanitize(value)
172
+ when Hash
173
+ value.each { |key, val| value[key] = sanitize_attributes(val) }
174
+ when Array
175
+ value.map { |item| sanitize_attributes(item) }
176
+ else
177
+ value
178
+ end
176
179
  end
177
180
 
178
- attributes
179
- end
181
+ def encode_attributes(value)
182
+ case value
183
+ when String
184
+ value.force_encoding('utf-8')
185
+ when Hash
186
+ value.each { |key, val| value[key] = encode_attributes(val) }
187
+ when Array
188
+ value.map { |x| encode_attributes(x) }
189
+ else
190
+ value
191
+ end
192
+ end
180
193
 
181
- def sanitize_attributes(v, sanitizer)
182
- case v
183
- when String
184
- sanitizer.sanitize(v)
185
- when Hash
186
- v.each { |key, value| v[key] = sanitize_attributes(value, sanitizer) }
187
- when Array
188
- v.map { |x| sanitize_attributes(x, sanitizer) }
189
- else
190
- v
194
+ def get_setting(name)
195
+ instance_variable_get("@#{name}")
191
196
  end
192
- end
193
197
 
194
- def encode_attributes(v)
195
- case v
196
- when String
197
- v.force_encoding('utf-8')
198
- when Hash
199
- v.each { |key, value| v[key] = encode_attributes(value) }
200
- when Array
201
- v.map { |x| encode_attributes(x) }
202
- else
203
- v
198
+ def to_settings
199
+ settings = {}
200
+ OPTIONS.each do |k|
201
+ v = get_setting(k)
202
+ settings[k] = v unless v.nil?
203
+ end
204
+ settings
204
205
  end
205
- end
206
206
 
207
- def get_setting(name)
208
- instance_variable_get("@#{name}")
209
- end
207
+ def add_index(index_uid, options = {}, &block)
208
+ raise ArgumentError, 'No block given' unless block_given?
209
+ if options[:auto_index] || options[:auto_remove]
210
+ raise ArgumentError, 'Options auto_index and auto_remove cannot be set on nested indexes'
211
+ end
210
212
 
211
- def to_settings
212
- settings = {}
213
- OPTIONS.each do |k|
214
- v = get_setting(k)
215
- settings[k] = v if !v.nil?
213
+ @additional_indexes ||= {}
214
+ options[:index_uid] = index_uid
215
+ @additional_indexes[options] = IndexSettings.new(options, &block)
216
216
  end
217
- settings
218
- end
219
217
 
220
- def add_index(index_uid, options = {}, &block)
221
- raise ArgumentError.new('No block given') if !block_given?
222
- raise ArgumentError.new('Options auto_index and auto_remove cannot be set on nested indexes') if options[:auto_index] || options[:auto_remove]
223
- @additional_indexes ||= {}
224
- options[:index_uid] = index_uid
225
- @additional_indexes[options] = IndexSettings.new(options, &block)
218
+ def additional_indexes
219
+ @additional_indexes || {}
220
+ end
226
221
  end
227
222
 
228
- def additional_indexes
229
- @additional_indexes || {}
223
+ # Default queueing system
224
+ if defined?(::ActiveJob::Base)
225
+ # lazy load the ActiveJob class to ensure the
226
+ # queue is initialized before using it
227
+ autoload :MSJob, 'meilisearch/ms_job'
230
228
  end
231
- end
232
-
233
- # Default queueing system
234
- if defined?(::ActiveJob::Base)
235
- # lazy load the ActiveJob class to ensure the
236
- # queue is initialized before using it
237
- autoload :MSJob, 'meilisearch/ms_job'
238
- end
239
229
 
240
- # this class wraps an MeiliSearch::Index document ensuring all raised exceptions
241
- # are correctly logged or thrown depending on the `raise_on_failure` option
242
- class SafeIndex
243
- def initialize(index_uid, raise_on_failure, options)
244
- client = MeiliSearch.client
245
- primary_key = options[:primary_key] || MeiliSearch::IndexSettings::DEFAULT_PRIMARY_KEY
246
- @index = client.get_or_create_index(index_uid, { primaryKey: primary_key })
247
- @raise_on_failure = raise_on_failure.nil? || raise_on_failure
248
- end
230
+ # this class wraps an MeiliSearch::Index document ensuring all raised exceptions
231
+ # are correctly logged or thrown depending on the `raise_on_failure` option
232
+ class SafeIndex
233
+ def initialize(index_uid, raise_on_failure, options)
234
+ client = MeiliSearch::Rails.client
235
+ primary_key = options[:primary_key] || MeiliSearch::Rails::IndexSettings::DEFAULT_PRIMARY_KEY
236
+ client.create_index(index_uid, { primaryKey: primary_key })
237
+ @index = client.index(index_uid)
238
+ @raise_on_failure = raise_on_failure.nil? || raise_on_failure
239
+ end
249
240
 
250
- ::MeiliSearch::Index.instance_methods(false).each do |m|
241
+ ::MeiliSearch::Index.instance_methods(false).each do |m|
251
242
  define_method(m) do |*args, &block|
252
- if (m == :update_settings)
243
+ if m == :update_settings
253
244
  args[0].delete(:attributesToHighlight) if args[0][:attributesToHighlight]
254
245
  args[0].delete(:attributesToCrop) if args[0][:attributesToCrop]
255
246
  args[0].delete(:cropLength) if args[0][:cropLength]
@@ -258,36 +249,35 @@ module MeiliSearch
258
249
  @index.send(m, *args, &block)
259
250
  end
260
251
  end
261
- end
252
+ end
253
+
254
+ # special handling of wait_for_task to handle null task_id
255
+ def wait_for_task(task_uid)
256
+ return if task_uid.nil? && !@raise_on_failure # ok
262
257
 
263
- # special handling of wait_for_pending_update to handle null task_id
264
- def wait_for_pending_update(update_id)
265
- return if update_id.nil? && !@raise_on_failure # ok
266
- SafeIndex.log_or_throw(:wait_for_pending_update, @raise_on_failure) do
267
- @index.wait_for_pending_update(update_id)
258
+ SafeIndex.log_or_throw(:wait_for_task, @raise_on_failure) do
259
+ @index.wait_for_task(task_uid)
260
+ end
268
261
  end
269
- end
270
262
 
271
- # special handling of settings to avoid raising errors on 404
272
- def settings(*args)
273
- SafeIndex.log_or_throw(:settings, @raise_on_failure) do
274
- begin
263
+ # special handling of settings to avoid raising errors on 404
264
+ def settings(*args)
265
+ SafeIndex.log_or_throw(:settings, @raise_on_failure) do
275
266
  @index.settings(*args)
276
267
  rescue ::MeiliSearch::ApiError => e
277
268
  return {} if e.code == 404 # not fatal
269
+
278
270
  raise e
279
271
  end
280
272
  end
281
- end
282
273
 
283
- private
284
- def self.log_or_throw(method, raise_on_failure, &block)
285
- begin
274
+ def self.log_or_throw(method, raise_on_failure, &block)
286
275
  yield
287
276
  rescue ::MeiliSearch::ApiError => e
288
277
  raise e if raise_on_failure
278
+
289
279
  # log the error
290
- (Rails.logger || Logger.new(STDOUT)).error("[meilisearch-rails] #{e.message}")
280
+ (::Rails.logger || Logger.new($stdout)).error("[meilisearch-rails] #{e.message}")
291
281
  # return something
292
282
  case method.to_s
293
283
  when 'search'
@@ -299,622 +289,635 @@ module MeiliSearch
299
289
  end
300
290
  end
301
291
  end
302
- end
303
292
 
304
- # these are the class methods added when MeiliSearch is included
305
- module ClassMethods
306
-
307
- def self.extended(base)
308
- class <<base
309
- alias_method :without_auto_index, :ms_without_auto_index unless method_defined? :without_auto_index
310
- alias_method :reindex!, :ms_reindex! unless method_defined? :reindex!
311
- alias_method :index_documents, :ms_index_documents unless method_defined? :index_documents
312
- alias_method :index!, :ms_index! unless method_defined? :index!
313
- alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
314
- alias_method :clear_index!, :ms_clear_index! unless method_defined? :clear_index!
315
- alias_method :search, :ms_search unless method_defined? :search
316
- alias_method :raw_search, :ms_raw_search unless method_defined? :raw_search
317
- alias_method :index, :ms_index unless method_defined? :index
318
- alias_method :index_uid, :ms_index_uid unless method_defined? :index_uid
319
- alias_method :must_reindex?, :ms_must_reindex? unless method_defined? :must_reindex?
320
- end
321
-
322
- base.cattr_accessor :meilisearch_options, :meilisearch_settings
323
- end
293
+ # these are the class methods added when MeiliSearch is included
294
+ module ClassMethods
295
+ def self.extended(base)
296
+ class << base
297
+ alias_method :without_auto_index, :ms_without_auto_index unless method_defined? :without_auto_index
298
+ alias_method :reindex!, :ms_reindex! unless method_defined? :reindex!
299
+ alias_method :index_documents, :ms_index_documents unless method_defined? :index_documents
300
+ alias_method :index!, :ms_index! unless method_defined? :index!
301
+ alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
302
+ alias_method :clear_index!, :ms_clear_index! unless method_defined? :clear_index!
303
+ alias_method :search, :ms_search unless method_defined? :search
304
+ alias_method :raw_search, :ms_raw_search unless method_defined? :raw_search
305
+ alias_method :index, :ms_index unless method_defined? :index
306
+ alias_method :index_uid, :ms_index_uid unless method_defined? :index_uid
307
+ alias_method :must_reindex?, :ms_must_reindex? unless method_defined? :must_reindex?
308
+ end
309
+
310
+ base.cattr_accessor :meilisearch_options, :meilisearch_settings
311
+ end
324
312
 
325
- def meilisearch(options = {}, &block)
326
- self.meilisearch_settings = IndexSettings.new(options, &block)
327
- self.meilisearch_options = { type: ms_full_const_get(model_name.to_s), per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1 }.merge(options)
313
+ def meilisearch(options = {}, &block)
314
+ self.meilisearch_settings = IndexSettings.new(options, &block)
315
+ self.meilisearch_options = {
316
+ type: model_name.to_s.constantize,
317
+ per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1
318
+ }.merge(options)
328
319
 
329
- attr_accessor :formatted
320
+ attr_accessor :formatted
330
321
 
331
- if options[:synchronous] == true
332
- if defined?(::Sequel) && self < Sequel::Model
333
- class_eval do
334
- copy_after_validation = instance_method(:after_validation)
335
- define_method(:after_validation) do |*args|
336
- super(*args)
337
- copy_after_validation.bind(self).call
338
- ms_mark_synchronous
322
+ if options[:synchronous] == true
323
+ if defined?(::Sequel) && self < Sequel::Model
324
+ class_eval do
325
+ copy_after_validation = instance_method(:after_validation)
326
+ define_method(:after_validation) do |*args|
327
+ super(*args)
328
+ copy_after_validation.bind(self).call
329
+ ms_mark_synchronous
330
+ end
339
331
  end
332
+ elsif respond_to?(:after_validation)
333
+ after_validation :ms_mark_synchronous
340
334
  end
341
- else
342
- after_validation :ms_mark_synchronous if respond_to?(:after_validation)
343
335
  end
344
- end
345
- if options[:enqueue]
346
- raise ArgumentError.new("Cannot use a enqueue if the `synchronous` option if set") if options[:synchronous]
347
- proc = if options[:enqueue] == true
348
- Proc.new do |record, remove|
349
- MSJob.perform_later(record, remove ? 'ms_remove_from_index!' : 'ms_index!')
336
+ if options[:enqueue]
337
+ raise ArgumentError, 'Cannot use a enqueue if the `synchronous` option if set' if options[:synchronous]
338
+
339
+ proc = if options[:enqueue] == true
340
+ proc do |record, remove|
341
+ MSJob.perform_later(record, remove ? 'ms_remove_from_index!' : 'ms_index!')
342
+ end
343
+ elsif options[:enqueue].respond_to?(:call)
344
+ options[:enqueue]
345
+ elsif options[:enqueue].is_a?(Symbol)
346
+ proc { |record, remove| send(options[:enqueue], record, remove) }
347
+ else
348
+ raise ArgumentError, "Invalid `enqueue` option: #{options[:enqueue]}"
349
+ end
350
+ meilisearch_options[:enqueue] = proc do |record, remove|
351
+ proc.call(record, remove) unless ms_without_auto_index_scope
350
352
  end
351
- elsif options[:enqueue].respond_to?(:call)
352
- options[:enqueue]
353
- elsif options[:enqueue].is_a?(Symbol)
354
- Proc.new { |record, remove| self.send(options[:enqueue], record, remove) }
355
- else
356
- raise ArgumentError.new("Invalid `enqueue` option: #{options[:enqueue]}")
357
353
  end
358
- meilisearch_options[:enqueue] = Proc.new do |record, remove|
359
- proc.call(record, remove) unless ms_without_auto_index_scope
360
- end
361
- end
362
- unless options[:auto_index] == false
363
- if defined?(::Sequel) && self < Sequel::Model
364
- class_eval do
365
- copy_after_validation = instance_method(:after_validation)
366
- copy_before_save = instance_method(:before_save)
354
+ unless options[:auto_index] == false
355
+ if defined?(::Sequel) && self < Sequel::Model
356
+ class_eval do
357
+ copy_after_validation = instance_method(:after_validation)
358
+ copy_before_save = instance_method(:before_save)
367
359
 
368
- define_method(:after_validation) do |*args|
369
- super(*args)
370
- copy_after_validation.bind(self).call
371
- ms_mark_must_reindex
372
- end
373
-
374
- define_method(:before_save) do |*args|
375
- copy_before_save.bind(self).call
376
- ms_mark_for_auto_indexing
377
- super(*args)
378
- end
379
-
380
- sequel_version = Gem::Version.new(Sequel.version)
381
- if sequel_version >= Gem::Version.new('4.0.0') && sequel_version < Gem::Version.new('5.0.0')
382
- copy_after_commit = instance_method(:after_commit)
383
- define_method(:after_commit) do |*args|
360
+ define_method(:after_validation) do |*args|
384
361
  super(*args)
385
- copy_after_commit.bind(self).call
386
- ms_perform_index_tasks
362
+ copy_after_validation.bind(self).call
363
+ ms_mark_must_reindex
387
364
  end
388
- else
389
- copy_after_save = instance_method(:after_save)
390
- define_method(:after_save) do |*args|
365
+
366
+ define_method(:before_save) do |*args|
367
+ copy_before_save.bind(self).call
368
+ ms_mark_for_auto_indexing
391
369
  super(*args)
392
- copy_after_save.bind(self).call
393
- self.db.after_commit do
370
+ end
371
+
372
+ sequel_version = Gem::Version.new(Sequel.version)
373
+ if sequel_version >= Gem::Version.new('4.0.0') && sequel_version < Gem::Version.new('5.0.0')
374
+ copy_after_commit = instance_method(:after_commit)
375
+ define_method(:after_commit) do |*args|
376
+ super(*args)
377
+ copy_after_commit.bind(self).call
394
378
  ms_perform_index_tasks
395
379
  end
380
+ else
381
+ copy_after_save = instance_method(:after_save)
382
+ define_method(:after_save) do |*args|
383
+ super(*args)
384
+ copy_after_save.bind(self).call
385
+ db.after_commit do
386
+ ms_perform_index_tasks
387
+ end
388
+ end
396
389
  end
397
390
  end
391
+ else
392
+ after_validation :ms_mark_must_reindex if respond_to?(:after_validation)
393
+ before_save :ms_mark_for_auto_indexing if respond_to?(:before_save)
394
+ if respond_to?(:after_commit)
395
+ after_commit :ms_perform_index_tasks
396
+ elsif respond_to?(:after_save)
397
+ after_save :ms_perform_index_tasks
398
+ end
398
399
  end
399
- else
400
- after_validation :ms_mark_must_reindex if respond_to?(:after_validation)
401
- before_save :ms_mark_for_auto_indexing if respond_to?(:before_save)
402
- if respond_to?(:after_commit)
403
- after_commit :ms_perform_index_tasks
404
- elsif respond_to?(:after_save)
405
- after_save :ms_perform_index_tasks
400
+ end
401
+ unless options[:auto_remove] == false
402
+ if defined?(::Sequel) && self < Sequel::Model
403
+ class_eval do
404
+ copy_after_destroy = instance_method(:after_destroy)
405
+
406
+ define_method(:after_destroy) do |*args|
407
+ copy_after_destroy.bind(self).call
408
+ ms_enqueue_remove_from_index!(ms_synchronous?)
409
+ super(*args)
410
+ end
411
+ end
412
+ elsif respond_to?(:after_destroy)
413
+ after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) }
406
414
  end
407
415
  end
408
416
  end
409
- unless options[:auto_remove] == false
410
- if defined?(::Sequel) && self < Sequel::Model
411
- class_eval do
412
- copy_after_destroy = instance_method(:after_destroy)
413
417
 
414
- define_method(:after_destroy) do |*args|
415
- copy_after_destroy.bind(self).call
416
- ms_enqueue_remove_from_index!(ms_synchronous?)
417
- super(*args)
418
- end
419
- end
420
- else
421
- after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) } if respond_to?(:after_destroy)
418
+ def ms_without_auto_index(&block)
419
+ self.ms_without_auto_index_scope = true
420
+ begin
421
+ yield
422
+ ensure
423
+ self.ms_without_auto_index_scope = false
422
424
  end
423
425
  end
424
- end
425
426
 
426
- def ms_without_auto_index(&block)
427
- self.ms_without_auto_index_scope = true
428
- begin
429
- yield
430
- ensure
431
- self.ms_without_auto_index_scope = false
427
+ def ms_without_auto_index_scope=(value)
428
+ Thread.current["ms_without_auto_index_scope_for_#{model_name}"] = value
432
429
  end
433
- end
434
430
 
435
- def ms_without_auto_index_scope=(value)
436
- Thread.current["ms_without_auto_index_scope_for_#{self.model_name}"] = value
437
- end
431
+ def ms_without_auto_index_scope
432
+ Thread.current["ms_without_auto_index_scope_for_#{model_name}"]
433
+ end
438
434
 
439
- def ms_without_auto_index_scope
440
- Thread.current["ms_without_auto_index_scope_for_#{self.model_name}"]
441
- end
435
+ def ms_reindex!(batch_size = MeiliSearch::Rails::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false)
436
+ return if ms_without_auto_index_scope
442
437
 
443
- def ms_reindex!(batch_size = MeiliSearch::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false)
444
- return if ms_without_auto_index_scope
445
- ms_configurations.each do |options, settings|
446
- next if ms_indexing_disabled?(options)
447
- index = ms_ensure_init(options, settings)
448
- last_update = nil
449
-
450
- ms_find_in_batches(batch_size) do |group|
451
- if ms_conditional_index?(options)
452
- # delete non-indexable documents
453
- ids = group.select { |d| !ms_indexable?(d, options) }.map { |d| ms_primary_key_of(d, options) }
454
- index.delete_documents(ids.select { |id| !id.blank? })
455
- # select only indexable documents
456
- group = group.select { |d| ms_indexable?(d, options) }
457
- end
458
- documents = group.map do |d|
459
- attributes = settings.get_attributes(d)
460
- unless attributes.class == Hash
461
- attributes = attributes.to_hash
438
+ ms_configurations.each do |options, settings|
439
+ next if ms_indexing_disabled?(options)
440
+
441
+ index = ms_ensure_init(options, settings)
442
+ last_task = nil
443
+
444
+ ms_find_in_batches(batch_size) do |group|
445
+ if ms_conditional_index?(options)
446
+ # delete non-indexable documents
447
+ ids = group.select { |d| !ms_indexable?(d, options) }.map { |d| ms_primary_key_of(d, options) }
448
+ index.delete_documents(ids.select(&:present?))
449
+ # select only indexable documents
450
+ group = group.select { |d| ms_indexable?(d, options) }
451
+ end
452
+ documents = group.map do |d|
453
+ attributes = settings.get_attributes(d)
454
+ attributes = attributes.to_hash unless attributes.instance_of?(Hash)
455
+ attributes.merge ms_pk(options) => ms_primary_key_of(d, options)
462
456
  end
463
- attributes.merge ms_pk(options) => ms_primary_key_of(d, options)
457
+ last_task = index.add_documents(documents)
464
458
  end
465
- last_update= index.add_documents(documents)
459
+ index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous])
466
460
  end
467
- index.wait_for_pending_update(last_update["updateId"]) if last_update and (synchronous || options[:synchronous])
461
+ nil
468
462
  end
469
- nil
470
- end
471
463
 
472
- def ms_set_settings(synchronous = false)
473
- ms_configurations.each do |options, settings|
474
- if options[:primary_settings] && options[:inherit]
475
- primary = options[:primary_settings].to_settings
476
- final_settings = primary.merge(settings.to_settings)
477
- else
478
- final_settings = settings.to_settings
479
- end
464
+ def ms_set_settings(synchronous = false)
465
+ ms_configurations.each do |options, settings|
466
+ if options[:primary_settings] && options[:inherit]
467
+ primary = options[:primary_settings].to_settings
468
+ final_settings = primary.merge(settings.to_settings)
469
+ else
470
+ final_settings = settings.to_settings
471
+ end
480
472
 
481
- index = SafeIndex.new(ms_index_uid(options), true, options)
482
- update = index.update_settings(final_settings)
483
- index.wait_for_pending_update(update["updateId"]) if synchronous
473
+ index = SafeIndex.new(ms_index_uid(options), true, options)
474
+ task = index.update_settings(final_settings)
475
+ index.wait_for_task(task['uid']) if synchronous
476
+ end
484
477
  end
485
- end
486
478
 
487
- def ms_index_documents(documents, synchronous = false)
488
- ms_configurations.each do |options, settings|
489
- next if ms_indexing_disabled?(options)
490
- index = ms_ensure_init(options, settings)
491
- update = index.add_documents(documents.map { |d| settings.get_attributes(d).merge ms_pk(options) => ms_primary_key_of(d, options) })
492
- index.wait_for_pending_update(update["updateId"]) if synchronous || options[:synchronous]
479
+ def ms_index_documents(documents, synchronous = false)
480
+ ms_configurations.each do |options, settings|
481
+ next if ms_indexing_disabled?(options)
482
+
483
+ index = ms_ensure_init(options, settings)
484
+ task = index.add_documents(documents.map { |d| settings.get_attributes(d).merge ms_pk(options) => ms_primary_key_of(d, options) })
485
+ index.wait_for_task(task['uid']) if synchronous || options[:synchronous]
486
+ end
493
487
  end
494
- end
495
488
 
496
- def ms_index!(document, synchronous = false)
497
- return if ms_without_auto_index_scope
498
- ms_configurations.each do |options, settings|
499
- next if ms_indexing_disabled?(options)
500
- primary_key = ms_primary_key_of(document, options)
501
- index = ms_ensure_init(options, settings)
502
- if ms_indexable?(document, options)
503
- raise ArgumentError.new("Cannot index a record without a primary key") if primary_key.blank?
504
- if synchronous || options[:synchronous]
505
- doc = settings.get_attributes(document)
506
- doc = doc.merge ms_pk(options) => primary_key
507
- index.add_documents!(doc)
508
- else
489
+ def ms_index!(document, synchronous = false)
490
+ return if ms_without_auto_index_scope
491
+
492
+ ms_configurations.each do |options, settings|
493
+ next if ms_indexing_disabled?(options)
494
+
495
+ primary_key = ms_primary_key_of(document, options)
496
+ index = ms_ensure_init(options, settings)
497
+ if ms_indexable?(document, options)
498
+ raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank?
499
+
509
500
  doc = settings.get_attributes(document)
510
501
  doc = doc.merge ms_pk(options) => primary_key
511
- index.add_documents(doc)
502
+
503
+ if synchronous || options[:synchronous]
504
+ index.add_documents!(doc)
505
+ else
506
+ index.add_documents(doc)
507
+ end
508
+ elsif ms_conditional_index?(options) && primary_key.present?
509
+ # remove non-indexable documents
510
+ if synchronous || options[:synchronous]
511
+ index.delete_document!(primary_key)
512
+ else
513
+ index.delete_document(primary_key)
514
+ end
512
515
  end
513
- elsif ms_conditional_index?(options) && !primary_key.blank?
514
- # remove non-indexable documents
516
+ end
517
+ nil
518
+ end
519
+
520
+ def ms_remove_from_index!(document, synchronous = false)
521
+ return if ms_without_auto_index_scope
522
+
523
+ primary_key = ms_primary_key_of(document)
524
+ raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank?
525
+
526
+ ms_configurations.each do |options, settings|
527
+ next if ms_indexing_disabled?(options)
528
+
529
+ index = ms_ensure_init(options, settings)
515
530
  if synchronous || options[:synchronous]
516
531
  index.delete_document!(primary_key)
517
532
  else
518
533
  index.delete_document(primary_key)
519
534
  end
520
535
  end
536
+ nil
521
537
  end
522
- nil
523
- end
524
538
 
525
- def ms_remove_from_index!(document, synchronous = false)
526
- return if ms_without_auto_index_scope
527
- primary_key = ms_primary_key_of(document)
528
- raise ArgumentError.new("Cannot index a record without a primary key") if primary_key.blank?
529
- ms_configurations.each do |options, settings|
530
- next if ms_indexing_disabled?(options)
531
- index = ms_ensure_init(options, settings)
532
- if synchronous || options[:synchronous]
533
- index.delete_document!(primary_key)
534
- else
535
- index.delete_document(primary_key)
536
- end
537
- end
538
- nil
539
- end
539
+ def ms_clear_index!(synchronous = false)
540
+ ms_configurations.each do |options, settings|
541
+ next if ms_indexing_disabled?(options)
540
542
 
541
- def ms_clear_index!(synchronous = false)
542
- ms_configurations.each do |options, settings|
543
- next if ms_indexing_disabled?(options)
544
- index = ms_ensure_init(options, settings)
545
- synchronous || options[:synchronous] ? index.delete_all_documents! : index.delete_all_documents
546
- @ms_indexes[settings] = nil
543
+ index = ms_ensure_init(options, settings)
544
+ synchronous || options[:synchronous] ? index.delete_all_documents! : index.delete_all_documents
545
+ @ms_indexes[settings] = nil
546
+ end
547
+ nil
547
548
  end
548
- nil
549
- end
550
549
 
551
- def ms_raw_search(q, params = {})
552
- index_uid = params.delete(:index) ||
553
- params.delete('index')
550
+ def ms_raw_search(q, params = {})
551
+ index_uid = params.delete(:index) || params.delete('index')
554
552
 
555
- if !meilisearch_settings.get_setting(:attributesToHighlight).nil?
556
- params[:attributesToHighlight] = meilisearch_settings.get_setting(:attributesToHighlight)
557
- end
553
+ unless meilisearch_settings.get_setting(:attributesToHighlight).nil?
554
+ params[:attributesToHighlight] = meilisearch_settings.get_setting(:attributesToHighlight)
555
+ end
558
556
 
559
- if !meilisearch_settings.get_setting(:attributesToCrop).nil?
560
- params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop)
561
- params[:cropLength] = meilisearch_settings.get_setting(:cropLength) if !meilisearch_settings.get_setting(:cropLength).nil?
562
- end
563
- index = ms_index(index_uid)
564
- index.search(q, Hash[params.map { |k,v| [k, v] }])
565
- end
557
+ unless meilisearch_settings.get_setting(:attributesToCrop).nil?
558
+ params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop)
566
559
 
567
- module AdditionalMethods
568
- def self.extended(base)
569
- class <<base
570
- alias_method :raw_answer, :ms_raw_answer unless method_defined? :raw_answer
571
- alias_method :facets_distribution, :ms_facets_distribution unless method_defined? :facets_distribution
560
+ unless meilisearch_settings.get_setting(:cropLength).nil?
561
+ params[:cropLength] = meilisearch_settings.get_setting(:cropLength)
562
+ end
572
563
  end
573
- end
574
564
 
575
- def ms_raw_answer
576
- @ms_json
565
+ index = ms_index(index_uid)
566
+ index.search(q, params.to_h { |k, v| [k, v] })
577
567
  end
578
568
 
579
- def ms_facets_distribution
580
- @ms_json['facetsDistribution']
581
- end
569
+ module AdditionalMethods
570
+ def self.extended(base)
571
+ class << base
572
+ alias_method :raw_answer, :ms_raw_answer unless method_defined? :raw_answer
573
+ alias_method :facets_distribution, :ms_facets_distribution unless method_defined? :facets_distribution
574
+ end
575
+ end
582
576
 
583
- private
584
- def ms_init_raw_answer(json)
585
- @ms_json = json
586
- end
587
- end
577
+ def ms_raw_answer
578
+ @ms_json
579
+ end
588
580
 
589
- def ms_search(q, params = {})
590
- if MeiliSearch.configuration[:pagination_backend]
581
+ def ms_facets_distribution
582
+ @ms_json['facetsDistribution']
583
+ end
591
584
 
592
- page = params[:page].nil? ? params[:page] : params[:page].to_i
593
- hits_per_page = params[:hitsPerPage].nil? ? params[:hitsPerPage] : params[:hitsPerPage].to_i
585
+ private
594
586
 
595
- params.delete(:page)
596
- params.delete(:hitsPerPage)
597
- params[:limit] = 200
587
+ def ms_init_raw_answer(json)
588
+ @ms_json = json
589
+ end
598
590
  end
599
591
 
600
- # Returns raw json hits as follows:
601
- # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "nbHits"=>1, "exhaustiveNbHits"=>false, "processingTimeMs"=>0, "query"=>"iphone"}
602
- json = ms_raw_search(q, params)
603
-
604
- # Returns the ids of the hits: 13
605
- hit_ids = json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] }
606
-
607
- # condition_key gets the primary key of the document; looks for "id" on the options
608
- if defined?(::Mongoid::Document) && self.include?(::Mongoid::Document)
609
- condition_key = ms_primary_key_method.in
610
- else
611
- condition_key = ms_primary_key_method
612
- end
592
+ def ms_search(query, params = {})
593
+ if MeiliSearch::Rails.configuration[:pagination_backend]
613
594
 
614
- # meilisearch_options[:type] refers to the Model name (e.g. Product)
615
- # results_by_id creates a hash with the primaryKey of the document (id) as the key and the document itself as the value
616
- # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil, description: "Puts even more features at your fingertips", release_date: nil>}
617
- results_by_id = meilisearch_options[:type].where(condition_key => hit_ids).index_by do |hit|
618
- ms_primary_key_of(hit)
619
- end
595
+ page = params[:page].nil? ? params[:page] : params[:page].to_i
596
+ hits_per_page = params[:hitsPerPage].nil? ? params[:hitsPerPage] : params[:hitsPerPage].to_i
620
597
 
621
- results = json['hits'].map do |hit|
598
+ params.delete(:page)
599
+ params.delete(:hitsPerPage)
600
+ params[:limit] = 200
601
+ end
622
602
 
623
- o = results_by_id[hit[ms_pk(meilisearch_options).to_s].to_s]
624
- if o
625
- o.formatted = hit['_formatted']
626
- o
603
+ # Returns raw json hits as follows:
604
+ # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "nbHits"=>1,
605
+ # "exhaustiveNbHits"=>false, "processingTimeMs"=>0, "query"=>"iphone"}
606
+ json = ms_raw_search(query, params)
607
+
608
+ # Returns the ids of the hits: 13
609
+ hit_ids = json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] }
610
+
611
+ # condition_key gets the primary key of the document; looks for "id" on the options
612
+ condition_key = if defined?(::Mongoid::Document) && include?(::Mongoid::Document)
613
+ ms_primary_key_method.in
614
+ else
615
+ ms_primary_key_method
616
+ end
617
+
618
+ # meilisearch_options[:type] refers to the Model name (e.g. Product)
619
+ # results_by_id creates a hash with the primaryKey of the document (id) as the key and doc itself as the value
620
+ # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil,
621
+ # description: "Puts even more features at your fingertips", release_date: nil>}
622
+ results_by_id = meilisearch_options[:type].where(condition_key => hit_ids).index_by do |hit|
623
+ ms_primary_key_of(hit)
627
624
  end
628
- end.compact
629
625
 
630
- total_hits = json['hits'].length
631
- hits_per_page ||= 20
632
- page ||= 1
626
+ results = json['hits'].map do |hit|
627
+ o = results_by_id[hit[ms_pk(meilisearch_options).to_s].to_s]
628
+ if o
629
+ o.formatted = hit['_formatted']
630
+ o
631
+ end
632
+ end.compact
633
633
 
634
- res = MeiliSearch::Pagination.create(results, total_hits, meilisearch_options.merge({ page: page , per_page: hits_per_page }))
635
- res.extend(AdditionalMethods)
636
- res.send(:ms_init_raw_answer, json)
637
- res
638
- end
634
+ total_hits = json['hits'].length
635
+ hits_per_page ||= 20
636
+ page ||= 1
639
637
 
640
- def ms_index(name = nil)
641
- if name
642
- ms_configurations.each do |o, s|
643
- return ms_ensure_init(o, s) if o[:index_uid].to_s == name.to_s
638
+ res = MeiliSearch::Rails::Pagination.create(results, total_hits, meilisearch_options.merge(page: page, per_page: hits_per_page))
639
+ res.extend(AdditionalMethods)
640
+ res.send(:ms_init_raw_answer, json)
641
+ res
642
+ end
643
+
644
+ def ms_index(name = nil)
645
+ if name
646
+ ms_configurations.each do |o, s|
647
+ return ms_ensure_init(o, s) if o[:index_uid].to_s == name.to_s
648
+ end
649
+ raise ArgumentError, "Invalid index name: #{name}"
644
650
  end
645
- raise ArgumentError.new("Invalid index name: #{name}")
651
+ ms_ensure_init
646
652
  end
647
- ms_ensure_init
648
- end
649
653
 
650
- def ms_index_uid(options = nil)
651
- options ||= meilisearch_options
652
- name = options[:index_uid] || model_name.to_s.gsub('::', '_')
653
- name = "#{name}_#{Rails.env.to_s}" if options[:per_environment]
654
- name
655
- end
654
+ def ms_index_uid(options = nil)
655
+ options ||= meilisearch_options
656
+ name = options[:index_uid] || model_name.to_s.gsub('::', '_')
657
+ name = "#{name}_#{::Rails.env}" if options[:per_environment]
658
+ name
659
+ end
656
660
 
657
- def ms_must_reindex?(document)
658
- # use +ms_dirty?+ method if implemented
659
- return document.send(:ms_dirty?) if (document.respond_to?(:ms_dirty?))
660
- # Loop over each index to see if a attribute used in records has changed
661
- ms_configurations.each do |options, settings|
662
- next if ms_indexing_disabled?(options)
663
- return true if ms_primary_key_changed?(document, options)
664
- settings.get_attribute_names(document).each do |k|
665
- return true if ms_attribute_changed?(document, k)
666
- # return true if !document.respond_to?(changed_method) || document.send(changed_method)
667
- end
668
- [options[:if], options[:unless]].each do |condition|
669
- case condition
670
- when nil
671
- when String, Symbol
672
- return true if ms_attribute_changed?(document, condition)
673
- else
674
- # if the :if, :unless condition is a anything else,
675
- # we have no idea whether we should reindex or not
676
- # let's always reindex then
677
- return true
661
+ def ms_must_reindex?(document)
662
+ # use +ms_dirty?+ method if implemented
663
+ return document.send(:ms_dirty?) if document.respond_to?(:ms_dirty?)
664
+
665
+ # Loop over each index to see if a attribute used in records has changed
666
+ ms_configurations.each do |options, settings|
667
+ next if ms_indexing_disabled?(options)
668
+ return true if ms_primary_key_changed?(document, options)
669
+
670
+ settings.get_attribute_names(document).each do |k|
671
+ return true if ms_attribute_changed?(document, k)
672
+ # return true if !document.respond_to?(changed_method) || document.send(changed_method)
673
+ end
674
+ [options[:if], options[:unless]].each do |condition|
675
+ case condition
676
+ when nil
677
+ when String, Symbol
678
+ return true if ms_attribute_changed?(document, condition)
679
+ else
680
+ # if the :if, :unless condition is a anything else,
681
+ # we have no idea whether we should reindex or not
682
+ # let's always reindex then
683
+ return true
684
+ end
678
685
  end
679
686
  end
687
+
688
+ # By default, we don't reindex
689
+ false
680
690
  end
681
- # By default, we don't reindex
682
- return false
683
- end
684
691
 
685
- protected
692
+ protected
686
693
 
687
- def ms_ensure_init(options = nil, settings = nil, index_settings = nil)
688
- raise ArgumentError.new('No `meilisearch` block found in your model.') if meilisearch_settings.nil?
694
+ def ms_ensure_init(options = nil, settings = nil, index_settings = nil)
695
+ raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
689
696
 
690
- @ms_indexes ||= {}
697
+ @ms_indexes ||= {}
691
698
 
692
- options ||= meilisearch_options
693
- settings ||= meilisearch_settings
699
+ options ||= meilisearch_options
700
+ settings ||= meilisearch_settings
694
701
 
695
- return @ms_indexes[settings] if @ms_indexes[settings]
702
+ return @ms_indexes[settings] if @ms_indexes[settings]
696
703
 
697
- @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options)
704
+ @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options)
698
705
 
699
- current_settings = @ms_indexes[settings].settings(:getVersion => 1) rescue nil # if the index doesn't exist
706
+ current_settings = @ms_indexes[settings].settings(getVersion: 1) rescue nil # if the index doesn't exist
700
707
 
701
- index_settings ||= settings.to_settings
702
- index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
708
+ index_settings ||= settings.to_settings
709
+ index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
703
710
 
704
- options[:check_settings] = true if options[:check_settings].nil?
711
+ options[:check_settings] = true if options[:check_settings].nil?
705
712
 
706
- if !ms_indexing_disabled?(options) && options[:check_settings] && meilisearch_settings_changed?(current_settings, index_settings)
707
- @ms_indexes[settings].update_settings(index_settings)
713
+ if !ms_indexing_disabled?(options) && options[:check_settings] && meilisearch_settings_changed?(current_settings, index_settings)
714
+ @ms_indexes[settings].update_settings(index_settings)
715
+ end
716
+
717
+ @ms_indexes[settings]
708
718
  end
709
719
 
710
- @ms_indexes[settings]
711
- end
720
+ private
712
721
 
713
- private
722
+ def ms_configurations
723
+ raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
714
724
 
715
- def ms_configurations
716
- raise ArgumentError.new('No `meilisearch` block found in your model.') if meilisearch_settings.nil?
717
- if @configurations.nil?
718
- @configurations = {}
719
- @configurations[meilisearch_options] = meilisearch_settings
720
- meilisearch_settings.additional_indexes.each do |k,v|
721
- @configurations[k] = v
725
+ if @configurations.nil?
726
+ @configurations = {}
727
+ @configurations[meilisearch_options] = meilisearch_settings
728
+ meilisearch_settings.additional_indexes.each do |k, v|
729
+ @configurations[k] = v
730
+
731
+ next unless v.additional_indexes.any?
722
732
 
723
- if v.additional_indexes.any?
724
733
  v.additional_indexes.each do |options, index|
725
734
  @configurations[options] = index
726
735
  end
727
736
  end
728
737
  end
738
+ @configurations
729
739
  end
730
- @configurations
731
- end
732
740
 
733
- def ms_primary_key_method(options = nil)
734
- options ||= meilisearch_options
735
- options[:primary_key] || options[:id] || :id
736
- end
741
+ def ms_primary_key_method(options = nil)
742
+ options ||= meilisearch_options
743
+ options[:primary_key] || options[:id] || :id
744
+ end
737
745
 
738
- def ms_primary_key_of(doc, options = nil)
739
- doc.send(ms_primary_key_method(options)).to_s
740
- end
746
+ def ms_primary_key_of(doc, options = nil)
747
+ doc.send(ms_primary_key_method(options)).to_s
748
+ end
741
749
 
742
- def ms_primary_key_changed?(doc, options = nil)
743
- changed = ms_attribute_changed?(doc, ms_primary_key_method(options))
744
- changed.nil? ? false : changed
745
- end
750
+ def ms_primary_key_changed?(doc, options = nil)
751
+ changed = ms_attribute_changed?(doc, ms_primary_key_method(options))
752
+ changed.nil? ? false : changed
753
+ end
746
754
 
747
- def ms_pk(options = nil)
748
- options[:primary_key] || MeiliSearch::IndexSettings::DEFAULT_PRIMARY_KEY
749
- end
755
+ def ms_pk(options = nil)
756
+ options[:primary_key] || MeiliSearch::Rails::IndexSettings::DEFAULT_PRIMARY_KEY
757
+ end
750
758
 
751
- def meilisearch_settings_changed?(prev, current)
752
- return true if prev.nil?
753
- current.each do |k, v|
754
- prev_v = prev[k.to_s]
755
- if v.is_a?(Array) and prev_v.is_a?(Array)
756
- # compare array of strings, avoiding symbols VS strings comparison
757
- return true if v.map { |x| x.to_s } != prev_v.map { |x| x.to_s }
758
- else
759
- return true if prev_v != v
759
+ def meilisearch_settings_changed?(prev, current)
760
+ return true if prev.nil?
761
+
762
+ current.each do |k, v|
763
+ prev_v = prev[k.to_s]
764
+ if v.is_a?(Array) && prev_v.is_a?(Array)
765
+ # compare array of strings, avoiding symbols VS strings comparison
766
+ return true if v.map(&:to_s) != prev_v.map(&:to_s)
767
+ elsif prev_v != v
768
+ return true
769
+ end
760
770
  end
771
+ false
761
772
  end
762
- false
763
- end
764
773
 
765
- def ms_full_const_get(name)
766
- list = name.split('::')
767
- list.shift if list.first.blank?
768
- obj = self
769
- list.each do |x|
770
- # This is required because const_get tries to look for constants in the
771
- # ancestor chain, but we only want constants that are HERE
772
- obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
774
+ def ms_conditional_index?(options = nil)
775
+ options ||= meilisearch_options
776
+ options[:if].present? || options[:unless].present?
773
777
  end
774
- obj
775
- end
776
778
 
777
- def ms_conditional_index?(options = nil)
778
- options ||= meilisearch_options
779
- options[:if].present? || options[:unless].present?
780
- end
779
+ def ms_indexable?(document, options = nil)
780
+ options ||= meilisearch_options
781
+ if_passes = options[:if].blank? || ms_constraint_passes?(document, options[:if])
782
+ unless_passes = options[:unless].blank? || !ms_constraint_passes?(document, options[:unless])
783
+ if_passes && unless_passes
784
+ end
781
785
 
782
- def ms_indexable?(document, options = nil)
783
- options ||= meilisearch_options
784
- if_passes = options[:if].blank? || ms_constraint_passes?(document, options[:if])
785
- unless_passes = options[:unless].blank? || !ms_constraint_passes?(document, options[:unless])
786
- if_passes && unless_passes
787
- end
786
+ def ms_constraint_passes?(document, constraint)
787
+ case constraint
788
+ when Symbol
789
+ document.send(constraint)
790
+ when String
791
+ document.send(constraint.to_sym)
792
+ when Enumerable
793
+ # All constraints must pass
794
+ constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) }
795
+ else
796
+ unless constraint.respond_to?(:call)
797
+ raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
798
+ end
788
799
 
789
- def ms_constraint_passes?(document, constraint)
790
- case constraint
791
- when Symbol
792
- document.send(constraint)
793
- when String
794
- document.send(constraint.to_sym)
795
- when Enumerable
796
- # All constraints must pass
797
- constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) }
798
- else
799
- if constraint.respond_to?(:call) # Proc
800
800
  constraint.call(document)
801
- else
802
- raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
803
801
  end
804
802
  end
805
- end
806
803
 
807
- def ms_indexing_disabled?(options = nil)
808
- options ||= meilisearch_options
809
- constraint = options[:disable_indexing] || options['disable_indexing']
810
- case constraint
811
- when nil
812
- return false
813
- when true, false
814
- return constraint
815
- when String, Symbol
816
- return send(constraint)
817
- else
818
- return constraint.call if constraint.respond_to?(:call) # Proc
819
- end
820
- raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
821
- end
804
+ def ms_indexing_disabled?(options = nil)
805
+ options ||= meilisearch_options
806
+ constraint = options[:disable_indexing] || options['disable_indexing']
807
+ case constraint
808
+ when nil
809
+ return false
810
+ when true, false
811
+ return constraint
812
+ when String, Symbol
813
+ return send(constraint)
814
+ else
815
+ return constraint.call if constraint.respond_to?(:call) # Proc
816
+ end
817
+ raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
818
+ end
822
819
 
823
- def ms_find_in_batches(batch_size, &block)
824
- if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches)
825
- find_in_batches(batch_size: batch_size, &block)
826
- elsif defined?(::Sequel) && self < Sequel::Model
827
- dataset.extension(:pagination).each_page(batch_size, &block)
828
- else
829
- # don't worry, mongoid has its own underlying cursor/streaming mechanism
830
- items = []
831
- all.each do |item|
832
- items << item
833
- if items.length % batch_size == 0
834
- yield items
835
- items = []
820
+ def ms_find_in_batches(batch_size, &block)
821
+ if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches)
822
+ find_in_batches(batch_size: batch_size, &block)
823
+ elsif defined?(::Sequel) && self < Sequel::Model
824
+ dataset.extension(:pagination).each_page(batch_size, &block)
825
+ else
826
+ # don't worry, mongoid has its own underlying cursor/streaming mechanism
827
+ items = []
828
+ all.each do |item|
829
+ items << item
830
+ if (items.length % batch_size).zero?
831
+ yield items
832
+ items = []
833
+ end
836
834
  end
835
+ yield items unless items.empty?
837
836
  end
838
- yield items unless items.empty?
839
837
  end
840
- end
841
838
 
842
- def ms_attribute_changed?(document, attr_name)
843
- if document.respond_to?("will_save_change_to_#{attr_name}?")
844
- return document.send("will_save_change_to_#{attr_name}?")
845
- end
839
+ def ms_attribute_changed?(document, attr_name)
840
+ if document.respond_to?("will_save_change_to_#{attr_name}?")
841
+ return document.send("will_save_change_to_#{attr_name}?")
842
+ end
846
843
 
847
- # We don't know if the attribute has changed, so conservatively assume it has
848
- true
844
+ # We don't know if the attribute has changed, so conservatively assume it has
845
+ true
846
+ end
849
847
  end
850
- end
851
848
 
852
- # these are the instance methods included
853
- module InstanceMethods
849
+ # these are the instance methods included
850
+ module InstanceMethods
854
851
 
855
- def self.included(base)
856
- base.instance_eval do
857
- alias_method :index!, :ms_index! unless method_defined? :index!
858
- alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
852
+ def self.included(base)
853
+ base.instance_eval do
854
+ alias_method :index!, :ms_index! unless method_defined? :index!
855
+ alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
856
+ end
859
857
  end
860
- end
861
858
 
862
- def ms_index!(synchronous = false)
863
- self.class.ms_index!(self, synchronous || ms_synchronous?)
864
- end
859
+ def ms_index!(synchronous = false)
860
+ self.class.ms_index!(self, synchronous || ms_synchronous?)
861
+ end
865
862
 
866
- def ms_remove_from_index!(synchronous = false)
867
- self.class.ms_remove_from_index!(self, synchronous || ms_synchronous?)
868
- end
863
+ def ms_remove_from_index!(synchronous = false)
864
+ self.class.ms_remove_from_index!(self, synchronous || ms_synchronous?)
865
+ end
869
866
 
870
- def ms_enqueue_remove_from_index!(synchronous)
871
- if meilisearch_options[:enqueue]
872
- meilisearch_options[:enqueue].call(self, true) unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
873
- else
874
- ms_remove_from_index!(synchronous || ms_synchronous?)
867
+ def ms_enqueue_remove_from_index!(synchronous)
868
+ if meilisearch_options[:enqueue]
869
+ unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
870
+ meilisearch_options[:enqueue].call(self, true)
871
+ end
872
+ else
873
+ ms_remove_from_index!(synchronous || ms_synchronous?)
874
+ end
875
+ end
876
+
877
+ def ms_enqueue_index!(synchronous)
878
+ if meilisearch_options[:enqueue]
879
+ unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
880
+ meilisearch_options[:enqueue].call(self, false)
881
+ end
882
+ else
883
+ ms_index!(synchronous)
884
+ end
875
885
  end
876
- end
877
886
 
878
- def ms_enqueue_index!(synchronous)
879
- if meilisearch_options[:enqueue]
880
- meilisearch_options[:enqueue].call(self, false) unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
881
- else
882
- ms_index!(synchronous)
887
+ def ms_synchronous?
888
+ @ms_synchronous
883
889
  end
884
- end
885
890
 
886
- private
891
+ private
887
892
 
888
- def ms_synchronous?
889
- @ms_synchronous == true
890
- end
893
+ def ms_mark_synchronous
894
+ @ms_synchronous = true
895
+ end
891
896
 
892
- def ms_mark_synchronous
893
- @ms_synchronous = true
894
- end
897
+ def ms_mark_for_auto_indexing
898
+ @ms_auto_indexing = true
899
+ end
895
900
 
896
- def ms_mark_for_auto_indexing
897
- @ms_auto_indexing = true
898
- end
901
+ def ms_mark_must_reindex
902
+ # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in
903
+ # a transaction, keep flag set until it is explicitly unset
904
+ @ms_must_reindex ||=
905
+ if defined?(::Sequel) && is_a?(Sequel::Model)
906
+ new? || self.class.ms_must_reindex?(self)
907
+ else
908
+ new_record? || self.class.ms_must_reindex?(self)
909
+ end
910
+ true
911
+ end
899
912
 
900
- def ms_mark_must_reindex
901
- # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in
902
- # a stransaction, keep flag set until it is explicitly unset
903
- @ms_must_reindex ||=
904
- if defined?(::Sequel) && is_a?(Sequel::Model)
905
- new? || self.class.ms_must_reindex?(self)
906
- else
907
- new_record? || self.class.ms_must_reindex?(self)
908
- end
909
- true
910
- end
913
+ def ms_perform_index_tasks
914
+ return if !@ms_auto_indexing || @ms_must_reindex == false
911
915
 
912
- def ms_perform_index_tasks
913
- return if !@ms_auto_indexing || @ms_must_reindex == false
914
- ms_enqueue_index!(ms_synchronous?)
915
- remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing)
916
- remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous)
917
- remove_instance_variable(:@ms_must_reindex) if instance_variable_defined?(:@ms_must_reindex)
916
+ ms_enqueue_index!(ms_synchronous?)
917
+ remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing)
918
+ remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous)
919
+ remove_instance_variable(:@ms_must_reindex) if instance_variable_defined?(:@ms_must_reindex)
920
+ end
918
921
  end
919
922
  end
920
923
  end