diff --git a/src/documents.rs b/src/documents.rs index 4918a572..20d77a23 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}; @@ -314,6 +315,35 @@ 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: Option<&'a str>, +} + +impl<'a> DocumentDeletionQuery<'a> { + pub fn new(index: &Index) -> DocumentDeletionQuery { + DocumentDeletionQuery { + index, + filter: None, + } + } + + pub fn with_filter<'b>(&'b mut self, filter: &'a str) -> &'b mut DocumentDeletionQuery<'a> { + self.filter = Some(filter); + self + } + + pub async fn execute(&self) -> Result { + self.index.delete_documents_with(self).await + } +} + #[cfg(test)] mod tests { use super::*; @@ -383,7 +413,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) @@ -399,6 +428,66 @@ 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? + .wait_for_completion(&client, None, None) + .await?; + + let mut query = DocumentDeletionQuery::new(&index); + query.with_filter("id = 1"); + index + .delete_documents_with(&query) + .await? + .wait_for_completion(&client, None, None) + .await?; + 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_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/indexes.rs b/src/indexes.rs index 45f10bef..437ede3f 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, MeilisearchCommunicationError, MeilisearchError, MEILISEARCH_VERSION_HINT}, request::*, search::*, @@ -959,6 +959,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, diff --git a/src/tasks.rs b/src/tasks.rs index 95509609..e7eb9726 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: Option, } #[derive(Debug, Clone, Deserialize)]