Skip to content

Commit bb35328

Browse files
meili-bors[bot]meili-botbidoubiwa
authored
Merge #467
467: Changes related to the next Meilisearch release (v1.2.0) r=bidoubiwa a=meili-bot Related to this issue: meilisearch/integration-guides#261 This PR: - gathers the changes related to the next Meilisearch release (v1.2.0) so that this package is ready when the official release is out. - should pass the tests against the [latest pre-release of Meilisearch](https://github.com/meilisearch/meilisearch/releases). - might eventually contain test failures until the Meilisearch v1.2.0 is out. ⚠️ This PR should NOT be merged until the next release of Meilisearch (v1.2.0) is out. _This PR is auto-generated for the [pre-release week](https://github.com/meilisearch/integration-guides/blob/main/resources/pre-release-week.md) purpose._ Co-authored-by: meili-bot <[email protected]> Co-authored-by: Charlotte Vermandel <[email protected]>
2 parents 1630d13 + d525db9 commit bb35328

File tree

5 files changed

+377
-13
lines changed

5 files changed

+377
-13
lines changed

src/documents.rs

Lines changed: 214 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::task_info::TaskInfo;
12
use async_trait::async_trait;
23
use serde::{de::DeserializeOwned, Deserialize, Serialize};
34

@@ -185,6 +186,13 @@ pub struct DocumentsQuery<'a> {
185186
/// The fields that should appear in the documents. By default all of the fields are present.
186187
#[serde(skip_serializing_if = "Option::is_none")]
187188
pub fields: Option<Vec<&'a str>>,
189+
190+
/// Filters to apply.
191+
///
192+
/// Available since v1.2 of Meilisearch
193+
/// Read the [dedicated guide](https://docs.meilisearch.com/reference/features/filtering.html) to learn the syntax.
194+
#[serde(skip_serializing_if = "Option::is_none")]
195+
pub filter: Option<&'a str>,
188196
}
189197

190198
impl<'a> DocumentsQuery<'a> {
@@ -194,6 +202,7 @@ impl<'a> DocumentsQuery<'a> {
194202
offset: None,
195203
limit: None,
196204
fields: None,
205+
filter: None,
197206
}
198207
}
199208

@@ -264,6 +273,11 @@ impl<'a> DocumentsQuery<'a> {
264273
self
265274
}
266275

276+
pub fn with_filter<'b>(&'b mut self, filter: &'a str) -> &'b mut DocumentsQuery<'a> {
277+
self.filter = Some(filter);
278+
self
279+
}
280+
267281
/// Execute the get documents query.
268282
///
269283
/// # Example
@@ -301,11 +315,39 @@ impl<'a> DocumentsQuery<'a> {
301315
}
302316
}
303317

318+
#[derive(Debug, Clone, Serialize)]
319+
pub struct DocumentDeletionQuery<'a> {
320+
#[serde(skip_serializing)]
321+
pub index: &'a Index,
322+
323+
/// Filters to apply.
324+
///
325+
/// Read the [dedicated guide](https://docs.meilisearch.com/reference/features/filtering.html) to learn the syntax.
326+
pub filter: Option<&'a str>,
327+
}
328+
329+
impl<'a> DocumentDeletionQuery<'a> {
330+
pub fn new(index: &Index) -> DocumentDeletionQuery {
331+
DocumentDeletionQuery {
332+
index,
333+
filter: None,
334+
}
335+
}
336+
337+
pub fn with_filter<'b>(&'b mut self, filter: &'a str) -> &'b mut DocumentDeletionQuery<'a> {
338+
self.filter = Some(filter);
339+
self
340+
}
341+
342+
pub async fn execute<T: DeserializeOwned + 'static>(&self) -> Result<TaskInfo, Error> {
343+
self.index.delete_documents_with(self).await
344+
}
345+
}
346+
304347
#[cfg(test)]
305348
mod tests {
306349
use super::*;
307-
use crate::{client::*, indexes::*};
308-
use ::meilisearch_sdk::documents::IndexConfig;
350+
use crate::{client::*, errors::*, indexes::*};
309351
use meilisearch_test_macro::meilisearch_test;
310352
use serde::{Deserialize, Serialize};
311353

@@ -371,7 +413,6 @@ mod tests {
371413
#[meilisearch_test]
372414
async fn test_get_documents_with_execute(client: Client, index: Index) -> Result<(), Error> {
373415
setup_test_index(&client, &index).await?;
374-
// let documents = index.get_documents(None, None, None).await.unwrap();
375416
let documents = DocumentsQuery::new(&index)
376417
.with_limit(1)
377418
.with_offset(1)
@@ -387,6 +428,66 @@ mod tests {
387428
Ok(())
388429
}
389430

431+
#[meilisearch_test]
432+
async fn test_delete_documents_with(client: Client, index: Index) -> Result<(), Error> {
433+
setup_test_index(&client, &index).await?;
434+
index
435+
.set_filterable_attributes(["id"])
436+
.await?
437+
.wait_for_completion(&client, None, None)
438+
.await?;
439+
440+
let mut query = DocumentDeletionQuery::new(&index);
441+
query.with_filter("id = 1");
442+
index
443+
.delete_documents_with(&query)
444+
.await?
445+
.wait_for_completion(&client, None, None)
446+
.await?;
447+
let document_result = index.get_document::<MyObject>("1").await;
448+
449+
match document_result {
450+
Ok(_) => panic!("The test was expecting no documents to be returned but got one."),
451+
Err(e) => match e {
452+
Error::Meilisearch(err) => {
453+
assert_eq!(err.error_code, ErrorCode::DocumentNotFound);
454+
}
455+
_ => panic!("The error was expected to be a Meilisearch error, but it was not."),
456+
},
457+
}
458+
459+
Ok(())
460+
}
461+
462+
#[meilisearch_test]
463+
async fn test_delete_documents_with_filter_not_filterable(
464+
client: Client,
465+
index: Index,
466+
) -> Result<(), Error> {
467+
setup_test_index(&client, &index).await?;
468+
469+
let mut query = DocumentDeletionQuery::new(&index);
470+
query.with_filter("id = 1");
471+
let error = index
472+
.delete_documents_with(&query)
473+
.await?
474+
.wait_for_completion(&client, None, None)
475+
.await?;
476+
477+
let error = error.unwrap_failure();
478+
479+
assert!(matches!(
480+
error,
481+
MeilisearchError {
482+
error_code: ErrorCode::InvalidDocumentFilter,
483+
error_type: ErrorType::InvalidRequest,
484+
..
485+
}
486+
));
487+
488+
Ok(())
489+
}
490+
390491
#[meilisearch_test]
391492
async fn test_get_documents_with_only_one_param(
392493
client: Client,
@@ -407,6 +508,116 @@ mod tests {
407508
Ok(())
408509
}
409510

511+
#[meilisearch_test]
512+
async fn test_get_documents_with_filter(client: Client, index: Index) -> Result<(), Error> {
513+
setup_test_index(&client, &index).await?;
514+
515+
index
516+
.set_filterable_attributes(["id"])
517+
.await
518+
.unwrap()
519+
.wait_for_completion(&client, None, None)
520+
.await
521+
.unwrap();
522+
523+
let documents = DocumentsQuery::new(&index)
524+
.with_filter("id = 1")
525+
.execute::<MyObject>()
526+
.await?;
527+
528+
assert_eq!(documents.results.len(), 1);
529+
530+
Ok(())
531+
}
532+
533+
#[meilisearch_test]
534+
async fn test_get_documents_with_error_hint() -> Result<(), Error> {
535+
let url = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700");
536+
let client = Client::new(format!("{}/hello", url), Some("masterKey"));
537+
let index = client.index("test_get_documents_with_filter_wrong_ms_version");
538+
539+
let documents = DocumentsQuery::new(&index)
540+
.with_filter("id = 1")
541+
.execute::<MyObject>()
542+
.await;
543+
544+
let error = documents.unwrap_err();
545+
546+
let message = Some("Hint: It might not be working because you're not up to date with the Meilisearch version that updated the get_documents_with method.".to_string());
547+
let url = "http://localhost:7700/hello/indexes/test_get_documents_with_filter_wrong_ms_version/documents/fetch".to_string();
548+
let status_code = 404;
549+
let displayed_error = "MeilisearchCommunicationError: The server responded with a 404. Hint: It might not be working because you're not up to date with the Meilisearch version that updated the get_documents_with method.\nurl: http://localhost:7700/hello/indexes/test_get_documents_with_filter_wrong_ms_version/documents/fetch";
550+
551+
match &error {
552+
Error::MeilisearchCommunication(error) => {
553+
assert_eq!(error.status_code, status_code);
554+
assert_eq!(error.message, message);
555+
assert_eq!(error.url, url);
556+
}
557+
_ => panic!("The error was expected to be a MeilisearchCommunicationError error, but it was not."),
558+
};
559+
assert_eq!(format!("{}", error), displayed_error);
560+
561+
Ok(())
562+
}
563+
564+
#[meilisearch_test]
565+
async fn test_get_documents_with_error_hint_meilisearch_api_error(
566+
index: Index,
567+
client: Client,
568+
) -> Result<(), Error> {
569+
setup_test_index(&client, &index).await?;
570+
571+
let error = DocumentsQuery::new(&index)
572+
.with_filter("id = 1")
573+
.execute::<MyObject>()
574+
.await
575+
.unwrap_err();
576+
577+
let message = "Attribute `id` is not filterable. This index does not have configured filterable attributes.
578+
1:3 id = 1
579+
Hint: It might not be working because you're not up to date with the Meilisearch version that updated the get_documents_with method.".to_string();
580+
let displayed_error = "Meilisearch invalid_request: invalid_document_filter: Attribute `id` is not filterable. This index does not have configured filterable attributes.
581+
1:3 id = 1
582+
Hint: It might not be working because you're not up to date with the Meilisearch version that updated the get_documents_with method.. https://docs.meilisearch.com/errors#invalid_document_filter";
583+
584+
match &error {
585+
Error::Meilisearch(error) => {
586+
assert_eq!(error.error_message, message);
587+
}
588+
_ => panic!("The error was expected to be a MeilisearchCommunicationError error, but it was not."),
589+
};
590+
assert_eq!(format!("{}", error), displayed_error);
591+
592+
Ok(())
593+
}
594+
595+
#[meilisearch_test]
596+
async fn test_get_documents_with_invalid_filter(
597+
client: Client,
598+
index: Index,
599+
) -> Result<(), Error> {
600+
setup_test_index(&client, &index).await?;
601+
602+
// Does not work because `id` is not filterable
603+
let error = DocumentsQuery::new(&index)
604+
.with_filter("id = 1")
605+
.execute::<MyObject>()
606+
.await
607+
.unwrap_err();
608+
609+
assert!(matches!(
610+
error,
611+
Error::Meilisearch(MeilisearchError {
612+
error_code: ErrorCode::InvalidDocumentFilter,
613+
error_type: ErrorType::InvalidRequest,
614+
..
615+
})
616+
));
617+
618+
Ok(())
619+
}
620+
410621
#[meilisearch_test]
411622
async fn test_settings_generated_by_macro(client: Client, index: Index) -> Result<(), Error> {
412623
setup_test_index(&client, &index).await?;

src/errors.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub enum Error {
1111
/// Also check out: <https://github.com/meilisearch/Meilisearch/blob/main/meilisearch-error/src/lib.rs>
1212
#[error(transparent)]
1313
Meilisearch(#[from] MeilisearchError),
14+
#[error(transparent)]
15+
MeilisearchCommunication(#[from] MeilisearchCommunicationError),
1416
/// There is no Meilisearch server listening on the [specified host]
1517
/// (../client/struct.Client.html#method.new).
1618
#[error("The Meilisearch server can't be reached.")]
@@ -65,6 +67,30 @@ pub enum Error {
6567
InvalidUuid4Version,
6668
}
6769

70+
#[derive(Debug, Clone, Deserialize, Error)]
71+
#[serde(rename_all = "camelCase")]
72+
73+
pub struct MeilisearchCommunicationError {
74+
pub status_code: u16,
75+
pub message: Option<String>,
76+
pub url: String,
77+
}
78+
79+
impl std::fmt::Display for MeilisearchCommunicationError {
80+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81+
write!(
82+
f,
83+
"MeilisearchCommunicationError: The server responded with a {}.",
84+
self.status_code
85+
)?;
86+
if let Some(message) = &self.message {
87+
write!(f, " {}", message)?;
88+
}
89+
write!(f, "\nurl: {}", self.url)?;
90+
Ok(())
91+
}
92+
}
93+
6894
#[derive(Debug, Clone, Deserialize, Error)]
6995
#[serde(rename_all = "camelCase")]
7096
#[error("Meilisearch {}: {}: {}. {}", .error_type, .error_code, .error_message, .error_link)]
@@ -162,6 +188,8 @@ pub enum ErrorCode {
162188
InvalidIndexOffset,
163189
InvalidIndexLimit,
164190
InvalidIndexPrimaryKey,
191+
InvalidDocumentFilter,
192+
MissingDocumentFilter,
165193
InvalidDocumentFields,
166194
InvalidDocumentLimit,
167195
InvalidDocumentOffset,
@@ -234,6 +262,8 @@ pub enum ErrorCode {
234262
Unknown,
235263
}
236264

265+
pub const MEILISEARCH_VERSION_HINT: &str = "Hint: It might not be working because you're not up to date with the Meilisearch version that updated the get_documents_with method";
266+
237267
impl std::fmt::Display for ErrorCode {
238268
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
239269
write!(
@@ -311,6 +341,28 @@ mod test {
311341

312342
assert_eq!(error.to_string(), ("Meilisearch internal: index_creation_failed: The cool error message.. https://the best link eveer"));
313343

344+
let error: MeilisearchCommunicationError = MeilisearchCommunicationError {
345+
status_code: 404,
346+
message: Some("Hint: something.".to_string()),
347+
url: "http://localhost:7700/something".to_string(),
348+
};
349+
350+
assert_eq!(
351+
error.to_string(),
352+
("MeilisearchCommunicationError: The server responded with a 404. Hint: something.\nurl: http://localhost:7700/something")
353+
);
354+
355+
let error: MeilisearchCommunicationError = MeilisearchCommunicationError {
356+
status_code: 404,
357+
message: None,
358+
url: "http://localhost:7700/something".to_string(),
359+
};
360+
361+
assert_eq!(
362+
error.to_string(),
363+
("MeilisearchCommunicationError: The server responded with a 404.\nurl: http://localhost:7700/something")
364+
);
365+
314366
let error = Error::UnreachableServer;
315367
assert_eq!(
316368
error.to_string(),

0 commit comments

Comments
 (0)