meilisearch-rails 0.4.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,907 +20,905 @@ 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.to_h { |name, value| [name.to_s, value.call(document)] }
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
- client.create_index(index_uid, { primaryKey: primary_key })
241
- @index = client.index(index_uid)
242
- @raise_on_failure = raise_on_failure.nil? || raise_on_failure
243
- 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
244
240
 
245
- ::MeiliSearch::Index.instance_methods(false).each do |m|
246
- define_method(m) do |*args, &block|
247
- if m == :update_settings
248
- args[0].delete(:attributesToHighlight) if args[0][:attributesToHighlight]
249
- args[0].delete(:attributesToCrop) if args[0][:attributesToCrop]
250
- 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
251
251
  end
252
- SafeIndex.log_or_throw(m, @raise_on_failure) do
253
- @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)
254
260
  end
255
261
  end
256
- end
257
262
 
258
- # special handling of wait_for_task to handle null task_id
259
- def wait_for_task(task_uid)
260
- return if task_uid.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
261
269
 
262
- SafeIndex.log_or_throw(:wait_for_task, @raise_on_failure) do
263
- @index.wait_for_task(task_uid)
270
+ raise e
271
+ end
264
272
  end
265
- end
266
273
 
267
- # special handling of settings to avoid raising errors on 404
268
- def settings(*args)
269
- SafeIndex.log_or_throw(:settings, @raise_on_failure) do
270
- @index.settings(*args)
274
+ def self.log_or_throw(method, raise_on_failure, &block)
275
+ yield
271
276
  rescue ::MeiliSearch::ApiError => e
272
- return {} if e.code == 404 # not fatal
273
-
274
- 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
275
290
  end
276
291
  end
277
292
 
278
- def self.log_or_throw(method, raise_on_failure, &block)
279
- yield
280
- rescue ::MeiliSearch::ApiError => e
281
- raise e if raise_on_failure
282
-
283
- # log the error
284
- (Rails.logger || Logger.new($stdout)).error("[meilisearch-rails] #{e.message}")
285
- # return something
286
- case method.to_s
287
- when 'search'
288
- # some attributes are required
289
- { 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facetsDistribution' => {}, 'error' => e }
290
- else
291
- # empty answer
292
- { '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
293
311
  end
294
- end
295
- end
296
312
 
297
- # these are the class methods added when MeiliSearch is included
298
- module ClassMethods
299
- def self.extended(base)
300
- class << base
301
- alias_method :without_auto_index, :ms_without_auto_index unless method_defined? :without_auto_index
302
- alias_method :reindex!, :ms_reindex! unless method_defined? :reindex!
303
- alias_method :index_documents, :ms_index_documents unless method_defined? :index_documents
304
- alias_method :index!, :ms_index! unless method_defined? :index!
305
- alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
306
- alias_method :clear_index!, :ms_clear_index! unless method_defined? :clear_index!
307
- alias_method :search, :ms_search unless method_defined? :search
308
- alias_method :raw_search, :ms_raw_search unless method_defined? :raw_search
309
- alias_method :index, :ms_index unless method_defined? :index
310
- alias_method :index_uid, :ms_index_uid unless method_defined? :index_uid
311
- alias_method :must_reindex?, :ms_must_reindex? unless method_defined? :must_reindex?
312
- end
313
-
314
- base.cattr_accessor :meilisearch_options, :meilisearch_settings
315
- 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)
316
319
 
317
- def meilisearch(options = {}, &block)
318
- self.meilisearch_settings = IndexSettings.new(options, &block)
319
- self.meilisearch_options = {
320
- type: model_name.to_s.constantize,
321
- per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1
322
- }.merge(options)
323
-
324
- attr_accessor :formatted
325
-
326
- if options[:synchronous] == true
327
- if defined?(::Sequel) && self < Sequel::Model
328
- class_eval do
329
- copy_after_validation = instance_method(:after_validation)
330
- define_method(:after_validation) do |*args|
331
- super(*args)
332
- copy_after_validation.bind(self).call
333
- 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
334
331
  end
332
+ elsif respond_to?(:after_validation)
333
+ after_validation :ms_mark_synchronous
335
334
  end
336
- elsif respond_to?(:after_validation)
337
- after_validation :ms_mark_synchronous
338
335
  end
339
- end
340
- if options[:enqueue]
341
- raise ArgumentError, 'Cannot use a enqueue if the `synchronous` option if set' if options[:synchronous]
342
-
343
- proc = if options[:enqueue] == true
344
- proc do |record, remove|
345
- 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]}"
346
349
  end
347
- elsif options[:enqueue].respond_to?(:call)
348
- options[:enqueue]
349
- elsif options[:enqueue].is_a?(Symbol)
350
- proc { |record, remove| send(options[:enqueue], record, remove) }
351
- else
352
- raise ArgumentError, "Invalid `enqueue` option: #{options[:enqueue]}"
353
- end
354
- meilisearch_options[:enqueue] = proc do |record, remove|
355
- proc.call(record, remove) unless ms_without_auto_index_scope
356
- end
357
- end
358
- unless options[:auto_index] == false
359
- if defined?(::Sequel) && self < Sequel::Model
360
- class_eval do
361
- copy_after_validation = instance_method(:after_validation)
362
- copy_before_save = instance_method(:before_save)
363
-
364
- define_method(:after_validation) do |*args|
365
- super(*args)
366
- copy_after_validation.bind(self).call
367
- ms_mark_must_reindex
368
- end
369
-
370
- define_method(:before_save) do |*args|
371
- copy_before_save.bind(self).call
372
- ms_mark_for_auto_indexing
373
- super(*args)
374
- 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)
375
359
 
376
- sequel_version = Gem::Version.new(Sequel.version)
377
- if sequel_version >= Gem::Version.new('4.0.0') && sequel_version < Gem::Version.new('5.0.0')
378
- copy_after_commit = instance_method(:after_commit)
379
- define_method(:after_commit) do |*args|
360
+ define_method(:after_validation) do |*args|
380
361
  super(*args)
381
- copy_after_commit.bind(self).call
382
- ms_perform_index_tasks
362
+ copy_after_validation.bind(self).call
363
+ ms_mark_must_reindex
383
364
  end
384
- else
385
- copy_after_save = instance_method(:after_save)
386
- 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
387
369
  super(*args)
388
- copy_after_save.bind(self).call
389
- 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
390
378
  ms_perform_index_tasks
391
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
392
389
  end
393
390
  end
394
- end
395
- else
396
- after_validation :ms_mark_must_reindex if respond_to?(:after_validation)
397
- before_save :ms_mark_for_auto_indexing if respond_to?(:before_save)
398
- if respond_to?(:after_commit)
399
- after_commit :ms_perform_index_tasks
400
- elsif respond_to?(:after_save)
401
- 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
402
399
  end
403
400
  end
404
- end
405
- unless options[:auto_remove] == false
406
- if defined?(::Sequel) && self < Sequel::Model
407
- class_eval do
408
- copy_after_destroy = instance_method(:after_destroy)
409
-
410
- define_method(:after_destroy) do |*args|
411
- copy_after_destroy.bind(self).call
412
- ms_enqueue_remove_from_index!(ms_synchronous?)
413
- 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
414
411
  end
412
+ elsif respond_to?(:after_destroy)
413
+ after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) }
415
414
  end
416
- elsif respond_to?(:after_destroy)
417
- after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) }
418
415
  end
419
416
  end
420
- end
421
417
 
422
- def ms_without_auto_index(&block)
423
- self.ms_without_auto_index_scope = true
424
- begin
425
- yield
426
- ensure
427
- 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
428
425
  end
429
- end
430
426
 
431
- def ms_without_auto_index_scope=(value)
432
- Thread.current["ms_without_auto_index_scope_for_#{model_name}"] = value
433
- end
427
+ def ms_without_auto_index_scope=(value)
428
+ Thread.current["ms_without_auto_index_scope_for_#{model_name}"] = value
429
+ end
434
430
 
435
- def ms_without_auto_index_scope
436
- Thread.current["ms_without_auto_index_scope_for_#{model_name}"]
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_reindex!(batch_size = MeiliSearch::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false)
440
- 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
441
437
 
442
- ms_configurations.each do |options, settings|
443
- next if ms_indexing_disabled?(options)
438
+ ms_configurations.each do |options, settings|
439
+ next if ms_indexing_disabled?(options)
444
440
 
445
- index = ms_ensure_init(options, settings)
446
- last_task = nil
441
+ index = ms_ensure_init(options, settings)
442
+ last_task = nil
447
443
 
448
- ms_find_in_batches(batch_size) do |group|
449
- if ms_conditional_index?(options)
450
- # delete non-indexable documents
451
- ids = group.select { |d| !ms_indexable?(d, options) }.map { |d| ms_primary_key_of(d, options) }
452
- index.delete_documents(ids.select(&:present?))
453
- # select only indexable documents
454
- group = group.select { |d| ms_indexable?(d, options) }
455
- end
456
- documents = group.map do |d|
457
- attributes = settings.get_attributes(d)
458
- attributes = attributes.to_hash unless attributes.instance_of?(Hash)
459
- 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)
460
458
  end
461
- last_task = index.add_documents(documents)
459
+ index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous])
462
460
  end
463
- index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous])
461
+ nil
464
462
  end
465
- nil
466
- end
467
463
 
468
- def ms_set_settings(synchronous = false)
469
- ms_configurations.each do |options, settings|
470
- if options[:primary_settings] && options[:inherit]
471
- primary = options[:primary_settings].to_settings
472
- final_settings = primary.merge(settings.to_settings)
473
- else
474
- final_settings = settings.to_settings
475
- 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
476
472
 
477
- index = SafeIndex.new(ms_index_uid(options), true, options)
478
- task = index.update_settings(final_settings)
479
- index.wait_for_task(task['uid']) 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
480
477
  end
481
- end
482
478
 
483
- def ms_index_documents(documents, synchronous = false)
484
- ms_configurations.each do |options, settings|
485
- 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)
486
482
 
487
- index = ms_ensure_init(options, settings)
488
- task = index.add_documents(documents.map { |d| settings.get_attributes(d).merge ms_pk(options) => ms_primary_key_of(d, options) })
489
- index.wait_for_task(task['uid']) 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
490
487
  end
491
- end
492
488
 
493
- def ms_index!(document, synchronous = false)
494
- return if ms_without_auto_index_scope
489
+ def ms_index!(document, synchronous = false)
490
+ return if ms_without_auto_index_scope
495
491
 
496
- ms_configurations.each do |options, settings|
497
- next if ms_indexing_disabled?(options)
492
+ ms_configurations.each do |options, settings|
493
+ next if ms_indexing_disabled?(options)
498
494
 
499
- primary_key = ms_primary_key_of(document, options)
500
- index = ms_ensure_init(options, settings)
501
- if ms_indexable?(document, options)
502
- 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?
503
499
 
504
- doc = settings.get_attributes(document)
505
- doc = doc.merge ms_pk(options) => primary_key
500
+ doc = settings.get_attributes(document)
501
+ doc = doc.merge ms_pk(options) => primary_key
506
502
 
507
- if synchronous || options[:synchronous]
508
- index.add_documents!(doc)
509
- else
510
- index.add_documents(doc)
511
- end
512
- elsif ms_conditional_index?(options) && primary_key.present?
513
- # remove non-indexable documents
514
- if synchronous || options[:synchronous]
515
- index.delete_document!(primary_key)
516
- else
517
- 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
518
515
  end
519
516
  end
517
+ nil
520
518
  end
521
- nil
522
- end
523
519
 
524
- def ms_remove_from_index!(document, synchronous = false)
525
- return if ms_without_auto_index_scope
520
+ def ms_remove_from_index!(document, synchronous = false)
521
+ return if ms_without_auto_index_scope
526
522
 
527
- primary_key = ms_primary_key_of(document)
528
- 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?
529
525
 
530
- ms_configurations.each do |options, settings|
531
- next if ms_indexing_disabled?(options)
526
+ ms_configurations.each do |options, settings|
527
+ next if ms_indexing_disabled?(options)
532
528
 
533
- index = ms_ensure_init(options, settings)
534
- if synchronous || options[:synchronous]
535
- index.delete_document!(primary_key)
536
- else
537
- 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
538
535
  end
536
+ nil
539
537
  end
540
- nil
541
- end
542
-
543
- def ms_clear_index!(synchronous = false)
544
- ms_configurations.each do |options, settings|
545
- next if ms_indexing_disabled?(options)
546
-
547
- index = ms_ensure_init(options, settings)
548
- synchronous || options[:synchronous] ? index.delete_all_documents! : index.delete_all_documents
549
- @ms_indexes[settings] = nil
550
- end
551
- nil
552
- end
553
538
 
554
- def ms_raw_search(q, params = {})
555
- 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)
556
542
 
557
- unless meilisearch_settings.get_setting(:attributesToHighlight).nil?
558
- 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
559
548
  end
560
549
 
561
- unless meilisearch_settings.get_setting(:attributesToCrop).nil?
562
- params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop)
550
+ def ms_raw_search(q, params = {})
551
+ index_uid = params.delete(:index) || params.delete('index')
563
552
 
564
- unless meilisearch_settings.get_setting(:cropLength).nil?
565
- params[:cropLength] = meilisearch_settings.get_setting(:cropLength)
553
+ unless meilisearch_settings.get_setting(:attributesToHighlight).nil?
554
+ params[:attributesToHighlight] = meilisearch_settings.get_setting(:attributesToHighlight)
566
555
  end
567
- end
568
556
 
569
- index = ms_index(index_uid)
570
- index.search(q, params.to_h { |k, v| [k, v] })
571
- end
557
+ unless meilisearch_settings.get_setting(:attributesToCrop).nil?
558
+ params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop)
572
559
 
573
- module AdditionalMethods
574
- def self.extended(base)
575
- class << base
576
- alias_method :raw_answer, :ms_raw_answer unless method_defined? :raw_answer
577
- 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
578
563
  end
579
- end
580
564
 
581
- def ms_raw_answer
582
- @ms_json
565
+ index = ms_index(index_uid)
566
+ index.search(q, params.to_h { |k, v| [k, v] })
583
567
  end
584
568
 
585
- def ms_facets_distribution
586
- @ms_json['facetsDistribution']
587
- end
588
-
589
- 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
590
576
 
591
- def ms_init_raw_answer(json)
592
- @ms_json = json
593
- end
594
- end
577
+ def ms_raw_answer
578
+ @ms_json
579
+ end
595
580
 
596
- def ms_search(query, params = {})
597
- if MeiliSearch.configuration[:pagination_backend]
581
+ def ms_facets_distribution
582
+ @ms_json['facetsDistribution']
583
+ end
598
584
 
599
- page = params[:page].nil? ? params[:page] : params[:page].to_i
600
- hits_per_page = params[:hitsPerPage].nil? ? params[:hitsPerPage] : params[:hitsPerPage].to_i
585
+ private
601
586
 
602
- params.delete(:page)
603
- params.delete(:hitsPerPage)
604
- params[:limit] = 200
587
+ def ms_init_raw_answer(json)
588
+ @ms_json = json
589
+ end
605
590
  end
606
591
 
607
- # Returns raw json hits as follows:
608
- # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "nbHits"=>1,
609
- # "exhaustiveNbHits"=>false, "processingTimeMs"=>0, "query"=>"iphone"}
610
- json = ms_raw_search(query, params)
592
+ def ms_search(query, params = {})
593
+ if MeiliSearch::Rails.configuration[:pagination_backend]
611
594
 
612
- # Returns the ids of the hits: 13
613
- 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
614
597
 
615
- # condition_key gets the primary key of the document; looks for "id" on the options
616
- condition_key = if defined?(::Mongoid::Document) && include?(::Mongoid::Document)
617
- ms_primary_key_method.in
618
- else
619
- ms_primary_key_method
620
- end
621
-
622
- # meilisearch_options[:type] refers to the Model name (e.g. Product)
623
- # results_by_id creates a hash with the primaryKey of the document (id) as the key and doc itself as the value
624
- # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil,
625
- # description: "Puts even more features at your fingertips", release_date: nil>}
626
- results_by_id = meilisearch_options[:type].where(condition_key => hit_ids).index_by do |hit|
627
- ms_primary_key_of(hit)
628
- end
598
+ params.delete(:page)
599
+ params.delete(:hitsPerPage)
600
+ params[:limit] = 200
601
+ end
629
602
 
630
- results = json['hits'].map do |hit|
631
- o = results_by_id[hit[ms_pk(meilisearch_options).to_s].to_s]
632
- if o
633
- o.formatted = hit['_formatted']
634
- 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)
635
624
  end
636
- end.compact
637
625
 
638
- total_hits = json['hits'].length
639
- hits_per_page ||= 20
640
- 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
641
633
 
642
- res = MeiliSearch::Pagination.create(results, total_hits, meilisearch_options.merge(page: page, per_page: hits_per_page))
643
- res.extend(AdditionalMethods)
644
- res.send(:ms_init_raw_answer, json)
645
- res
646
- end
634
+ total_hits = json['hits'].length
635
+ hits_per_page ||= 20
636
+ page ||= 1
647
637
 
648
- def ms_index(name = nil)
649
- if name
650
- ms_configurations.each do |o, s|
651
- 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}"
652
650
  end
653
- raise ArgumentError, "Invalid index name: #{name}"
651
+ ms_ensure_init
654
652
  end
655
- ms_ensure_init
656
- end
657
653
 
658
- def ms_index_uid(options = nil)
659
- options ||= meilisearch_options
660
- name = options[:index_uid] || model_name.to_s.gsub('::', '_')
661
- name = "#{name}_#{Rails.env}" if options[:per_environment]
662
- name
663
- 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
664
660
 
665
- def ms_must_reindex?(document)
666
- # use +ms_dirty?+ method if implemented
667
- 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?)
668
664
 
669
- # Loop over each index to see if a attribute used in records has changed
670
- ms_configurations.each do |options, settings|
671
- next if ms_indexing_disabled?(options)
672
- 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)
673
669
 
674
- settings.get_attribute_names(document).each do |k|
675
- return true if ms_attribute_changed?(document, k)
676
- # return true if !document.respond_to?(changed_method) || document.send(changed_method)
677
- end
678
- [options[:if], options[:unless]].each do |condition|
679
- case condition
680
- when nil
681
- when String, Symbol
682
- return true if ms_attribute_changed?(document, condition)
683
- else
684
- # if the :if, :unless condition is a anything else,
685
- # we have no idea whether we should reindex or not
686
- # let's always reindex then
687
- 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
+ return false
678
+ when String, Symbol
679
+ return true if ms_attribute_changed?(document, condition)
680
+ else
681
+ # if the :if, :unless condition is a anything else,
682
+ # we have no idea whether we should reindex or not
683
+ # let's always reindex then
684
+ return true
685
+ end
688
686
  end
689
687
  end
688
+
689
+ # By default, we don't reindex
690
+ false
690
691
  end
691
692
 
692
- # By default, we don't reindex
693
- false
694
- end
693
+ protected
695
694
 
696
- protected
695
+ def ms_ensure_init(options = nil, settings = nil, index_settings = nil)
696
+ raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
697
697
 
698
- def ms_ensure_init(options = nil, settings = nil, index_settings = nil)
699
- raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
698
+ @ms_indexes ||= {}
700
699
 
701
- @ms_indexes ||= {}
700
+ options ||= meilisearch_options
701
+ settings ||= meilisearch_settings
702
702
 
703
- options ||= meilisearch_options
704
- settings ||= meilisearch_settings
703
+ return @ms_indexes[settings] if @ms_indexes[settings]
705
704
 
706
- return @ms_indexes[settings] if @ms_indexes[settings]
705
+ @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options)
707
706
 
708
- @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options)
707
+ current_settings = @ms_indexes[settings].settings(getVersion: 1) rescue nil # if the index doesn't exist
709
708
 
710
- current_settings = @ms_indexes[settings].settings(getVersion: 1) rescue nil # if the index doesn't exist
709
+ index_settings ||= settings.to_settings
710
+ index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
711
711
 
712
- index_settings ||= settings.to_settings
713
- index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
712
+ options[:check_settings] = true if options[:check_settings].nil?
714
713
 
715
- options[:check_settings] = true if options[:check_settings].nil?
714
+ if !ms_indexing_disabled?(options) && options[:check_settings] && meilisearch_settings_changed?(current_settings, index_settings)
715
+ @ms_indexes[settings].update_settings(index_settings)
716
+ end
716
717
 
717
- if !ms_indexing_disabled?(options) && options[:check_settings] && meilisearch_settings_changed?(current_settings, index_settings)
718
- @ms_indexes[settings].update_settings(index_settings)
718
+ @ms_indexes[settings]
719
719
  end
720
720
 
721
- @ms_indexes[settings]
722
- end
723
-
724
- private
721
+ private
725
722
 
726
- def ms_configurations
727
- raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
723
+ def ms_configurations
724
+ raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
728
725
 
729
- if @configurations.nil?
730
- @configurations = {}
731
- @configurations[meilisearch_options] = meilisearch_settings
732
- meilisearch_settings.additional_indexes.each do |k, v|
733
- @configurations[k] = v
726
+ if @configurations.nil?
727
+ @configurations = {}
728
+ @configurations[meilisearch_options] = meilisearch_settings
729
+ meilisearch_settings.additional_indexes.each do |k, v|
730
+ @configurations[k] = v
734
731
 
735
- next unless v.additional_indexes.any?
732
+ next unless v.additional_indexes.any?
736
733
 
737
- v.additional_indexes.each do |options, index|
738
- @configurations[options] = index
734
+ v.additional_indexes.each do |options, index|
735
+ @configurations[options] = index
736
+ end
739
737
  end
740
738
  end
739
+ @configurations
741
740
  end
742
- @configurations
743
- end
744
741
 
745
- def ms_primary_key_method(options = nil)
746
- options ||= meilisearch_options
747
- options[:primary_key] || options[:id] || :id
748
- end
742
+ def ms_primary_key_method(options = nil)
743
+ options ||= meilisearch_options
744
+ options[:primary_key] || options[:id] || :id
745
+ end
749
746
 
750
- def ms_primary_key_of(doc, options = nil)
751
- doc.send(ms_primary_key_method(options)).to_s
752
- end
747
+ def ms_primary_key_of(doc, options = nil)
748
+ doc.send(ms_primary_key_method(options)).to_s
749
+ end
753
750
 
754
- def ms_primary_key_changed?(doc, options = nil)
755
- changed = ms_attribute_changed?(doc, ms_primary_key_method(options))
756
- changed.nil? ? false : changed
757
- end
751
+ def ms_primary_key_changed?(doc, options = nil)
752
+ changed = ms_attribute_changed?(doc, ms_primary_key_method(options))
753
+ changed.nil? ? false : changed
754
+ end
758
755
 
759
- def ms_pk(options = nil)
760
- options[:primary_key] || MeiliSearch::IndexSettings::DEFAULT_PRIMARY_KEY
761
- end
756
+ def ms_pk(options = nil)
757
+ options[:primary_key] || MeiliSearch::Rails::IndexSettings::DEFAULT_PRIMARY_KEY
758
+ end
762
759
 
763
- def meilisearch_settings_changed?(prev, current)
764
- return true if prev.nil?
760
+ def meilisearch_settings_changed?(prev, current)
761
+ return true if prev.nil?
765
762
 
766
- current.each do |k, v|
767
- prev_v = prev[k.to_s]
768
- if v.is_a?(Array) && prev_v.is_a?(Array)
769
- # compare array of strings, avoiding symbols VS strings comparison
770
- return true if v.map(&:to_s) != prev_v.map(&:to_s)
771
- elsif prev_v != v
772
- return true
763
+ current.each do |k, v|
764
+ prev_v = prev[k.to_s]
765
+ if v.is_a?(Array) && prev_v.is_a?(Array)
766
+ # compare array of strings, avoiding symbols VS strings comparison
767
+ return true if v.map(&:to_s) != prev_v.map(&:to_s)
768
+ elsif prev_v != v
769
+ return true
770
+ end
773
771
  end
772
+ false
774
773
  end
775
- false
776
- end
777
774
 
778
- def ms_conditional_index?(options = nil)
779
- options ||= meilisearch_options
780
- options[:if].present? || options[:unless].present?
781
- end
775
+ def ms_conditional_index?(options = nil)
776
+ options ||= meilisearch_options
777
+ options[:if].present? || options[:unless].present?
778
+ end
782
779
 
783
- def ms_indexable?(document, options = nil)
784
- options ||= meilisearch_options
785
- if_passes = options[:if].blank? || ms_constraint_passes?(document, options[:if])
786
- unless_passes = options[:unless].blank? || !ms_constraint_passes?(document, options[:unless])
787
- if_passes && unless_passes
788
- end
780
+ def ms_indexable?(document, options = nil)
781
+ options ||= meilisearch_options
782
+ if_passes = options[:if].blank? || ms_constraint_passes?(document, options[:if])
783
+ unless_passes = options[:unless].blank? || !ms_constraint_passes?(document, options[:unless])
784
+ if_passes && unless_passes
785
+ end
789
786
 
790
- def ms_constraint_passes?(document, constraint)
791
- case constraint
792
- when Symbol
793
- document.send(constraint)
794
- when String
795
- document.send(constraint.to_sym)
796
- when Enumerable
797
- # All constraints must pass
798
- constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) }
799
- else
800
- unless constraint.respond_to?(:call)
801
- raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
802
- end
787
+ def ms_constraint_passes?(document, constraint)
788
+ case constraint
789
+ when Symbol
790
+ document.send(constraint)
791
+ when String
792
+ document.send(constraint.to_sym)
793
+ when Enumerable
794
+ # All constraints must pass
795
+ constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) }
796
+ else
797
+ unless constraint.respond_to?(:call)
798
+ raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
799
+ end
803
800
 
804
- constraint.call(document)
801
+ constraint.call(document)
802
+ end
805
803
  end
806
- end
807
804
 
808
- def ms_indexing_disabled?(options = nil)
809
- options ||= meilisearch_options
810
- constraint = options[:disable_indexing] || options['disable_indexing']
811
- case constraint
812
- when nil
813
- return false
814
- when true, false
815
- return constraint
816
- when String, Symbol
817
- return send(constraint)
818
- else
819
- return constraint.call if constraint.respond_to?(:call) # Proc
820
- end
821
- raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
822
- end
805
+ def ms_indexing_disabled?(options = nil)
806
+ options ||= meilisearch_options
807
+ constraint = options[:disable_indexing] || options['disable_indexing']
808
+ case constraint
809
+ when nil
810
+ return false
811
+ when true, false
812
+ return constraint
813
+ when String, Symbol
814
+ return send(constraint)
815
+ else
816
+ return constraint.call if constraint.respond_to?(:call) # Proc
817
+ end
818
+ raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
819
+ end
823
820
 
824
- def ms_find_in_batches(batch_size, &block)
825
- if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches)
826
- find_in_batches(batch_size: batch_size, &block)
827
- elsif defined?(::Sequel) && self < Sequel::Model
828
- dataset.extension(:pagination).each_page(batch_size, &block)
829
- else
830
- # don't worry, mongoid has its own underlying cursor/streaming mechanism
831
- items = []
832
- all.each do |item|
833
- items << item
834
- if (items.length % batch_size).zero?
835
- yield items
836
- items = []
821
+ def ms_find_in_batches(batch_size, &block)
822
+ if (defined?(::ActiveRecord) && ancestors.include?(::ActiveRecord::Base)) || respond_to?(:find_in_batches)
823
+ find_in_batches(batch_size: batch_size, &block)
824
+ elsif defined?(::Sequel) && self < Sequel::Model
825
+ dataset.extension(:pagination).each_page(batch_size, &block)
826
+ else
827
+ # don't worry, mongoid has its own underlying cursor/streaming mechanism
828
+ items = []
829
+ all.each do |item|
830
+ items << item
831
+ if (items.length % batch_size).zero?
832
+ yield items
833
+ items = []
834
+ end
837
835
  end
836
+ yield items unless items.empty?
838
837
  end
839
- yield items unless items.empty?
840
838
  end
841
- end
842
839
 
843
- def ms_attribute_changed?(document, attr_name)
844
- if document.respond_to?("will_save_change_to_#{attr_name}?")
845
- return document.send("will_save_change_to_#{attr_name}?")
846
- end
840
+ def ms_attribute_changed?(document, attr_name)
841
+ if document.respond_to?("will_save_change_to_#{attr_name}?")
842
+ return document.send("will_save_change_to_#{attr_name}?")
843
+ end
847
844
 
848
- # We don't know if the attribute has changed, so conservatively assume it has
849
- true
845
+ # We don't know if the attribute has changed, so conservatively assume it has
846
+ true
847
+ end
850
848
  end
851
- end
852
849
 
853
- # these are the instance methods included
854
- module InstanceMethods
850
+ # these are the instance methods included
851
+ module InstanceMethods
855
852
 
856
- def self.included(base)
857
- base.instance_eval do
858
- alias_method :index!, :ms_index! unless method_defined? :index!
859
- alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
853
+ def self.included(base)
854
+ base.instance_eval do
855
+ alias_method :index!, :ms_index! unless method_defined? :index!
856
+ alias_method :remove_from_index!, :ms_remove_from_index! unless method_defined? :remove_from_index!
857
+ end
860
858
  end
861
- end
862
859
 
863
- def ms_index!(synchronous = false)
864
- self.class.ms_index!(self, synchronous || ms_synchronous?)
865
- end
860
+ def ms_index!(synchronous = false)
861
+ self.class.ms_index!(self, synchronous || ms_synchronous?)
862
+ end
866
863
 
867
- def ms_remove_from_index!(synchronous = false)
868
- self.class.ms_remove_from_index!(self, synchronous || ms_synchronous?)
869
- end
864
+ def ms_remove_from_index!(synchronous = false)
865
+ self.class.ms_remove_from_index!(self, synchronous || ms_synchronous?)
866
+ end
870
867
 
871
- def ms_enqueue_remove_from_index!(synchronous)
872
- if meilisearch_options[:enqueue]
873
- unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
874
- meilisearch_options[:enqueue].call(self, true)
868
+ def ms_enqueue_remove_from_index!(synchronous)
869
+ if meilisearch_options[:enqueue]
870
+ unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
871
+ meilisearch_options[:enqueue].call(self, true)
872
+ end
873
+ else
874
+ ms_remove_from_index!(synchronous || ms_synchronous?)
875
875
  end
876
- else
877
- ms_remove_from_index!(synchronous || ms_synchronous?)
878
876
  end
879
- end
880
877
 
881
- def ms_enqueue_index!(synchronous)
882
- if meilisearch_options[:enqueue]
883
- unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
884
- meilisearch_options[:enqueue].call(self, false)
878
+ def ms_enqueue_index!(synchronous)
879
+ if meilisearch_options[:enqueue]
880
+ unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
881
+ meilisearch_options[:enqueue].call(self, false)
882
+ end
883
+ else
884
+ ms_index!(synchronous)
885
885
  end
886
- else
887
- ms_index!(synchronous)
888
886
  end
889
- end
890
887
 
891
- def ms_synchronous?
892
- @ms_synchronous
893
- end
888
+ def ms_synchronous?
889
+ @ms_synchronous
890
+ end
894
891
 
895
- private
892
+ private
896
893
 
897
- def ms_mark_synchronous
898
- @ms_synchronous = true
899
- end
894
+ def ms_mark_synchronous
895
+ @ms_synchronous = true
896
+ end
900
897
 
901
- def ms_mark_for_auto_indexing
902
- @ms_auto_indexing = true
903
- end
898
+ def ms_mark_for_auto_indexing
899
+ @ms_auto_indexing = true
900
+ end
904
901
 
905
- def ms_mark_must_reindex
906
- # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in
907
- # a transaction, keep flag set until it is explicitly unset
908
- @ms_must_reindex ||=
909
- if defined?(::Sequel) && is_a?(Sequel::Model)
910
- new? || self.class.ms_must_reindex?(self)
911
- else
912
- new_record? || self.class.ms_must_reindex?(self)
913
- end
914
- true
915
- end
902
+ def ms_mark_must_reindex
903
+ # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in
904
+ # a transaction, keep flag set until it is explicitly unset
905
+ @ms_must_reindex ||=
906
+ if defined?(::Sequel) && is_a?(Sequel::Model)
907
+ new? || self.class.ms_must_reindex?(self)
908
+ else
909
+ new_record? || self.class.ms_must_reindex?(self)
910
+ end
911
+ true
912
+ end
916
913
 
917
- def ms_perform_index_tasks
918
- return if !@ms_auto_indexing || @ms_must_reindex == false
914
+ def ms_perform_index_tasks
915
+ return if !@ms_auto_indexing || @ms_must_reindex == false
919
916
 
920
- ms_enqueue_index!(ms_synchronous?)
921
- remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing)
922
- remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous)
923
- remove_instance_variable(:@ms_must_reindex) if instance_variable_defined?(:@ms_must_reindex)
917
+ ms_enqueue_index!(ms_synchronous?)
918
+ remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing)
919
+ remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous)
920
+ remove_instance_variable(:@ms_must_reindex) if instance_variable_defined?(:@ms_must_reindex)
921
+ end
924
922
  end
925
923
  end
926
924
  end