From ee5952f6b11b94319aba8e163c38bb5ea63ad4b3 Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Sun, 21 Jun 2026 09:14:32 -0300 Subject: [PATCH 1/2] docs: document the personalize search parameter Meilisearch v1.47.0 adds experimental personalized search (a `personalize` object with a `userContext` string). `Index.search` forwards `opt_params` to the API as-is, so the parameter already works at runtime; document it alongside the other common search options. Closes #1245 --- meilisearch/index.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meilisearch/index.py b/meilisearch/index.py index 6c65874a..40b8731f 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -348,6 +348,8 @@ def search(self, query: str, opt_params: Optional[Mapping[str, Any]] = None) -> - hybrid: Dict with 'semanticRatio' and 'embedder' fields for hybrid search - vector: Array of numbers for vector search - retrieveVectors: Boolean to include vector data in search results + - personalize: Dict with a 'userContext' string to personalize the + results (experimental; requires Meilisearch >= v1.47) - filter: Filter queries by an attribute's value - limit: Maximum number of documents returned - offset: Number of documents to skip From d1b8fbfb510c06ecfcf93a79d114db0337db97ea Mon Sep 17 00:00:00 2001 From: Tiago Ferreira Date: Mon, 22 Jun 2026 06:35:40 -0300 Subject: [PATCH 2/2] feat: add explicit personalize parameter to Index.search Address review: expose `personalize` as an explicit, typed parameter on `Index.search` and forward it in the request body, instead of only documenting it as an `opt_params` key. Add a test asserting it is serialized correctly. --- meilisearch/index.py | 14 +++++++++++--- tests/index/test_index_search_meilisearch.py | 12 ++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index 40b8731f..814f6b3d 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -333,7 +333,12 @@ def get_stats(self) -> IndexStats: return IndexStats(**stats) @version_error_hint_message - def search(self, query: str, opt_params: Optional[Mapping[str, Any]] = None) -> Dict[str, Any]: + def search( + self, + query: str, + opt_params: Optional[Mapping[str, Any]] = None, + personalize: Optional[Dict[str, Any]] = None, + ) -> Dict[str, Any]: """Search in the index. https://www.meilisearch.com/docs/reference/api/search @@ -348,13 +353,14 @@ def search(self, query: str, opt_params: Optional[Mapping[str, Any]] = None) -> - hybrid: Dict with 'semanticRatio' and 'embedder' fields for hybrid search - vector: Array of numbers for vector search - retrieveVectors: Boolean to include vector data in search results - - personalize: Dict with a 'userContext' string to personalize the - results (experimental; requires Meilisearch >= v1.47) - filter: Filter queries by an attribute's value - limit: Maximum number of documents returned - offset: Number of documents to skip - showPerformanceDetails: If true, the response includes a performanceDetails object (raw data; fields may change in future API versions) + personalize (optional): + Dict with a 'userContext' string to personalize the search results + (experimental; requires Meilisearch >= v1.47). Returns ------- @@ -371,6 +377,8 @@ def search(self, query: str, opt_params: Optional[Mapping[str, Any]] = None) -> opt_params = {} body = {"q": query, **opt_params} + if personalize is not None: + body["personalize"] = personalize return self.http.post( f"{self.config.paths.index}/{self.uid}/{self.config.paths.search}", diff --git a/tests/index/test_index_search_meilisearch.py b/tests/index/test_index_search_meilisearch.py index 1bd6adb0..a6436a0f 100644 --- a/tests/index/test_index_search_meilisearch.py +++ b/tests/index/test_index_search_meilisearch.py @@ -1,6 +1,7 @@ # pylint: disable=invalid-name from collections import Counter +from unittest.mock import patch import pytest @@ -14,6 +15,17 @@ def test_basic_search(index_with_documents): assert "hitsPerPage" is not response +def test_search_serializes_personalize(client): + """The `personalize` parameter must be forwarded in the search request body.""" + index = client.index("books") + with patch.object(index.http, "post", return_value={"hits": []}) as mock_post: + index.search("prince", personalize={"userContext": "user-123"}) + + _, kwargs = mock_post.call_args + assert kwargs["body"]["q"] == "prince" + assert kwargs["body"]["personalize"] == {"userContext": "user-123"} + + def test_basic_search_with_empty_params(index_with_documents): """Tests search with a simple query and empty params.""" response = index_with_documents().search("How to Train Your Dragon", {})