meilisearch-rails 0.3.0 → 0.5.1

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,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