meilisearch-rails 0.4.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
1
  require 'meilisearch'
2
2
 
3
- require 'meilisearch/version'
4
- require 'meilisearch/utilities'
5
- require 'meilisearch/errors'
3
+ require 'meilisearch/rails/version'
4
+ require 'meilisearch/rails/utilities'
5
+ require 'meilisearch/rails/errors'
6
6
 
7
7
  if defined? Rails
8
8
  begin
9
- require 'meilisearch/railtie'
9
+ require 'meilisearch/rails/railtie'
10
10
  rescue LoadError
11
11
  end
12
12
  end
@@ -20,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