Skip to content

Commit 1891ff3

Browse files
authored
Merge branch 'main' into feat/multi-modal-search
2 parents f72885d + 5e84bdf commit 1891ff3

File tree

6 files changed

+246
-2
lines changed

6 files changed

+246
-2
lines changed

src/client.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
errors::*,
99
indexes::*,
1010
key::{Key, KeyBuilder, KeyUpdater, KeysQuery, KeysResults},
11+
network::{NetworkState, NetworkUpdate},
1112
request::*,
1213
search::*,
1314
task_info::TaskInfo,
@@ -1148,6 +1149,46 @@ impl<Http: HttpClient> Client<Http> {
11481149
crate::tenant_tokens::generate_tenant_token(api_key_uid, search_rules, api_key, expires_at)
11491150
}
11501151

1152+
/// Get the current network state (/network)
1153+
pub async fn get_network_state(&self) -> Result<NetworkState, Error> {
1154+
self.http_client
1155+
.request::<(), (), NetworkState>(
1156+
&format!("{}/network", self.host),
1157+
Method::Get { query: () },
1158+
200,
1159+
)
1160+
.await
1161+
}
1162+
1163+
/// Partially update the network state (/network)
1164+
pub async fn update_network_state(&self, body: &NetworkUpdate) -> Result<NetworkState, Error> {
1165+
self.http_client
1166+
.request::<(), &NetworkUpdate, NetworkState>(
1167+
&format!("{}/network", self.host),
1168+
Method::Patch { query: (), body },
1169+
200,
1170+
)
1171+
.await
1172+
}
1173+
1174+
/// Convenience: set sharding=true/false
1175+
pub async fn set_sharding(&self, enabled: bool) -> Result<NetworkState, Error> {
1176+
let update = NetworkUpdate {
1177+
sharding: Some(enabled),
1178+
..NetworkUpdate::default()
1179+
};
1180+
self.update_network_state(&update).await
1181+
}
1182+
1183+
/// Convenience: set self to a remote name
1184+
pub async fn set_self_remote(&self, name: &str) -> Result<NetworkState, Error> {
1185+
let update = NetworkUpdate {
1186+
self_name: Some(name.to_string()),
1187+
..NetworkUpdate::default()
1188+
};
1189+
self.update_network_state(&update).await
1190+
}
1191+
11511192
fn sleep_backend(&self) -> SleepBackend {
11521193
SleepBackend::infer(self.http_client.is_tokio())
11531194
}
@@ -1207,6 +1248,49 @@ pub struct Version {
12071248

12081249
#[cfg(test)]
12091250
mod tests {
1251+
use super::*;
1252+
use mockito::Matcher;
1253+
1254+
#[tokio::test]
1255+
async fn test_network_update_and_deserialize_remotes() {
1256+
let mut s = mockito::Server::new_async().await;
1257+
let base = s.url();
1258+
1259+
let response_body = serde_json::json!({
1260+
"remotes": {
1261+
"ms-00": {
1262+
"url": "http://ms-00",
1263+
"searchApiKey": "SEARCH",
1264+
"writeApiKey": "WRITE"
1265+
}
1266+
},
1267+
"self": "ms-00",
1268+
"sharding": true
1269+
})
1270+
.to_string();
1271+
1272+
let _m = s
1273+
.mock("PATCH", "/network")
1274+
.match_body(Matcher::Regex(
1275+
r#"\{.*"sharding"\s*:\s*true.*\}"#.to_string(),
1276+
))
1277+
.with_status(200)
1278+
.with_header("content-type", "application/json")
1279+
.with_body(response_body)
1280+
.create_async()
1281+
.await;
1282+
1283+
let client = Client::new(base, None::<String>).unwrap();
1284+
let updated = client
1285+
.set_sharding(true)
1286+
.await
1287+
.expect("update_network_state failed");
1288+
assert_eq!(updated.sharding, Some(true));
1289+
let remotes = updated.remotes.expect("remotes should be present");
1290+
let ms00 = remotes.get("ms-00").expect("ms-00 should exist");
1291+
assert_eq!(ms00.write_api_key.as_deref(), Some("WRITE"));
1292+
}
1293+
12101294
use big_s::S;
12111295
use time::OffsetDateTime;
12121296

src/documents.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ pub struct DocumentsQuery<'a, Http: HttpClient> {
190190
#[serde(skip_serializing_if = "Option::is_none")]
191191
pub fields: Option<Vec<&'a str>>,
192192

193+
/// Attributes used to sort the returned documents.
194+
///
195+
/// Available since v1.16 of Meilisearch.
196+
#[serde(skip_serializing_if = "Option::is_none")]
197+
pub sort: Option<Vec<&'a str>>,
198+
193199
/// Filters to apply.
194200
///
195201
/// Available since v1.2 of Meilisearch
@@ -206,6 +212,7 @@ impl<'a, Http: HttpClient> DocumentsQuery<'a, Http> {
206212
offset: None,
207213
limit: None,
208214
fields: None,
215+
sort: None,
209216
filter: None,
210217
}
211218
}
@@ -277,6 +284,31 @@ impl<'a, Http: HttpClient> DocumentsQuery<'a, Http> {
277284
self
278285
}
279286

287+
/// Specify the sort order of the returned documents.
288+
///
289+
/// # Example
290+
///
291+
/// ```
292+
/// # use meilisearch_sdk::{client::*, indexes::*, documents::*};
293+
/// #
294+
/// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
295+
/// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey");
296+
/// #
297+
/// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)).unwrap();
298+
/// let index = client.index("documents_with_sort");
299+
///
300+
/// let mut documents_query = DocumentsQuery::new(&index);
301+
///
302+
/// documents_query.with_sort(["release_date:desc"]);
303+
/// ```
304+
pub fn with_sort(
305+
&mut self,
306+
sort: impl IntoIterator<Item = &'a str>,
307+
) -> &mut DocumentsQuery<'a, Http> {
308+
self.sort = Some(sort.into_iter().collect());
309+
self
310+
}
311+
280312
pub fn with_filter<'b>(&'b mut self, filter: &'a str) -> &'b mut DocumentsQuery<'a, Http> {
281313
self.filter = Some(filter);
282314
self
@@ -538,6 +570,33 @@ mod tests {
538570
Ok(())
539571
}
540572

573+
#[meilisearch_test]
574+
async fn test_get_documents_with_sort(client: Client, index: Index) -> Result<(), Error> {
575+
setup_test_index(&client, &index).await?;
576+
577+
index
578+
.set_sortable_attributes(["id"])
579+
.await?
580+
.wait_for_completion(&client, None, None)
581+
.await?;
582+
583+
let documents = DocumentsQuery::new(&index)
584+
.with_sort(["id:desc"])
585+
.execute::<MyObject>()
586+
.await?;
587+
588+
assert_eq!(
589+
documents.results.first().and_then(|document| document.id),
590+
Some(3)
591+
);
592+
assert_eq!(
593+
documents.results.last().and_then(|document| document.id),
594+
Some(0)
595+
);
596+
597+
Ok(())
598+
}
599+
541600
#[meilisearch_test]
542601
async fn test_get_documents_with_error_hint() -> Result<(), Error> {
543602
let meilisearch_url = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ pub mod features;
244244
pub mod indexes;
245245
/// Module containing the [`Key`](key::Key) struct.
246246
pub mod key;
247+
/// Module for Network configuration API (sharding/remotes).
248+
pub mod network;
247249
pub mod request;
248250
/// Module related to search queries and results.
249251
pub mod search;

src/network.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use serde::{Deserialize, Serialize};
2+
use std::collections::HashMap;
3+
4+
#[derive(Clone, Serialize, Deserialize)]
5+
#[serde(rename_all = "camelCase")]
6+
pub struct RemoteConfig {
7+
pub url: String,
8+
#[serde(rename = "searchApiKey")]
9+
pub search_api_key: String,
10+
#[serde(rename = "writeApiKey", skip_serializing_if = "Option::is_none")]
11+
// present in responses since 1.19
12+
pub write_api_key: Option<String>,
13+
}
14+
15+
pub type RemotesMap = HashMap<String, RemoteConfig>;
16+
17+
/// Full network state returned by GET /network
18+
#[derive(Clone, Serialize, Deserialize)]
19+
#[serde(rename_all = "camelCase")]
20+
pub struct NetworkState {
21+
pub remotes: Option<RemotesMap>,
22+
#[serde(rename = "self")]
23+
pub self_name: Option<String>,
24+
pub sharding: Option<bool>,
25+
}
26+
27+
/// Partial update body for PATCH /network
28+
#[derive(Default, Clone, Serialize, Deserialize)]
29+
#[serde(rename_all = "camelCase")]
30+
pub struct NetworkUpdate {
31+
#[serde(skip_serializing_if = "Option::is_none")]
32+
pub remotes: Option<RemotesMap>,
33+
#[serde(rename = "self", skip_serializing_if = "Option::is_none")]
34+
pub self_name: Option<String>,
35+
#[serde(skip_serializing_if = "Option::is_none")]
36+
pub sharding: Option<bool>,
37+
}

src/search.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,12 @@ pub struct SearchQuery<'a, Http: HttpClient> {
419419
#[derive(Debug, Serialize, Clone)]
420420
#[serde(rename_all = "camelCase")]
421421
pub struct QueryFederationOptions {
422+
/// Weight multiplier for this query when merging federated results
422423
#[serde(skip_serializing_if = "Option::is_none")]
423424
pub weight: Option<f32>,
425+
/// Remote instance name to target when sharding; corresponds to a key in network.remotes
426+
#[serde(skip_serializing_if = "Option::is_none")]
427+
pub remote: Option<String>,
424428
}
425429

426430
#[allow(missing_docs)]
@@ -777,6 +781,7 @@ impl<'a, 'b, Http: HttpClient> MultiSearchQuery<'a, 'b, Http> {
777781
search_query,
778782
QueryFederationOptions {
779783
weight: Some(weight),
784+
remote: None,
780785
},
781786
)
782787
}

src/tasks.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use serde::{Deserialize, Deserializer, Serialize};
2+
use serde_json::{Map, Value};
23
use std::time::Duration;
34
use time::OffsetDateTime;
45

@@ -157,6 +158,9 @@ pub struct SucceededTask {
157158
pub canceled_by: Option<usize>,
158159
pub index_uid: Option<String>,
159160
pub error: Option<MeilisearchError>,
161+
/// Remotes object returned by the server for this task (present since Meilisearch 1.19)
162+
#[serde(skip_serializing_if = "Option::is_none")]
163+
pub remotes: Option<Map<String, Value>>,
160164
#[serde(flatten)]
161165
pub update_type: TaskType,
162166
pub uid: u32,
@@ -174,6 +178,9 @@ pub struct EnqueuedTask {
174178
#[serde(with = "time::serde::rfc3339")]
175179
pub enqueued_at: OffsetDateTime,
176180
pub index_uid: Option<String>,
181+
/// Remotes object returned by the server for this enqueued task
182+
#[serde(skip_serializing_if = "Option::is_none")]
183+
pub remotes: Option<Map<String, Value>>,
177184
#[serde(flatten)]
178185
pub update_type: TaskType,
179186
pub uid: u32,
@@ -193,6 +200,9 @@ pub struct ProcessingTask {
193200
#[serde(with = "time::serde::rfc3339")]
194201
pub started_at: OffsetDateTime,
195202
pub index_uid: Option<String>,
203+
/// Remotes object returned by the server for this processing task
204+
#[serde(skip_serializing_if = "Option::is_none")]
205+
pub remotes: Option<Map<String, Value>>,
196206
#[serde(flatten)]
197207
pub update_type: TaskType,
198208
pub uid: u32,
@@ -738,6 +748,54 @@ impl<'a, Http: HttpClient> TasksQuery<'a, TasksPaginationFilters, Http> {
738748

739749
#[cfg(test)]
740750
mod test {
751+
752+
#[test]
753+
fn test_deserialize_enqueued_task_with_remotes() {
754+
let json = r#"{
755+
"enqueuedAt": "2022-02-03T13:02:38.369634Z",
756+
"indexUid": "movies",
757+
"status": "enqueued",
758+
"type": "indexUpdate",
759+
"uid": 12,
760+
"remotes": { "ms-00": { "status": "ok" } }
761+
}"#;
762+
let task: Task = serde_json::from_str(json).unwrap();
763+
match task {
764+
Task::Enqueued { content } => {
765+
let remotes = content.remotes.expect("remotes should be present");
766+
assert!(remotes.contains_key("ms-00"));
767+
}
768+
_ => panic!("expected enqueued task"),
769+
}
770+
}
771+
772+
#[test]
773+
fn test_deserialize_processing_task_with_remotes() {
774+
let json = r#"{
775+
"details": {
776+
"indexedDocuments": null,
777+
"receivedDocuments": 10
778+
},
779+
"duration": null,
780+
"enqueuedAt": "2022-02-03T15:17:02.801341Z",
781+
"finishedAt": null,
782+
"indexUid": "movies",
783+
"startedAt": "2022-02-03T15:17:02.812338Z",
784+
"status": "processing",
785+
"type": "documentAdditionOrUpdate",
786+
"uid": 14,
787+
"remotes": { "ms-00": { "status": "ok" } }
788+
}"#;
789+
let task: Task = serde_json::from_str(json).unwrap();
790+
match task {
791+
Task::Processing { content } => {
792+
let remotes = content.remotes.expect("remotes should be present");
793+
assert!(remotes.contains_key("ms-00"));
794+
}
795+
_ => panic!("expected processing task"),
796+
}
797+
}
798+
741799
use super::*;
742800
use crate::{
743801
client::*,
@@ -782,8 +840,7 @@ mod test {
782840
enqueued_at,
783841
index_uid: Some(index_uid),
784842
update_type: TaskType::DocumentAdditionOrUpdate { details: None },
785-
uid: 12,
786-
}
843+
uid: 12, .. }
787844
}
788845
if enqueued_at == datetime && index_uid == "meili"));
789846

0 commit comments

Comments
 (0)