Skip to content

Commit 646810d

Browse files
committed
Add multi search method for v1.1.0 of Meilisearch
1 parent 14361b5 commit 646810d

File tree

2 files changed

+136
-4
lines changed

2 files changed

+136
-4
lines changed

src/client.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use serde::{Deserialize, Serialize};
1+
use serde::{de::DeserializeOwned, Deserialize, Serialize};
22
use serde_json::{json, Value};
33
use std::{collections::HashMap, time::Duration};
44
use time::OffsetDateTime;
@@ -8,6 +8,7 @@ use crate::{
88
indexes::*,
99
key::{Key, KeyBuilder, KeyUpdater, KeysQuery, KeysResults},
1010
request::*,
11+
search::*,
1112
task_info::TaskInfo,
1213
tasks::{Task, TasksCancelQuery, TasksDeleteQuery, TasksResults, TasksSearchQuery},
1314
utils::async_sleep,
@@ -63,6 +64,67 @@ impl Client {
6364
Ok(indexes_results)
6465
}
6566

67+
pub async fn execute_multi_search_query<T: 'static + DeserializeOwned>(
68+
&self,
69+
body: &MultiSearchQuery<'_, '_>,
70+
) -> Result<MultiSearchResponse<T>, Error> {
71+
request::<(), &MultiSearchQuery, MultiSearchResponse<T>>(
72+
&format!("{}/multi-search", &self.host),
73+
&self.api_key,
74+
Method::Post { body, query: () },
75+
200,
76+
)
77+
.await
78+
}
79+
80+
/// Make multiple search requests.
81+
///
82+
/// # Example
83+
///
84+
/// ```
85+
/// use serde::{Serialize, Deserialize};
86+
/// # use meilisearch_sdk::{client::*, indexes::*, search::*};
87+
///
88+
/// #
89+
/// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
90+
/// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
91+
/// #
92+
/// #[derive(Serialize, Deserialize, Debug)]
93+
/// struct Movie {
94+
/// name: String,
95+
/// description: String,
96+
/// }
97+
///
98+
/// # futures::executor::block_on(async move {
99+
/// let client = Client::new(MEILISEARCH_URL, MEILISEARCH_API_KEY);
100+
/// let mut movies = client.index("search");
101+
///
102+
/// // add some documents
103+
/// # movies.add_or_replace(&[Movie{name:String::from("Interstellar"), description:String::from("Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.")},Movie{name:String::from("Unknown"), description:String::from("Unknown")}], Some("name")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
104+
///
105+
/// let search_query_1 = SearchQuery::new(&movies)
106+
/// .with_query("Interstellar")
107+
/// .build();
108+
/// let search_query_2 = SearchQuery::new(&movies)
109+
/// .with_query("")
110+
/// .build();
111+
///
112+
/// let response = client
113+
/// .multi_search()
114+
/// .with_search_query(search_query_1)
115+
/// .with_search_query(search_query_2)
116+
/// .execute::<Movie>()
117+
/// .await
118+
/// .unwrap();
119+
///
120+
/// assert_eq!(response.results.len(), 2);
121+
/// # movies.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
122+
/// # });
123+
/// ```
124+
pub fn multi_search(&self) -> MultiSearchQuery {
125+
MultiSearchQuery::new(self)
126+
}
127+
66128
/// Return the host associated with this index.
67129
///
68130
/// # Example

src/search.rs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::{errors::Error, indexes::Index};
1+
use crate::{client::Client, errors::Error, indexes::Index};
22
use either::Either;
33
use serde::{de::DeserializeOwned, Deserialize, Serialize, Serializer};
44
use serde_json::{Map, Value};
55
use std::collections::HashMap;
66

7-
#[derive(Deserialize, Debug, Eq, PartialEq)]
7+
#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
88
pub struct MatchRange {
99
pub start: usize,
1010
pub length: usize,
@@ -33,7 +33,7 @@ pub enum MatchingStrategies {
3333

3434
/// A single result.
3535
/// Contains the complete object, optionally the formatted object, and optionally an object that contains information about the matches.
36-
#[derive(Deserialize, Debug)]
36+
#[derive(Deserialize, Debug, Clone)]
3737
pub struct SearchResult<T> {
3838
/// The full result.
3939
#[serde(flatten)]
@@ -80,6 +80,8 @@ pub struct SearchResults<T> {
8080
pub processing_time_ms: usize,
8181
/// Query originating the response
8282
pub query: String,
83+
/// Index uid on which the search was made
84+
pub index_uid: Option<String>,
8385
}
8486

8587
fn serialize_with_wildcard<S: Serializer, T: Serialize>(
@@ -287,6 +289,9 @@ pub struct SearchQuery<'a> {
287289
/// Defines the strategy on how to handle queries containing multiple words.
288290
#[serde(skip_serializing_if = "Option::is_none")]
289291
pub matching_strategy: Option<MatchingStrategies>,
292+
293+
#[serde(skip_serializing_if = "Option::is_none")]
294+
pub(crate) index_uid: Option<&'a str>,
290295
}
291296

292297
#[allow(missing_docs)]
@@ -311,6 +316,7 @@ impl<'a> SearchQuery<'a> {
311316
highlight_post_tag: None,
312317
show_matches_position: None,
313318
matching_strategy: None,
319+
index_uid: None,
314320
}
315321
}
316322
pub fn with_query<'b>(&'b mut self, query: &'a str) -> &'b mut SearchQuery<'a> {
@@ -461,6 +467,10 @@ impl<'a> SearchQuery<'a> {
461467
self.matching_strategy = Some(matching_strategy);
462468
self
463469
}
470+
pub fn with_index_uid<'b>(&'b mut self) -> &'b mut SearchQuery<'a> {
471+
self.index_uid = Some(&self.index.uid);
472+
self
473+
}
464474
pub fn build(&mut self) -> SearchQuery<'a> {
465475
self.clone()
466476
}
@@ -472,6 +482,44 @@ impl<'a> SearchQuery<'a> {
472482
}
473483
}
474484

485+
#[derive(Debug, Serialize, Clone)]
486+
#[serde(rename_all = "camelCase")]
487+
pub struct MultiSearchQuery<'a, 'b> {
488+
#[serde(skip_serializing)]
489+
client: &'a Client,
490+
// pub sort: Option<&'a [&'a str]>,
491+
pub queries: Vec<SearchQuery<'b>>,
492+
}
493+
494+
#[allow(missing_docs)]
495+
impl<'a, 'b> MultiSearchQuery<'a, 'b> {
496+
pub fn new(client: &'a Client) -> MultiSearchQuery<'a, 'b> {
497+
MultiSearchQuery {
498+
client,
499+
queries: Vec::new(),
500+
}
501+
}
502+
pub fn with_search_query(
503+
&mut self,
504+
mut search_query: SearchQuery<'b>,
505+
) -> &mut MultiSearchQuery<'a, 'b> {
506+
search_query.with_index_uid();
507+
self.queries.push(search_query);
508+
self
509+
}
510+
511+
/// Execute the query and fetch the results.
512+
pub async fn execute<T: 'static + DeserializeOwned>(
513+
&'a self,
514+
) -> Result<MultiSearchResponse<T>, Error> {
515+
self.client.execute_multi_search_query::<T>(self).await
516+
}
517+
}
518+
#[derive(Debug, Deserialize)]
519+
pub struct MultiSearchResponse<T> {
520+
pub results: Vec<SearchResults<T>>,
521+
}
522+
475523
#[cfg(test)]
476524
mod tests {
477525
use crate::{client::*, search::*};
@@ -524,6 +572,28 @@ mod tests {
524572
Ok(())
525573
}
526574

575+
#[meilisearch_test]
576+
async fn test_multi_search(client: Client, index: Index) -> Result<(), Error> {
577+
setup_test_index(&client, &index).await?;
578+
let search_query_1 = SearchQuery::new(&index)
579+
.with_query("Sorcerer's Stone")
580+
.build();
581+
let search_query_2 = SearchQuery::new(&index)
582+
.with_query("Chamber of Secrets")
583+
.build();
584+
585+
let response = client
586+
.multi_search()
587+
.with_search_query(search_query_1)
588+
.with_search_query(search_query_2)
589+
.execute::<Document>()
590+
.await
591+
.unwrap();
592+
593+
assert_eq!(response.results.len(), 2);
594+
Ok(())
595+
}
596+
527597
#[meilisearch_test]
528598
async fn test_query_builder(_client: Client, index: Index) -> Result<(), Error> {
529599
let mut query = SearchQuery::new(&index);

0 commit comments

Comments
 (0)