Skip to content

Commit bf13815

Browse files
committed
Add specs for FederatedSearchResult
1 parent fc4d29a commit bf13815

File tree

3 files changed

+276
-0
lines changed

3 files changed

+276
-0
lines changed

spec/federated_search_spec.rb

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
require 'spec_helper'
2+
require 'support/async_helper'
3+
require 'support/models/book'
4+
require 'support/models/product'
5+
require 'support/models/color'
6+
7+
describe 'federated-search' do
8+
before do
9+
[Book, Color, Product].each(&:delete_all)
10+
[Book, Color, Product].each(&:clear_index!)
11+
12+
# rubocop:disable Rails::SkipsModelValidations
13+
Product.insert_all([
14+
{ name: 'palm pixi plus', href: 'ebay', tags: ['terrible'] },
15+
{ name: 'lg vortex', href: 'ebay', tags: ['decent'] },
16+
{ name: 'palmpre', href: 'ebay', tags: ['discontinued', 'worst phone ever'] }
17+
])
18+
19+
Color.insert_all([
20+
{ name: 'blue', short_name: 'blu', hex: 0x0000FF },
21+
{ name: 'black', short_name: 'bla', hex: 0x000000 },
22+
{ name: 'green', short_name: 'gre', hex: 0x00FF00 }
23+
])
24+
25+
Book.insert_all([
26+
{ name: 'Steve Jobs', author: 'Walter Isaacson' },
27+
{ name: 'Moby Dick', author: 'Herman Melville' }
28+
])
29+
# rubocop:enable Rails::SkipsModelValidations
30+
31+
[Book, Color, Product].each(&:reindex!)
32+
AsyncHelper.await_last_task
33+
end
34+
35+
let!(:products) do
36+
Product.all.index_by(&:name)
37+
end
38+
39+
let!(:books) do
40+
Book.all.index_by(&:name)
41+
end
42+
43+
let!(:colors) do
44+
Color.all.index_by(&:name)
45+
end
46+
47+
context 'with queries passed as arrays' do
48+
it 'ranks better match above worse match' do
49+
results = Meilisearch::Rails.federated_search(
50+
queries: [
51+
{ q: 'Steve', class_name: 'Book' },
52+
{ q: 'black', class_name: 'Color' }
53+
]
54+
)
55+
56+
expect(results.first).to eq(colors['black'])
57+
expect(results.last).to eq(books['Steve Jobs'])
58+
end
59+
60+
context 'when :index_uid is passed' do
61+
it 'takes precedence over other sources of index uids' do
62+
Meilisearch::Rails.client.create_index('temp_books').await
63+
Meilisearch::Rails.client.swap_indexes(['temp_books', Book.index.uid]).await
64+
65+
results = Meilisearch::Rails.federated_search(
66+
queries: [{ q: 'Moby', class_name: 'Book', index_uid: 'temp_books' }]
67+
)
68+
69+
expect(results).to contain_exactly(books['Moby Dick'])
70+
71+
Meilisearch::Rails.client.delete_index('temp_books')
72+
end
73+
end
74+
75+
context 'when :class_name is passed' do
76+
it 'returns ORM records with inferred index names' do
77+
results = Meilisearch::Rails.federated_search(
78+
queries: [
79+
{ q: 'Steve', class_name: 'Book' },
80+
{ q: 'palm', class_name: 'Product' },
81+
{ q: 'bl', class_name: 'Color' }
82+
]
83+
)
84+
85+
expect(results).to contain_exactly(
86+
books['Steve Jobs'], products['palmpre'], products['palm pixi plus'], colors['blue'], colors['black']
87+
)
88+
end
89+
end
90+
91+
context 'without :class_name' do
92+
it 'returns raw hashes' do
93+
results = Meilisearch::Rails.federated_search(
94+
queries: [{ q: 'Steve', index_uid: Book.index.uid }]
95+
)
96+
97+
expect(results).to contain_exactly(a_hash_including('name' => 'Steve Jobs'))
98+
end
99+
end
100+
end
101+
102+
context 'with queries passed as a hash' do
103+
context 'when the keys are index names' do
104+
it 'loads the right models with :class_name' do
105+
Meilisearch::Rails.client.create_index('temp_books').await
106+
Meilisearch::Rails.client.swap_indexes(['temp_books', Book.index.uid]).await
107+
108+
results = Meilisearch::Rails.federated_search(
109+
queries: {
110+
'temp_books' => { q: 'Steve', class_name: 'Book' }
111+
}
112+
)
113+
114+
expect(results).to contain_exactly(books['Steve Jobs'])
115+
116+
Meilisearch::Rails.client.delete_index('temp_books')
117+
end
118+
119+
it 'returns hashes without :class_name' do
120+
results = Meilisearch::Rails.federated_search(
121+
queries: {
122+
Book.index.uid => { q: 'Steve' }
123+
}
124+
)
125+
126+
expect(results).to contain_exactly(a_hash_including('name' => 'Steve Jobs'))
127+
end
128+
end
129+
130+
context 'when the keys are models' do
131+
it 'loads the correct models' do
132+
results = Meilisearch::Rails.federated_search(
133+
queries: {
134+
Book => { q: 'Steve' }
135+
}
136+
)
137+
138+
expect(results).to contain_exactly(books['Steve Jobs'])
139+
end
140+
141+
it 'allows overriding index_uid' do
142+
Meilisearch::Rails.client.create_index('temp_books').await
143+
Meilisearch::Rails.client.swap_indexes(['temp_books', Book.index.uid]).await
144+
145+
results = Meilisearch::Rails.federated_search(
146+
queries: {
147+
Book => { q: 'Steve', index_uid: 'temp_books' }
148+
}
149+
)
150+
151+
expect(results).to contain_exactly(books['Steve Jobs'])
152+
153+
Meilisearch::Rails.client.delete_index('temp_books')
154+
end
155+
end
156+
157+
context 'when the keys are arbitrary' do
158+
it 'acts the same as if the keys were arrays' do
159+
Meilisearch::Rails.client.create_index('temp_books').await
160+
Meilisearch::Rails.client.swap_indexes(['temp_books', Book.index.uid]).await
161+
162+
results = Meilisearch::Rails.federated_search(
163+
queries: {
164+
classics: { q: 'Moby', class_name: 'Book', index_uid: 'temp_books' }
165+
}
166+
)
167+
168+
expect(results).to contain_exactly(books['Moby Dick'])
169+
170+
Meilisearch::Rails.client.delete_index('temp_books')
171+
end
172+
173+
it 'requires :index_uid to search the correct index' do
174+
expect do
175+
Meilisearch::Rails.federated_search(
176+
queries: { all_books: { q: 'Moby', class_name: 'Book' } }
177+
)
178+
end.to raise_error(Meilisearch::ApiError).with_message(/Index `all_books` not found/)
179+
end
180+
end
181+
end
182+
183+
describe 'warnings' do
184+
let(:logger) { instance_double('Logger', warn: nil) }
185+
186+
before do
187+
allow(Meilisearch::Rails).to receive(:logger).and_return(logger)
188+
end
189+
190+
it 'warns if query has pagination options' do
191+
results = Meilisearch::Rails.federated_search(
192+
queries: [
193+
{ q: 'Steve', class_name: 'Book', limit: 1 },
194+
{ q: 'No results please', class_name: 'Book', offset: 1 },
195+
{ q: 'No results please', class_name: 'Book', hits_per_page: 1 },
196+
{ q: 'No results please', class_name: 'Book', page: 1 }
197+
]
198+
)
199+
200+
expect(logger).to have_received('warn').with(a_string_including('options have been removed: limit'))
201+
expect(logger).to have_received('warn').with(a_string_including('options have been removed: offset'))
202+
expect(logger).to have_received('warn').with(a_string_including('options have been removed: hits_per_page'))
203+
expect(logger).to have_received('warn').with(a_string_including('options have been removed: page'))
204+
205+
expect(results).to contain_exactly(books['Steve Jobs'])
206+
end
207+
208+
it 'warns if :class_name argument is not a meilisearch model' do
209+
results = Meilisearch::Rails.federated_search(
210+
queries: [{ q: 'Steve', class_name: 'String' }]
211+
)
212+
213+
expect(logger).to have_received('warn').with(a_string_including('does not have an #index'))
214+
expect(results).to be_empty
215+
end
216+
217+
it 'warns if :federation argument is nil' do
218+
# This would disable federated search if not caught
219+
results = Meilisearch::Rails.federated_search(
220+
queries: [{ q: 'Steve', class_name: 'Book' }],
221+
federation: nil
222+
)
223+
224+
expect(logger).to have_received('warn').with(a_string_including('`nil` is an invalid `:federation` option.'))
225+
expect(results).to contain_exactly(books['Steve Jobs'])
226+
end
227+
end
228+
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
require 'spec_helper'
2+
3+
describe Meilisearch::Rails::FederatedSearchResult do # rubocop:todo RSpec/FilePath
4+
subject(:result) { described_class.new(searches, raw_results) }
5+
6+
let(:raw_results) do
7+
{
8+
'hits' => [
9+
{ 'name' => 'Steve Jobs', 'id' => '3', 'author' => 'Walter Isaacson', 'premium' => nil, 'released' => nil, 'genre' => nil,
10+
'_federation' => { 'queriesPosition' => 0 } },
11+
{ 'id' => '4', 'href' => 'ebay', 'name' => 'palm pixi plus', '_federation' => { 'queriesPosition' => 1 } },
12+
{ 'name' => 'black', 'id' => '5', 'short_name' => 'bla', 'hex' => 0, '_federation' => { 'queriesPosition' => 2 } },
13+
{ 'name' => 'blue', 'id' => '4', 'short_name' => 'blu', 'hex' => 255, '_federation' => { 'queriesPosition' => 2 } }
14+
],
15+
'offset' => 10,
16+
'limit' => 5
17+
}
18+
end
19+
20+
let(:searches) do
21+
{
22+
'books_index' => { q: 'Steve' },
23+
'products_index' => { q: 'palm' },
24+
'color_index' => { q: 'bl' }
25+
}
26+
end
27+
28+
it 'is enumerable' do
29+
expect(described_class).to include(Enumerable)
30+
end
31+
32+
context 'with index name keys' do
33+
it 'enumerates through the hits' do
34+
expect(result).to contain_exactly(
35+
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs'),
36+
a_hash_including('name' => 'palm pixi plus'),
37+
a_hash_including('name' => 'blue', 'short_name' => 'blu'),
38+
a_hash_including('name' => 'black', 'short_name' => 'bla')
39+
)
40+
end
41+
end
42+
43+
describe '#metadata' do
44+
it 'returns search metadata for the search' do
45+
expect(result.metadata).to eq({ 'offset' => 10, 'limit' => 5 })
46+
end
47+
end
48+
end
File renamed without changes.

0 commit comments

Comments
 (0)