meilisearch-rails 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,7 @@ require 'meilisearch'
2
2
 
3
3
  require 'meilisearch/version'
4
4
  require 'meilisearch/utilities'
5
+ require 'meilisearch/errors'
5
6
 
6
7
  if defined? Rails
7
8
  begin
@@ -19,11 +20,6 @@ end
19
20
  require 'logger'
20
21
 
21
22
  module MeiliSearch
22
-
23
- class NotConfigured < StandardError; end
24
- class BadConfiguration < StandardError; end
25
- class NoBlockGiven < StandardError; end
26
-
27
23
  autoload :Configuration, 'meilisearch/configuration'
28
24
  extend Configuration
29
25
 
@@ -42,27 +38,26 @@ module MeiliSearch
42
38
  include InstanceMethods
43
39
  end
44
40
  end
45
-
46
41
  end
47
42
 
48
43
  class IndexSettings
49
44
  DEFAULT_BATCH_SIZE = 1000
50
45
 
51
- DEFAULT_PRIMARY_KEY = 'id'
46
+ DEFAULT_PRIMARY_KEY = 'id'.freeze
52
47
 
53
48
  # MeiliSearch settings
54
- OPTIONS = [
55
- :searchableAttributes,
56
- :attributesForFaceting,
57
- :displayedAttributes,
58
- :distinctAttribute,
59
- :synonyms,
60
- :stopWords,
61
- :rankingRules,
62
- :attributesToHighlight,
63
- :attributesToCrop,
64
- :cropLength,
65
- ]
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
66
61
 
67
62
  OPTIONS.each do |option|
68
63
  define_method option do |value|
@@ -84,40 +79,42 @@ module MeiliSearch
84
79
  end
85
80
 
86
81
  def attribute(*names, &block)
87
- raise ArgumentError.new('Cannot pass multiple attribute names if block given') if block_given? and names.length > 1
82
+ raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1)
83
+
88
84
  @attributes ||= {}
89
85
  names.flatten.each do |name|
90
- @attributes[name.to_s] = block_given? ? Proc.new { |d| d.instance_eval(&block) } : Proc.new { |d| d.send(name) }
86
+ @attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) }
91
87
  end
92
88
  end
93
- alias :attributes :attribute
89
+ alias attributes attribute
94
90
 
95
91
  def add_attribute(*names, &block)
96
- raise ArgumentError.new('Cannot pass multiple attribute names if block given') if block_given? and names.length > 1
92
+ raise ArgumentError, 'Cannot pass multiple attribute names if block given' if block_given? && (names.length > 1)
93
+
97
94
  @additional_attributes ||= {}
98
95
  names.each do |name|
99
- @additional_attributes[name.to_s] = block_given? ? Proc.new { |d| d.instance_eval(&block) } : Proc.new { |d| d.send(name) }
96
+ @additional_attributes[name.to_s] = block_given? ? proc { |d| d.instance_eval(&block) } : proc { |d| d.send(name) }
100
97
  end
101
98
  end
102
- alias :add_attributes :add_attribute
99
+ alias add_attributes add_attribute
103
100
 
104
- def is_mongoid?(document)
101
+ def mongoid?(document)
105
102
  defined?(::Mongoid::Document) && document.class.include?(::Mongoid::Document)
106
103
  end
107
104
 
108
- def is_sequel?(document)
105
+ def sequel?(document)
109
106
  defined?(::Sequel) && document.class < ::Sequel::Model
110
107
  end
111
108
 
112
- def is_active_record?(document)
113
- !is_mongoid?(document) && !is_sequel?(document)
109
+ def active_record?(document)
110
+ !mongoid?(document) && !sequel?(document)
114
111
  end
115
112
 
116
113
  def get_default_attributes(document)
117
- if is_mongoid?(document)
114
+ if mongoid?(document)
118
115
  # work-around mongoid 2.4's unscoped method, not accepting a block
119
116
  document.attributes
120
- elsif is_sequel?(document)
117
+ elsif sequel?(document)
121
118
  document.to_hash
122
119
  else
123
120
  document.class.unscoped do
@@ -132,7 +129,7 @@ module MeiliSearch
132
129
 
133
130
  def attributes_to_hash(attributes, document)
134
131
  if attributes
135
- Hash[attributes.map { |name, value| [name.to_s, value.call(document) ] }]
132
+ attributes.to_h { |name, value| [name.to_s, value.call(document)] }
136
133
  else
137
134
  {}
138
135
  end
@@ -141,22 +138,18 @@ module MeiliSearch
141
138
  def get_attributes(document)
142
139
  # If a serializer is set, we ignore attributes
143
140
  # everything should be done via the serializer
144
- if not @serializer.nil?
141
+ if !@serializer.nil?
145
142
  attributes = @serializer.new(document).attributes
146
- else
147
- if @attributes.nil? || @attributes.length == 0
143
+ elsif @attributes.blank?
144
+ attributes = get_default_attributes(document)
148
145
  # no `attribute ...` have been configured, use the default attributes of the model
149
- attributes = get_default_attributes(document)
150
- else
146
+ elsif active_record?(document)
151
147
  # at least 1 `attribute ...` has been configured, therefore use ONLY the one configured
152
- if is_active_record?(document)
153
- document.class.unscoped do
154
- attributes = attributes_to_hash(@attributes, document)
155
- end
156
- else
148
+ document.class.unscoped do
157
149
  attributes = attributes_to_hash(@attributes, document)
158
150
  end
159
- end
151
+ else
152
+ attributes = attributes_to_hash(@attributes, document)
160
153
  end
161
154
 
162
155
  attributes.merge!(attributes_to_hash(@additional_attributes, document)) if @additional_attributes
@@ -171,36 +164,34 @@ module MeiliSearch
171
164
  attributes = sanitize_attributes(attributes, sanitizer)
172
165
  end
173
166
 
174
- if @options[:force_utf8_encoding]
175
- attributes = encode_attributes(attributes)
176
- end
167
+ attributes = encode_attributes(attributes) if @options[:force_utf8_encoding]
177
168
 
178
169
  attributes
179
170
  end
180
171
 
181
- def sanitize_attributes(v, sanitizer)
182
- case v
172
+ def sanitize_attributes(value, sanitizer)
173
+ case value
183
174
  when String
184
- sanitizer.sanitize(v)
175
+ sanitizer.sanitize(value)
185
176
  when Hash
186
- v.each { |key, value| v[key] = sanitize_attributes(value, sanitizer) }
177
+ value.each { |key, val| value[key] = sanitize_attributes(val, sanitizer) }
187
178
  when Array
188
- v.map { |x| sanitize_attributes(x, sanitizer) }
179
+ value.map { |item| sanitize_attributes(item, sanitizer) }
189
180
  else
190
- v
181
+ value
191
182
  end
192
183
  end
193
184
 
194
- def encode_attributes(v)
195
- case v
185
+ def encode_attributes(value)
186
+ case value
196
187
  when String
197
- v.force_encoding('utf-8')
188
+ value.force_encoding('utf-8')
198
189
  when Hash
199
- v.each { |key, value| v[key] = encode_attributes(value) }
190
+ value.each { |key, val| value[key] = encode_attributes(val) }
200
191
  when Array
201
- v.map { |x| encode_attributes(x) }
192
+ value.map { |x| encode_attributes(x) }
202
193
  else
203
- v
194
+ value
204
195
  end
205
196
  end
206
197
 
@@ -212,14 +203,17 @@ module MeiliSearch
212
203
  settings = {}
213
204
  OPTIONS.each do |k|
214
205
  v = get_setting(k)
215
- settings[k] = v if !v.nil?
206
+ settings[k] = v unless v.nil?
216
207
  end
217
208
  settings
218
209
  end
219
210
 
220
211
  def add_index(index_uid, options = {}, &block)
221
- raise ArgumentError.new('No block given') if !block_given?
222
- raise ArgumentError.new('Options auto_index and auto_remove cannot be set on nested indexes') if options[:auto_index] || options[:auto_remove]
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'
215
+ end
216
+
223
217
  @additional_indexes ||= {}
224
218
  options[:index_uid] = index_uid
225
219
  @additional_indexes[options] = IndexSettings.new(options, &block)
@@ -242,70 +236,68 @@ module MeiliSearch
242
236
  class SafeIndex
243
237
  def initialize(index_uid, raise_on_failure, options)
244
238
  client = MeiliSearch.client
245
- primary_key = options[:primary_key] || MeiliSearch::IndexSettings::DEFAULT_PRIMARY_KEY
246
- @index = client.get_or_create_index(index_uid, { primaryKey: primary_key })
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)
247
242
  @raise_on_failure = raise_on_failure.nil? || raise_on_failure
248
243
  end
249
244
 
250
245
  ::MeiliSearch::Index.instance_methods(false).each do |m|
251
- define_method(m) do |*args, &block|
252
- if (m == :update_settings)
253
- args[0].delete(:attributesToHighlight) if args[0][:attributesToHighlight]
254
- args[0].delete(:attributesToCrop) if args[0][:attributesToCrop]
255
- args[0].delete(:cropLength) if args[0][:cropLength]
256
- end
257
- SafeIndex.log_or_throw(m, @raise_on_failure) do
258
- @index.send(m, *args, &block)
259
- end
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]
260
251
  end
252
+ SafeIndex.log_or_throw(m, @raise_on_failure) do
253
+ @index.send(m, *args, &block)
254
+ end
255
+ end
261
256
  end
262
257
 
263
- # special handling of wait_for_pending_update to handle null task_id
264
- def wait_for_pending_update(update_id)
265
- return if update_id.nil? && !@raise_on_failure # ok
266
- SafeIndex.log_or_throw(:wait_for_pending_update, @raise_on_failure) do
267
- @index.wait_for_pending_update(update_id)
258
+ # 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
261
+
262
+ SafeIndex.log_or_throw(:wait_for_task, @raise_on_failure) do
263
+ @index.wait_for_task(task_uid)
268
264
  end
269
265
  end
270
266
 
271
267
  # special handling of settings to avoid raising errors on 404
272
268
  def settings(*args)
273
269
  SafeIndex.log_or_throw(:settings, @raise_on_failure) do
274
- begin
275
- @index.settings(*args)
276
- rescue ::MeiliSearch::ApiError => e
277
- return {} if e.code == 404 # not fatal
278
- raise e
279
- end
270
+ @index.settings(*args)
271
+ rescue ::MeiliSearch::ApiError => e
272
+ return {} if e.code == 404 # not fatal
273
+
274
+ raise e
280
275
  end
281
276
  end
282
277
 
283
- private
284
278
  def self.log_or_throw(method, raise_on_failure, &block)
285
- begin
286
- yield
287
- rescue ::MeiliSearch::ApiError => e
288
- raise e if raise_on_failure
289
- # log the error
290
- (Rails.logger || Logger.new(STDOUT)).error("[meilisearch-rails] #{e.message}")
291
- # return something
292
- case method.to_s
293
- when 'search'
294
- # some attributes are required
295
- { 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facetsDistribution' => {}, 'error' => e }
296
- else
297
- # empty answer
298
- { 'error' => e }
299
- end
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 }
300
293
  end
301
294
  end
302
295
  end
303
296
 
304
297
  # these are the class methods added when MeiliSearch is included
305
298
  module ClassMethods
306
-
307
299
  def self.extended(base)
308
- class <<base
300
+ class << base
309
301
  alias_method :without_auto_index, :ms_without_auto_index unless method_defined? :without_auto_index
310
302
  alias_method :reindex!, :ms_reindex! unless method_defined? :reindex!
311
303
  alias_method :index_documents, :ms_index_documents unless method_defined? :index_documents
@@ -324,7 +316,10 @@ module MeiliSearch
324
316
 
325
317
  def meilisearch(options = {}, &block)
326
318
  self.meilisearch_settings = IndexSettings.new(options, &block)
327
- self.meilisearch_options = { type: ms_full_const_get(model_name.to_s), per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1 }.merge(options)
319
+ self.meilisearch_options = {
320
+ type: ms_full_const_get(model_name.to_s),
321
+ per_page: meilisearch_settings.get_setting(:hitsPerPage) || 20, page: 1
322
+ }.merge(options)
328
323
 
329
324
  attr_accessor :formatted
330
325
 
@@ -338,24 +333,25 @@ module MeiliSearch
338
333
  ms_mark_synchronous
339
334
  end
340
335
  end
341
- else
342
- after_validation :ms_mark_synchronous if respond_to?(:after_validation)
336
+ elsif respond_to?(:after_validation)
337
+ after_validation :ms_mark_synchronous
343
338
  end
344
339
  end
345
340
  if options[:enqueue]
346
- raise ArgumentError.new("Cannot use a enqueue if the `synchronous` option if set") if options[:synchronous]
341
+ raise ArgumentError, 'Cannot use a enqueue if the `synchronous` option if set' if options[:synchronous]
342
+
347
343
  proc = if options[:enqueue] == true
348
- Proc.new do |record, remove|
349
- MSJob.perform_later(record, remove ? 'ms_remove_from_index!' : 'ms_index!')
350
- end
351
- elsif options[:enqueue].respond_to?(:call)
352
- options[:enqueue]
353
- elsif options[:enqueue].is_a?(Symbol)
354
- Proc.new { |record, remove| self.send(options[:enqueue], record, remove) }
355
- else
356
- raise ArgumentError.new("Invalid `enqueue` option: #{options[:enqueue]}")
357
- end
358
- meilisearch_options[:enqueue] = Proc.new do |record, remove|
344
+ proc do |record, remove|
345
+ MSJob.perform_later(record, remove ? 'ms_remove_from_index!' : 'ms_index!')
346
+ 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|
359
355
  proc.call(record, remove) unless ms_without_auto_index_scope
360
356
  end
361
357
  end
@@ -390,7 +386,7 @@ module MeiliSearch
390
386
  define_method(:after_save) do |*args|
391
387
  super(*args)
392
388
  copy_after_save.bind(self).call
393
- self.db.after_commit do
389
+ db.after_commit do
394
390
  ms_perform_index_tasks
395
391
  end
396
392
  end
@@ -417,8 +413,8 @@ module MeiliSearch
417
413
  super(*args)
418
414
  end
419
415
  end
420
- else
421
- after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) } if respond_to?(:after_destroy)
416
+ elsif respond_to?(:after_destroy)
417
+ after_destroy { |searchable| searchable.ms_enqueue_remove_from_index!(ms_synchronous?) }
422
418
  end
423
419
  end
424
420
  end
@@ -433,38 +429,38 @@ module MeiliSearch
433
429
  end
434
430
 
435
431
  def ms_without_auto_index_scope=(value)
436
- Thread.current["ms_without_auto_index_scope_for_#{self.model_name}"] = value
432
+ Thread.current["ms_without_auto_index_scope_for_#{model_name}"] = value
437
433
  end
438
434
 
439
435
  def ms_without_auto_index_scope
440
- Thread.current["ms_without_auto_index_scope_for_#{self.model_name}"]
436
+ Thread.current["ms_without_auto_index_scope_for_#{model_name}"]
441
437
  end
442
438
 
443
439
  def ms_reindex!(batch_size = MeiliSearch::IndexSettings::DEFAULT_BATCH_SIZE, synchronous = false)
444
440
  return if ms_without_auto_index_scope
441
+
445
442
  ms_configurations.each do |options, settings|
446
443
  next if ms_indexing_disabled?(options)
444
+
447
445
  index = ms_ensure_init(options, settings)
448
- last_update = nil
446
+ last_task = nil
449
447
 
450
448
  ms_find_in_batches(batch_size) do |group|
451
449
  if ms_conditional_index?(options)
452
450
  # delete non-indexable documents
453
451
  ids = group.select { |d| !ms_indexable?(d, options) }.map { |d| ms_primary_key_of(d, options) }
454
- index.delete_documents(ids.select { |id| !id.blank? })
452
+ index.delete_documents(ids.select(&:present?))
455
453
  # select only indexable documents
456
454
  group = group.select { |d| ms_indexable?(d, options) }
457
455
  end
458
456
  documents = group.map do |d|
459
457
  attributes = settings.get_attributes(d)
460
- unless attributes.class == Hash
461
- attributes = attributes.to_hash
462
- end
463
- attributes.merge ms_pk(options) => ms_primary_key_of(d, options)
458
+ attributes = attributes.to_hash unless attributes.instance_of?(Hash)
459
+ attributes.merge ms_pk(options) => ms_primary_key_of(d, options)
464
460
  end
465
- last_update= index.add_documents(documents)
461
+ last_task = index.add_documents(documents)
466
462
  end
467
- index.wait_for_pending_update(last_update["updateId"]) if last_update and (synchronous || options[:synchronous])
463
+ index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous])
468
464
  end
469
465
  nil
470
466
  end
@@ -479,38 +475,41 @@ module MeiliSearch
479
475
  end
480
476
 
481
477
  index = SafeIndex.new(ms_index_uid(options), true, options)
482
- update = index.update_settings(final_settings)
483
- index.wait_for_pending_update(update["updateId"]) if synchronous
478
+ task = index.update_settings(final_settings)
479
+ index.wait_for_task(task['uid']) if synchronous
484
480
  end
485
481
  end
486
482
 
487
483
  def ms_index_documents(documents, synchronous = false)
488
484
  ms_configurations.each do |options, settings|
489
485
  next if ms_indexing_disabled?(options)
486
+
490
487
  index = ms_ensure_init(options, settings)
491
- update = index.add_documents(documents.map { |d| settings.get_attributes(d).merge ms_pk(options) => ms_primary_key_of(d, options) })
492
- index.wait_for_pending_update(update["updateId"]) if synchronous || options[:synchronous]
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]
493
490
  end
494
491
  end
495
492
 
496
493
  def ms_index!(document, synchronous = false)
497
494
  return if ms_without_auto_index_scope
495
+
498
496
  ms_configurations.each do |options, settings|
499
497
  next if ms_indexing_disabled?(options)
498
+
500
499
  primary_key = ms_primary_key_of(document, options)
501
500
  index = ms_ensure_init(options, settings)
502
501
  if ms_indexable?(document, options)
503
- raise ArgumentError.new("Cannot index a record without a primary key") if primary_key.blank?
502
+ raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank?
503
+
504
+ doc = settings.get_attributes(document)
505
+ doc = doc.merge ms_pk(options) => primary_key
506
+
504
507
  if synchronous || options[:synchronous]
505
- doc = settings.get_attributes(document)
506
- doc = doc.merge ms_pk(options) => primary_key
507
508
  index.add_documents!(doc)
508
509
  else
509
- doc = settings.get_attributes(document)
510
- doc = doc.merge ms_pk(options) => primary_key
511
510
  index.add_documents(doc)
512
511
  end
513
- elsif ms_conditional_index?(options) && !primary_key.blank?
512
+ elsif ms_conditional_index?(options) && primary_key.present?
514
513
  # remove non-indexable documents
515
514
  if synchronous || options[:synchronous]
516
515
  index.delete_document!(primary_key)
@@ -524,10 +523,13 @@ module MeiliSearch
524
523
 
525
524
  def ms_remove_from_index!(document, synchronous = false)
526
525
  return if ms_without_auto_index_scope
526
+
527
527
  primary_key = ms_primary_key_of(document)
528
- raise ArgumentError.new("Cannot index a record without a primary key") if primary_key.blank?
528
+ raise ArgumentError, 'Cannot index a record without a primary key' if primary_key.blank?
529
+
529
530
  ms_configurations.each do |options, settings|
530
531
  next if ms_indexing_disabled?(options)
532
+
531
533
  index = ms_ensure_init(options, settings)
532
534
  if synchronous || options[:synchronous]
533
535
  index.delete_document!(primary_key)
@@ -541,6 +543,7 @@ module MeiliSearch
541
543
  def ms_clear_index!(synchronous = false)
542
544
  ms_configurations.each do |options, settings|
543
545
  next if ms_indexing_disabled?(options)
546
+
544
547
  index = ms_ensure_init(options, settings)
545
548
  synchronous || options[:synchronous] ? index.delete_all_documents! : index.delete_all_documents
546
549
  @ms_indexes[settings] = nil
@@ -549,24 +552,27 @@ module MeiliSearch
549
552
  end
550
553
 
551
554
  def ms_raw_search(q, params = {})
552
- index_uid = params.delete(:index) ||
553
- params.delete('index')
555
+ index_uid = params.delete(:index) || params.delete('index')
554
556
 
555
- if !meilisearch_settings.get_setting(:attributesToHighlight).nil?
557
+ unless meilisearch_settings.get_setting(:attributesToHighlight).nil?
556
558
  params[:attributesToHighlight] = meilisearch_settings.get_setting(:attributesToHighlight)
557
559
  end
558
560
 
559
- if !meilisearch_settings.get_setting(:attributesToCrop).nil?
561
+ unless meilisearch_settings.get_setting(:attributesToCrop).nil?
560
562
  params[:attributesToCrop] = meilisearch_settings.get_setting(:attributesToCrop)
561
- params[:cropLength] = meilisearch_settings.get_setting(:cropLength) if !meilisearch_settings.get_setting(:cropLength).nil?
563
+
564
+ unless meilisearch_settings.get_setting(:cropLength).nil?
565
+ params[:cropLength] = meilisearch_settings.get_setting(:cropLength)
566
+ end
562
567
  end
568
+
563
569
  index = ms_index(index_uid)
564
- index.search(q, Hash[params.map { |k,v| [k, v] }])
570
+ index.search(q, params.to_h { |k, v| [k, v] })
565
571
  end
566
572
 
567
573
  module AdditionalMethods
568
574
  def self.extended(base)
569
- class <<base
575
+ class << base
570
576
  alias_method :raw_answer, :ms_raw_answer unless method_defined? :raw_answer
571
577
  alias_method :facets_distribution, :ms_facets_distribution unless method_defined? :facets_distribution
572
578
  end
@@ -581,12 +587,13 @@ module MeiliSearch
581
587
  end
582
588
 
583
589
  private
590
+
584
591
  def ms_init_raw_answer(json)
585
592
  @ms_json = json
586
593
  end
587
594
  end
588
595
 
589
- def ms_search(q, params = {})
596
+ def ms_search(query, params = {})
590
597
  if MeiliSearch.configuration[:pagination_backend]
591
598
 
592
599
  page = params[:page].nil? ? params[:page] : params[:page].to_i
@@ -598,28 +605,29 @@ module MeiliSearch
598
605
  end
599
606
 
600
607
  # Returns raw json hits as follows:
601
- # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "nbHits"=>1, "exhaustiveNbHits"=>false, "processingTimeMs"=>0, "query"=>"iphone"}
602
- json = ms_raw_search(q, params)
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)
603
611
 
604
612
  # Returns the ids of the hits: 13
605
613
  hit_ids = json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] }
606
614
 
607
615
  # condition_key gets the primary key of the document; looks for "id" on the options
608
- if defined?(::Mongoid::Document) && self.include?(::Mongoid::Document)
609
- condition_key = ms_primary_key_method.in
610
- else
611
- condition_key = ms_primary_key_method
612
- end
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
613
621
 
614
622
  # meilisearch_options[:type] refers to the Model name (e.g. Product)
615
- # results_by_id creates a hash with the primaryKey of the document (id) as the key and the document itself as the value
616
- # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil, description: "Puts even more features at your fingertips", release_date: nil>}
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>}
617
626
  results_by_id = meilisearch_options[:type].where(condition_key => hit_ids).index_by do |hit|
618
627
  ms_primary_key_of(hit)
619
628
  end
620
629
 
621
630
  results = json['hits'].map do |hit|
622
-
623
631
  o = results_by_id[hit[ms_pk(meilisearch_options).to_s].to_s]
624
632
  if o
625
633
  o.formatted = hit['_formatted']
@@ -631,7 +639,7 @@ module MeiliSearch
631
639
  hits_per_page ||= 20
632
640
  page ||= 1
633
641
 
634
- res = MeiliSearch::Pagination.create(results, total_hits, meilisearch_options.merge({ page: page , per_page: hits_per_page }))
642
+ res = MeiliSearch::Pagination.create(results, total_hits, meilisearch_options.merge(page: page, per_page: hits_per_page))
635
643
  res.extend(AdditionalMethods)
636
644
  res.send(:ms_init_raw_answer, json)
637
645
  res
@@ -642,7 +650,7 @@ module MeiliSearch
642
650
  ms_configurations.each do |o, s|
643
651
  return ms_ensure_init(o, s) if o[:index_uid].to_s == name.to_s
644
652
  end
645
- raise ArgumentError.new("Invalid index name: #{name}")
653
+ raise ArgumentError, "Invalid index name: #{name}"
646
654
  end
647
655
  ms_ensure_init
648
656
  end
@@ -650,17 +658,19 @@ module MeiliSearch
650
658
  def ms_index_uid(options = nil)
651
659
  options ||= meilisearch_options
652
660
  name = options[:index_uid] || model_name.to_s.gsub('::', '_')
653
- name = "#{name}_#{Rails.env.to_s}" if options[:per_environment]
661
+ name = "#{name}_#{Rails.env}" if options[:per_environment]
654
662
  name
655
663
  end
656
664
 
657
665
  def ms_must_reindex?(document)
658
666
  # use +ms_dirty?+ method if implemented
659
- return document.send(:ms_dirty?) if (document.respond_to?(:ms_dirty?))
667
+ return document.send(:ms_dirty?) if document.respond_to?(:ms_dirty?)
668
+
660
669
  # Loop over each index to see if a attribute used in records has changed
661
670
  ms_configurations.each do |options, settings|
662
671
  next if ms_indexing_disabled?(options)
663
672
  return true if ms_primary_key_changed?(document, options)
673
+
664
674
  settings.get_attribute_names(document).each do |k|
665
675
  return true if ms_attribute_changed?(document, k)
666
676
  # return true if !document.respond_to?(changed_method) || document.send(changed_method)
@@ -678,14 +688,15 @@ module MeiliSearch
678
688
  end
679
689
  end
680
690
  end
691
+
681
692
  # By default, we don't reindex
682
- return false
693
+ false
683
694
  end
684
695
 
685
696
  protected
686
697
 
687
698
  def ms_ensure_init(options = nil, settings = nil, index_settings = nil)
688
- raise ArgumentError.new('No `meilisearch` block found in your model.') if meilisearch_settings.nil?
699
+ raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
689
700
 
690
701
  @ms_indexes ||= {}
691
702
 
@@ -696,7 +707,7 @@ module MeiliSearch
696
707
 
697
708
  @ms_indexes[settings] = SafeIndex.new(ms_index_uid(options), meilisearch_options[:raise_on_failure], meilisearch_options)
698
709
 
699
- current_settings = @ms_indexes[settings].settings(:getVersion => 1) rescue nil # if the index doesn't exist
710
+ current_settings = @ms_indexes[settings].settings(getVersion: 1) rescue nil # if the index doesn't exist
700
711
 
701
712
  index_settings ||= settings.to_settings
702
713
  index_settings = options[:primary_settings].to_settings.merge(index_settings) if options[:inherit]
@@ -713,17 +724,18 @@ module MeiliSearch
713
724
  private
714
725
 
715
726
  def ms_configurations
716
- raise ArgumentError.new('No `meilisearch` block found in your model.') if meilisearch_settings.nil?
727
+ raise ArgumentError, 'No `meilisearch` block found in your model.' if meilisearch_settings.nil?
728
+
717
729
  if @configurations.nil?
718
730
  @configurations = {}
719
731
  @configurations[meilisearch_options] = meilisearch_settings
720
- meilisearch_settings.additional_indexes.each do |k,v|
732
+ meilisearch_settings.additional_indexes.each do |k, v|
721
733
  @configurations[k] = v
722
734
 
723
- if v.additional_indexes.any?
724
- v.additional_indexes.each do |options, index|
725
- @configurations[options] = index
726
- end
735
+ next unless v.additional_indexes.any?
736
+
737
+ v.additional_indexes.each do |options, index|
738
+ @configurations[options] = index
727
739
  end
728
740
  end
729
741
  end
@@ -750,13 +762,14 @@ module MeiliSearch
750
762
 
751
763
  def meilisearch_settings_changed?(prev, current)
752
764
  return true if prev.nil?
765
+
753
766
  current.each do |k, v|
754
767
  prev_v = prev[k.to_s]
755
- if v.is_a?(Array) and prev_v.is_a?(Array)
768
+ if v.is_a?(Array) && prev_v.is_a?(Array)
756
769
  # compare array of strings, avoiding symbols VS strings comparison
757
- return true if v.map { |x| x.to_s } != prev_v.map { |x| x.to_s }
758
- else
759
- return true if prev_v != v
770
+ return true if v.map(&:to_s) != prev_v.map(&:to_s)
771
+ elsif prev_v != v
772
+ return true
760
773
  end
761
774
  end
762
775
  false
@@ -796,11 +809,11 @@ module MeiliSearch
796
809
  # All constraints must pass
797
810
  constraint.all? { |inner_constraint| ms_constraint_passes?(document, inner_constraint) }
798
811
  else
799
- if constraint.respond_to?(:call) # Proc
800
- constraint.call(document)
801
- else
812
+ unless constraint.respond_to?(:call)
802
813
  raise ArgumentError, "Unknown constraint type: #{constraint} (#{constraint.class})"
803
814
  end
815
+
816
+ constraint.call(document)
804
817
  end
805
818
  end
806
819
 
@@ -830,7 +843,7 @@ module MeiliSearch
830
843
  items = []
831
844
  all.each do |item|
832
845
  items << item
833
- if items.length % batch_size == 0
846
+ if (items.length % batch_size).zero?
834
847
  yield items
835
848
  items = []
836
849
  end
@@ -869,7 +882,9 @@ module MeiliSearch
869
882
 
870
883
  def ms_enqueue_remove_from_index!(synchronous)
871
884
  if meilisearch_options[:enqueue]
872
- meilisearch_options[:enqueue].call(self, true) unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
885
+ unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
886
+ meilisearch_options[:enqueue].call(self, true)
887
+ end
873
888
  else
874
889
  ms_remove_from_index!(synchronous || ms_synchronous?)
875
890
  end
@@ -877,18 +892,20 @@ module MeiliSearch
877
892
 
878
893
  def ms_enqueue_index!(synchronous)
879
894
  if meilisearch_options[:enqueue]
880
- meilisearch_options[:enqueue].call(self, false) unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
895
+ unless self.class.send(:ms_indexing_disabled?, meilisearch_options)
896
+ meilisearch_options[:enqueue].call(self, false)
897
+ end
881
898
  else
882
899
  ms_index!(synchronous)
883
900
  end
884
901
  end
885
902
 
886
- private
887
-
888
903
  def ms_synchronous?
889
- @ms_synchronous == true
904
+ @ms_synchronous
890
905
  end
891
906
 
907
+ private
908
+
892
909
  def ms_mark_synchronous
893
910
  @ms_synchronous = true
894
911
  end
@@ -899,18 +916,19 @@ module MeiliSearch
899
916
 
900
917
  def ms_mark_must_reindex
901
918
  # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in
902
- # a stransaction, keep flag set until it is explicitly unset
919
+ # a transaction, keep flag set until it is explicitly unset
903
920
  @ms_must_reindex ||=
904
- if defined?(::Sequel) && is_a?(Sequel::Model)
905
- new? || self.class.ms_must_reindex?(self)
906
- else
907
- new_record? || self.class.ms_must_reindex?(self)
908
- end
921
+ if defined?(::Sequel) && is_a?(Sequel::Model)
922
+ new? || self.class.ms_must_reindex?(self)
923
+ else
924
+ new_record? || self.class.ms_must_reindex?(self)
925
+ end
909
926
  true
910
927
  end
911
928
 
912
929
  def ms_perform_index_tasks
913
930
  return if !@ms_auto_indexing || @ms_must_reindex == false
931
+
914
932
  ms_enqueue_index!(ms_synchronous?)
915
933
  remove_instance_variable(:@ms_auto_indexing) if instance_variable_defined?(:@ms_auto_indexing)
916
934
  remove_instance_variable(:@ms_synchronous) if instance_variable_defined?(:@ms_synchronous)