meilisearch-rails 0.5.2 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2541e903db43e95ae8290c07308159dc9d724949dc1ffa27214df3e52cd37fe6
4
- data.tar.gz: 8b4e32b34e9e3c4fbae0e5c21efa20d87e611ebada16889ba01ba236343b0f6d
3
+ metadata.gz: 2957f44b71394e8f2c6c3e3fa8aa3907b85ef4405c2cbb5230080383eebbe034
4
+ data.tar.gz: 99ebb927e9194b10746355e5ba3ae44853fa4c1ee2e98f6818e910c073f01b8d
5
5
  SHA512:
6
- metadata.gz: 1eba16d0d03695d817998c5249a1ad33d343df4816332a63c7e7b220d42cd2734b9d1ed90e1893a12de6b67d5ed4b1f4e4ca8cad21f3d3e206f8e108f3ffc07f
7
- data.tar.gz: 53ffa781649f9370e7fe35aa5b7263f8792a7f04ede7c28da6e2f6d85b015ae2fcef236affeea659b11c538385a39ca50fb13dab7857d50e8e76067b813f7dfc
6
+ metadata.gz: '08834c370f32354ad47eb1d904212e8535a5b47f89a705ad3aeb42bf7a4768b0dd51f4198b86855f3691c886ccac850f9ef88e6528433a90a9774d6d3bacff0c'
7
+ data.tar.gz: 22ac7b9564df2fef7f23b5227578713d095492f615e9fa652c0d1fb55167d1d9188fda65e4b683db8d413cb38086e27930d1dd6077975b088fd89dfbd1ac8114
data/README.md CHANGED
@@ -30,6 +30,7 @@
30
30
  - [📖 Documentation](#-documentation)
31
31
  - [🤖 Compatibility with Meilisearch](#-compatibility-with-meilisearch)
32
32
  - [🚀 Getting Started](#-getting-started)
33
+ - [Compatibility](#-compatibility)
33
34
  - [⚙️ Settings](#️-settings)
34
35
  - [🔍 Custom search](#-custom-search)
35
36
  - [🪛 Options](#-options)
@@ -43,6 +44,7 @@
43
44
  - [Relations](#relations)
44
45
  - [Sanitize attributes](#sanitize-attributes)
45
46
  - [UTF-8 encoding](#utf-8-encoding)
47
+ - [Eager loading](#eager-loading)
46
48
  - [Manual operations](#manual-operations)
47
49
  - [Indexing & deletion](#indexing--deletion)
48
50
  - [Access the underlying index object](#access-the-underlying-index-object)
@@ -58,7 +60,7 @@ To learn more about Meilisearch, check out our [Documentation](https://docs.meil
58
60
 
59
61
  ## 🤖 Compatibility with Meilisearch
60
62
 
61
- This package only guarantees the compatibility with the [version v0.27.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.27.0).
63
+ This package only guarantees the compatibility with the [version v0.28.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.28.0).
62
64
 
63
65
  ## 🔧 Installation <!-- omit in toc -->
64
66
 
@@ -170,10 +172,10 @@ Then, as soon as you use the `search` method, the returning results will be pagi
170
172
  <%= will_paginate @hits %> # if using will_paginate
171
173
  ```
172
174
 
173
- The **number of hits per page defaults to 20**, you can customize it by adding the `hitsPerPage` parameter to your search:
175
+ The **number of hits per page defaults to 20**, you can customize it by adding the `hits_per_page` parameter to your search:
174
176
 
175
177
  ```ruby
176
- Book.search('harry potter', hitsPerPage: 10)
178
+ Book.search('harry potter', hits_per_page: 10)
177
179
  ```
178
180
 
179
181
  #### Extra Configuration <!-- omit in toc -->
@@ -190,6 +192,10 @@ MeiliSearch::Rails.configuration = {
190
192
  }
191
193
  ```
192
194
 
195
+ ## Compatibility
196
+
197
+ If your model already has methods that meilisearch-rails defines such as `search` and `index`, they will not be redefined. You can target the meilisearch-rails-defined methods by prefixing with `ms_`, e.g. `Book.ms_search('harry potter')`.
198
+
193
199
  ## ⚙️ Settings
194
200
 
195
201
  You can configure the index settings by adding them inside the `meilisearch` block as shown below:
@@ -228,7 +234,7 @@ Check the dedicated section of the documentation, for more information on the [s
228
234
  All the supported options are described in the [search parameters](https://docs.meilisearch.com/reference/features/search_parameters.html) section of the documentation.
229
235
 
230
236
  ```ruby
231
- Book.search('Harry', attributesToHighlight: ['*'])
237
+ Book.search('Harry', attributes_to_highlight: ['*'])
232
238
  ```
233
239
  👉 Don't forget that `attributes_to_highlight`, `attributes_to_crop`, and
234
240
  `crop_length` can be set up in the `meilisearch` block of your model.
@@ -256,24 +262,24 @@ By default, the **index_uid** will be the class name, e.g. `Book`. You can custo
256
262
  class Book < ActiveRecord::Base
257
263
  include MeiliSearch::Rails
258
264
 
259
- meilisearch index_uid: 'MyCustomUID' do
260
- end
265
+ meilisearch index_uid: 'MyCustomUID'
261
266
  end
262
267
  ```
263
268
 
264
269
  #### Index UID according to the environment <!-- omit in toc -->
265
270
 
266
- You can suffix the index UID with the current Rails environment using the following option:
271
+ You can suffix the index UID with the current Rails environment by setting it globally:
267
272
 
268
273
  ```ruby
269
- class Book < ActiveRecord::Base
270
- include MeiliSearch::Rails
271
-
272
- meilisearch per_environment: true do # The index UID will be "Book_#{Rails.env}"
273
- end
274
- end
274
+ MeiliSearch::Rails.configuration = {
275
+ meilisearch_host: 'YourMeilisearchHost',
276
+ meilisearch_api_key: 'YourMeilisearchAPIKey',
277
+ per_environment: true
278
+ }
275
279
  ```
276
280
 
281
+ This way your index UID will look like this `"Book_#{Rails.env}"`.
282
+
277
283
  ### Index configuration
278
284
 
279
285
  #### Custom attribute definition
@@ -312,16 +318,33 @@ end
312
318
 
313
319
  By default, the primary key is based on your record's id. You can change this behavior by specifying the `primary_key:` option.
314
320
 
315
- Note that the primary key must have a **unique value**.
321
+ Note that the primary key must return a **unique value** otherwise your data could be overwritten.
316
322
 
317
323
  ```ruby
318
324
  class Book < ActiveRecord::Base
319
325
  include MeiliSearch::Rails
320
326
 
321
- meilisearch primary_key: 'ISBN' do
327
+ meilisearch primary_key: :isbn # isbn is a column in your table definition.
328
+ end
329
+ ```
330
+
331
+ You can also set the `primary_key` as a method, this method will be evaluated in runtime, and its return
332
+ will be used as the reference to the document when Meilisearch needs it.
333
+
334
+ ```rb
335
+ class Book < ActiveRecord::Base
336
+ include MeiliSearch::Rails
337
+
338
+ meilisearch primary_key: :my_custom_ms_id
339
+
340
+ private
341
+
342
+ def my_custom_ms_id
343
+ "isbn_#{primary_key}" # ensure this return is unique, otherwise you'll lose data.
322
344
  end
323
345
  end
324
346
  ```
347
+
325
348
  #### Conditional indexing
326
349
 
327
350
  You can control if a record must be indexed by using the `if:` or `unless:` options.<br>
@@ -331,8 +354,7 @@ As soon as you use those constraints, `add_documents` and `delete_documents` cal
331
354
  class Book < ActiveRecord::Base
332
355
  include MeiliSearch::Rails
333
356
 
334
- meilisearch if: :published?, unless: :premium? do
335
- end
357
+ meilisearch if: :published?, unless: :premium?
336
358
 
337
359
  def published?
338
360
  # [...]
@@ -369,6 +391,7 @@ class Book < ActiveRecord::Base
369
391
  end
370
392
 
371
393
  private
394
+
372
395
  def public?
373
396
  released? && !premium?
374
397
  end
@@ -383,10 +406,10 @@ You may want to share an index between several models. You'll need to ensure you
383
406
  class Cat < ActiveRecord::Base
384
407
  include MeiliSearch::Rails
385
408
 
386
- meilisearch index_uid: 'Animals', primary_key: :ms_id do
387
- end
409
+ meilisearch index_uid: 'Animals', primary_key: :ms_id
388
410
 
389
411
  private
412
+
390
413
  def ms_id
391
414
  "cat_#{primary_key}" # ensure the cats & dogs primary_keys are not conflicting
392
415
  end
@@ -395,10 +418,10 @@ end
395
418
  class Dog < ActiveRecord::Base
396
419
  include MeiliSearch::Rails
397
420
 
398
- meilisearch index_uid: 'Animals', primary_key: :ms_id do
399
- end
421
+ meilisearch index_uid: 'Animals', primary_key: :ms_id
400
422
 
401
423
  private
424
+
402
425
  def ms_id
403
426
  "dog_#{primary_key}" # ensure the cats & dogs primary_keys are not conflicting
404
427
  end
@@ -413,8 +436,7 @@ You can configure the auto-indexing & auto-removal process to use a queue to per
413
436
  class Book < ActiveRecord::Base
414
437
  include MeiliSearch::Rails
415
438
 
416
- meilisearch enqueue: true do # ActiveJob will be triggered using a `meilisearch` queue
417
- end
439
+ meilisearch enqueue: true # ActiveJob will be triggered using a `meilisearch` queue
418
440
  end
419
441
  ```
420
442
 
@@ -586,8 +608,7 @@ You can strip all HTML tags from your attributes with the `sanitize` option.
586
608
  class Book < ActiveRecord::Base
587
609
  include MeiliSearch::Rails
588
610
 
589
- meilisearch sanitize: true do
590
- end
611
+ meilisearch sanitize: true
591
612
  end
592
613
  ```
593
614
 
@@ -599,8 +620,21 @@ You can force the UTF-8 encoding of all your attributes using the `force_utf8_en
599
620
  class Book < ActiveRecord::Base
600
621
  include MeiliSearch::Rails
601
622
 
602
- meilisearch force_utf8_encoding: true do
603
- end
623
+ meilisearch force_utf8_encoding: true
624
+ end
625
+ ```
626
+
627
+ #### Eager loading
628
+
629
+ You can eager load associations using `meilisearch_import` scope.
630
+
631
+ ```ruby
632
+ class Author < ActiveRecord::Base
633
+ include MeiliSearch::Rails
634
+
635
+ has_many :books
636
+
637
+ scope :meilisearch_import, -> { includes(:books) }
604
638
  end
605
639
  ```
606
640
 
@@ -652,8 +686,7 @@ class Book < ActiveRecord::Base
652
686
  include MeiliSearch::Rails
653
687
 
654
688
  # Only raise exceptions in development environment.
655
- meilisearch raise_on_failure: Rails.env.development? do
656
- end
689
+ meilisearch raise_on_failure: Rails.env.development?
657
690
  end
658
691
  ```
659
692
 
@@ -667,8 +700,7 @@ You can force indexing and removing to be synchronous by setting the following o
667
700
  class Book < ActiveRecord::Base
668
701
  include MeiliSearch::Rails
669
702
 
670
- meilisearch synchronous: true do
671
- end
703
+ meilisearch synchronous: true
672
704
  end
673
705
  ```
674
706
  🚨 This is only recommended for testing purposes, the gem will call the `wait_for_task` method that will stop your code execution until the asynchronous task has been processed by MeilSearch.
@@ -681,8 +713,7 @@ You can disable auto-indexing and auto-removing setting the following options:
681
713
  class Book < ActiveRecord::Base
682
714
  include MeiliSearch::Rails
683
715
 
684
- meilisearch auto_index: false, auto_remove: false do
685
- end
716
+ meilisearch auto_index: false, auto_remove: false
686
717
  end
687
718
  ```
688
719
 
@@ -16,6 +16,7 @@ module MeiliSearch
16
16
  configuration[:meilisearch_host] || 'http://localhost:7700',
17
17
  configuration[:meilisearch_api_key],
18
18
  configuration.slice(:timeout, :max_retries)
19
+ .merge(client_agents: MeiliSearch::Rails.qualified_version)
19
20
  )
20
21
  end
21
22
  end
@@ -2,6 +2,10 @@
2
2
 
3
3
  module MeiliSearch
4
4
  module Rails
5
- VERSION = '0.5.2'
5
+ VERSION = '0.7.1'
6
+
7
+ def self.qualified_version
8
+ "Meilisearch Rails (v#{VERSION})"
9
+ end
6
10
  end
7
11
  end
@@ -105,7 +105,7 @@ module MeiliSearch
105
105
  end
106
106
 
107
107
  def sequel?(document)
108
- defined?(::Sequel) && document.class < ::Sequel::Model
108
+ defined?(::Sequel::Model) && document.class < ::Sequel::Model
109
109
  end
110
110
 
111
111
  def active_record?(document)
@@ -233,9 +233,13 @@ module MeiliSearch
233
233
  def initialize(index_uid, raise_on_failure, options)
234
234
  client = MeiliSearch::Rails.client
235
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
236
  @raise_on_failure = raise_on_failure.nil? || raise_on_failure
237
+
238
+ SafeIndex.log_or_throw(nil, @raise_on_failure) do
239
+ client.create_index(index_uid, { primary_key: primary_key })
240
+ end
241
+
242
+ @index = client.index(index_uid)
239
243
  end
240
244
 
241
245
  ::MeiliSearch::Index.instance_methods(false).each do |m|
@@ -273,7 +277,7 @@ module MeiliSearch
273
277
 
274
278
  def self.log_or_throw(method, raise_on_failure, &block)
275
279
  yield
276
- rescue ::MeiliSearch::ApiError => e
280
+ rescue ::MeiliSearch::TimeoutError, ::MeiliSearch::ApiError => e
277
281
  raise e if raise_on_failure
278
282
 
279
283
  # log the error
@@ -282,7 +286,7 @@ module MeiliSearch
282
286
  case method.to_s
283
287
  when 'search'
284
288
  # some attributes are required
285
- { 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facetsDistribution' => {}, 'error' => e }
289
+ { 'hits' => [], 'hitsPerPage' => 0, 'page' => 0, 'facetDistribution' => {}, 'error' => e }
286
290
  else
287
291
  # empty answer
288
292
  { 'error' => e }
@@ -319,8 +323,12 @@ module MeiliSearch
319
323
 
320
324
  attr_accessor :formatted
321
325
 
326
+ if options.key?(:per_environment)
327
+ raise BadConfiguration, ':per_environment option should be defined globally on MeiliSearch::Rails.configuration block.'
328
+ end
329
+
322
330
  if options[:synchronous] == true
323
- if defined?(::Sequel) && self < Sequel::Model
331
+ if defined?(::Sequel::Model) && self < Sequel::Model
324
332
  class_eval do
325
333
  copy_after_validation = instance_method(:after_validation)
326
334
  define_method(:after_validation) do |*args|
@@ -352,7 +360,7 @@ module MeiliSearch
352
360
  end
353
361
  end
354
362
  unless options[:auto_index] == false
355
- if defined?(::Sequel) && self < Sequel::Model
363
+ if defined?(::Sequel::Model) && self < Sequel::Model
356
364
  class_eval do
357
365
  copy_after_validation = instance_method(:after_validation)
358
366
  copy_before_save = instance_method(:before_save)
@@ -399,7 +407,7 @@ module MeiliSearch
399
407
  end
400
408
  end
401
409
  unless options[:auto_remove] == false
402
- if defined?(::Sequel) && self < Sequel::Model
410
+ if defined?(::Sequel::Model) && self < Sequel::Model
403
411
  class_eval do
404
412
  copy_after_destroy = instance_method(:after_destroy)
405
413
 
@@ -456,7 +464,7 @@ module MeiliSearch
456
464
  end
457
465
  last_task = index.add_documents(documents)
458
466
  end
459
- index.wait_for_task(last_task['uid']) if last_task && (synchronous || options[:synchronous])
467
+ index.wait_for_task(last_task['taskUid']) if last_task && (synchronous || options[:synchronous])
460
468
  end
461
469
  nil
462
470
  end
@@ -472,7 +480,7 @@ module MeiliSearch
472
480
 
473
481
  index = SafeIndex.new(ms_index_uid(options), true, options)
474
482
  task = index.update_settings(final_settings)
475
- index.wait_for_task(task['uid']) if synchronous
483
+ index.wait_for_task(task['taskUid']) if synchronous
476
484
  end
477
485
  end
478
486
 
@@ -482,7 +490,7 @@ module MeiliSearch
482
490
 
483
491
  index = ms_ensure_init(options, settings)
484
492
  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]
493
+ index.wait_for_task(task['taskUid']) if synchronous || options[:synchronous]
486
494
  end
487
495
  end
488
496
 
@@ -579,7 +587,7 @@ module MeiliSearch
579
587
  end
580
588
 
581
589
  def ms_facets_distribution
582
- @ms_json['facetsDistribution']
590
+ @ms_json['facetDistribution']
583
591
  end
584
592
 
585
593
  private
@@ -601,13 +609,10 @@ module MeiliSearch
601
609
  end
602
610
 
603
611
  # 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"}
612
+ # {"hits"=>[{"id"=>"13", "href"=>"apple", "name"=>"iphone"}], "offset"=>0, "limit"=>|| 20, "estimatedTotalHits"=>1,
613
+ # "processingTimeMs"=>0, "query"=>"iphone"}
606
614
  json = ms_raw_search(query, params)
607
615
 
608
- # Returns the ids of the hits: 13
609
- hit_ids = json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] }
610
-
611
616
  # condition_key gets the primary key of the document; looks for "id" on the options
612
617
  condition_key = if defined?(::Mongoid::Document) && include?(::Mongoid::Document)
613
618
  ms_primary_key_method.in
@@ -615,6 +620,23 @@ module MeiliSearch
615
620
  ms_primary_key_method
616
621
  end
617
622
 
623
+ # The condition_key must be a valid column otherwise, the `.where` below will not work
624
+ # Since we provide a way to customize the primary_key value, `ms_pk(meilisearch_options)` may not
625
+ # respond with a valid database column. The blocks below prevent that from happening.
626
+ has_virtual_column_as_pk = if defined?(::Sequel::Model) && self < Sequel::Model
627
+ meilisearch_options[:type].columns.map(&:to_s).exclude?(condition_key.to_s)
628
+ else
629
+ meilisearch_options[:type].columns.map(&:name).map(&:to_s).exclude?(condition_key.to_s)
630
+ end
631
+
632
+ condition_key = meilisearch_options[:type].primary_key if has_virtual_column_as_pk
633
+
634
+ hit_ids = if has_virtual_column_as_pk
635
+ json['hits'].map { |hit| hit[condition_key] }
636
+ else
637
+ json['hits'].map { |hit| hit[ms_pk(meilisearch_options).to_s] }
638
+ end
639
+
618
640
  # meilisearch_options[:type] refers to the Model name (e.g. Product)
619
641
  # results_by_id creates a hash with the primaryKey of the document (id) as the key and doc itself as the value
620
642
  # {"13"=>#<Product id: 13, name: "iphone", href: "apple", tags: nil, type: nil,
@@ -653,8 +675,11 @@ module MeiliSearch
653
675
 
654
676
  def ms_index_uid(options = nil)
655
677
  options ||= meilisearch_options
678
+ global_options ||= MeiliSearch::Rails.configuration
679
+
656
680
  name = options[:index_uid] || model_name.to_s.gsub('::', '_')
657
- name = "#{name}_#{::Rails.env}" if options[:per_environment]
681
+ name = "#{name}_#{::Rails.env}" if global_options[:per_environment]
682
+
658
683
  name
659
684
  end
660
685
 
@@ -820,8 +845,9 @@ module MeiliSearch
820
845
 
821
846
  def ms_find_in_batches(batch_size, &block)
822
847
  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
848
+ scope = respond_to?(:meilisearch_import) ? meilisearch_import : all
849
+ scope.find_in_batches(batch_size: batch_size, &block)
850
+ elsif defined?(::Sequel::Model) && self < Sequel::Model
825
851
  dataset.extension(:pagination).each_page(batch_size, &block)
826
852
  else
827
853
  # don't worry, mongoid has its own underlying cursor/streaming mechanism
@@ -903,7 +929,7 @@ module MeiliSearch
903
929
  # ms_must_reindex flag is reset after every commit as part. If we must reindex at any point in
904
930
  # a transaction, keep flag set until it is explicitly unset
905
931
  @ms_must_reindex ||=
906
- if defined?(::Sequel) && is_a?(Sequel::Model)
932
+ if defined?(::Sequel::Model) && is_a?(Sequel::Model)
907
933
  new? || self.class.ms_must_reindex?(self)
908
934
  else
909
935
  new_record? || self.class.ms_must_reindex?(self)
@@ -34,5 +34,5 @@ Gem::Specification.new do |s|
34
34
 
35
35
  s.required_ruby_version = '>= 2.6.0'
36
36
 
37
- s.add_dependency 'meilisearch', '~> 0.18'
37
+ s.add_dependency 'meilisearch', '~> 0.19.2'
38
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meilisearch-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Meili
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-18 00:00:00.000000000 Z
11
+ date: 2022-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: meilisearch
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.18'
19
+ version: 0.19.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.18'
26
+ version: 0.19.2
27
27
  description: Meilisearch integration for Ruby on Rails. See https://github.com/meilisearch/meilisearch
28
28
  email: bonjour@meilisearch.com
29
29
  executables: []