meilisearch-python-sdk 4.4.0__tar.gz → 4.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of meilisearch-python-sdk might be problematic. Click here for more details.

Files changed (101) hide show
  1. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/workflows/testing.yml +4 -4
  2. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.pre-commit-config.yaml +1 -1
  3. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/PKG-INFO +1 -1
  4. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/_client.py +111 -1
  5. meilisearch_python_sdk-4.5.0/meilisearch_python_sdk/_version.py +1 -0
  6. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/index.py +75 -9
  7. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/batch.py +3 -0
  8. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/client.py +11 -0
  9. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/settings.py +17 -1
  10. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/pyproject.toml +2 -2
  11. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/conftest.py +9 -0
  12. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_async_client.py +23 -1
  13. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_async_index.py +31 -5
  14. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_async_search.py +14 -0
  15. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_client.py +23 -1
  16. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_index.py +31 -5
  17. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_search.py +14 -0
  18. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/uv.lock +38 -38
  19. meilisearch_python_sdk-4.4.0/meilisearch_python_sdk/_version.py +0 -1
  20. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/FUNDING.yml +0 -0
  21. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/release-draft-template.yaml +0 -0
  22. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/renovate.json5 +0 -0
  23. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/workflows/docs_publish.yml +0 -0
  24. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/workflows/nightly_testing.yml +0 -0
  25. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/workflows/pypi_publish.yml +0 -0
  26. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.github/workflows/release-drafter.yml +0 -0
  27. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/.gitignore +0 -0
  28. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/CONTRIBUTING.md +0 -0
  29. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/LICENSE +0 -0
  30. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/README.md +0 -0
  31. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/assets/add_in_batches.png +0 -0
  32. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/assets/searches.png +0 -0
  33. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/benchmark/run_benchmark.py +0 -0
  34. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/codecov.yml +0 -0
  35. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/datasets/small_movies.json +0 -0
  36. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docker-compose.https.yml +0 -0
  37. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docker-compose.yml +0 -0
  38. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/.nojekyll +0 -0
  39. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/CNAME +0 -0
  40. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/async_client_api.md +0 -0
  41. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/async_index_api.md +0 -0
  42. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/client_api.md +0 -0
  43. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/css/custom.css +0 -0
  44. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/decorators_api.md +0 -0
  45. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/index.md +0 -0
  46. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/index_api.md +0 -0
  47. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/js/umami.js +0 -0
  48. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/json_handler.md +0 -0
  49. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/plugins.md +0 -0
  50. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/docs/pydantic.md +0 -0
  51. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/.gitignore +0 -0
  52. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/README.md +0 -0
  53. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/__init__.py +0 -0
  54. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/add_documents_decorator.py +0 -0
  55. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/add_documents_in_batches.py +0 -0
  56. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/async_add_documents_decorator.py +0 -0
  57. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/async_add_documents_in_batches.py +0 -0
  58. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/async_documents_and_search_results.py +0 -0
  59. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/async_search_tracker.py +0 -0
  60. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/async_update_settings.py +0 -0
  61. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/documents_and_search_results.py +0 -0
  62. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/fastapi_example.py +0 -0
  63. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/orjson_example.py +0 -0
  64. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/pyproject.toml +0 -0
  65. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/requirements.txt +0 -0
  66. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/search_tracker.py +0 -0
  67. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/tests/__init__.py +0 -0
  68. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/tests/conftest.py +0 -0
  69. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/tests/test_async_examples.py +0 -0
  70. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/tests/test_examples.py +0 -0
  71. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/ujson_example.py +0 -0
  72. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/examples/update_settings.py +0 -0
  73. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/justfile +0 -0
  74. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/__init__.py +0 -0
  75. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/_batch.py +0 -0
  76. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/_http_requests.py +0 -0
  77. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/_task.py +0 -0
  78. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/_utils.py +0 -0
  79. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/decorators.py +0 -0
  80. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/errors.py +0 -0
  81. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/json_handler.py +0 -0
  82. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/__init__.py +0 -0
  83. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/documents.py +0 -0
  84. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/health.py +0 -0
  85. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/index.py +0 -0
  86. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/search.py +0 -0
  87. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/task.py +0 -0
  88. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/models/version.py +0 -0
  89. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/plugins.py +0 -0
  90. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/py.typed +0 -0
  91. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/meilisearch_python_sdk/types.py +0 -0
  92. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/mkdocs.yaml +0 -0
  93. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/__init__.py +0 -0
  94. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_async_documents.py +0 -0
  95. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_async_index_plugins.py +0 -0
  96. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_decorators.py +0 -0
  97. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_documents.py +0 -0
  98. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_errors.py +0 -0
  99. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_index_plugins.py +0 -0
  100. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_utils.py +0 -0
  101. {meilisearch_python_sdk-4.4.0 → meilisearch_python_sdk-4.5.0}/tests/test_version.py +0 -0
@@ -50,7 +50,7 @@ jobs:
50
50
  - name: Test with pytest
51
51
  run: just test-parallel-ci
52
52
  - name: Upload coverage
53
- uses: codecov/codecov-action@v5
53
+ uses: codecov/codecov-action@v5.4.0
54
54
  with:
55
55
  token: ${{ secrets.CODECOV_TOKEN }}
56
56
  fail_ci_if_error: true
@@ -90,7 +90,7 @@ jobs:
90
90
  - name: Test with pytest
91
91
  run: just test-parallel-ci-http2
92
92
  - name: Upload coverage
93
- uses: codecov/codecov-action@v5
93
+ uses: codecov/codecov-action@v5.4.0
94
94
  with:
95
95
  token: ${{ secrets.CODECOV_TOKEN }}
96
96
  fail_ci_if_error: true
@@ -118,7 +118,7 @@ jobs:
118
118
  - name: Test with pytest
119
119
  run: just test-no-parallel-ci
120
120
  - name: Upload coverage
121
- uses: codecov/codecov-action@v5
121
+ uses: codecov/codecov-action@v5.4.0
122
122
  with:
123
123
  token: ${{ secrets.CODECOV_TOKEN }}
124
124
  fail_ci_if_error: true
@@ -158,7 +158,7 @@ jobs:
158
158
  - name: Test with pytest
159
159
  run: just test-no-parallel-ci-http2
160
160
  - name: Upload coverage
161
- uses: codecov/codecov-action@v5
161
+ uses: codecov/codecov-action@v5.4.0
162
162
  with:
163
163
  token: ${{ secrets.CODECOV_TOKEN }}
164
164
  fail_ci_if_error: true
@@ -14,7 +14,7 @@ repos:
14
14
  - id: mypy
15
15
  additional_dependencies: [pydantic, orjson, types-aiofiles, types-ujson]
16
16
  - repo: https://github.com/astral-sh/ruff-pre-commit
17
- rev: v0.11.4
17
+ rev: v0.11.5
18
18
  hooks:
19
19
  - id: ruff
20
20
  args: [--fix, --exit-non-zero-on-fix]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meilisearch-python-sdk
3
- Version: 4.4.0
3
+ Version: 4.5.0
4
4
  Summary: A Python client providing both async and sync support for the Meilisearch API
5
5
  Project-URL: repository, https://github.com/sanders41/meilisearch-python-sdk
6
6
  Project-URL: homepage, https://github.com/sanders41/meilisearch-python-sdk
@@ -22,6 +22,7 @@ from meilisearch_python_sdk.models.client import (
22
22
  KeyCreate,
23
23
  KeySearch,
24
24
  KeyUpdate,
25
+ Network,
25
26
  )
26
27
  from meilisearch_python_sdk.models.health import Health
27
28
  from meilisearch_python_sdk.models.index import IndexInfo
@@ -196,12 +197,66 @@ class AsyncClient(BaseClient):
196
197
  """
197
198
  await self.http_client.aclose()
198
199
 
200
+ async def add_or_update_networks(self, *, network: Network) -> Network:
201
+ """Set or update remote networks.
202
+
203
+ Args:
204
+ network: Information to use for the networks.
205
+
206
+ Returns:
207
+ An instance of Network containing the network information.
208
+
209
+ Raises:
210
+ MeilisearchCommunicationError: If there was an error communicating with the server.
211
+ MeilisearchApiError: If the Meilisearch API returned an error.
212
+
213
+ Examples:
214
+ >>> from meilisearch_python_sdk import AsyncClient
215
+ >>> from meilisearch_python_sdk.models.client import Network, Remote
216
+ >>>
217
+ >>>
218
+ >>> network = Network(
219
+ >>> self_="remote_1",
220
+ >>> remotes={
221
+ >>> "remote_1": {"url": "http://localhost:7700", "searchApiKey": "xxxx"},
222
+ >>> "remote_2": {"url": "http://localhost:7720", "searchApiKey": "xxxx"},
223
+ >>> },
224
+ >>> )
225
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
226
+ >>> response = await client.add_or_update_networks(network=network)
227
+ """
228
+ response = await self._http_requests.patch(
229
+ "network", network.model_dump(by_alias=True, exclude_none=True)
230
+ )
231
+
232
+ return Network(**response.json())
233
+
234
+ async def get_networks(self) -> Network:
235
+ """Fetches the remote-networks
236
+
237
+ Returns:
238
+ An instance of Network containing information about each remote.
239
+
240
+ Raises:
241
+ MeilisearchCommunicationError: If there was an error communicating with the server.
242
+ MeilisearchApiError: If the Meilisearch API returned an error.
243
+
244
+ Examples:
245
+ >>> from meilisearch_python_sdk import AsyncClient
246
+ >>>
247
+ >>>
248
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
249
+ >>> response = await client.get_networks()
250
+ """
251
+ response = await self._http_requests.get("network")
252
+
253
+ return Network(**response.json())
254
+
199
255
  async def create_dump(self) -> TaskInfo:
200
256
  """Trigger the creation of a Meilisearch dump.
201
257
 
202
258
  Returns:
203
259
  The details of the task.
204
-
205
260
  Raises:
206
261
  MeilisearchCommunicationError: If there was an error communicating with the server.
207
262
  MeilisearchApiError: If the Meilisearch API returned an error.
@@ -1061,6 +1116,61 @@ class Client(BaseClient):
1061
1116
 
1062
1117
  self._http_requests = HttpRequests(self.http_client, json_handler=self.json_handler)
1063
1118
 
1119
+ def add_or_update_networks(self, *, network: Network) -> Network:
1120
+ """Set or update remote networks.
1121
+
1122
+ Args:
1123
+ network: Information to use for the networks.
1124
+
1125
+ Returns:
1126
+ An instance of Network containing the network information.
1127
+
1128
+ Raises:
1129
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1130
+ MeilisearchApiError: If the Meilisearch API returned an error.
1131
+
1132
+ Examples:
1133
+ >>> from meilisearch_python_sdk import Client
1134
+ >>> from meilisearch_python_sdk.models.client import Network, Remote
1135
+ >>>
1136
+ >>>
1137
+ >>> network = Network(
1138
+ >>> self_="remote_1",
1139
+ >>> remotes={
1140
+ >>> "remote_1": {"url": "http://localhost:7700", "searchApiKey": "xxxx"},
1141
+ >>> "remote_2": {"url": "http://localhost:7720", "searchApiKey": "xxxx"},
1142
+ >>> },
1143
+ >>> )
1144
+ >>> client = Client("http://localhost.com", "masterKey")
1145
+ >>> response = client.add_or_update_networks(network=network)
1146
+ """
1147
+ response = self._http_requests.patch(
1148
+ "network", network.model_dump(by_alias=True, exclude_none=True)
1149
+ )
1150
+
1151
+ return Network(**response.json())
1152
+
1153
+ def get_networks(self) -> Network:
1154
+ """Fetches the remote-networks
1155
+
1156
+ Returns:
1157
+ An instance of Network containing information about each remote.
1158
+
1159
+ Raises:
1160
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1161
+ MeilisearchApiError: If the Meilisearch API returned an error.
1162
+
1163
+ Examples:
1164
+ >>> from meilisearch_python_sdk import AsyncClient
1165
+ >>>
1166
+ >>>
1167
+ >>> client = Client("http://localhost.com", "masterKey")
1168
+ >>> response = client.get_networks()
1169
+ """
1170
+ response = self._http_requests.get("network")
1171
+
1172
+ return Network(**response.json())
1173
+
1064
1174
  def create_dump(self) -> TaskInfo:
1065
1175
  """Trigger the creation of a Meilisearch dump.
1066
1176
 
@@ -0,0 +1 @@
1
+ VERSION = "4.5.0"
@@ -29,6 +29,8 @@ from meilisearch_python_sdk.models.search import (
29
29
  from meilisearch_python_sdk.models.settings import (
30
30
  Embedders,
31
31
  Faceting,
32
+ FilterableAttributeFeatures,
33
+ FilterableAttributes,
32
34
  HuggingFaceEmbedder,
33
35
  LocalizedAttributes,
34
36
  MeilisearchSettings,
@@ -990,6 +992,7 @@ class AsyncIndex(_BaseIndex):
990
992
  vector: list[float] | None = None,
991
993
  locales: list[str] | None = None,
992
994
  retrieve_vectors: bool | None = None,
995
+ exhaustive_facet_count: bool | None = None,
993
996
  ) -> FacetSearchResults:
994
997
  """Search the index.
995
998
 
@@ -1043,6 +1046,9 @@ class AsyncIndex(_BaseIndex):
1043
1046
  locales: Specifies the languages for the search. This parameter can only be used with
1044
1047
  Milisearch >= v1.10.0. Defaults to None letting the Meilisearch pick.
1045
1048
  retrieve_vectors: Return document vector data with search result.
1049
+ exhaustive_facet_count: forcing the facet search to compute the facet counts the same
1050
+ way as the paginated search. This parameter can only be used with Milisearch >=
1051
+ v1.14.0. Defaults to None.
1046
1052
 
1047
1053
  Returns:
1048
1054
  Results of the search
@@ -1091,6 +1097,7 @@ class AsyncIndex(_BaseIndex):
1091
1097
  vector=vector,
1092
1098
  locales=locales,
1093
1099
  retrieve_vectors=retrieve_vectors,
1100
+ exhaustive_facet_count=exhaustive_facet_count,
1094
1101
  )
1095
1102
  search_url = f"{self._base_url_with_uid}/facet-search"
1096
1103
 
@@ -1120,6 +1127,7 @@ class AsyncIndex(_BaseIndex):
1120
1127
  show_ranking_score_details=show_ranking_score_details,
1121
1128
  ranking_score_threshold=ranking_score_threshold,
1122
1129
  vector=vector,
1130
+ exhaustive_facet_count=exhaustive_facet_count,
1123
1131
  )
1124
1132
 
1125
1133
  if self._concurrent_facet_search_plugins:
@@ -1152,6 +1160,7 @@ class AsyncIndex(_BaseIndex):
1152
1160
  show_ranking_score_details=show_ranking_score_details,
1153
1161
  ranking_score_threshold=ranking_score_threshold,
1154
1162
  vector=vector,
1163
+ exhaustive_facet_count=exhaustive_facet_count,
1155
1164
  )
1156
1165
  )
1157
1166
 
@@ -1195,6 +1204,7 @@ class AsyncIndex(_BaseIndex):
1195
1204
  show_ranking_score_details=show_ranking_score_details,
1196
1205
  ranking_score_threshold=ranking_score_threshold,
1197
1206
  vector=vector,
1207
+ exhaustive_facet_count=exhaustive_facet_count,
1198
1208
  )
1199
1209
  )
1200
1210
 
@@ -3568,11 +3578,11 @@ class AsyncIndex(_BaseIndex):
3568
3578
 
3569
3579
  return TaskInfo(**response.json())
3570
3580
 
3571
- async def get_filterable_attributes(self) -> list[str] | None:
3581
+ async def get_filterable_attributes(self) -> list[str] | list[FilterableAttributes] | None:
3572
3582
  """Get filterable attributes of the index.
3573
3583
 
3574
3584
  Returns:
3575
- List containing the filterable attributes of the index.
3585
+ Filterable attributes of the index.
3576
3586
 
3577
3587
  Raises:
3578
3588
  MeilisearchCommunicationError: If there was an error communicating with the server.
@@ -3589,10 +3599,24 @@ class AsyncIndex(_BaseIndex):
3589
3599
  if not response.json():
3590
3600
  return None
3591
3601
 
3592
- return response.json()
3602
+ response_json = response.json()
3603
+
3604
+ if isinstance(response_json[0], str):
3605
+ return response_json
3606
+
3607
+ filterable_attributes = []
3608
+ for r in response_json:
3609
+ filterable_attributes.append(
3610
+ FilterableAttributes(
3611
+ attribute_patterns=r["attributePatterns"],
3612
+ features=FilterableAttributeFeatures(**r["features"]),
3613
+ )
3614
+ )
3615
+
3616
+ return filterable_attributes
3593
3617
 
3594
3618
  async def update_filterable_attributes(
3595
- self, body: list[str], *, compress: bool = False
3619
+ self, body: list[str] | list[FilterableAttributes], *, compress: bool = False
3596
3620
  ) -> TaskInfo:
3597
3621
  """Update filterable attributes of the index.
3598
3622
 
@@ -3613,8 +3637,16 @@ class AsyncIndex(_BaseIndex):
3613
3637
  >>> index = client.index("movies")
3614
3638
  >>> await index.update_filterable_attributes(["genre", "director"])
3615
3639
  """
3640
+ payload: list[str | JsonDict] = []
3641
+
3642
+ for b in body:
3643
+ if isinstance(b, FilterableAttributes):
3644
+ payload.append(b.model_dump(by_alias=True))
3645
+ else:
3646
+ payload.append(b)
3647
+
3616
3648
  response = await self._http_requests.put(
3617
- f"{self._settings_url}/filterable-attributes", body, compress=compress
3649
+ f"{self._settings_url}/filterable-attributes", payload, compress=compress
3618
3650
  )
3619
3651
 
3620
3652
  return TaskInfo(**response.json())
@@ -5292,6 +5324,7 @@ class Index(_BaseIndex):
5292
5324
  vector: list[float] | None = None,
5293
5325
  locales: list[str] | None = None,
5294
5326
  retrieve_vectors: bool | None = None,
5327
+ exhaustive_facet_count: bool | None = None,
5295
5328
  ) -> FacetSearchResults:
5296
5329
  """Search the index.
5297
5330
 
@@ -5345,6 +5378,9 @@ class Index(_BaseIndex):
5345
5378
  locales: Specifies the languages for the search. This parameter can only be used with
5346
5379
  Milisearch >= v1.10.0. Defaults to None letting the Meilisearch pick.
5347
5380
  retrieve_vectors: Return document vector data with search result.
5381
+ exhaustive_facet_count: forcing the facet search to compute the facet counts the same
5382
+ way as the paginated search. This parameter can only be used with Milisearch >=
5383
+ v1.14.0. Defaults to None.
5348
5384
 
5349
5385
  Returns:
5350
5386
  Results of the search
@@ -5393,6 +5429,7 @@ class Index(_BaseIndex):
5393
5429
  vector=vector,
5394
5430
  locales=locales,
5395
5431
  retrieve_vectors=retrieve_vectors,
5432
+ exhaustive_facet_count=exhaustive_facet_count,
5396
5433
  )
5397
5434
 
5398
5435
  if self._pre_facet_search_plugins:
@@ -5421,6 +5458,7 @@ class Index(_BaseIndex):
5421
5458
  show_ranking_score_details=show_ranking_score_details,
5422
5459
  ranking_score_threshold=ranking_score_threshold,
5423
5460
  vector=vector,
5461
+ exhaustive_facet_count=exhaustive_facet_count,
5424
5462
  )
5425
5463
 
5426
5464
  response = self._http_requests.post(f"{self._base_url_with_uid}/facet-search", body=body)
@@ -7156,7 +7194,7 @@ class Index(_BaseIndex):
7156
7194
 
7157
7195
  return TaskInfo(**response.json())
7158
7196
 
7159
- def get_filterable_attributes(self) -> list[str] | None:
7197
+ def get_filterable_attributes(self) -> list[str] | list[FilterableAttributes] | None:
7160
7198
  """Get filterable attributes of the index.
7161
7199
 
7162
7200
  Returns:
@@ -7177,9 +7215,25 @@ class Index(_BaseIndex):
7177
7215
  if not response.json():
7178
7216
  return None
7179
7217
 
7180
- return response.json()
7218
+ response_json = response.json()
7219
+
7220
+ if isinstance(response_json[0], str):
7221
+ return response_json
7222
+
7223
+ filterable_attributes = []
7224
+ for r in response_json:
7225
+ filterable_attributes.append(
7226
+ FilterableAttributes(
7227
+ attribute_patterns=r["attributePatterns"],
7228
+ features=FilterableAttributeFeatures(**r["features"]),
7229
+ )
7230
+ )
7181
7231
 
7182
- def update_filterable_attributes(self, body: list[str], *, compress: bool = False) -> TaskInfo:
7232
+ return filterable_attributes
7233
+
7234
+ def update_filterable_attributes(
7235
+ self, body: list[str] | list[FilterableAttributes], *, compress: bool = False
7236
+ ) -> TaskInfo:
7183
7237
  """Update filterable attributes of the index.
7184
7238
 
7185
7239
  Args:
@@ -7199,8 +7253,16 @@ class Index(_BaseIndex):
7199
7253
  >>> index = client.index("movies")
7200
7254
  >>> index.update_filterable_attributes(["genre", "director"])
7201
7255
  """
7256
+ payload: list[str | JsonDict] = []
7257
+
7258
+ for b in body:
7259
+ if isinstance(b, FilterableAttributes):
7260
+ payload.append(b.model_dump(by_alias=True))
7261
+ else:
7262
+ payload.append(b)
7263
+
7202
7264
  response = self._http_requests.put(
7203
- f"{self._settings_url}/filterable-attributes", body, compress=compress
7265
+ f"{self._settings_url}/filterable-attributes", payload, compress=compress
7204
7266
  )
7205
7267
 
7206
7268
  return TaskInfo(**response.json())
@@ -8326,6 +8388,7 @@ def _process_search_parameters(
8326
8388
  hybrid: Hybrid | None = None,
8327
8389
  locales: list[str] | None = None,
8328
8390
  retrieve_vectors: bool | None = None,
8391
+ exhaustive_facet_count: bool | None = None,
8329
8392
  ) -> JsonDict:
8330
8393
  if attributes_to_retrieve is None:
8331
8394
  attributes_to_retrieve = ["*"]
@@ -8377,6 +8440,9 @@ def _process_search_parameters(
8377
8440
  if retrieve_vectors is not None:
8378
8441
  body["retrieveVectors"] = retrieve_vectors
8379
8442
 
8443
+ if exhaustive_facet_count is not None:
8444
+ body["exhaustivefacetCount"] = exhaustive_facet_count
8445
+
8380
8446
  return body
8381
8447
 
8382
8448
 
@@ -26,6 +26,9 @@ class Stats(CamelBase):
26
26
  status: Status
27
27
  batch_types: JsonDict | None = Field(None, alias="types")
28
28
  index_uids: JsonDict | None = None
29
+ progress_trace: JsonDict | None = None
30
+ write_channel_congestion: JsonDict | None = None
31
+ internal_database_sizes: JsonDict | None = None
29
32
 
30
33
 
31
34
  class BatchResult(BatchId):
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Mapping
3
4
  from datetime import datetime
4
5
 
5
6
  import pydantic
@@ -84,3 +85,13 @@ class KeySearch(CamelBase):
84
85
  offset: int
85
86
  limit: int
86
87
  total: int
88
+
89
+
90
+ class Remote(CamelBase):
91
+ url: str | None = None
92
+ search_api_key: str | None = None
93
+
94
+
95
+ class Network(CamelBase):
96
+ self_: str | None = pydantic.Field(None, alias="self")
97
+ remotes: Mapping[str, Remote] | None = None
@@ -68,6 +68,7 @@ class HuggingFaceEmbedder(CamelBase):
68
68
  distribution: Distribution | None = None
69
69
  dimensions: int | None = None
70
70
  binary_quantized: bool | None = None
71
+ pooling: Literal["useModel", "forceMean", "forceCls"] | None = None
71
72
 
72
73
 
73
74
  class OllamaEmbedder(CamelBase):
@@ -122,11 +123,26 @@ class LocalizedAttributes(CamelBase):
122
123
  attribute_patterns: list[str]
123
124
 
124
125
 
126
+ class Filter(CamelBase):
127
+ equality: bool
128
+ comparison: bool
129
+
130
+
131
+ class FilterableAttributeFeatures(CamelBase):
132
+ facet_search: bool
133
+ filter: Filter
134
+
135
+
136
+ class FilterableAttributes(CamelBase):
137
+ attribute_patterns: list[str]
138
+ features: FilterableAttributeFeatures
139
+
140
+
125
141
  class MeilisearchSettings(CamelBase):
126
142
  synonyms: JsonDict | None = None
127
143
  stop_words: list[str] | None = None
128
144
  ranking_rules: list[str] | None = None
129
- filterable_attributes: list[str] | None = None
145
+ filterable_attributes: list[str] | list[FilterableAttributes] | None = None
130
146
  distinct_attribute: str | None = None
131
147
  searchable_attributes: list[str] | None = None
132
148
  displayed_attributes: list[str] | None = None
@@ -49,9 +49,9 @@ dev = [
49
49
  "pytest-cov==6.1.1",
50
50
  "pytest-asyncio==0.26.0",
51
51
  "pytest-xdist==3.6.1",
52
- "ruff==0.11.4",
52
+ "ruff==0.11.5",
53
53
  "types-aiofiles==24.1.0.20250326",
54
- "typing-extensions==4.13.1",
54
+ "typing-extensions==4.13.2",
55
55
  "types-ujson==5.10.0.20250326",
56
56
  "meilisearch==0.34.1",
57
57
  "rich==14.0.0",
@@ -304,6 +304,15 @@ async def enable_edit_by_function(base_url, ssl_verify):
304
304
  yield
305
305
 
306
306
 
307
+ @pytest.fixture(scope="session", autouse=True)
308
+ async def enable_network(base_url, ssl_verify):
309
+ async with HttpxAsyncClient(
310
+ base_url=base_url, headers={"Authorization": f"Bearer {MASTER_KEY}"}, verify=ssl_verify
311
+ ) as client:
312
+ await client.patch("/experimental-features", json={"network": True})
313
+ yield
314
+
315
+
307
316
  @pytest.fixture
308
317
  async def create_tasks(async_empty_index, small_movies):
309
318
  """Ensures there are some tasks present for testing."""
@@ -22,7 +22,7 @@ from meilisearch_python_sdk.errors import (
22
22
  MeilisearchTaskFailedError,
23
23
  MeilisearchTimeoutError,
24
24
  )
25
- from meilisearch_python_sdk.models.client import KeyCreate, KeyUpdate
25
+ from meilisearch_python_sdk.models.client import KeyCreate, KeyUpdate, Network
26
26
  from meilisearch_python_sdk.models.index import IndexInfo
27
27
  from meilisearch_python_sdk.models.version import Version
28
28
  from meilisearch_python_sdk.types import JsonDict
@@ -1048,3 +1048,25 @@ async def test_get_batch(async_client, async_empty_index, small_movies):
1048
1048
  async def test_get_batch_not_found(async_client):
1049
1049
  with pytest.raises(BatchNotFoundError):
1050
1050
  await async_client.get_batch(999999999)
1051
+
1052
+
1053
+ async def test_get_networks(async_client):
1054
+ response = await async_client.get_networks()
1055
+
1056
+ assert isinstance(response, Network)
1057
+
1058
+
1059
+ async def test_add_or_update_networks(async_client):
1060
+ network = Network(
1061
+ self_="remote_1",
1062
+ remotes={
1063
+ "remote_1": {"url": "http://localhost:7700", "searchApiKey": "xxxxxxxxxxxxxx"},
1064
+ "remote_2": {"url": "http://localhost:7720", "searchApiKey": "xxxxxxxxxxxxxxx"},
1065
+ },
1066
+ )
1067
+ response = await async_client.add_or_update_networks(network=network)
1068
+
1069
+ assert response.self_ == "remote_1"
1070
+ assert len(response.remotes) >= 2
1071
+ assert "remote_1" in response.remotes
1072
+ assert "remote_2" in response.remotes
@@ -7,6 +7,9 @@ from meilisearch_python_sdk.errors import MeilisearchApiError
7
7
  from meilisearch_python_sdk.models.settings import (
8
8
  Embedders,
9
9
  Faceting,
10
+ Filter,
11
+ FilterableAttributeFeatures,
12
+ FilterableAttributes,
10
13
  LocalizedAttributes,
11
14
  MinWordSizeForTypos,
12
15
  OpenAiEmbedder,
@@ -63,11 +66,6 @@ def new_synonyms():
63
66
  return {"hp": ["harry potter"]}
64
67
 
65
68
 
66
- @pytest.fixture
67
- def filterable_attributes():
68
- return ["release_date", "title"]
69
-
70
-
71
69
  @pytest.fixture
72
70
  def default_pagination():
73
71
  return Pagination(max_total_hits=1000)
@@ -577,6 +575,20 @@ async def test_get_filterable_attributes(async_empty_index):
577
575
 
578
576
 
579
577
  @pytest.mark.parametrize("compress", (True, False))
578
+ @pytest.mark.parametrize(
579
+ "filterable_attributes",
580
+ (
581
+ ["release_date", "title"],
582
+ [
583
+ FilterableAttributes(
584
+ attribute_patterns=["release_date", "title"],
585
+ features=FilterableAttributeFeatures(
586
+ facet_search=True, filter=Filter(equality=True, comparison=False)
587
+ ),
588
+ ),
589
+ ],
590
+ ),
591
+ )
580
592
  async def test_update_filterable_attributes(compress, async_empty_index, filterable_attributes):
581
593
  index = await async_empty_index()
582
594
  response = await index.update_filterable_attributes(filterable_attributes, compress=compress)
@@ -585,6 +597,20 @@ async def test_update_filterable_attributes(compress, async_empty_index, filtera
585
597
  assert sorted(response) == filterable_attributes
586
598
 
587
599
 
600
+ @pytest.mark.parametrize(
601
+ "filterable_attributes",
602
+ (
603
+ ["release_date", "title"],
604
+ [
605
+ FilterableAttributes(
606
+ attribute_patterns=["release_date", "title"],
607
+ features=FilterableAttributeFeatures(
608
+ facet_search=True, filter=Filter(equality=True, comparison=False)
609
+ ),
610
+ ),
611
+ ],
612
+ ),
613
+ )
588
614
  async def test_reset_filterable_attributes(async_empty_index, filterable_attributes):
589
615
  index = await async_empty_index()
590
616
  response = await index.update_filterable_attributes(filterable_attributes)
@@ -576,6 +576,20 @@ async def test_facet_search_locales(async_index_with_documents):
576
576
  assert response.facet_hits[0].count == 1
577
577
 
578
578
 
579
+ async def test_facet_search_exhaustive_facet_count(async_index_with_documents):
580
+ index = await async_index_with_documents()
581
+ update = await index.update_filterable_attributes(["genre"])
582
+ await async_wait_for_task(index.http_client, update.task_uid)
583
+ response = await index.facet_search(
584
+ "How to Train Your Dragon",
585
+ facet_name="genre",
586
+ facet_query="cartoon",
587
+ exhaustive_facet_count=True,
588
+ )
589
+
590
+ assert response.facet_hits[0].value == "cartoon"
591
+
592
+
579
593
  @pytest.mark.parametrize("ranking_score_threshold", (-0.1, 1.1))
580
594
  async def test_search_invalid_ranking_score_threshold(
581
595
  ranking_score_threshold, async_index_with_documents
@@ -19,7 +19,7 @@ from meilisearch_python_sdk.errors import (
19
19
  MeilisearchTaskFailedError,
20
20
  MeilisearchTimeoutError,
21
21
  )
22
- from meilisearch_python_sdk.models.client import KeyCreate, KeyUpdate
22
+ from meilisearch_python_sdk.models.client import KeyCreate, KeyUpdate, Network
23
23
  from meilisearch_python_sdk.models.index import IndexInfo
24
24
  from meilisearch_python_sdk.models.version import Version
25
25
  from meilisearch_python_sdk.types import JsonDict
@@ -1029,3 +1029,25 @@ def test_get_batch(client, empty_index, small_movies):
1029
1029
  def test_get_batch_not_found(client):
1030
1030
  with pytest.raises(BatchNotFoundError):
1031
1031
  client.get_batch(999999999)
1032
+
1033
+
1034
+ def test_get_networks(client):
1035
+ response = client.get_networks()
1036
+
1037
+ assert isinstance(response, Network)
1038
+
1039
+
1040
+ def test_add_or_update_networks(client):
1041
+ network = Network(
1042
+ self_="remote_1",
1043
+ remotes={
1044
+ "remote_1": {"url": "http://localhost:7700", "searchApiKey": "xxxxxxxxxxxxxx"},
1045
+ "remote_2": {"url": "http://localhost:7720", "searchApiKey": "xxxxxxxxxxxxxxx"},
1046
+ },
1047
+ )
1048
+ response = client.add_or_update_networks(network=network)
1049
+
1050
+ assert response.self_ == "remote_1"
1051
+ assert len(response.remotes) >= 2
1052
+ assert "remote_1" in response.remotes
1053
+ assert "remote_2" in response.remotes