Skip to content

Commit da5d6f5

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 93f5a4a commit da5d6f5

File tree

4 files changed

+88
-53
lines changed

4 files changed

+88
-53
lines changed

README.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,8 @@ Federated search is similar to multi search, except that results are not grouped
328328
```ruby
329329
results = Meilisearch::Rails.federated_search(
330330
queries: [
331-
{ q: 'Harry', class_name: 'Book' },
332-
{ q: 'Attack on Titan', class_name: 'Manga' }
331+
{ q: 'Harry', scope: Book.all },
332+
{ q: 'Attack on Titan', scope: Manga.all }
333333
]
334334
)
335335
```
@@ -367,30 +367,31 @@ results = Meilisearch::Rails.federated_search(
367367
```ruby
368368
results = Meilisearch::Rails.federated_search(
369369
queries: {
370-
'books_production' => { q: 'Harry', class_name: 'Book' },
371-
'mangas_production' => { q: 'Attack on Titan', class_name: 'Manga' }
370+
'books_production' => { q: 'Harry', scope: Book.all },
371+
'mangas_production' => { q: 'Attack on Titan', scope: Manga.all }
372372
}
373373
)
374374
```
375375

376376
```ruby
377377
results = Meilisearch::Rails.federated_search(
378378
queries: {
379-
'potter' => { q: 'Harry', class_name: 'Book', index_uid: 'books_production' },
380-
'titan' => { q: 'Attack on Titan', class_name: 'Manga', index_uid: 'mangas_production' }
379+
'potter' => { q: 'Harry', scope: Book.all, index_uid: 'books_production' },
380+
'titan' => { q: 'Attack on Titan', scope: Manga.all, index_uid: 'mangas_production' }
381381
}
382382
)
383383
```
384384

385385
### Loading records <!-- omit in toc -->
386386

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

389390
```ruby
390391
results = Meilisearch::Rails.federated_search(
391392
queries: [
392-
{ q: 'Harry', class_name: 'Book' },
393-
{ q: 'Attack on Titan', class_name: 'Manga' },
393+
{ q: 'Harry', scope: Book },
394+
{ q: 'Attack on Titan', scope: Manga },
394395
]
395396
)
396397
```
@@ -406,6 +407,19 @@ results = Meilisearch::Rails.federated_search(
406407

407408
If the model is not provided, hashes are returned!
408409

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

411425
In order of precedence, to figure out which index to search, Meilisearch Rails will check:
@@ -415,7 +429,7 @@ In order of precedence, to figure out which index to search, Meilisearch Rails w
415429
results = Meilisearch::Rails.federated_search(
416430
queries: [
417431
# Searching the 'fantasy_books' index
418-
{ q: 'Harry', class_name: 'Book', index_uid: 'fantasy_books' },
432+
{ q: 'Harry', scope: Book, index_uid: 'fantasy_books' },
419433
]
420434
)
421435
```
@@ -425,7 +439,7 @@ In order of precedence, to figure out which index to search, Meilisearch Rails w
425439
queries: [
426440
# Searching the index associated with the Book model
427441
# i. e. Book.index.uid
428-
{ q: 'Harry', class_name: 'Book' },
442+
{ q: 'Harry', scope: Book },
429443
]
430444
)
431445
```
@@ -434,7 +448,7 @@ In order of precedence, to figure out which index to search, Meilisearch Rails w
434448
results = Meilisearch::Rails.federated_search(
435449
queries: {
436450
# Searching index 'books_production'
437-
books_production: { q: 'Harry', class_name: 'Book' },
451+
books_production: { q: 'Harry', scope: Book },
438452
}
439453
)
440454
```
@@ -446,8 +460,8 @@ In addition to queries, federated search also accepts `:federation` parameters w
446460
```ruby
447461
results = Meilisearch::Rails.federated_search(
448462
queries: [
449-
{ q: 'Harry', class_name: 'Book' },
450-
{ q: 'Attack on Titan', class_name: 'Manga' },
463+
{ q: 'Harry', scope: Book },
464+
{ q: 'Attack on Titan', scope: Manga },
451465
],
452466
federation: { offset: 10, limit: 5 }
453467
)

lib/meilisearch/rails/multi_search.rb

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

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

3233
strip_pagination_options(options)
3334
normalize(options, index_target)
@@ -46,7 +47,7 @@ def normalize(options, index_target)
4647
return nil if index_target.nil?
4748

4849
options
49-
.except(:class_name)
50+
.except(:scope, :class_name)
5051
.merge!(index_uid: index_target)
5152
end
5253

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)