From c49b8985211d4a6fce5377c88d24b0a76e8a56b1 Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Tue, 16 May 2023 20:48:14 -0400 Subject: [PATCH 1/7] Add delete documents by filter --- meilisearch/index.py | 27 +++++++++++++++++++ .../index/test_index_document_meilisearch.py | 14 ++++++++++ 2 files changed, 41 insertions(+) diff --git a/meilisearch/index.py b/meilisearch/index.py index 16dfb74b..f3acae1f 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -754,6 +754,33 @@ def delete_documents(self, ids: List[Union[str, int]]) -> TaskInfo: ) return TaskInfo(**response) + def delete_documents_by_filter( + self, filter: Union[str, List[Union[str, List[str]]]] + ) -> TaskInfo: + """Delete documents from the index by filter. + + Parameters + ---------- + filter: + The filter value information. + + Returns + ------- + task_info: + TaskInfo instance containing information about a task to track the progress of an asynchronous process. + https://docs.meilisearch.com/reference/api/tasks.html#get-one-task + + Raises + ------ + MeilisearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors + """ + response = self.http.post( + f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}/delete", + body={"filter": filter}, + ) + return TaskInfo(**response) + def delete_all_documents(self) -> TaskInfo: """Delete all documents from the index. diff --git a/tests/index/test_index_document_meilisearch.py b/tests/index/test_index_document_meilisearch.py index 086326b1..79a405a8 100644 --- a/tests/index/test_index_document_meilisearch.py +++ b/tests/index/test_index_document_meilisearch.py @@ -174,6 +174,20 @@ def test_delete_documents(index_with_documents): index.get_document(document) +def test_delete_documents_by_filter(index_with_documents): + index = index_with_documents() + response = index.update_filterable_attributes(["genre"]) + index.wait_for_task(response.task_uid) + response = index.get_documents() + assert "action" in ([x.__dict__.get("genre") for x in response.results]) + response = index.delete_documents_by_filter("genre=action") + index.wait_for_task(response.task_uid) + response = index.get_documents() + genres = [x.__dict__.get("genre") for x in response.results] + assert "action" not in genres + assert "cartoon" in genres + + def test_delete_all_documents(index_with_documents): """Tests deleting all the documents in the index.""" index = index_with_documents() From 056ed0f6db33533d46ca82842f2982a4a8141df7 Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Tue, 16 May 2023 20:56:29 -0400 Subject: [PATCH 2/7] Fix pylint error --- meilisearch/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index f3acae1f..deb16051 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -755,7 +755,7 @@ def delete_documents(self, ids: List[Union[str, int]]) -> TaskInfo: return TaskInfo(**response) def delete_documents_by_filter( - self, filter: Union[str, List[Union[str, List[str]]]] + self, filter: Union[str, List[Union[str, List[str]]]] # pylint: disable=redefined-builtin ) -> TaskInfo: """Delete documents from the index by filter. From 9e79e6093d686502b4e3ddcf272ccf5d7b51931e Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Wed, 17 May 2023 08:38:43 -0400 Subject: [PATCH 3/7] Add version_error_hint_message decorator --- meilisearch/errors.py | 19 +++++++++++++++++++ meilisearch/index.py | 9 +++++++-- meilisearch/models/document.py | 4 ++-- tests/errors/test_api_error_meilisearch.py | 21 ++++++++++++++++++++- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/meilisearch/errors.py b/meilisearch/errors.py index 0da4e654..43418cfe 100644 --- a/meilisearch/errors.py +++ b/meilisearch/errors.py @@ -1,9 +1,16 @@ from __future__ import annotations import json +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable from requests import Response +if TYPE_CHECKING: + from meilisearch.client import Client + from meilisearch.index import Index + from meilisearch.task import TaskHandler + class MeilisearchError(Exception): """Generic class for Meilisearch error handling""" @@ -54,3 +61,15 @@ class MeilisearchTimeoutError(MeilisearchError): def __str__(self) -> str: return f"MeilisearchTimeoutError, {self.message}" + + +def version_error_hint_message(func: Callable) -> Any: + @wraps(func) + def wrapper(*args: Any, **kwargs: Any) -> Any: + try: + return func(*args, **kwargs) + except MeilisearchApiError as e: + e.message = f"{e.message}. Hint: It might not be working because you're not up to date with the Meilisearch version that {func.__name__} call requires." + raise e + + return wrapper diff --git a/meilisearch/index.py b/meilisearch/index.py index deb16051..df561ce0 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -6,6 +6,7 @@ from meilisearch._httprequests import HttpRequests from meilisearch.config import Config +from meilisearch.errors import version_error_hint_message from meilisearch.models.document import Document, DocumentsResults from meilisearch.models.index import Faceting, IndexStats, Pagination, TypoTolerance from meilisearch.models.task import Task, TaskInfo, TaskResults @@ -729,12 +730,15 @@ def delete_document(self, document_id: Union[str, int]) -> TaskInfo: ) return TaskInfo(**response) - def delete_documents(self, ids: List[Union[str, int]]) -> TaskInfo: + def delete_documents( + self, + ids: List[Union[str, int]], + ) -> TaskInfo: """Delete multiple documents from the index. Parameters ---------- - list: + ids: List of unique identifiers of documents. Returns @@ -754,6 +758,7 @@ def delete_documents(self, ids: List[Union[str, int]]) -> TaskInfo: ) return TaskInfo(**response) + @version_error_hint_message def delete_documents_by_filter( self, filter: Union[str, List[Union[str, List[str]]]] # pylint: disable=redefined-builtin ) -> TaskInfo: diff --git a/meilisearch/models/document.py b/meilisearch/models/document.py index f68247a2..c5eeb812 100644 --- a/meilisearch/models/document.py +++ b/meilisearch/models/document.py @@ -1,6 +1,6 @@ -from __future__ import annotations +from typing import Any, Dict, Iterator, List, Union -from typing import Any, Dict, Iterator +from camel_converter.pydantic_base import CamelBase class Document: diff --git a/tests/errors/test_api_error_meilisearch.py b/tests/errors/test_api_error_meilisearch.py index f9a4064c..5394f717 100644 --- a/tests/errors/test_api_error_meilisearch.py +++ b/tests/errors/test_api_error_meilisearch.py @@ -6,7 +6,7 @@ import requests import meilisearch -from meilisearch.errors import MeilisearchApiError +from meilisearch.errors import MeilisearchApiError, version_error_hint_message from tests import BASE_URL, MASTER_KEY @@ -33,3 +33,22 @@ def test_meilisearch_api_error_no_code(mock_post): with pytest.raises(MeilisearchApiError): client = meilisearch.Client(BASE_URL, MASTER_KEY + "123") client.create_index("some_index") + + +def test_version_error_hint_message(): + mock_response = requests.models.Response() + mock_response.status_code = 408 + + class FakeClass: + @version_error_hint_message + def test_method(self): + raise MeilisearchApiError("This is a test", mock_response) + + with pytest.raises(MeilisearchApiError) as e: + fake = FakeClass() + fake.test_method() + + assert ( + "MeilisearchApiError. This is a test. Hint: It might not be working because you're not up to date with the Meilisearch version that test_method call requires." + == str(e.value) + ) From aaefae799e0ccaed4a595cdfc149bc0efa8297c8 Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Wed, 17 May 2023 08:47:10 -0400 Subject: [PATCH 4/7] Make pylint happy --- meilisearch/errors.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meilisearch/errors.py b/meilisearch/errors.py index 43418cfe..2330a250 100644 --- a/meilisearch/errors.py +++ b/meilisearch/errors.py @@ -68,8 +68,8 @@ def version_error_hint_message(func: Callable) -> Any: def wrapper(*args: Any, **kwargs: Any) -> Any: try: return func(*args, **kwargs) - except MeilisearchApiError as e: - e.message = f"{e.message}. Hint: It might not be working because you're not up to date with the Meilisearch version that {func.__name__} call requires." - raise e + except MeilisearchApiError as exc: + exc.message = f"{exc.message}. Hint: It might not be working because you're not up to date with the Meilisearch version that {func.__name__} call requires." + raise exc return wrapper From 42d8218e9dff1094a4375291c5a4099108d3fa48 Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Wed, 17 May 2023 08:50:18 -0400 Subject: [PATCH 5/7] Remove unused imports --- meilisearch/models/document.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/meilisearch/models/document.py b/meilisearch/models/document.py index c5eeb812..f7c621d8 100644 --- a/meilisearch/models/document.py +++ b/meilisearch/models/document.py @@ -1,6 +1,4 @@ -from typing import Any, Dict, Iterator, List, Union - -from camel_converter.pydantic_base import CamelBase +from typing import Any, Dict, Iterator class Document: From 24464e561f3354e30e4f44eaa32b8c70629ff6d6 Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Fri, 19 May 2023 18:13:20 -0400 Subject: [PATCH 6/7] Combine delete_documents and delete_documents_by_filter --- meilisearch/index.py | 32 +++++++++++++++---- .../index/test_index_document_meilisearch.py | 27 +++++++++------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index df561ce0..b8a69aad 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -3,6 +3,7 @@ from datetime import datetime from typing import Any, Dict, Generator, List, Optional, Union from urllib import parse +from warnings import warn from meilisearch._httprequests import HttpRequests from meilisearch.config import Config @@ -732,14 +733,21 @@ def delete_document(self, document_id: Union[str, int]) -> TaskInfo: def delete_documents( self, - ids: List[Union[str, int]], + ids: Optional[List[Union[str, int]]] = None, + *, + filter: Optional[ # pylint: disable=redefined-builtin + Union[str, List[Union[str, List[str]]]] + ] = None, ) -> TaskInfo: - """Delete multiple documents from the index. + """Delete multiple documents from the index by id or filter. Parameters ---------- ids: - List of unique identifiers of documents. + List of unique identifiers of documents. Note: using ids is depreciated and will be + removed in a future version. + filter: + The filter value information. Returns ------- @@ -752,10 +760,20 @@ def delete_documents( MeilisearchApiError An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors """ - response = self.http.post( - f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}/delete-batch", - [str(i) for i in ids], - ) + if ids: + warn( + "The use of ids is depreciated and will be removed in the future", + DeprecationWarning, + ) + response = self.http.post( + f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}/delete-batch", + [str(i) for i in ids], + ) + else: + response = self.http.post( + f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}/delete", + body={"filter": filter}, + ) return TaskInfo(**response) @version_error_hint_message diff --git a/tests/index/test_index_document_meilisearch.py b/tests/index/test_index_document_meilisearch.py index 79a405a8..cb92fec7 100644 --- a/tests/index/test_index_document_meilisearch.py +++ b/tests/index/test_index_document_meilisearch.py @@ -1,6 +1,7 @@ # pylint: disable=invalid-name from math import ceil +from warnings import catch_warnings import pytest @@ -161,26 +162,28 @@ def test_delete_document(index_with_documents): index.get_document("500682") -def test_delete_documents(index_with_documents): +def test_delete_documents_by_id(index_with_documents): """Tests deleting a set of documents.""" - to_delete = [522681, "450465", 329996] - index = index_with_documents() - response = index.delete_documents(to_delete) - assert isinstance(response, TaskInfo) - assert response.task_uid is not None - index.wait_for_task(response.task_uid) - for document in to_delete: - with pytest.raises(Exception): - index.get_document(document) + with catch_warnings(record=True) as w: + to_delete = [522681, "450465", 329996] + index = index_with_documents() + response = index.delete_documents(to_delete) + assert isinstance(response, TaskInfo) + assert response.task_uid is not None + index.wait_for_task(response.task_uid) + for document in to_delete: + with pytest.raises(Exception): + index.get_document(document) + assert "The use of ids is depreciated" in str(w[0].message) -def test_delete_documents_by_filter(index_with_documents): +def test_delete_documents(index_with_documents): index = index_with_documents() response = index.update_filterable_attributes(["genre"]) index.wait_for_task(response.task_uid) response = index.get_documents() assert "action" in ([x.__dict__.get("genre") for x in response.results]) - response = index.delete_documents_by_filter("genre=action") + response = index.delete_documents(filter="genre=action") index.wait_for_task(response.task_uid) response = index.get_documents() genres = [x.__dict__.get("genre") for x in response.results] From cc21b640b79ea7931d09bf0f2a50053118d53d4c Mon Sep 17 00:00:00 2001 From: Paul Sanders Date: Sat, 20 May 2023 15:16:38 -0400 Subject: [PATCH 7/7] Remove delete_documents_by_filter --- meilisearch/index.py | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index b8a69aad..b26e04a2 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -731,6 +731,7 @@ def delete_document(self, document_id: Union[str, int]) -> TaskInfo: ) return TaskInfo(**response) + @version_error_hint_message def delete_documents( self, ids: Optional[List[Union[str, int]]] = None, @@ -776,34 +777,6 @@ def delete_documents( ) return TaskInfo(**response) - @version_error_hint_message - def delete_documents_by_filter( - self, filter: Union[str, List[Union[str, List[str]]]] # pylint: disable=redefined-builtin - ) -> TaskInfo: - """Delete documents from the index by filter. - - Parameters - ---------- - filter: - The filter value information. - - Returns - ------- - task_info: - TaskInfo instance containing information about a task to track the progress of an asynchronous process. - https://docs.meilisearch.com/reference/api/tasks.html#get-one-task - - Raises - ------ - MeilisearchApiError - An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors - """ - response = self.http.post( - f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}/delete", - body={"filter": filter}, - ) - return TaskInfo(**response) - def delete_all_documents(self) -> TaskInfo: """Delete all documents from the index.