Skip to content

Commit a3675cb

Browse files
Merge #391
391: Allow custom group names in multi search r=ellnix a=ellnix ## Related issue Fixes #364 Todo: - [x] Update README - [x] Test Result class - [ ] Refactor Two more issues remain in multi search after gets completed: #362 and #389 Co-authored-by: ellnix <[email protected]>
2 parents 6afcd40 + 38730fb commit a3675cb

File tree

5 files changed

+208
-27
lines changed

5 files changed

+208
-27
lines changed

README.md

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -241,22 +241,7 @@ multi_search_results = MeiliSearch::Rails.multi_search(
241241
)
242242
```
243243

244-
You can iterate through the results with `.each` or `.each_result`:
245-
246-
```erb
247-
<% multi_search_results.each do |record| %>
248-
<p><%= record.title %></p>
249-
<p><%= record.author %></p>
250-
<% end %>
251-
252-
<p>Harry Potter and the Philosopher's Stone</p>
253-
<p>J. K. Rowling</p>
254-
<p>Harry Potter and the Chamber of Secrets</p>
255-
<p>J. K. Rowling</p>
256-
<p>Attack on Titan</p>
257-
<p>Iseyama</p>
258-
```
259-
244+
Use `#each_result` to loop through pairs of your provided keys and the results:
260245
```erb
261246
<% multi_search_results.each_result do |klass, results| %>
262247
<p><%= klass.name.pluralize %></p>
@@ -280,6 +265,59 @@ You can iterate through the results with `.each` or `.each_result`:
280265
</ul>
281266
```
282267

268+
Records are loaded when the keys are models, or when `:class_name` option is passed:
269+
270+
```ruby
271+
multi_search_results = MeiliSearch::Rails.multi_search(
272+
'books' => { q: 'Harry', class_name: 'Book' },
273+
'mangas' => { q: 'Attack', class_name: 'Manga' }
274+
)
275+
```
276+
277+
Otherwise, hashes are returned.
278+
279+
The index to search is inferred from the model if the key is a model, if the key is a string the key is assumed to be the index unless the `:index_name` option is passed:
280+
281+
```ruby
282+
multi_search_results = MeiliSearch::Rails.multi_search(
283+
'western' => { q: 'Harry', class_name: 'Book', index_name: 'books_production' },
284+
'japanese' => { q: 'Attack', class_name: 'Manga', index_name: 'mangas_production' }
285+
)
286+
```
287+
288+
### Multi search the same index <!-- omit in toc -->
289+
290+
You can search the same index multiple times by specifying `:index_name`:
291+
292+
```ruby
293+
query = 'hero'
294+
multi_search_results = MeiliSearch::Rails.multi_search(
295+
'Isekai Manga' => { q: query, class_name: 'Manga', filters: 'genre:isekai', index_name: 'mangas_production' }
296+
'Shounen Manga' => { q: query, class_name: 'Manga', filters: 'genre:shounen', index_name: 'mangas_production' }
297+
'Steampunk Manga' => { q: query, class_name: 'Manga', filters: 'genre:steampunk', index_name: 'mangas_production' }
298+
)
299+
```
300+
301+
### Deprecated #each <!-- omit in toc -->
302+
303+
**DEPRECATED:** You used to be able to iterate through a flattened collection with `.each`:
304+
305+
```erb
306+
<% multi_search_results.each do |record| %>
307+
<p><%= record.title %></p>
308+
<p><%= record.author %></p>
309+
<% end %>
310+
311+
<p>Harry Potter and the Philosopher's Stone</p>
312+
<p>J. K. Rowling</p>
313+
<p>Harry Potter and the Chamber of Secrets</p>
314+
<p>J. K. Rowling</p>
315+
<p>Attack on Titan</p>
316+
<p>Iseyama</p>
317+
```
318+
319+
But this has been deprecated in favor of **federated search**.
320+
283321
See the [official multi search documentation](https://www.meilisearch.com/docs/reference/api/multi_search).
284322

285323
## 🪛 Options

lib/meilisearch/rails/multi_search.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ module Rails
55
class << self
66
def multi_search(searches)
77
search_parameters = searches.map do |(index_target, options)|
8+
index_target = options.delete(:index_name) || index_target
9+
810
paginate(options) if pagination_enabled?
911
normalize(options, index_target)
1012
end

lib/meilisearch/rails/multi_search/result.rb

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,57 @@ def initialize(searches, raw_results)
77
@results = {}
88
@metadata = {}
99

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]
10+
searches.zip(raw_results['results']).each do |(target, search_options), result|
11+
results_class = if search_options[:class_name]
12+
search_options[:class_name].constantize
13+
elsif target.instance_of?(Class)
14+
target
15+
end
1216

13-
@results[index_target] = case index_target
14-
when String, Symbol
15-
result['hits']
16-
else
17-
load_results(index_target, result)
18-
end
17+
@results[target] = results_class ? load_results(results_class, result) : result['hits']
1918

20-
@metadata[index_target] = result.except('hits')
19+
@metadata[target] = result.except('hits')
2120
end
2221
end
2322

2423
include Enumerable
2524

2625
def each_hit(&block)
27-
@results.each do |_index_target, results|
26+
MeiliSearch::Rails.logger.warn(
27+
<<~DEPRECATION
28+
[meilisearch-rails] Flattening multi search results is deprecated.
29+
If you do not want the results to be grouped, please use federated search instead.
30+
DEPRECATION
31+
)
32+
33+
@results.each_value do |results|
2834
results.each(&block)
2935
end
3036
end
31-
alias each each_hit
37+
38+
def each(&block)
39+
MeiliSearch::Rails.logger.info(
40+
<<~INFO
41+
[meilisearch-rails] #each on a multi search now iterates through grouped results.
42+
If you do not want the results to be grouped, please use federated search instead.
43+
To quickly go back to the old deprecated behavior, use `#each_hit`.
44+
INFO
45+
)
46+
47+
@results.each(&block)
48+
end
3249

3350
def each_result(&block)
3451
@results.each(&block)
3552
end
3653

3754
def to_a
55+
MeiliSearch::Rails.logger.warn(
56+
<<~DEPRECATION
57+
[meilisearch-rails] Flattening multi search results is deprecated.
58+
If you do not want the results to be grouped, please use federated search instead.
59+
DEPRECATION
60+
)
3861
@results.values.flatten(1)
3962
end
4063
alias to_ary to_a

spec/multi_search/result_spec.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,57 @@
6060
)
6161
end
6262

63+
describe '#each' do
64+
let(:logger) { instance_double('Logger', info: nil) }
65+
66+
before do
67+
allow(MeiliSearch::Rails).to receive(:logger).and_return(logger)
68+
end
69+
70+
it 'has the same behavior as #each_result' do
71+
expect(result.each).to be_an(Enumerator)
72+
expect(result.each).to contain_exactly(
73+
['books_index', contain_exactly(
74+
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs')
75+
)],
76+
['products_index', contain_exactly(
77+
a_hash_including('name' => 'palm pixi plus')
78+
)],
79+
['color_index', contain_exactly(
80+
a_hash_including('name' => 'blue', 'short_name' => 'blu'),
81+
a_hash_including('name' => 'black', 'short_name' => 'bla')
82+
)]
83+
)
84+
end
85+
86+
it 'warns about changed behavior' do
87+
result.each(&:to_s)
88+
89+
expect(logger).to have_received(:info).with(a_string_including('#each on a multi search now iterates through grouped results.'))
90+
end
91+
end
92+
93+
describe '#each_hit' do
94+
let(:logger) { instance_double('Logger', warn: nil) }
95+
96+
before do
97+
allow(MeiliSearch::Rails).to receive(:logger).and_return(logger)
98+
end
99+
100+
it 'warns about deprecation' do
101+
result.each_hit(&:to_s)
102+
103+
expect(logger).to have_received(:warn).with(a_string_including('Flattening multi search'))
104+
end
105+
end
106+
63107
describe '#to_a' do
108+
let(:logger) { instance_double('Logger', warn: nil) }
109+
110+
before do
111+
allow(MeiliSearch::Rails).to receive(:logger).and_return(logger)
112+
end
113+
64114
it 'returns the hits' do
65115
expect(result.to_a).to contain_exactly(
66116
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs'),
@@ -73,6 +123,12 @@
73123
it 'aliases as #to_ary' do
74124
expect(result.method(:to_ary).original_name).to eq :to_a
75125
end
126+
127+
it 'warns about deprecation' do
128+
result.to_a
129+
130+
expect(logger).to have_received(:warn).with(a_string_including('Flattening multi search'))
131+
end
76132
end
77133

78134
describe '#to_h' do

spec/multi_search_spec.rb

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
require 'spec_helper'
2+
require 'support/models/book'
3+
require 'support/models/product'
4+
require 'support/models/color'
25

36
describe 'multi-search' do
47
def reset_indexes
@@ -43,6 +46,65 @@ def reset_indexes
4346
end
4447
end
4548

49+
context 'with arbitrary keys' do
50+
context 'when index_name is not present' do
51+
it 'assumes key is index and errors' do
52+
expect do
53+
MeiliSearch::Rails.multi_search(
54+
'test_group' => { q: 'Steve' }
55+
)
56+
end.to raise_error(MeiliSearch::ApiError)
57+
end
58+
end
59+
60+
context 'when :index_name is present' do
61+
it 'searches the correct index' do
62+
results = MeiliSearch::Rails.multi_search(
63+
'books' => { q: 'Steve', index_name: Book.index.uid },
64+
'products' => { q: 'palm', index_name: Product.index.uid, limit: 1 },
65+
'colors' => { q: 'bl', index_name: Color.index.uid }
66+
)
67+
68+
expect(results).to contain_exactly(
69+
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs'),
70+
a_hash_including('name' => 'palm pixi plus'),
71+
a_hash_including('name' => 'blue', 'short_name' => 'blu'),
72+
a_hash_including('name' => 'black', 'short_name' => 'bla')
73+
)
74+
end
75+
76+
it 'allows searching the same index n times' do
77+
index_name = Color.index.uid
78+
79+
results = MeiliSearch::Rails.multi_search(
80+
'dark_colors' => { q: 'black', index_name: index_name },
81+
'bright_colors' => { q: 'blue', index_name: index_name },
82+
'nature_colors' => { q: 'green', index_name: index_name }
83+
)
84+
85+
expect(results).to contain_exactly(
86+
a_hash_including('name' => 'blue', 'short_name' => 'blu'),
87+
a_hash_including('name' => 'black', 'short_name' => 'bla'),
88+
a_hash_including('name' => 'green', 'short_name' => 'gre')
89+
)
90+
end
91+
92+
context 'when :class_name is also present' do
93+
it 'loads results from the correct models' do
94+
results = MeiliSearch::Rails.multi_search(
95+
'books' => { q: 'Steve', index_name: Book.index.uid, class_name: 'Book' },
96+
'products' => { q: 'palm', limit: 1, index_name: Product.index.uid, class_name: 'Product' },
97+
'colors' => { q: 'bl', index_name: Color.index.uid, class_name: 'Color' }
98+
)
99+
100+
expect(results).to contain_exactly(
101+
steve_jobs, palm_pixi_plus, blue, black
102+
)
103+
end
104+
end
105+
end
106+
end
107+
46108
context 'with index name keys' do
47109
it 'returns hashes' do
48110
results = MeiliSearch::Rails.multi_search(

0 commit comments

Comments
 (0)