Skip to content

Commit 8479765

Browse files
authored
Merge pull request #625 from LukasKalbertodt/implement-federated-multi-search
Implement federated multi search
2 parents 7d0d3ee + 9958a86 commit 8479765

File tree

4 files changed

+322
-54
lines changed

4 files changed

+322
-54
lines changed

meilisearch-test-macro/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ There are a few rules, though:
6868
- `String`: It returns the name of the test.
6969
- `Client`: It creates a client like that: `Client::new("http://localhost:7700", "masterKey")`.
7070
- `Index`: It creates and deletes an index, as we've seen before.
71+
You can include multiple `Index` parameter to automatically create multiple indices.
7172

7273
2. You only get what you asked for. That means if you don't ask for an index, no index will be created in meilisearch.
7374
So, if you are testing the creation of indexes, you can ask for a `Client` and a `String` and then create it yourself.

meilisearch-test-macro/src/lib.rs

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ pub fn meilisearch_test(params: TokenStream, input: TokenStream) -> TokenStream
7777
let use_name = params
7878
.iter()
7979
.any(|param| matches!(param, Param::String | Param::Index));
80-
let use_index = params.contains(&Param::Index);
8180

8281
// Now we are going to build the body of the outer function
8382
let mut outer_block: Vec<Stmt> = Vec::new();
@@ -106,59 +105,77 @@ pub fn meilisearch_test(params: TokenStream, input: TokenStream) -> TokenStream
106105
));
107106
}
108107

108+
let index_var = |idx: usize| Ident::new(&format!("index_{idx}"), Span::call_site());
109+
109110
// And finally if an index was asked, we delete it, and we (re)create it and wait until meilisearch confirm its creation.
110-
if use_index {
111-
outer_block.push(parse_quote!({
112-
let res = client
113-
.delete_index(&name)
114-
.await
115-
.expect("Network issue while sending the delete index task")
116-
.wait_for_completion(&client, None, None)
117-
.await
118-
.expect("Network issue while waiting for the index deletion");
119-
if res.is_failure() {
120-
let error = res.unwrap_failure();
121-
assert_eq!(
122-
error.error_code,
123-
crate::errors::ErrorCode::IndexNotFound,
124-
"{:?}",
125-
error
126-
);
127-
}
128-
}));
111+
for (i, param) in params.iter().enumerate() {
112+
if !matches!(param, Param::Index) {
113+
continue;
114+
}
129115

116+
let var_name = index_var(i);
130117
outer_block.push(parse_quote!(
131-
let index = client
132-
.create_index(&name, None)
133-
.await
134-
.expect("Network issue while sending the create index task")
135-
.wait_for_completion(&client, None, None)
136-
.await
137-
.expect("Network issue while waiting for the index creation")
138-
.try_make_index(&client)
139-
.expect("Could not create the index out of the create index task");
118+
let #var_name = {
119+
let index_uid = format!("{name}_{}", #i);
120+
let res = client
121+
.delete_index(&index_uid)
122+
.await
123+
.expect("Network issue while sending the delete index task")
124+
.wait_for_completion(&client, None, None)
125+
.await
126+
.expect("Network issue while waiting for the index deletion");
127+
128+
if res.is_failure() {
129+
let error = res.unwrap_failure();
130+
assert_eq!(
131+
error.error_code,
132+
crate::errors::ErrorCode::IndexNotFound,
133+
"{:?}",
134+
error
135+
);
136+
}
137+
138+
client
139+
.create_index(&index_uid, None)
140+
.await
141+
.expect("Network issue while sending the create index task")
142+
.wait_for_completion(&client, None, None)
143+
.await
144+
.expect("Network issue while waiting for the index creation")
145+
.try_make_index(&client)
146+
.expect("Could not create the index out of the create index task")
147+
};
140148
));
141149
}
142150

143151
// Create a list of params separated by comma with the name we defined previously.
144-
let params: Vec<Expr> = params
145-
.into_iter()
146-
.map(|param| match param {
152+
let args: Vec<Expr> = params
153+
.iter()
154+
.enumerate()
155+
.map(|(i, param)| match param {
147156
Param::Client => parse_quote!(client),
148-
Param::Index => parse_quote!(index),
157+
Param::Index => {
158+
let var = index_var(i);
159+
parse_quote!(#var)
160+
}
149161
Param::String => parse_quote!(name),
150162
})
151163
.collect();
152164

153165
// Now we can call the user code with our parameters :tada:
154166
outer_block.push(parse_quote!(
155-
let result = #inner_ident(#(#params.clone()),*).await;
167+
let result = #inner_ident(#(#args.clone()),*).await;
156168
));
157169

158170
// And right before the end, if an index was created and the tests successfully executed we delete it.
159-
if use_index {
171+
for (i, param) in params.iter().enumerate() {
172+
if !matches!(param, Param::Index) {
173+
continue;
174+
}
175+
176+
let var_name = index_var(i);
160177
outer_block.push(parse_quote!(
161-
index
178+
#var_name
162179
.delete()
163180
.await
164181
.expect("Network issue while sending the last delete index task");

src/client.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,21 @@ impl<Http: HttpClient> Client<Http> {
128128
.await
129129
}
130130

131+
pub async fn execute_federated_multi_search_query<
132+
T: 'static + DeserializeOwned + Send + Sync,
133+
>(
134+
&self,
135+
body: &FederatedMultiSearchQuery<'_, '_, Http>,
136+
) -> Result<FederatedMultiSearchResponse<T>, Error> {
137+
self.http_client
138+
.request::<(), &FederatedMultiSearchQuery<Http>, FederatedMultiSearchResponse<T>>(
139+
&format!("{}/multi-search", &self.host),
140+
Method::Post { body, query: () },
141+
200,
142+
)
143+
.await
144+
}
145+
131146
/// Make multiple search requests.
132147
///
133148
/// # Example
@@ -170,6 +185,22 @@ impl<Http: HttpClient> Client<Http> {
170185
/// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
171186
/// # });
172187
/// ```
188+
///
189+
/// # Federated Search
190+
///
191+
/// You can use [`MultiSearchQuery::with_federation`] to perform a [federated
192+
/// search][1] where results from different indexes are merged and returned as
193+
/// one list.
194+
///
195+
/// When executing a federated query, the type parameter `T` is less clear,
196+
/// as the documents in the different indexes potentially have different
197+
/// fields and you might have one Rust type per index. In most cases, you
198+
/// either want to create an enum with one variant per index and `#[serde
199+
/// (untagged)]` attribute, or if you need more control, just pass
200+
/// `serde_json::Map<String, serde_json::Value>` and then deserialize that
201+
/// into the appropriate target types later.
202+
///
203+
/// [1]: https://www.meilisearch.com/docs/learn/multi_search/multi_search_vs_federated_search#what-is-federated-search
173204
#[must_use]
174205
pub fn multi_search(&self) -> MultiSearchQuery<Http> {
175206
MultiSearchQuery::new(self)

0 commit comments

Comments
 (0)