Skip to content

Commit 8cb1a8d

Browse files
committed
Remove :class_name option in favor of :scope
No soft deprecation here, since federated search has not been published yet.
1 parent 84232fd commit 8cb1a8d

File tree

4 files changed

+87
-52
lines changed

4 files changed

+87
-52
lines changed

README.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,8 @@ Federated search is similar to multi search, except that results are not grouped
331331
```ruby
332332
results = Meilisearch::Rails.federated_search(
333333
queries: [
334-
{ q: 'Harry', class_name: 'Book' },
335-
{ q: 'Attack on Titan', class_name: 'Manga' }
334+
{ q: 'Harry', scope: Book.all },
335+
{ q: 'Attack on Titan', scope: Manga.all }
336336
]
337337
)
338338
```
@@ -370,30 +370,31 @@ results = Meilisearch::Rails.federated_search(
370370
```ruby
371371
results = Meilisearch::Rails.federated_search(
372372
queries: {
373-
'books_production' => { q: 'Harry', class_name: 'Book' },
374-
'mangas_production' => { q: 'Attack on Titan', class_name: 'Manga' }
373+
'books_production' => { q: 'Harry', scope: Book.all },
374+
'mangas_production' => { q: 'Attack on Titan', scope: Manga.all }
375375
}
376376
)
377377
```
378378

379379
```ruby
380380
results = Meilisearch::Rails.federated_search(
381381
queries: {
382-
'potter' => { q: 'Harry', class_name: 'Book', index_uid: 'books_production' },
383-
'titan' => { q: 'Attack on Titan', class_name: 'Manga', index_uid: 'mangas_production' }
382+
'potter' => { q: 'Harry', scope: Book.all, index_uid: 'books_production' },
383+
'titan' => { q: 'Attack on Titan', scope: Manga.all, index_uid: 'mangas_production' }
384384
}
385385
)
386386
```
387387

388388
### Loading records <!-- omit in toc -->
389389

390-
Records are loaded when the `:class_name` option is passed, or when a hash query is used with models as keys:
390+
Records are loaded when the `:scope` option is passed (may be a model or a relation),
391+
or when a hash query is used with models as keys:
391392

392393
```ruby
393394
results = Meilisearch::Rails.federated_search(
394395
queries: [
395-
{ q: 'Harry', class_name: 'Book' },
396-
{ q: 'Attack on Titan', class_name: 'Manga' },
396+
{ q: 'Harry', scope: Book },
397+
{ q: 'Attack on Titan', scope: Manga },
397398
]
398399
)
399400
```
@@ -409,6 +410,19 @@ results = Meilisearch::Rails.federated_search(
409410

410411
If the model is not provided, hashes are returned!
411412

413+
### Scoping records <!-- omit in toc -->
414+
415+
Any relation passed as `:scope` is used as the starting point when loading records:
416+
417+
```ruby
418+
results = Meilisearch::Rails.federated_search(
419+
queries: [
420+
{ q: 'Harry', scope: Book.where('year <= 2006') },
421+
{ q: 'Attack on Titan', scope: Manga.where(author: Author.find_by(name: 'Iseyama')) },
422+
]
423+
)
424+
```
425+
412426
### Specifying the search index <!-- omit in toc -->
413427

414428
In order of precedence, to figure out which index to search, Meilisearch Rails will check:
@@ -418,7 +432,7 @@ In order of precedence, to figure out which index to search, Meilisearch Rails w
418432
results = Meilisearch::Rails.federated_search(
419433
queries: [
420434
# Searching the 'fantasy_books' index
421-
{ q: 'Harry', class_name: 'Book', index_uid: 'fantasy_books' },
435+
{ q: 'Harry', scope: Book, index_uid: 'fantasy_books' },
422436
]
423437
)
424438
```
@@ -428,7 +442,7 @@ In order of precedence, to figure out which index to search, Meilisearch Rails w
428442
queries: [
429443
# Searching the index associated with the Book model
430444
# i. e. Book.index.uid
431-
{ q: 'Harry', class_name: 'Book' },
445+
{ q: 'Harry', scope: Book },
432446
]
433447
)
434448
```
@@ -437,7 +451,7 @@ In order of precedence, to figure out which index to search, Meilisearch Rails w
437451
results = Meilisearch::Rails.federated_search(
438452
queries: {
439453
# Searching index 'books_production'
440-
books_production: { q: 'Harry', class_name: 'Book' },
454+
books_production: { q: 'Harry', scope: Book },
441455
}
442456
)
443457
```
@@ -449,8 +463,8 @@ In addition to queries, federated search also accepts `:federation` parameters w
449463
```ruby
450464
results = Meilisearch::Rails.federated_search(
451465
queries: [
452-
{ q: 'Harry', class_name: 'Book' },
453-
{ q: 'Attack on Titan', class_name: 'Manga' },
466+
{ q: 'Harry', scope: Book },
467+
{ q: 'Attack on Titan', scope: Manga },
454468
],
455469
federation: { offset: 10, limit: 5 }
456470
)

lib/meilisearch/rails/multi_search.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ def federated_search(queries:, federation: {})
2828
queries.map! { |item| [nil, item] } if queries.is_a?(Array)
2929

3030
cleaned_queries = queries.filter_map do |(index_target, options)|
31-
index_target = options.delete(:index_uid) || index_target || options[:class_name]&.constantize
31+
model_class = options[:scope].respond_to?(:model) ? options[:scope].model : options[:scope]
32+
index_target = options.delete(:index_uid) || index_target || model_class
3233

3334
strip_pagination_options(options)
3435
normalize(options, index_target)

lib/meilisearch/rails/multi_search/federated_search_result.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ def load_hits(hits, searches)
2323
keys_and_records_by_pos = hits_by_pos.to_h do |pos, group_hits|
2424
search_target, search_opts = searches[pos]
2525

26-
klass = if search_opts[:class_name]
27-
search_opts[:class_name].constantize
26+
scope = if search_opts[:scope]
27+
search_opts[:scope]
2828
elsif search_target.instance_of?(Class)
2929
search_target
3030
end
3131

32-
if klass.present?
33-
[pos, load_results(klass, group_hits)]
32+
if scope.present?
33+
[pos, load_results(scope, group_hits)]
3434
else
3535
[pos, [nil, group_hits]]
3636
end
@@ -49,15 +49,17 @@ def load_hits(hits, searches)
4949
end
5050
end
5151

52-
def load_results(klass, hits)
52+
def load_results(scope, hits)
53+
klass = scope.respond_to?(:model) ? scope.model : scope
54+
5355
pk_method = klass.ms_primary_key_method
5456
pk_method = pk_method.in if Utilities.mongo_model?(klass)
5557

5658
condition_key = pk_is_virtual?(klass, pk_method) ? klass.primary_key : pk_method
5759

5860
hits_by_id = hits.index_by { |hit| hit[condition_key.to_s] }
5961

60-
records = klass.where(condition_key => hits_by_id.keys)
62+
records = scope.where(condition_key => hits_by_id.keys)
6163

6264
results_by_id = records.index_by do |record|
6365
record.send(condition_key).to_s

spec/federated_search_spec.rb

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
it 'ranks better match above worse match' do
4747
results = Meilisearch::Rails.federated_search(
4848
queries: [
49-
{ q: 'Steve', class_name: 'Book' },
50-
{ q: 'black', class_name: 'Color' }
49+
{ q: 'Steve', scope: Book.all },
50+
{ q: 'black', scope: Color.all }
5151
]
5252
)
5353

@@ -57,11 +57,11 @@
5757

5858
context 'when :index_uid is passed' do
5959
it 'takes precedence over other sources of index uids' do
60-
Meilisearch::Rails.client.create_index('temp_books').await
60+
Meilisearch::Rails.client.create_index('temp_books')
6161
Meilisearch::Rails.client.swap_indexes(['temp_books', Book.index.uid]).await
6262

6363
results = Meilisearch::Rails.federated_search(
64-
queries: [{ q: 'Moby', class_name: 'Book', index_uid: 'temp_books' }]
64+
queries: [{ q: 'Moby', scope: Book.all, index_uid: 'temp_books' }]
6565
)
6666

6767
expect(results).to contain_exactly(books['Moby Dick'])
@@ -70,23 +70,41 @@
7070
end
7171
end
7272

73-
context 'when :class_name is passed' do
74-
it 'returns ORM records with inferred index names' do
75-
results = Meilisearch::Rails.federated_search(
76-
queries: [
77-
{ q: 'Steve', class_name: 'Book' },
78-
{ q: 'palm', class_name: 'Product' },
79-
{ q: 'bl', class_name: 'Color' }
80-
]
81-
)
73+
context 'when :scope is passed' do
74+
context 'when :scope is a model' do
75+
it 'returns ORM records with inferred index names' do
76+
results = Meilisearch::Rails.federated_search(
77+
queries: [
78+
{ q: 'Steve', scope: Book },
79+
{ q: 'palm', scope: Product },
80+
{ q: 'bl', scope: Color }
81+
]
82+
)
8283

83-
expect(results).to contain_exactly(
84-
books['Steve Jobs'], products['palmpre'], products['palm pixi plus'], colors['blue'], colors['black']
85-
)
84+
expect(results).to contain_exactly(
85+
books['Steve Jobs'], products['palmpre'], products['palm pixi plus'], colors['blue'], colors['black']
86+
)
87+
end
88+
end
89+
90+
context 'when :scope is a Relation' do
91+
it 'returns results within scope' do
92+
results = Meilisearch::Rails.federated_search(
93+
queries: [
94+
{ q: 'Steve', scope: Book },
95+
{ q: 'palm', scope: Product.where(name: 'palmpre') },
96+
{ q: 'bl', scope: Color }
97+
]
98+
)
99+
100+
expect(results).to contain_exactly(
101+
books['Steve Jobs'], products['palmpre'], colors['blue'], colors['black']
102+
)
103+
end
86104
end
87105
end
88106

89-
context 'without :class_name' do
107+
context 'without :scope' do
90108
it 'returns raw hashes' do
91109
results = Meilisearch::Rails.federated_search(
92110
queries: [{ q: 'Steve', index_uid: Book.index.uid }]
@@ -99,13 +117,13 @@
99117

100118
context 'with queries passed as a hash' do
101119
context 'when the keys are index names' do
102-
it 'loads the right models with :class_name' do
120+
it 'loads the right models with :scope' do
103121
Meilisearch::Rails.client.create_index('temp_books').await
104122
Meilisearch::Rails.client.swap_indexes(['temp_books', Book.index.uid]).await
105123

106124
results = Meilisearch::Rails.federated_search(
107125
queries: {
108-
'temp_books' => { q: 'Steve', class_name: 'Book' }
126+
'temp_books' => { q: 'Steve', scope: Book }
109127
}
110128
)
111129

@@ -114,7 +132,7 @@
114132
Meilisearch::Rails.client.delete_index('temp_books')
115133
end
116134

117-
it 'returns hashes without :class_name' do
135+
it 'returns hashes without :scope' do
118136
results = Meilisearch::Rails.federated_search(
119137
queries: {
120138
Book.index.uid => { q: 'Steve' }
@@ -159,7 +177,7 @@
159177

160178
results = Meilisearch::Rails.federated_search(
161179
queries: {
162-
classics: { q: 'Moby', class_name: 'Book', index_uid: 'temp_books' }
180+
classics: { q: 'Moby', scope: Book, index_uid: 'temp_books' }
163181
}
164182
)
165183

@@ -171,7 +189,7 @@
171189
it 'requires :index_uid to search the correct index' do
172190
expect do
173191
Meilisearch::Rails.federated_search(
174-
queries: { all_books: { q: 'Moby', class_name: 'Book' } }
192+
queries: { all_books: { q: 'Moby', scope: Book } }
175193
)
176194
end.to raise_error(Meilisearch::ApiError).with_message(/Index `all_books` not found/)
177195
end
@@ -185,13 +203,13 @@
185203
allow(Meilisearch::Rails).to receive(:logger).and_return(logger)
186204
end
187205

188-
it 'warns if query has pagination options' do
206+
it 'warns if query has pagination options, but completes the search anyway' do
189207
results = Meilisearch::Rails.federated_search(
190208
queries: [
191-
{ q: 'Steve', class_name: 'Book', limit: 1 },
192-
{ q: 'No results please', class_name: 'Book', offset: 1 },
193-
{ q: 'No results please', class_name: 'Book', hits_per_page: 1 },
194-
{ q: 'No results please', class_name: 'Book', page: 1 }
209+
{ q: 'Steve', scope: Book, limit: 1 },
210+
{ q: 'No results please', scope: Book, offset: 1 },
211+
{ q: 'No results please', scope: Book, hits_per_page: 1 },
212+
{ q: 'No results please', scope: Book, page: 1 }
195213
]
196214
)
197215

@@ -203,19 +221,19 @@
203221
expect(results).to contain_exactly(books['Steve Jobs'])
204222
end
205223

206-
it 'warns if :class_name argument is not a meilisearch model' do
224+
it 'warns if :scope model is not a meilisearch model' do
207225
results = Meilisearch::Rails.federated_search(
208-
queries: [{ q: 'Steve', class_name: 'String' }]
226+
queries: [{ q: 'Steve', scope: String }]
209227
)
210228

211229
expect(logger).to have_received('warn').with(a_string_including('does not have an #index'))
212230
expect(results).to be_empty
213231
end
214232

215-
it 'warns if :federation argument is nil' do
233+
it 'warns if :federation argument is nil, but completes search anyway' do
216234
# This would disable federated search if not caught
217235
results = Meilisearch::Rails.federated_search(
218-
queries: [{ q: 'Steve', class_name: 'Book' }],
236+
queries: [{ q: 'Steve', scope: Book }],
219237
federation: nil
220238
)
221239

0 commit comments

Comments
 (0)