From 034ad31af58be266621310781bf39b68d8373bbf Mon Sep 17 00:00:00 2001 From: meili-bot <74670311+meili-bot@users.noreply.github.com> Date: Fri, 12 May 2023 18:38:10 -0300 Subject: [PATCH 1/5] Add delete_document_with method for Meilisearch v1.2 --- src/documents.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++--- src/indexes.rs | 57 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/documents.rs b/src/documents.rs index 79c31856..61ccdf35 100644 --- a/src/documents.rs +++ b/src/documents.rs @@ -1,3 +1,4 @@ +use crate::task_info::TaskInfo; use async_trait::async_trait; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -301,11 +302,36 @@ impl<'a> DocumentsQuery<'a> { } } +#[derive(Debug, Clone, Serialize)] +pub struct DocumentDeletionQuery<'a> { + #[serde(skip_serializing)] + pub index: &'a Index, + + /// Filters to apply. + /// + /// Read the [dedicated guide](https://docs.meilisearch.com/reference/features/filtering.html) to learn the syntax. + pub filter: &'a str, +} + +impl<'a> DocumentDeletionQuery<'a> { + pub fn new(index: &Index) -> DocumentDeletionQuery { + DocumentDeletionQuery { index, filter: "" } + } + + pub fn with_filter<'b>(&'b mut self, filter: &'a str) -> &'b mut DocumentDeletionQuery<'a> { + self.filter = filter; + self + } + + pub async fn execute(&self) -> Result { + self.index.delete_documents_with(self).await + } +} + #[cfg(test)] mod tests { use super::*; - use crate::{client::*, indexes::*}; - use ::meilisearch_sdk::documents::IndexConfig; + use crate::{client::*, errors::*, indexes::*}; use meilisearch_test_macro::meilisearch_test; use serde::{Deserialize, Serialize}; @@ -371,7 +397,6 @@ mod tests { #[meilisearch_test] async fn test_get_documents_with_execute(client: Client, index: Index) -> Result<(), Error> { setup_test_index(&client, &index).await?; - // let documents = index.get_documents(None, None, None).await.unwrap(); let documents = DocumentsQuery::new(&index) .with_limit(1) .with_offset(1) @@ -387,6 +412,41 @@ mod tests { Ok(()) } + #[meilisearch_test] + async fn test_delete_documents_with(client: Client, index: Index) -> Result<(), Error> { + setup_test_index(&client, &index).await?; + index + .set_filterable_attributes(["id"]) + .await + .unwrap() + .wait_for_completion(&client, None, None) + .await + .unwrap(); + let mut query = DocumentDeletionQuery::new(&index); + query.with_filter("id = 1"); + + index + .delete_documents_with(&query) + .await + .unwrap() + .wait_for_completion(&client, None, None) + .await + .unwrap(); + let document_result = index.get_document::("1").await; + + match document_result { + Ok(_) => panic!("The test was expecting no documents to be returned but got one."), + Err(e) => match e { + Error::Meilisearch(err) => { + assert_eq!(err.error_code, ErrorCode::DocumentNotFound); + } + _ => panic!("The error was expected to be a Meilisearch error, but it was not."), + }, + } + + Ok(()) + } + #[meilisearch_test] async fn test_get_documents_with_only_one_param( client: Client, diff --git a/src/indexes.rs b/src/indexes.rs index c80023f2..2fccd514 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -1,6 +1,6 @@ use crate::{ client::Client, - documents::{DocumentQuery, DocumentsQuery, DocumentsResults}, + documents::{DocumentDeletionQuery, DocumentQuery, DocumentsQuery, DocumentsResults}, errors::Error, request::*, search::*, @@ -926,6 +926,61 @@ impl Index { .await } + /// Delete a selection of documents with filters. + /// + /// # Example + /// + /// ``` + /// # use serde::{Serialize, Deserialize}; + /// # use meilisearch_sdk::{client::*, documents::*}; + /// # + /// # let MEILISEARCH_URL = option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # #[derive(Serialize, Deserialize, Debug)] + /// # struct Movie { + /// # name: String, + /// # id: String, + /// # } + /// # + /// # + /// # futures::executor::block_on(async move { + /// # + /// # let client = Client::new(MEILISEARCH_URL, Some(MEILISEARCH_API_KEY)); + /// let index = client.index("delete_documents_with"); + /// # + /// # index.set_filterable_attributes(["id"]); + /// # // add some documents + /// # index.add_or_replace(&[Movie{id:String::from("1"), name: String::from("First movie") }, Movie{id:String::from("1"), name: String::from("First movie") }], Some("id")).await.unwrap().wait_for_completion(&client, None, None).await.unwrap(); + /// + /// let mut query = DocumentDeletionQuery::new(&index); + /// query.with_filter("id = 1"); + /// // delete some documents + /// index.delete_documents_with(&query) + /// .await + /// .unwrap() + /// .wait_for_completion(&client, None, None) + /// .await + /// .unwrap(); + /// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap(); + /// # }); + /// ``` + pub async fn delete_documents_with( + &self, + query: &DocumentDeletionQuery<'_>, + ) -> Result { + request::<(), &DocumentDeletionQuery, TaskInfo>( + &format!("{}/indexes/{}/documents/delete", self.client.host, self.uid), + self.client.get_api_key(), + Method::Post { + query: (), + body: query, + }, + 202, + ) + .await + } + /// Alias for the [Index::update] method. pub async fn set_primary_key( &mut self, From 7f83e6c940cfd751982ce4d89501837eabea1ed2 Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Mon, 22 May 2023 18:12:30 +0200 Subject: [PATCH 2/5] Add original_filter detail in DocumentDeletion task type --- src/tasks.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tasks.rs b/src/tasks.rs index 95509609..700506c5 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -66,6 +66,7 @@ pub struct DocumentAdditionOrUpdate { pub struct DocumentDeletion { pub provided_ids: Option, pub deleted_documents: Option, + pub original_filter: String, } #[derive(Debug, Clone, Deserialize)] From 63090a0c771fb30abe21bf38fc63df345544e943 Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Tue, 23 May 2023 11:54:47 +0200 Subject: [PATCH 3/5] Change filter to become optional in DocumentDeletionQuery --- src/documents.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/documents.rs b/src/documents.rs index 61ccdf35..903b79d1 100644 --- a/src/documents.rs +++ b/src/documents.rs @@ -310,16 +310,19 @@ pub struct DocumentDeletionQuery<'a> { /// Filters to apply. /// /// Read the [dedicated guide](https://docs.meilisearch.com/reference/features/filtering.html) to learn the syntax. - pub filter: &'a str, + pub filter: Option<&'a str>, } impl<'a> DocumentDeletionQuery<'a> { pub fn new(index: &Index) -> DocumentDeletionQuery { - DocumentDeletionQuery { index, filter: "" } + DocumentDeletionQuery { + index, + filter: None, + } } pub fn with_filter<'b>(&'b mut self, filter: &'a str) -> &'b mut DocumentDeletionQuery<'a> { - self.filter = filter; + self.filter = Some(filter); self } From 9fce33b02b433ddd067a4a97e8bc02f4502d7929 Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Tue, 23 May 2023 11:55:06 +0200 Subject: [PATCH 4/5] Change original_filter to be Optional in the returned details of a DocumentDeletion task --- src/tasks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks.rs b/src/tasks.rs index 700506c5..e7eb9726 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -66,7 +66,7 @@ pub struct DocumentAdditionOrUpdate { pub struct DocumentDeletion { pub provided_ids: Option, pub deleted_documents: Option, - pub original_filter: String, + pub original_filter: Option, } #[derive(Debug, Clone, Deserialize)] From 977e9da7a56e3f244ec51f0b7c199e7402a4779f Mon Sep 17 00:00:00 2001 From: Charlotte Vermandel Date: Tue, 23 May 2023 12:00:40 +0200 Subject: [PATCH 5/5] Improve testing on document deletion with a filter --- src/documents.rs | 43 ++++++++++++++++++++++++++++++++++--------- src/errors.rs | 2 ++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/documents.rs b/src/documents.rs index 903b79d1..d77acc9f 100644 --- a/src/documents.rs +++ b/src/documents.rs @@ -420,21 +420,17 @@ mod tests { setup_test_index(&client, &index).await?; index .set_filterable_attributes(["id"]) - .await - .unwrap() + .await? .wait_for_completion(&client, None, None) - .await - .unwrap(); + .await?; + let mut query = DocumentDeletionQuery::new(&index); query.with_filter("id = 1"); - index .delete_documents_with(&query) - .await - .unwrap() + .await? .wait_for_completion(&client, None, None) - .await - .unwrap(); + .await?; let document_result = index.get_document::("1").await; match document_result { @@ -450,6 +446,35 @@ mod tests { Ok(()) } + #[meilisearch_test] + async fn test_delete_documents_with_filter_not_filterable( + client: Client, + index: Index, + ) -> Result<(), Error> { + setup_test_index(&client, &index).await?; + + let mut query = DocumentDeletionQuery::new(&index); + query.with_filter("id = 1"); + let error = index + .delete_documents_with(&query) + .await? + .wait_for_completion(&client, None, None) + .await?; + + let error = error.unwrap_failure(); + + assert!(matches!( + error, + MeilisearchError { + error_code: ErrorCode::InvalidDocumentFilter, + error_type: ErrorType::InvalidRequest, + .. + } + )); + + Ok(()) + } + #[meilisearch_test] async fn test_get_documents_with_only_one_param( client: Client, diff --git a/src/errors.rs b/src/errors.rs index 0c6d6db3..7ab030b4 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -162,6 +162,8 @@ pub enum ErrorCode { InvalidIndexOffset, InvalidIndexLimit, InvalidIndexPrimaryKey, + InvalidDocumentFilter, + MissingDocumentFilter, InvalidDocumentFields, InvalidDocumentLimit, InvalidDocumentOffset,