meilisearch-rails 0.3.0 → 0.5.1

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