meilisearch-rails 0.11.1 → 0.12.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75f8a8a3ebfe381cbb7c9b40072dcf859882ccf9bf78bc5bb8ab0b94c05231d9
4
- data.tar.gz: b3b5ec98f2524a08c738847a6b70671201f27cf466a62730f1aabbb0e177e08e
3
+ metadata.gz: 3aebe8eb25a9272e97c71563db1dae9758c50f5423adc3909228964097116047
4
+ data.tar.gz: 18e9133418b2c512d8645ce4d1ee9a06113076c9c2af4e5b53e4e295c5d47fe7
5
5
  SHA512:
6
- metadata.gz: 5602abb0df9c02912553d45b6afbef167b735e1a585e5f19484b2bc0a38f366b34d2081130cd0e6257b3228023798e4e36ea5bbea85e5e9a1beed60370f7f0e4
7
- data.tar.gz: d40ba9defa8b7eeb754a8aff10ad9dfccdef04911c867a89c8a7d236c183582e60100573a1305e76e52da4d871d7a59d3413f0c8e0cdfe50405bb8f43072fdac
6
+ metadata.gz: 7730a5763da77d4dfb383ee223e11c29db59e91fbfb7d16eb93e54c901b6b4c2d8472da909efcf2541745ac343a75676196d82a5be7754729b30c6ecd4096ed3
7
+ data.tar.gz: 68ea1f648aa284a1a1c5e63715109fd545c7ec53b5e784188abee97ed30dada6ba06d8e0799500a99401161fe2351fd43871ca04464b06f7ae40313d0b462f2e
data/README.md CHANGED
@@ -38,6 +38,7 @@
38
38
  - [Compatibility](#-compatibility)
39
39
  - [⚙️ Settings](#️-settings)
40
40
  - [🔍 Custom search](#-custom-search)
41
+ - [🔍🔍 Multi search](#-multi-search)
41
42
  - [🪛 Options](#-options)
42
43
  - [Meilisearch configuration & environment](#meilisearch-configuration--environment)
43
44
  - [Pagination with `kaminari` or `will_paginate`](#backend-pagination-with-kaminari-or-will_paginate-)
@@ -240,6 +241,58 @@ Book.search('*', sort: ['title:asc'])
240
241
 
241
242
  👉 Don't forget to set up the `sortable_attributes` option in the `meilisearch` block of your model.
242
243
 
244
+ ## 🔍🔍 Multi search
245
+
246
+ Meilisearch supports searching multiple models at the same time (see [🔍 Custom search](#-custom-search) for search options):
247
+
248
+ ```ruby
249
+ multi_search_results = MeiliSearch::Rails.multi_search(
250
+ Book => { q: 'Harry' },
251
+ Manga => { q: 'Attack' }
252
+ )
253
+ ```
254
+
255
+ You can iterate through the results with `.each` or `.each_result`:
256
+
257
+ ```erb
258
+ <% multi_search_results.each do |record| %>
259
+ <p><%= record.title %></p>
260
+ <p><%= record.author %></p>
261
+ <% end %>
262
+
263
+ <p>Harry Potter and the Philosopher's Stone</p>
264
+ <p>J. K. Rowling</p>
265
+ <p>Harry Potter and the Chamber of Secrets</p>
266
+ <p>J. K. Rowling</p>
267
+ <p>Attack on Titan</p>
268
+ <p>Iseyama</p>
269
+ ```
270
+
271
+ ```erb
272
+ <% multi_search_results.each_result do |klass, results| %>
273
+ <p><%= klass.name.pluralize %></p>
274
+
275
+ <ul>
276
+ <% results.each do |record| %>
277
+ <li><%= record.title %></li>
278
+ <% end %>
279
+ </ul>
280
+ <% end %>
281
+
282
+
283
+ <p>Books</p>
284
+ <ul>
285
+ <li>Harry Potter and the Philosopher's Stone</li>
286
+ <li>Harry Potter and the Chamber of Secrets</li>
287
+ </ul>
288
+ <p>Mangas</p>
289
+ <ul>
290
+ <li>Attack on Titan</li>
291
+ </ul>
292
+ ```
293
+
294
+ See the [official multi search documentation](https://www.meilisearch.com/docs/reference/api/multi_search).
295
+
243
296
  ## 🪛 Options
244
297
 
245
298
  ### Meilisearch configuration & environment
@@ -0,0 +1,84 @@
1
+ module MeiliSearch
2
+ module Rails
3
+ class MultiSearchResult
4
+ attr_reader :metadata
5
+
6
+ def initialize(searches, raw_results)
7
+ @results = {}
8
+ @metadata = {}
9
+
10
+ searches.zip(raw_results['results']).each do |(index_target, search_options), result|
11
+ index_target = search_options[:class_name].constantize if search_options[:class_name]
12
+
13
+ @results[index_target] = case index_target
14
+ when String, Symbol
15
+ result['hits']
16
+ else
17
+ load_results(index_target, result)
18
+ end
19
+
20
+ @metadata[index_target] = result.except('hits')
21
+ end
22
+ end
23
+
24
+ include Enumerable
25
+
26
+ def each_hit(&block)
27
+ @results.each do |_index_target, results|
28
+ results.each(&block)
29
+ end
30
+ end
31
+ alias each each_hit
32
+
33
+ def each_result
34
+ @results.each
35
+ end
36
+
37
+ def to_a
38
+ @results.values.flatten(1)
39
+ end
40
+ alias to_ary to_a
41
+
42
+ def to_h
43
+ @results
44
+ end
45
+ alias to_hash to_h
46
+
47
+ private
48
+
49
+ def load_results(klass, result)
50
+ pk_method = klass.ms_primary_key_method
51
+ pk_method = pk_method.in if Utilities.mongo_model?(klass)
52
+
53
+ condition_key = pk_is_virtual?(klass, pk_method) ? klass.primary_key : pk_method
54
+
55
+ hits_by_id =
56
+ result['hits'].index_by { |hit| hit[condition_key.to_s] }
57
+
58
+ records = klass.where(condition_key => hits_by_id.keys)
59
+
60
+ if records.respond_to? :in_order_of
61
+ records.in_order_of(condition_key, hits_by_id.keys).each do |record|
62
+ record.formatted = hits_by_id[record.send(condition_key).to_s]['_formatted']
63
+ end
64
+ else
65
+ results_by_id = records.index_by do |hit|
66
+ hit.send(condition_key).to_s
67
+ end
68
+
69
+ result['hits'].filter_map do |hit|
70
+ record = results_by_id[hit[condition_key.to_s].to_s]
71
+ record&.formatted = hit['_formatted']
72
+ record
73
+ end
74
+ end
75
+ end
76
+
77
+ def pk_is_virtual?(model_class, pk_method)
78
+ model_class.columns
79
+ .map(&(Utilities.sequel_model?(model_class) ? :to_s : :name))
80
+ .exclude?(pk_method.to_s)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'multi_search/result'
2
+
3
+ module MeiliSearch
4
+ module Rails
5
+ class << self
6
+ def multi_search(searches)
7
+ search_parameters = searches.map do |(index_target, options)|
8
+ paginate(options) if pagination_enabled?
9
+ normalize(options, index_target)
10
+ end
11
+
12
+ MultiSearchResult.new(searches, client.multi_search(search_parameters))
13
+ end
14
+
15
+ private
16
+
17
+ def normalize(options, index_target)
18
+ options
19
+ .except(:class_name)
20
+ .merge!(index_uid: index_uid_from_target(index_target))
21
+ end
22
+
23
+ def index_uid_from_target(index_target)
24
+ case index_target
25
+ when String, Symbol
26
+ index_target
27
+ else
28
+ index_target.index.uid
29
+ end
30
+ end
31
+
32
+ def paginate(options)
33
+ %w[page hitsPerPage hits_per_page].each do |key|
34
+ # Deletes hitsPerPage to avoid passing along a meilisearch-ruby warning/exception
35
+ value = options.delete(key) || options.delete(key.to_sym)
36
+ options[key.underscore.to_sym] = value.to_i if value
37
+ end
38
+
39
+ # It is required to activate the finite pagination in Meilisearch v0.30 (or newer),
40
+ # to have at least `hits_per_page` defined or `page` in the search request.
41
+ options[:page] ||= 1
42
+ end
43
+
44
+ def pagination_enabled?
45
+ MeiliSearch::Rails.configuration[:pagination_backend]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -48,6 +48,14 @@ module MeiliSearch
48
48
  true
49
49
  end
50
50
 
51
+ def mongo_model?(model_class)
52
+ defined?(::Mongoid::Document) && model_class.include?(::Mongoid::Document)
53
+ end
54
+
55
+ def sequel_model?(model_class)
56
+ defined?(::Sequel::Model) && model_class < Sequel::Model
57
+ end
58
+
51
59
  private
52
60
 
53
61
  def constraint_passes?(record, constraint)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module MeiliSearch
4
4
  module Rails
5
- VERSION = '0.11.1'
5
+ VERSION = '0.12.0'
6
6
 
7
7
  def self.qualified_version
8
8
  "Meilisearch Rails (v#{VERSION})"
@@ -3,6 +3,7 @@ require 'meilisearch/rails/null_object'
3
3
  require 'meilisearch/rails/version'
4
4
  require 'meilisearch/rails/utilities'
5
5
  require 'meilisearch/rails/errors'
6
+ require 'meilisearch/rails/multi_search'
6
7
 
7
8
  if defined? Rails
8
9
  begin
@@ -760,6 +761,11 @@ module MeiliSearch
760
761
  false
761
762
  end
762
763
 
764
+ def ms_primary_key_method(options = nil)
765
+ options ||= meilisearch_options
766
+ options[:primary_key] || options[:id] || :id
767
+ end
768
+
763
769
  protected
764
770
 
765
771
  def ms_ensure_init(options = meilisearch_options, settings = meilisearch_settings, user_configuration = settings.to_settings)
@@ -814,11 +820,6 @@ module MeiliSearch
814
820
  @configurations
815
821
  end
816
822
 
817
- def ms_primary_key_method(options = nil)
818
- options ||= meilisearch_options
819
- options[:primary_key] || options[:id] || :id
820
- end
821
-
822
823
  def ms_primary_key_of(doc, options = nil)
823
824
  doc.send(ms_primary_key_method(options)).to_s
824
825
  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.11.1
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Meili
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-26 00:00:00.000000000 Z
11
+ date: 2024-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: meilisearch
@@ -42,6 +42,8 @@ files:
42
42
  - lib/meilisearch/rails/errors.rb
43
43
  - lib/meilisearch/rails/ms_clean_up_job.rb
44
44
  - lib/meilisearch/rails/ms_job.rb
45
+ - lib/meilisearch/rails/multi_search.rb
46
+ - lib/meilisearch/rails/multi_search/result.rb
45
47
  - lib/meilisearch/rails/null_object.rb
46
48
  - lib/meilisearch/rails/pagination.rb
47
49
  - lib/meilisearch/rails/pagination/kaminari.rb