From 3841018f7974a55f076e917241f2e16a10601bd8 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 14:42:42 +0500 Subject: [PATCH 1/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/index.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/meilisearch/index.py b/meilisearch/index.py index 9db92f80..fe8ec7c7 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -2550,6 +2550,44 @@ def reset_localized_attributes(self) -> TaskInfo: return TaskInfo(**task) + + def get_fields(self) -> List[Dict[str, Any]]: + """Get all fields of the index. + + Returns detailed metadata about all fields in the index, including + display, search, filtering, and localization settings for each field. + + https://www.meilisearch.com/docs/reference/api/indexes#get-fields + + Returns + ------- + fields: + List of dictionaries containing metadata for each field. + Each field entry includes: + - name: The field name + - displayed: Object with 'enabled' boolean indicating if field is displayed + - searchable: Object with 'enabled' boolean indicating if field is searchable + - sortable: Object with 'enabled' boolean indicating if field is sortable + - distinct: Object with 'enabled' boolean indicating if field is distinct + - rankingRule: Object with 'enabled' boolean and optional 'order' ('asc' or 'desc') + indicating if field is used in ranking rules + - filterable: Object with 'enabled' boolean and filter settings: + - sortBy: Sort order for facet values (e.g., 'alpha') + - facetSearch: Whether facet search is enabled + - equality: Whether equality filtering is enabled + - comparison: Whether comparison filtering is enabled + - localized: Object with 'locales' array of locale codes + + Raises + ------ + MeilisearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors + """ + return self.http.post( + f"{self.config.paths.index}/{self.uid}/{self.config.paths.fields}", + body={}, + ) + @staticmethod def _batch( documents: Sequence[Mapping[str, Any]], batch_size: int From 553b61941bc508622b664ca1766af232632dde82 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 14:42:47 +0500 Subject: [PATCH 2/8] chore: Adding new endpoint to get metadata about all fields in an index. --- tests/index/test_index.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/index/test_index.py b/tests/index/test_index.py index 38b11943..2c980850 100644 --- a/tests/index/test_index.py +++ b/tests/index/test_index.py @@ -283,3 +283,37 @@ def test_index_update_without_params(client): index.update() assert "primary_key" in str(exc.value) or "new_uid" in str(exc.value) + + +@pytest.mark.usefixtures("indexes_sample") +def test_get_fields(client, small_movies): + """Tests getting all fields of an index via the new /fields endpoint.""" + index = client.index(uid=common.INDEX_UID) + task = index.add_documents(small_movies) + client.wait_for_task(task.task_uid) + + fields = index.get_fields() + + assert isinstance(fields, list) + assert len(fields) > 0 + assert "name" in fields[0] + assert "searchable" in fields[0] + assert "filterable" in fields[0] + assert "sortable" in fields[0] + + +@pytest.mark.usefixtures("indexes_sample") +def test_get_fields_with_configurations(client, small_movies): + """Tests get_fields() reflects index settings configurations.""" + index = client.index(uid=common.INDEX_UID) + task = index.add_documents(small_movies) + client.wait_for_task(task.task_uid) + + task = index.update_searchable_attributes(["title"]) + client.wait_for_task(task.task_uid) + + fields = index.get_fields() + title_field = next((f for f in fields if f["name"] == "title"), None) + + assert title_field is not None + assert title_field["searchable"]["enabled"] is True From bba3c700b84567cfb03372b00f6d583e13cd35a6 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 14:48:42 +0500 Subject: [PATCH 3/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/meilisearch/config.py b/meilisearch/config.py index df39e64a..8637e1d0 100644 --- a/meilisearch/config.py +++ b/meilisearch/config.py @@ -45,6 +45,7 @@ class Paths: prefix_search = "prefix-search" proximity_precision = "proximity-precision" localized_attributes = "localized-attributes" + fields = "fields" edit = "edit" network = "network" experimental_features = "experimental-features" From 0590d54f401c901af588d35d80b7fb6d38d71265 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 14:50:19 +0500 Subject: [PATCH 4/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/index.py | 1 - 1 file changed, 1 deletion(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index fe8ec7c7..c809df62 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -2550,7 +2550,6 @@ def reset_localized_attributes(self) -> TaskInfo: return TaskInfo(**task) - def get_fields(self) -> List[Dict[str, Any]]: """Get all fields of the index. From 5bc01256967627dc9d1e7b88ca3ba0b83199c45d Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 16:43:18 +0500 Subject: [PATCH 5/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/index.py | 36 +++++++++++++++++++++++-- tests/index/test_index.py | 55 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index c809df62..368ed17c 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -2550,7 +2550,12 @@ def reset_localized_attributes(self) -> TaskInfo: return TaskInfo(**task) - def get_fields(self) -> List[Dict[str, Any]]: + def get_fields( + self, + offset: Optional[int] = None, + limit: Optional[int] = None, + filter: Optional[MutableMapping[str, Any]] = None, + ) -> List[Dict[str, Any]]: """Get all fields of the index. Returns detailed metadata about all fields in the index, including @@ -2558,6 +2563,24 @@ def get_fields(self) -> List[Dict[str, Any]]: https://www.meilisearch.com/docs/reference/api/indexes#get-fields + Parameters + ---------- + offset (optional): + Number of fields to skip. Defaults to 0. + limit (optional): + Maximum number of fields to return. Defaults to 20. + filter (optional): + Dictionary containing filter configuration. All filter properties are optional + and can be combined using AND logic. Available filters: + - attributePatterns: List of attribute patterns (supports wildcards: * for any characters) + Examples: ["cuisine.*", "*_id"] matches cuisine.type and all fields ending with _id + - displayed: Boolean - true for only displayed fields, false for only hidden fields + - searchable: Boolean - true for only searchable fields, false for only non-searchable fields + - sortable: Boolean - true for only sortable fields, false for only non-sortable fields + - distinct: Boolean - true for only the distinct field, false for only non-distinct fields + - rankingRule: Boolean - true for only fields used in ranking, false for fields not used in ranking + - filterable: Boolean - true for only filterable fields, false for only non-filterable fields + Returns ------- fields: @@ -2582,9 +2605,18 @@ def get_fields(self) -> List[Dict[str, Any]]: MeilisearchApiError An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors """ + body: Dict[str, Any] = {} + + if offset is not None: + body["offset"] = offset + if limit is not None: + body["limit"] = limit + if filter is not None: + body["filter"] = filter + return self.http.post( f"{self.config.paths.index}/{self.uid}/{self.config.paths.fields}", - body={}, + body=body, ) @staticmethod diff --git a/tests/index/test_index.py b/tests/index/test_index.py index 2c980850..32658712 100644 --- a/tests/index/test_index.py +++ b/tests/index/test_index.py @@ -317,3 +317,58 @@ def test_get_fields_with_configurations(client, small_movies): assert title_field is not None assert title_field["searchable"]["enabled"] is True + + +@pytest.mark.usefixtures("indexes_sample") +def test_get_fields_with_filter(client, small_movies): + """Tests get_fields() with filter parameters.""" + index = client.index(uid=common.INDEX_UID) + task = index.add_documents(small_movies) + client.wait_for_task(task.task_uid) + + task = index.update_searchable_attributes(["title"]) + client.wait_for_task(task.task_uid) + + # Filter only searchable fields + searchable_fields = index.get_fields(filter={"searchable": True}) + + assert isinstance(searchable_fields, list) + assert len(searchable_fields) > 0 + assert all(field["searchable"]["enabled"] is True for field in searchable_fields) + + +@pytest.mark.usefixtures("indexes_sample") +def test_get_fields_with_pagination(client, small_movies): + """Tests get_fields() with pagination parameters.""" + index = client.index(uid=common.INDEX_UID) + task = index.add_documents(small_movies) + client.wait_for_task(task.task_uid) + + # Get all fields first to know total count + all_fields = index.get_fields() + total_fields = len(all_fields) + + # Test pagination with offset and limit + page1 = index.get_fields(offset=0, limit=2) + assert isinstance(page1, list) + assert len(page1) <= 2 + + # If we have more than 2 fields, test second page + if total_fields > 2: + page2 = index.get_fields(offset=2, limit=2) + assert isinstance(page2, list) + assert len(page2) <= 2 + + # Verify pages don't overlap + page1_names = {f["name"] for f in page1} + page2_names = {f["name"] for f in page2} + assert page1_names.isdisjoint(page2_names) + + # Test with just limit (no offset) + limited = index.get_fields(limit=3) + assert isinstance(limited, list) + assert len(limited) <= 3 + + # Test with just offset (no limit, uses default) + offset_only = index.get_fields(offset=1) + assert isinstance(offset_only, list) From b0a560439e5b6e52ed385dfdcab12abb8a4b98a8 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 16:56:45 +0500 Subject: [PATCH 6/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/index.py | 2 +- tests/index/test_index.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index 368ed17c..7783b482 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -2606,7 +2606,7 @@ def get_fields( An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors """ body: Dict[str, Any] = {} - + if offset is not None: body["offset"] = offset if limit is not None: diff --git a/tests/index/test_index.py b/tests/index/test_index.py index 32658712..47d08753 100644 --- a/tests/index/test_index.py +++ b/tests/index/test_index.py @@ -347,28 +347,28 @@ def test_get_fields_with_pagination(client, small_movies): # Get all fields first to know total count all_fields = index.get_fields() total_fields = len(all_fields) - + # Test pagination with offset and limit page1 = index.get_fields(offset=0, limit=2) assert isinstance(page1, list) assert len(page1) <= 2 - + # If we have more than 2 fields, test second page if total_fields > 2: page2 = index.get_fields(offset=2, limit=2) assert isinstance(page2, list) assert len(page2) <= 2 - + # Verify pages don't overlap page1_names = {f["name"] for f in page1} page2_names = {f["name"] for f in page2} assert page1_names.isdisjoint(page2_names) - + # Test with just limit (no offset) limited = index.get_fields(limit=3) assert isinstance(limited, list) assert len(limited) <= 3 - + # Test with just offset (no limit, uses default) offset_only = index.get_fields(offset=1) assert isinstance(offset_only, list) From 41c8ac8dfe106cb08c99a321b3fb67a7888d2965 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 16:59:45 +0500 Subject: [PATCH 7/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index 7783b482..712e1468 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -2554,7 +2554,7 @@ def get_fields( self, offset: Optional[int] = None, limit: Optional[int] = None, - filter: Optional[MutableMapping[str, Any]] = None, + filter: Optional[MutableMapping[str, Any]] = None, # pylint: disable=redefined-builtin ) -> List[Dict[str, Any]]: """Get all fields of the index. @@ -2606,7 +2606,7 @@ def get_fields( An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors """ body: Dict[str, Any] = {} - + if offset is not None: body["offset"] = offset if limit is not None: From 9c4d3868c07735862b7b76cf6f634a3b2070233f Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 21 Jan 2026 17:04:20 +0500 Subject: [PATCH 8/8] chore: Adding new endpoint to get metadata about all fields in an index. --- meilisearch/index.py | 1 - 1 file changed, 1 deletion(-) diff --git a/meilisearch/index.py b/meilisearch/index.py index 712e1468..b7f657e5 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -2606,7 +2606,6 @@ def get_fields( An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors """ body: Dict[str, Any] = {} - if offset is not None: body["offset"] = offset if limit is not None: