meilisearch-python-sdk 2.0.0__py3-none-any.whl

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.

@@ -0,0 +1,1762 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from datetime import datetime, timezone
5
+ from ssl import SSLContext
6
+ from types import TracebackType
7
+ from typing import Any
8
+
9
+ import jwt
10
+ from httpx import AsyncClient as HttpxAsyncClient
11
+ from httpx import Client as HttpxClient
12
+
13
+ from meilisearch_python_sdk import _task
14
+ from meilisearch_python_sdk._http_requests import AsyncHttpRequests, HttpRequests
15
+ from meilisearch_python_sdk._index import AsyncIndex, Index
16
+ from meilisearch_python_sdk._utils import is_pydantic_2
17
+ from meilisearch_python_sdk.errors import InvalidRestriction, MeilisearchApiError
18
+ from meilisearch_python_sdk.models.client import (
19
+ ClientStats,
20
+ Key,
21
+ KeyCreate,
22
+ KeySearch,
23
+ KeyUpdate,
24
+ )
25
+ from meilisearch_python_sdk.models.health import Health
26
+ from meilisearch_python_sdk.models.index import IndexInfo
27
+ from meilisearch_python_sdk.models.search import SearchParams, SearchResultsWithUID
28
+ from meilisearch_python_sdk.models.task import TaskInfo, TaskResult, TaskStatus
29
+ from meilisearch_python_sdk.models.version import Version
30
+
31
+
32
+ class BaseClient:
33
+ def __init__(
34
+ self,
35
+ api_key: str | None = None,
36
+ ) -> None:
37
+ self._headers: dict[str, str] | None
38
+ if api_key:
39
+ self._headers = {"Authorization": f"Bearer {api_key}"}
40
+ else:
41
+ self._headers = None
42
+
43
+ def generate_tenant_token(
44
+ self,
45
+ search_rules: dict[str, Any] | list[str],
46
+ *,
47
+ api_key: Key,
48
+ expires_at: datetime | None = None,
49
+ ) -> str:
50
+ """Generates a JWT token to use for searching.
51
+
52
+ Args:
53
+
54
+ search_rules: Contains restrictions to use for the token. The default rules used for
55
+ the API key used for signing can be used by setting searchRules to ["*"]. If "indexes"
56
+ is included it must be equal to or more restrictive than the key used to generate the
57
+ token.
58
+ api_key: The API key to use to generate the token.
59
+ expires_at: The timepoint at which the token should expire. If value is provided it
60
+ shoud be a UTC time in the future. Default = None.
61
+
62
+ Returns:
63
+
64
+ A JWT token
65
+
66
+ Raises:
67
+
68
+ InvalidRestriction: If the restrictions are less strict than the permissions allowed
69
+ in the API key.
70
+ KeyNotFoundError: If no API search key is found.
71
+
72
+ Examples:
73
+
74
+ Async:
75
+
76
+ >>> from datetime import datetime, timedelta, timezone
77
+ >>> from meilisearch_python_sdk import AsyncClient
78
+ >>>
79
+ >>> expires_at = datetime.now(tz=timezone.utc) + timedelta(days=7)
80
+ >>>
81
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
82
+ >>> token = client.generate_tenant_token(
83
+ >>> search_rules = ["*"], api_key=api_key, expires_at=expires_at
84
+ >>> )
85
+
86
+ Sync:
87
+
88
+ >>> from datetime import datetime, timedelta, timezone
89
+ >>> from meilisearch_python_sdk import Client
90
+ >>>
91
+ >>> expires_at = datetime.now(tz=timezone.utc) + timedelta(days=7)
92
+ >>>
93
+ >>> client = Client("http://localhost.com", "masterKey")
94
+ >>> token = client.generate_tenant_token(
95
+ >>> search_rules = ["*"], api_key=api_key, expires_at=expires_at
96
+ >>> )
97
+ """
98
+ if isinstance(search_rules, dict) and search_rules.get("indexes"):
99
+ for index in search_rules["indexes"]:
100
+ if api_key.indexes != ["*"] and index not in api_key.indexes:
101
+ raise InvalidRestriction(
102
+ "Invalid index. The token cannot be less restrictive than the API key"
103
+ )
104
+
105
+ payload: dict[str, Any] = {"searchRules": search_rules}
106
+
107
+ payload["apiKeyUid"] = api_key.uid
108
+ if expires_at:
109
+ if expires_at <= datetime.now(tz=timezone.utc):
110
+ raise ValueError("expires_at must be a time in the future")
111
+
112
+ payload["exp"] = int(datetime.timestamp(expires_at))
113
+
114
+ return jwt.encode(payload, api_key.key, algorithm="HS256")
115
+
116
+
117
+ class AsyncClient(BaseClient):
118
+ """Async client to connect to the Meilisearch API."""
119
+
120
+ def __init__(
121
+ self,
122
+ url: str,
123
+ api_key: str | None = None,
124
+ *,
125
+ timeout: int | None = None,
126
+ verify: str | bool | SSLContext = True,
127
+ ) -> None:
128
+ """Class initializer.
129
+
130
+ Args:
131
+
132
+ url: The url to the Meilisearch API (ex: http://localhost:7700)
133
+ api_key: The optional API key for Meilisearch. Defaults to None.
134
+ timeout: The amount of time in seconds that the client will wait for a response before
135
+ timing out. Defaults to None.
136
+ verify: SSL certificates (a.k.a CA bundle) used to
137
+ verify the identity of requested hosts. Either `True` (default CA bundle),
138
+ a path to an SSL certificate file, or `False` (disable verification)
139
+ """
140
+ super().__init__(api_key)
141
+
142
+ self.http_client = HttpxAsyncClient(
143
+ base_url=url, timeout=timeout, headers=self._headers, verify=verify
144
+ )
145
+ self._http_requests = AsyncHttpRequests(self.http_client)
146
+
147
+ async def __aenter__(self) -> AsyncClient:
148
+ return self
149
+
150
+ async def __aexit__(
151
+ self,
152
+ et: type[BaseException] | None,
153
+ ev: type[BaseException] | None,
154
+ traceback: TracebackType | None,
155
+ ) -> None:
156
+ await self.aclose()
157
+
158
+ async def aclose(self) -> None:
159
+ """Closes the client.
160
+
161
+ This only needs to be used if the client was not created with a context manager.
162
+ """
163
+ await self.http_client.aclose()
164
+
165
+ async def create_dump(self) -> TaskInfo:
166
+ """Trigger the creation of a Meilisearch dump.
167
+
168
+ Returns:
169
+
170
+ The details of the task.
171
+
172
+ Raises:
173
+
174
+ MeilisearchCommunicationError: If there was an error communicating with the server.
175
+ MeilisearchApiError: If the Meilisearch API returned an error.
176
+
177
+ Examples:
178
+
179
+ >>> from meilisearch_python_sdk import AsyncClient
180
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
181
+ >>> await client.create_dump()
182
+ """
183
+ response = await self._http_requests.post("dumps")
184
+
185
+ return TaskInfo(**response.json())
186
+
187
+ async def create_index(self, uid: str, primary_key: str | None = None) -> AsyncIndex:
188
+ """Creates a new index.
189
+
190
+ Args:
191
+
192
+ uid: The index's unique identifier.
193
+ primary_key: The primary key of the documents. Defaults to None.
194
+
195
+ Returns:
196
+
197
+ An instance of AsyncIndex containing the information of the newly created index.
198
+
199
+ Raises:
200
+
201
+ MeilisearchCommunicationError: If there was an error communicating with the server.
202
+ MeilisearchApiError: If the Meilisearch API returned an error.
203
+
204
+ Examples:
205
+
206
+ >>> from meilisearch_python_sdk import AsyncClient
207
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
208
+ >>> index = await client.create_index("movies")
209
+ """
210
+ return await AsyncIndex.create(self.http_client, uid, primary_key)
211
+
212
+ async def delete_index_if_exists(self, uid: str) -> bool:
213
+ """Deletes an index if it already exists.
214
+
215
+ Args:
216
+
217
+ uid: The index's unique identifier.
218
+
219
+ Returns:
220
+
221
+ True if an index was deleted for False if not.
222
+
223
+ Raises:
224
+
225
+ MeilisearchCommunicationError: If there was an error communicating with the server.
226
+ MeilisearchApiError: If the Meilisearch API returned an error.
227
+
228
+ Examples:
229
+
230
+ >>> from meilisearch_python_sdk import AsyncClient
231
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
232
+ >>> await client.delete_index_if_exists()
233
+ """
234
+ response = await self._http_requests.delete(f"indexes/{uid}")
235
+ status = await self.wait_for_task(response.json()["taskUid"], timeout_in_ms=100000)
236
+ if status.status == "succeeded":
237
+ return True
238
+ return False
239
+
240
+ async def get_indexes(
241
+ self, *, offset: int | None = None, limit: int | None = None
242
+ ) -> list[AsyncIndex] | None:
243
+ """Get all indexes.
244
+ Args:
245
+
246
+ offset: Number of indexes to skip. The default of None will use the Meilisearch
247
+ default.
248
+ limit: Number of indexes to return. The default of None will use the Meilisearch
249
+ default.
250
+
251
+ Returns:
252
+
253
+ A list of all indexes.
254
+
255
+ Raises:
256
+
257
+ MeilisearchCommunicationError: If there was an error communicating with the server.
258
+ MeilisearchApiError: If the Meilisearch API returned an error.
259
+
260
+ Examples:
261
+
262
+ >>> from meilisearch_python_sdk import AsyncClient
263
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
264
+ >>> indexes = await client.get_indexes()
265
+ """
266
+ url = _build_offset_limit_url("indexes", offset, limit)
267
+ response = await self._http_requests.get(url)
268
+
269
+ if not response.json()["results"]:
270
+ return None
271
+
272
+ return [
273
+ AsyncIndex(
274
+ http_client=self.http_client,
275
+ uid=x["uid"],
276
+ primary_key=x["primaryKey"],
277
+ created_at=x["createdAt"],
278
+ updated_at=x["updatedAt"],
279
+ )
280
+ for x in response.json()["results"]
281
+ ]
282
+
283
+ async def get_index(self, uid: str) -> AsyncIndex:
284
+ """Gets a single index based on the uid of the index.
285
+
286
+ Args:
287
+
288
+ uid: The index's unique identifier.
289
+
290
+ Returns:
291
+
292
+ An AsyncIndex instance containing the information of the fetched index.
293
+
294
+ Raises:
295
+
296
+ MeilisearchCommunicationError: If there was an error communicating with the server.
297
+ MeilisearchApiError: If the Meilisearch API returned an error.
298
+
299
+ Examples:
300
+
301
+ >>> from meilisearch_python_sdk import AsyncClient
302
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
303
+ >>> index = await client.get_index()
304
+ """
305
+ return await AsyncIndex(self.http_client, uid).fetch_info()
306
+
307
+ def index(self, uid: str) -> AsyncIndex:
308
+ """Create a local reference to an index identified by UID, without making an HTTP call.
309
+
310
+ Because no network call is made this method is not awaitable.
311
+
312
+ Args:
313
+
314
+ uid: The index's unique identifier.
315
+
316
+ Returns:
317
+
318
+ An AsyncIndex instance.
319
+
320
+ Raises:
321
+
322
+ MeilisearchCommunicationError: If there was an error communicating with the server.
323
+ MeilisearchApiError: If the Meilisearch API returned an error.
324
+
325
+ Examples:
326
+
327
+ >>> from meilisearch_python_sdk import AsyncClient
328
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
329
+ >>> index = client.index("movies")
330
+ """
331
+ return AsyncIndex(self.http_client, uid=uid)
332
+
333
+ async def get_all_stats(self) -> ClientStats:
334
+ """Get stats for all indexes.
335
+
336
+ Returns:
337
+
338
+ Information about database size and all indexes.
339
+ https://docs.meilisearch.com/reference/api/stats.html
340
+
341
+ Raises:
342
+
343
+ MeilisearchCommunicationError: If there was an error communicating with the server.
344
+ MeilisearchApiError: If the Meilisearch API returned an error.
345
+
346
+ Examples:
347
+
348
+ >>> from meilisearch_python_sdk import AsyncClient
349
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
350
+ >>> stats = await client.get_all_stats()
351
+ """
352
+ response = await self._http_requests.get("stats")
353
+
354
+ return ClientStats(**response.json())
355
+
356
+ async def get_or_create_index(self, uid: str, primary_key: str | None = None) -> AsyncIndex:
357
+ """Get an index, or create it if it doesn't exist.
358
+
359
+ Args:
360
+
361
+ uid: The index's unique identifier.
362
+ primary_key: The primary key of the documents. Defaults to None.
363
+
364
+ Returns:
365
+
366
+ An instance of AsyncIndex containing the information of the retrieved or newly created index.
367
+
368
+ Raises:
369
+
370
+ MeilisearchCommunicationError: If there was an error communicating with the server.
371
+ MeilisearchApiError: If the Meilisearch API returned an error.MeilisearchTimeoutError: If the connection times out.
372
+ MeilisearchTimeoutError: If the connection times out.
373
+
374
+ Examples:
375
+
376
+ >>> from meilisearch_python_sdk import AsyncClient
377
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
378
+ >>> index = await client.get_or_create_index("movies")
379
+ """
380
+ try:
381
+ index_instance = await self.get_index(uid)
382
+ except MeilisearchApiError as err:
383
+ if "index_not_found" not in err.code:
384
+ raise
385
+ index_instance = await self.create_index(uid, primary_key)
386
+ return index_instance
387
+
388
+ async def create_key(self, key: KeyCreate) -> Key:
389
+ """Creates a new API key.
390
+
391
+ Args:
392
+
393
+ key: The information to use in creating the key. Note that if an expires_at value
394
+ is included it should be in UTC time.
395
+
396
+ Returns:
397
+
398
+ The new API key.
399
+
400
+ Raises:
401
+
402
+ MeilisearchCommunicationError: If there was an error communicating with the server.
403
+ MeilisearchApiError: If the Meilisearch API returned an error.
404
+
405
+ Examples:
406
+
407
+ >>> from meilisearch_python_sdk import AsyncClient
408
+ >>> from meilissearch_async_client.models.client import KeyCreate
409
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
410
+ >>> key_info = KeyCreate(
411
+ >>> description="My new key",
412
+ >>> actions=["search"],
413
+ >>> indexes=["movies"],
414
+ >>> )
415
+ >>> keys = await client.create_key(key_info)
416
+ """
417
+ if is_pydantic_2():
418
+ response = await self._http_requests.post("keys", json.loads(key.model_dump_json(by_alias=True))) # type: ignore[attr-defined]
419
+ else: # pragma: no cover
420
+ response = await self._http_requests.post("keys", json.loads(key.json(by_alias=True))) # type: ignore[attr-defined]
421
+
422
+ return Key(**response.json())
423
+
424
+ async def delete_key(self, key: str) -> int:
425
+ """Deletes an API key.
426
+
427
+ Args:
428
+
429
+ key: The key or uid to delete.
430
+
431
+ Returns:
432
+
433
+ The Response status code. 204 signifies a successful delete.
434
+
435
+ Raises:
436
+
437
+ MeilisearchCommunicationError: If there was an error communicating with the server.
438
+ MeilisearchApiError: If the Meilisearch API returned an error.
439
+
440
+ Examples:
441
+
442
+ >>> from meilisearch_python_sdk import AsyncClient
443
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
444
+ >>> await client.delete_key("abc123")
445
+ """
446
+ response = await self._http_requests.delete(f"keys/{key}")
447
+ return response.status_code
448
+
449
+ async def get_keys(self, *, offset: int | None = None, limit: int | None = None) -> KeySearch:
450
+ """Gets the Meilisearch API keys.
451
+ Args:
452
+
453
+ offset: Number of indexes to skip. The default of None will use the Meilisearch
454
+ default.
455
+ limit: Number of indexes to return. The default of None will use the Meilisearch
456
+ default.
457
+
458
+ Returns:
459
+
460
+ API keys.
461
+
462
+ Raises:
463
+
464
+ MeilisearchCommunicationError: If there was an error communicating with the server.
465
+ MeilisearchApiError: If the Meilisearch API returned an error.
466
+
467
+ Examples:
468
+
469
+ from meilisearch_python_sdk import AsyncClient
470
+ async with AsyncClient("http://localhost.com", "masterKey") as client:
471
+ keys = await client.get_keys()
472
+ """
473
+ url = _build_offset_limit_url("keys", offset, limit)
474
+ response = await self._http_requests.get(url)
475
+
476
+ return KeySearch(**response.json())
477
+
478
+ async def get_key(self, key: str) -> Key:
479
+ """Gets information about a specific API key.
480
+
481
+ Args:
482
+
483
+ key: The key for which to retrieve the information.
484
+
485
+ Returns:
486
+
487
+ The API key, or `None` if the key is not found.
488
+
489
+ Raises:
490
+
491
+ MeilisearchCommunicationError: If there was an error communicating with the server.
492
+ MeilisearchApiError: If the Meilisearch API returned an error.
493
+
494
+ Examples:
495
+
496
+ >>> from meilisearch_python_sdk import AsyncClient
497
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
498
+ >>> keys = await client.get_key("abc123")
499
+ """
500
+ response = await self._http_requests.get(f"keys/{key}")
501
+
502
+ return Key(**response.json())
503
+
504
+ async def update_key(self, key: KeyUpdate) -> Key:
505
+ """Update an API key.
506
+
507
+ Args:
508
+
509
+ key: The information to use in updating the key. Note that if an expires_at value
510
+ is included it should be in UTC time.
511
+
512
+ Returns:
513
+
514
+ The updated API key.
515
+
516
+ Raises:
517
+
518
+ MeilisearchCommunicationError: If there was an error communicating with the server.
519
+ MeilisearchApiError: If the Meilisearch API returned an error.
520
+
521
+ Examples:
522
+
523
+ >>> from meilisearch_python_sdk import AsyncClient
524
+ >>> from meilissearch_async_client.models.client import KeyUpdate
525
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
526
+ >>> key_info = KeyUpdate(
527
+ key="abc123",
528
+ >>> indexes=["*"],
529
+ >>> )
530
+ >>> keys = await client.update_key(key_info)
531
+ """
532
+ payload = _build_update_key_payload(key)
533
+ response = await self._http_requests.patch(f"keys/{key.key}", payload)
534
+
535
+ return Key(**response.json())
536
+
537
+ async def multi_search(self, queries: list[SearchParams]) -> list[SearchResultsWithUID]:
538
+ """Multi-index search.
539
+
540
+ Args:
541
+
542
+ queries: List of SearchParameters
543
+
544
+ Returns:
545
+
546
+ Results of the search
547
+
548
+ Raises:
549
+
550
+ MeilisearchCommunicationError: If there was an error communicating with the server.
551
+ MeilisearchApiError: If the Meilisearch API returned an error.
552
+
553
+ Examples:
554
+
555
+ >>> from meilisearch_python_sdk import AsyncClient
556
+ >>> from meilisearch_python_sdk.models.search import SearchParams
557
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
558
+ >>> queries = [
559
+ >>> SearchParams(index_uid="my_first_index", query"Some search"),
560
+ >>> SearchParams(index_uid="my_second_index", query="Another search")
561
+ >>> ]
562
+ >>> search_results = await client.search(queries)
563
+ """
564
+ url = "multi-search"
565
+ if is_pydantic_2():
566
+ response = await self._http_requests.post(
567
+ url, body={"queries": [x.model_dump(by_alias=True) for x in queries]} # type: ignore[attr-defined]
568
+ )
569
+ else: # pragma: no cover
570
+ response = await self._http_requests.post(
571
+ url, body={"queries": [x.dict(by_alias=True) for x in queries]} # type: ignore[attr-defined]
572
+ )
573
+
574
+ return [SearchResultsWithUID(**x) for x in response.json()["results"]]
575
+
576
+ async def get_raw_index(self, uid: str) -> IndexInfo | None:
577
+ """Gets the index and returns all the index information rather than an AsyncIndex instance.
578
+
579
+ Args:
580
+
581
+ uid: The index's unique identifier.
582
+
583
+ Returns:
584
+
585
+ Index information rather than an AsyncIndex instance.
586
+
587
+ Raises:
588
+
589
+ MeilisearchCommunicationError: If there was an error communicating with the server.
590
+ MeilisearchApiError: If the Meilisearch API returned an error.
591
+
592
+ Examples:
593
+
594
+ >>> from meilisearch_python_sdk import AsyncClient
595
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
596
+ >>> index = await client.get_raw_index("movies")
597
+ """
598
+ response = await self.http_client.get(f"indexes/{uid}")
599
+
600
+ if response.status_code == 404:
601
+ return None
602
+
603
+ return IndexInfo(**response.json())
604
+
605
+ async def get_raw_indexes(
606
+ self, *, offset: int | None = None, limit: int | None = None
607
+ ) -> list[IndexInfo] | None:
608
+ """Gets all the indexes.
609
+ Args:
610
+
611
+ offset: Number of indexes to skip. The default of None will use the Meilisearch
612
+ default.
613
+ limit: Number of indexes to return. The default of None will use the Meilisearch
614
+ default.
615
+
616
+ Returns all the index information rather than an AsyncIndex instance.
617
+
618
+ Returns:
619
+
620
+ A list of the Index information rather than an AsyncIndex instances.
621
+
622
+ Raises:
623
+
624
+ MeilisearchCommunicationError: If there was an error communicating with the server.
625
+ MeilisearchApiError: If the Meilisearch API returned an error.
626
+
627
+ Examples:
628
+
629
+ >>> from meilisearch_python_sdk import AsyncClient
630
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
631
+ >>> index = await client.get_raw_indexes()
632
+ """
633
+ url = _build_offset_limit_url("indexes", offset, limit)
634
+ response = await self._http_requests.get(url)
635
+
636
+ if not response.json()["results"]:
637
+ return None
638
+
639
+ return [IndexInfo(**x) for x in response.json()["results"]]
640
+
641
+ async def get_version(self) -> Version:
642
+ """Get the Meilisearch version.
643
+
644
+ Returns:
645
+
646
+ Information about the version of Meilisearch.
647
+
648
+ Raises:
649
+
650
+ MeilisearchCommunicationError: If there was an error communicating with the server.
651
+ MeilisearchApiError: If the Meilisearch API returned an error.
652
+
653
+ Examples:
654
+
655
+ >>> from meilisearch_python_sdk import AsyncClient
656
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
657
+ >>> version = await client.get_version()
658
+ """
659
+ response = await self._http_requests.get("version")
660
+
661
+ return Version(**response.json())
662
+
663
+ async def health(self) -> Health:
664
+ """Get health of the Meilisearch server.
665
+
666
+ Returns:
667
+
668
+ The status of the Meilisearch server.
669
+
670
+ Raises:
671
+
672
+ MeilisearchCommunicationError: If there was an error communicating with the server.
673
+ MeilisearchApiError: If the Meilisearch API returned an error.
674
+
675
+ Examples:
676
+
677
+ >>> from meilisearch_python_sdk import AsyncClient
678
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
679
+ >>> health = await client.get_healths()
680
+ """
681
+ response = await self._http_requests.get("health")
682
+
683
+ return Health(**response.json())
684
+
685
+ async def swap_indexes(self, indexes: list[tuple[str, str]]) -> TaskInfo:
686
+ """Swap two indexes.
687
+
688
+ Args:
689
+
690
+ indexes: A list of tuples, each tuple should contain the indexes to swap.
691
+
692
+ Returns:
693
+
694
+ The details of the task.
695
+
696
+ Raises:
697
+
698
+ MeilisearchCommunicationError: If there was an error communicating with the server.
699
+ MeilisearchApiError: If the Meilisearch API returned an error.
700
+
701
+ Examples:
702
+
703
+ >>> from meilisearch_python_sdk import AsyncClient
704
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
705
+ >>> index = await client.swap_indexes([("index_a", "index_b")])
706
+ """
707
+ processed_indexes = [{"indexes": x} for x in indexes]
708
+ response = await self._http_requests.post("swap-indexes", processed_indexes)
709
+
710
+ return TaskInfo(**response.json())
711
+
712
+ async def cancel_tasks(
713
+ self,
714
+ *,
715
+ uids: list[str] | None = None,
716
+ index_uids: list[str] | None = None,
717
+ statuses: list[str] | None = None,
718
+ types: list[str] | None = None,
719
+ before_enqueued_at: datetime | None = None,
720
+ after_enqueued_at: datetime | None = None,
721
+ before_started_at: datetime | None = None,
722
+ after_finished_at: datetime | None = None,
723
+ ) -> TaskInfo:
724
+ """Cancel a list of enqueued or processing tasks.
725
+
726
+ Defaults to cancelling all tasks.
727
+
728
+ Args:
729
+
730
+ uids: A list of task UIDs to cancel.
731
+ index_uids: A list of index UIDs for which to cancel tasks.
732
+ statuses: A list of statuses to cancel.
733
+ types: A list of types to cancel.
734
+ before_enqueued_at: Cancel tasks that were enqueued before the specified date time.
735
+ after_enqueued_at: Cancel tasks that were enqueued after the specified date time.
736
+ before_started_at: Cancel tasks that were started before the specified date time.
737
+ after_finished_at: Cancel tasks that were finished after the specified date time.
738
+
739
+ Returns:
740
+
741
+ The details of the task
742
+
743
+ Raises:
744
+
745
+ MeilisearchCommunicationError: If there was an error communicating with the server.
746
+ MeilisearchApiError: If the Meilisearch API returned an error.
747
+ MeilisearchTimeoutError: If the connection times out.
748
+
749
+ Examples:
750
+
751
+ >>> from meilisearch_python_sdk import AsyncClient
752
+ >>>
753
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
754
+ >>> await client.cancel_tasks(uids=[1, 2])
755
+ """
756
+ return await _task.async_cancel_tasks(
757
+ self.http_client,
758
+ uids=uids,
759
+ index_uids=index_uids,
760
+ statuses=statuses,
761
+ types=types,
762
+ before_enqueued_at=before_enqueued_at,
763
+ after_enqueued_at=after_enqueued_at,
764
+ before_started_at=before_started_at,
765
+ after_finished_at=after_finished_at,
766
+ )
767
+
768
+ async def get_task(self, task_id: int) -> TaskResult:
769
+ """Get a single task from it's task id.
770
+
771
+ Args:
772
+
773
+ task_id: Identifier of the task to retrieve.
774
+
775
+ Returns:
776
+
777
+ Results of a task.
778
+
779
+ Raises:
780
+
781
+ MeilisearchCommunicationError: If there was an error communicating with the server.
782
+ MeilisearchApiError: If the Meilisearch API returned an error.
783
+ MeilisearchTimeoutError: If the connection times out.
784
+
785
+ Examples:
786
+
787
+ >>> from meilisearch_python_sdk import AsyncClient
788
+ >>> from meilisearch_python_sdk.task import get_task
789
+ >>>
790
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
791
+ >>> await client.get_task(client, 1244)
792
+ """
793
+ return await _task.async_get_task(self.http_client, task_id=task_id)
794
+
795
+ async def delete_tasks(
796
+ self,
797
+ *,
798
+ uids: list[str] | None = None,
799
+ index_uids: list[str] | None = None,
800
+ statuses: list[str] | None = None,
801
+ types: list[str] | None = None,
802
+ before_enqueued_at: datetime | None = None,
803
+ after_enqueued_at: datetime | None = None,
804
+ before_started_at: datetime | None = None,
805
+ after_finished_at: datetime | None = None,
806
+ ) -> TaskInfo:
807
+ """Delete a list of tasks.
808
+
809
+ Defaults to deleting all tasks.
810
+
811
+ Args:
812
+
813
+ uids: A list of task UIDs to cancel.
814
+ index_uids: A list of index UIDs for which to cancel tasks.
815
+ statuses: A list of statuses to cancel.
816
+ types: A list of types to cancel.
817
+ before_enqueued_at: Cancel tasks that were enqueued before the specified date time.
818
+ after_enqueued_at: Cancel tasks that were enqueued after the specified date time.
819
+ before_started_at: Cancel tasks that were started before the specified date time.
820
+ after_finished_at: Cancel tasks that were finished after the specified date time.
821
+
822
+ Returns:
823
+
824
+ The details of the task
825
+
826
+ Raises:
827
+
828
+ MeilisearchCommunicationError: If there was an error communicating with the server.
829
+ MeilisearchApiError: If the Meilisearch API returned an error.
830
+ MeilisearchTimeoutError: If the connection times out.
831
+
832
+ Examples:
833
+
834
+ >>> from meilisearch_python_sdk import AsyncClient
835
+ >>> from meilisearch_python_sdk.task import delete_tasks
836
+ >>>
837
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
838
+ >>> await client.delete_tasks(uids=[1, 2])
839
+ """
840
+ return await _task.async_delete_tasks(
841
+ self.http_client,
842
+ uids=uids,
843
+ index_uids=index_uids,
844
+ statuses=statuses,
845
+ types=types,
846
+ before_enqueued_at=before_enqueued_at,
847
+ after_enqueued_at=after_enqueued_at,
848
+ before_started_at=before_started_at,
849
+ after_finished_at=after_finished_at,
850
+ )
851
+
852
+ async def get_tasks(
853
+ self,
854
+ *,
855
+ index_ids: list[str] | None = None,
856
+ types: str | list[str] | None = None,
857
+ ) -> TaskStatus:
858
+ """Get multiple tasks.
859
+
860
+ Args:
861
+
862
+ index_ids: A list of index UIDs for which to get the tasks. If provided this will get the
863
+ tasks only for the specified indexes, if not all tasks will be returned. Default = None
864
+ types: Specify specific task types to retrieve. Default = None
865
+
866
+ Returns:
867
+
868
+ Task statuses.
869
+
870
+ Raises:
871
+
872
+ MeilisearchCommunicationError: If there was an error communicating with the server.
873
+ MeilisearchApiError: If the Meilisearch API returned an error.
874
+ MeilisearchTimeoutError: If the connection times out.
875
+
876
+ Examples:
877
+
878
+ >>> from meilisearch_python_sdk import AsyncClient
879
+ >>>
880
+ >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
881
+ >>> await client.get_tasks()
882
+ """
883
+ return await _task.async_get_tasks(self.http_client, index_ids=index_ids, types=types)
884
+
885
+ async def wait_for_task(
886
+ self,
887
+ task_id: int,
888
+ *,
889
+ timeout_in_ms: int | None = 5000,
890
+ interval_in_ms: int = 50,
891
+ raise_for_status: bool = False,
892
+ ) -> TaskResult:
893
+ """Wait until Meilisearch processes a task, and get its status.
894
+
895
+ Args:
896
+
897
+ task_id: Identifier of the task to retrieve.
898
+ timeout_in_ms: Amount of time in milliseconds to wait before raising a
899
+ MeilisearchTimeoutError. `None` can also be passed to wait indefinitely. Be aware that
900
+ if the `None` option is used the wait time could be very long. Defaults to 5000.
901
+ interval_in_ms: Time interval in miliseconds to sleep between requests. Defaults to 50.
902
+ raise_for_status: When set to `True` a MeilisearchTaskFailedError will be raised if a task
903
+ has a failed status. Defaults to False.
904
+
905
+ Returns:
906
+
907
+ Details of the processed update status.
908
+
909
+ Raises:
910
+
911
+ MeilisearchCommunicationError: If there was an error communicating with the server.
912
+ MeilisearchApiError: If the Meilisearch API returned an error.
913
+ MeilisearchTimeoutError: If the connection times out.
914
+ MeilisearchTaskFailedError: If `raise_for_status` is `True` and a task has a failed status.
915
+
916
+ Examples:
917
+
918
+ >>> from meilisearch_python_sdk import AsyncClient
919
+ >>> >>> documents = [
920
+ >>> {"id": 1, "title": "Movie 1", "genre": "comedy"},
921
+ >>> {"id": 2, "title": "Movie 2", "genre": "drama"},
922
+ >>> ]
923
+ >>> async with Client("http://localhost.com", "masterKey") as client:
924
+ >>> index = client.index("movies")
925
+ >>> response = await index.add_documents(documents)
926
+ >>> await client.wait_for_task(client, response.update_id)
927
+ """
928
+ return await _task.async_wait_for_task(
929
+ self.http_client,
930
+ task_id=task_id,
931
+ timeout_in_ms=timeout_in_ms,
932
+ interval_in_ms=interval_in_ms,
933
+ raise_for_status=raise_for_status,
934
+ )
935
+
936
+
937
+ class Client(BaseClient):
938
+ """client to connect to the Meilisearch API."""
939
+
940
+ def __init__(
941
+ self,
942
+ url: str,
943
+ api_key: str | None = None,
944
+ *,
945
+ timeout: int | None = None,
946
+ verify: str | bool | SSLContext = True,
947
+ ) -> None:
948
+ """Class initializer.
949
+
950
+ Args:
951
+
952
+ url: The url to the Meilisearch API (ex: http://localhost:7700)
953
+ api_key: The optional API key for Meilisearch. Defaults to None.
954
+ timeout: The amount of time in seconds that the client will wait for a response before
955
+ timing out. Defaults to None.
956
+ verify: SSL certificates (a.k.a CA bundle) used to
957
+ verify the identity of requested hosts. Either `True` (default CA bundle),
958
+ a path to an SSL certificate file, or `False` (disable verification)
959
+ """
960
+ super().__init__(api_key)
961
+
962
+ self.http_client = HttpxClient(
963
+ base_url=url, timeout=timeout, headers=self._headers, verify=verify
964
+ )
965
+ self._http_requests = HttpRequests(self.http_client)
966
+
967
+ def create_dump(self) -> TaskInfo:
968
+ """Trigger the creation of a Meilisearch dump.
969
+
970
+ Returns:
971
+
972
+ The details of the task.
973
+
974
+ Raises:
975
+
976
+ MeilisearchCommunicationError: If there was an error communicating with the server.
977
+ MeilisearchApiError: If the Meilisearch API returned an error.
978
+
979
+ Examples:
980
+
981
+ >>> from meilisearch_python_sdk import Client
982
+ >>> client = Client("http://localhost.com", "masterKey")
983
+ >>> client.create_dump()
984
+ """
985
+ response = self._http_requests.post("dumps")
986
+
987
+ return TaskInfo(**response.json())
988
+
989
+ def create_index(self, uid: str, primary_key: str | None = None) -> Index:
990
+ """Creates a new index.
991
+
992
+ Args:
993
+
994
+ uid: The index's unique identifier.
995
+ primary_key: The primary key of the documents. Defaults to None.
996
+
997
+ Returns:
998
+
999
+ An instance of Index containing the information of the newly created index.
1000
+
1001
+ Raises:
1002
+
1003
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1004
+ MeilisearchApiError: If the Meilisearch API returned an error.
1005
+
1006
+ Examples:
1007
+
1008
+ >>> from meilisearch_python_sdk import Client
1009
+ >>> client = Client("http://localhost.com", "masterKey")
1010
+ >>> index = client.create_index("movies")
1011
+ """
1012
+ return Index.create(self.http_client, uid, primary_key)
1013
+
1014
+ def delete_index_if_exists(self, uid: str) -> bool:
1015
+ """Deletes an index if it already exists.
1016
+
1017
+ Args:
1018
+
1019
+ uid: The index's unique identifier.
1020
+
1021
+ Returns:
1022
+
1023
+ True if an index was deleted for False if not.
1024
+
1025
+ Raises:
1026
+
1027
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1028
+ MeilisearchApiError: If the Meilisearch API returned an error.
1029
+
1030
+ Examples:
1031
+
1032
+ >>> from meilisearch_python_sdk import Client
1033
+ >>> client = Client("http://localhost.com", "masterKey")
1034
+ >>> client.delete_index_if_exists()
1035
+ """
1036
+ response = self._http_requests.delete(f"indexes/{uid}")
1037
+ status = self.wait_for_task(response.json()["taskUid"], timeout_in_ms=100000)
1038
+ if status.status == "succeeded":
1039
+ return True
1040
+ return False
1041
+
1042
+ def get_indexes(
1043
+ self, *, offset: int | None = None, limit: int | None = None
1044
+ ) -> list[Index] | None:
1045
+ """Get all indexes.
1046
+ Args:
1047
+
1048
+ offset: Number of indexes to skip. The default of None will use the Meilisearch
1049
+ default.
1050
+ limit: Number of indexes to return. The default of None will use the Meilisearch
1051
+ default.
1052
+
1053
+ Returns:
1054
+
1055
+ A list of all indexes.
1056
+
1057
+ Raises:
1058
+
1059
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1060
+ MeilisearchApiError: If the Meilisearch API returned an error.
1061
+
1062
+ Examples:
1063
+
1064
+ >>> from meilisearch_python_sdk import Client
1065
+ >>> client = Client("http://localhost.com", "masterKey") as client:
1066
+ >>> indexes = client.get_indexes()
1067
+ """
1068
+ url = _build_offset_limit_url("indexes", offset, limit)
1069
+ response = self._http_requests.get(url)
1070
+
1071
+ if not response.json()["results"]:
1072
+ return None
1073
+
1074
+ return [
1075
+ Index(
1076
+ http_client=self.http_client,
1077
+ uid=x["uid"],
1078
+ primary_key=x["primaryKey"],
1079
+ created_at=x["createdAt"],
1080
+ updated_at=x["updatedAt"],
1081
+ )
1082
+ for x in response.json()["results"]
1083
+ ]
1084
+
1085
+ def get_index(self, uid: str) -> Index:
1086
+ """Gets a single index based on the uid of the index.
1087
+
1088
+ Args:
1089
+
1090
+ uid: The index's unique identifier.
1091
+
1092
+ Returns:
1093
+
1094
+ An Index instance containing the information of the fetched index.
1095
+
1096
+ Raises:
1097
+
1098
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1099
+ MeilisearchApiError: If the Meilisearch API returned an error.
1100
+
1101
+ Examples:
1102
+
1103
+ >>> from meilisearch_python_sdk import Client
1104
+ >>> client = Client("http://localhost.com", "masterKey")
1105
+ >>> index = client.get_index()
1106
+ """
1107
+ return Index(self.http_client, uid).fetch_info()
1108
+
1109
+ def index(self, uid: str) -> Index:
1110
+ """Create a local reference to an index identified by UID, without making an HTTP call.
1111
+
1112
+ Args:
1113
+
1114
+ uid: The index's unique identifier.
1115
+
1116
+ Returns:
1117
+
1118
+ An Index instance.
1119
+
1120
+ Raises:
1121
+
1122
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1123
+ MeilisearchApiError: If the Meilisearch API returned an error.
1124
+
1125
+ Examples:
1126
+
1127
+ >>> from meilisearch_python_sdk import Client
1128
+ >>> client = Client("http://localhost.com", "masterKey")
1129
+ >>> index = client.index("movies")
1130
+ """
1131
+ return Index(self.http_client, uid=uid)
1132
+
1133
+ def get_all_stats(self) -> ClientStats:
1134
+ """Get stats for all indexes.
1135
+
1136
+ Returns:
1137
+
1138
+ Information about database size and all indexes.
1139
+ https://docs.meilisearch.com/reference/api/stats.html
1140
+
1141
+ Raises:
1142
+
1143
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1144
+ MeilisearchApiError: If the Meilisearch API returned an error.
1145
+
1146
+ Examples:
1147
+
1148
+ >>> from meilisearch_python_sdk import Client
1149
+ >>> client = Client("http://localhost.com", "masterKey")
1150
+ >>> stats = client.get_all_stats()
1151
+ """
1152
+ response = self._http_requests.get("stats")
1153
+
1154
+ return ClientStats(**response.json())
1155
+
1156
+ def get_or_create_index(self, uid: str, primary_key: str | None = None) -> Index:
1157
+ """Get an index, or create it if it doesn't exist.
1158
+
1159
+ Args:
1160
+
1161
+ uid: The index's unique identifier.
1162
+ primary_key: The primary key of the documents. Defaults to None.
1163
+
1164
+ Returns:
1165
+
1166
+ An instance of Index containing the information of the retrieved or newly created index.
1167
+
1168
+ Raises:
1169
+
1170
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1171
+ MeilisearchApiError: If the Meilisearch API returned an error.MeilisearchTimeoutError: If the connection times out.
1172
+ MeilisearchTimeoutError: If the connection times out.
1173
+
1174
+ Examples:
1175
+
1176
+ >>> from meilisearch_python_sdk import Client
1177
+ >>> client = Client("http://localhost.com", "masterKey")
1178
+ >>> index = client.get_or_create_index("movies")
1179
+ """
1180
+ try:
1181
+ index_instance = self.get_index(uid)
1182
+ except MeilisearchApiError as err:
1183
+ if "index_not_found" not in err.code:
1184
+ raise
1185
+ index_instance = self.create_index(uid, primary_key)
1186
+ return index_instance
1187
+
1188
+ def create_key(self, key: KeyCreate) -> Key:
1189
+ """Creates a new API key.
1190
+
1191
+ Args:
1192
+
1193
+ key: The information to use in creating the key. Note that if an expires_at value
1194
+ is included it should be in UTC time.
1195
+
1196
+ Returns:
1197
+
1198
+ The new API key.
1199
+
1200
+ Raises:
1201
+
1202
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1203
+ MeilisearchApiError: If the Meilisearch API returned an error.
1204
+
1205
+ Examples:
1206
+
1207
+ >>> from meilisearch_python_sdk import Client
1208
+ >>> from meilissearch_async_client.models.client import KeyCreate
1209
+ >>> client = Client("http://localhost.com", "masterKey")
1210
+ >>> key_info = KeyCreate(
1211
+ >>> description="My new key",
1212
+ >>> actions=["search"],
1213
+ >>> indexes=["movies"],
1214
+ >>> )
1215
+ >>> keys = client.create_key(key_info)
1216
+ """
1217
+ if is_pydantic_2():
1218
+ response = self._http_requests.post("keys", json.loads(key.model_dump_json(by_alias=True))) # type: ignore[attr-defined]
1219
+ else: # pragma: no cover
1220
+ response = self._http_requests.post("keys", json.loads(key.json(by_alias=True))) # type: ignore[attr-defined]
1221
+
1222
+ return Key(**response.json())
1223
+
1224
+ def delete_key(self, key: str) -> int:
1225
+ """Deletes an API key.
1226
+
1227
+ Args:
1228
+
1229
+ key: The key or uid to delete.
1230
+
1231
+ Returns:
1232
+
1233
+ The Response status code. 204 signifies a successful delete.
1234
+
1235
+ Raises:
1236
+
1237
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1238
+ MeilisearchApiError: If the Meilisearch API returned an error.
1239
+
1240
+ Examples:
1241
+
1242
+ >>> from meilisearch_python_sdk import Client
1243
+ >>> client = Client("http://localhost.com", "masterKey")
1244
+ >>> client.delete_key("abc123")
1245
+ """
1246
+ response = self._http_requests.delete(f"keys/{key}")
1247
+ return response.status_code
1248
+
1249
+ def get_keys(self, *, offset: int | None = None, limit: int | None = None) -> KeySearch:
1250
+ """Gets the Meilisearch API keys.
1251
+ Args:
1252
+
1253
+ offset: Number of indexes to skip. The default of None will use the Meilisearch
1254
+ default.
1255
+ limit: Number of indexes to return. The default of None will use the Meilisearch
1256
+ default.
1257
+
1258
+ Returns:
1259
+
1260
+ API keys.
1261
+
1262
+ Raises:
1263
+
1264
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1265
+ MeilisearchApiError: If the Meilisearch API returned an error.
1266
+
1267
+ Examples:
1268
+
1269
+ >>> from meilisearch_python_sdk import Client
1270
+ >>> client = AsyncClient("http://localhost.com", "masterKey")
1271
+ >>> keys = client.get_keys()
1272
+ """
1273
+ url = _build_offset_limit_url("keys", offset, limit)
1274
+ response = self._http_requests.get(url)
1275
+
1276
+ return KeySearch(**response.json())
1277
+
1278
+ def get_key(self, key: str) -> Key:
1279
+ """Gets information about a specific API key.
1280
+
1281
+ Args:
1282
+
1283
+ key: The key for which to retrieve the information.
1284
+
1285
+ Returns:
1286
+
1287
+ The API key, or `None` if the key is not found.
1288
+
1289
+ Raises:
1290
+
1291
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1292
+ MeilisearchApiError: If the Meilisearch API returned an error.
1293
+
1294
+ Examples:
1295
+
1296
+ >>> from meilisearch_python_sdk import Client
1297
+ >>> client = Client("http://localhost.com", "masterKey")
1298
+ >>> keys = client.get_key("abc123")
1299
+ """
1300
+ response = self._http_requests.get(f"keys/{key}")
1301
+
1302
+ return Key(**response.json())
1303
+
1304
+ def update_key(self, key: KeyUpdate) -> Key:
1305
+ """Update an API key.
1306
+
1307
+ Args:
1308
+
1309
+ key: The information to use in updating the key. Note that if an expires_at value
1310
+ is included it should be in UTC time.
1311
+
1312
+ Returns:
1313
+
1314
+ The updated API key.
1315
+
1316
+ Raises:
1317
+
1318
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1319
+ MeilisearchApiError: If the Meilisearch API returned an error.
1320
+
1321
+ Examples:
1322
+
1323
+ >>> from meilisearch_python_sdk import Client
1324
+ >>> from meilissearch_async_client.models.client import KeyUpdate
1325
+ >>> client = Client("http://localhost.com", "masterKey")
1326
+ >>> key_info = KeyUpdate(
1327
+ key="abc123",
1328
+ >>> indexes=["*"],
1329
+ >>> )
1330
+ >>> keys = client.update_key(key_info)
1331
+ """
1332
+ payload = _build_update_key_payload(key)
1333
+ response = self._http_requests.patch(f"keys/{key.key}", payload)
1334
+
1335
+ return Key(**response.json())
1336
+
1337
+ def multi_search(self, queries: list[SearchParams]) -> list[SearchResultsWithUID]:
1338
+ """Multi-index search.
1339
+
1340
+ Args:
1341
+
1342
+ queries: List of SearchParameters
1343
+
1344
+ Returns:
1345
+
1346
+ Results of the search
1347
+
1348
+ Raises:
1349
+
1350
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1351
+ MeilisearchApiError: If the Meilisearch API returned an error.
1352
+
1353
+ Examples:
1354
+
1355
+ >>> from meilisearch_python_sdk import Client
1356
+ >>> from meilisearch_python_sdk.models.search import SearchParams
1357
+ >>> client = Client("http://localhost.com", "masterKey")
1358
+ >>> queries = [
1359
+ >>> SearchParams(index_uid="my_first_index", query"Some search"),
1360
+ >>> SearchParams(index_uid="my_second_index", query="Another search")
1361
+ >>> ]
1362
+ >>> search_results = client.search(queries)
1363
+ """
1364
+ url = "multi-search"
1365
+ if is_pydantic_2():
1366
+ response = self._http_requests.post(
1367
+ url, body={"queries": [x.model_dump(by_alias=True) for x in queries]} # type: ignore[attr-defined]
1368
+ )
1369
+ else: # pragma: no cover
1370
+ response = self._http_requests.post(
1371
+ url, body={"queries": [x.dict(by_alias=True) for x in queries]} # type: ignore[attr-defined]
1372
+ )
1373
+
1374
+ return [SearchResultsWithUID(**x) for x in response.json()["results"]]
1375
+
1376
+ def get_raw_index(self, uid: str) -> IndexInfo | None:
1377
+ """Gets the index and returns all the index information rather than an Index instance.
1378
+
1379
+ Args:
1380
+
1381
+ uid: The index's unique identifier.
1382
+
1383
+ Returns:
1384
+
1385
+ Index information rather than an Index instance.
1386
+
1387
+ Raises:
1388
+
1389
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1390
+ MeilisearchApiError: If the Meilisearch API returned an error.
1391
+
1392
+ Examples:
1393
+
1394
+ >>> from meilisearch_python_sdk import Client
1395
+ >>> client = Client("http://localhost.com", "masterKey")
1396
+ >>> index = client.get_raw_index("movies")
1397
+ """
1398
+ response = self.http_client.get(f"indexes/{uid}")
1399
+
1400
+ if response.status_code == 404:
1401
+ return None
1402
+
1403
+ return IndexInfo(**response.json())
1404
+
1405
+ def get_raw_indexes(
1406
+ self, *, offset: int | None = None, limit: int | None = None
1407
+ ) -> list[IndexInfo] | None:
1408
+ """Gets all the indexes.
1409
+ Args:
1410
+
1411
+ offset: Number of indexes to skip. The default of None will use the Meilisearch
1412
+ default.
1413
+ limit: Number of indexes to return. The default of None will use the Meilisearch
1414
+ default.
1415
+
1416
+ Returns all the index information rather than an AsyncIndex instance.
1417
+
1418
+ Returns:
1419
+
1420
+ A list of the Index information rather than an AsyncIndex instances.
1421
+
1422
+ Raises:
1423
+
1424
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1425
+ MeilisearchApiError: If the Meilisearch API returned an error.
1426
+
1427
+ Examples:
1428
+
1429
+ >>> from meilisearch_python_sdk import Client
1430
+ >>> client = Client("http://localhost.com", "masterKey")
1431
+ >>> index = client.get_raw_indexes()
1432
+ """
1433
+ url = _build_offset_limit_url("indexes", offset, limit)
1434
+ response = self._http_requests.get(url)
1435
+
1436
+ if not response.json()["results"]:
1437
+ return None
1438
+
1439
+ return [IndexInfo(**x) for x in response.json()["results"]]
1440
+
1441
+ def get_version(self) -> Version:
1442
+ """Get the Meilisearch version.
1443
+
1444
+ Returns:
1445
+
1446
+ Information about the version of Meilisearch.
1447
+
1448
+ Raises:
1449
+
1450
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1451
+ MeilisearchApiError: If the Meilisearch API returned an error.
1452
+
1453
+ Examples:
1454
+
1455
+ >>> from meilisearch_python_sdk import Client
1456
+ >>> client = Client("http://localhost.com", "masterKey")
1457
+ >>> version = client.get_version()
1458
+ """
1459
+ response = self._http_requests.get("version")
1460
+
1461
+ return Version(**response.json())
1462
+
1463
+ def health(self) -> Health:
1464
+ """Get health of the Meilisearch server.
1465
+
1466
+ Returns:
1467
+
1468
+ The status of the Meilisearch server.
1469
+
1470
+ Raises:
1471
+
1472
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1473
+ MeilisearchApiError: If the Meilisearch API returned an error.
1474
+
1475
+ Examples:
1476
+
1477
+ >>> from meilisearch_python_sdk import Client
1478
+ >>> client = Client("http://localhost.com", "masterKey")
1479
+ >>> health = client.get_healths()
1480
+ """
1481
+ response = self._http_requests.get("health")
1482
+
1483
+ return Health(**response.json())
1484
+
1485
+ def swap_indexes(self, indexes: list[tuple[str, str]]) -> TaskInfo:
1486
+ """Swap two indexes.
1487
+
1488
+ Args:
1489
+
1490
+ indexes: A list of tuples, each tuple should contain the indexes to swap.
1491
+
1492
+ Returns:
1493
+
1494
+ The details of the task.
1495
+
1496
+ Raises:
1497
+
1498
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1499
+ MeilisearchApiError: If the Meilisearch API returned an error.
1500
+
1501
+ Examples:
1502
+
1503
+ >>> from meilisearch_python_sdk import Client
1504
+ >>> client = Client("http://localhost.com", "masterKey")
1505
+ >>> index = client.swap_indexes([("index_a", "index_b")])
1506
+ """
1507
+ processed_indexes = [{"indexes": x} for x in indexes]
1508
+ response = self._http_requests.post("swap-indexes", processed_indexes)
1509
+
1510
+ return TaskInfo(**response.json())
1511
+
1512
+ def cancel_tasks(
1513
+ self,
1514
+ *,
1515
+ uids: list[str] | None = None,
1516
+ index_uids: list[str] | None = None,
1517
+ statuses: list[str] | None = None,
1518
+ types: list[str] | None = None,
1519
+ before_enqueued_at: datetime | None = None,
1520
+ after_enqueued_at: datetime | None = None,
1521
+ before_started_at: datetime | None = None,
1522
+ after_finished_at: datetime | None = None,
1523
+ ) -> TaskInfo:
1524
+ """Cancel a list of enqueued or processing tasks.
1525
+
1526
+ Defaults to cancelling all tasks.
1527
+
1528
+ Args:
1529
+
1530
+ uids: A list of task UIDs to cancel.
1531
+ index_uids: A list of index UIDs for which to cancel tasks.
1532
+ statuses: A list of statuses to cancel.
1533
+ types: A list of types to cancel.
1534
+ before_enqueued_at: Cancel tasks that were enqueued before the specified date time.
1535
+ after_enqueued_at: Cancel tasks that were enqueued after the specified date time.
1536
+ before_started_at: Cancel tasks that were started before the specified date time.
1537
+ after_finished_at: Cancel tasks that were finished after the specified date time.
1538
+
1539
+ Returns:
1540
+
1541
+ The details of the task
1542
+
1543
+ Raises:
1544
+
1545
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1546
+ MeilisearchApiError: If the Meilisearch API returned an error.
1547
+ MeilisearchTimeoutError: If the connection times out.
1548
+
1549
+ Examples:
1550
+
1551
+ >>> from meilisearch_python_sdk import Client
1552
+ >>> from meilisearch_python_sdk.task import cancel_tasks
1553
+ >>>
1554
+ >>> client = Client("http://localhost.com", "masterKey")
1555
+ >>> client.cancel_tasks(uids=[1, 2])
1556
+ """
1557
+ return _task.cancel_tasks(
1558
+ self.http_client,
1559
+ uids=uids,
1560
+ index_uids=index_uids,
1561
+ statuses=statuses,
1562
+ types=types,
1563
+ before_enqueued_at=before_enqueued_at,
1564
+ after_enqueued_at=after_enqueued_at,
1565
+ before_started_at=before_started_at,
1566
+ after_finished_at=after_finished_at,
1567
+ )
1568
+
1569
+ def delete_tasks(
1570
+ self,
1571
+ *,
1572
+ uids: list[str] | None = None,
1573
+ index_uids: list[str] | None = None,
1574
+ statuses: list[str] | None = None,
1575
+ types: list[str] | None = None,
1576
+ before_enqueued_at: datetime | None = None,
1577
+ after_enqueued_at: datetime | None = None,
1578
+ before_started_at: datetime | None = None,
1579
+ after_finished_at: datetime | None = None,
1580
+ ) -> TaskInfo:
1581
+ """Delete a list of tasks.
1582
+
1583
+ Defaults to deleting all tasks.
1584
+
1585
+ Args:
1586
+
1587
+ uids: A list of task UIDs to cancel.
1588
+ index_uids: A list of index UIDs for which to cancel tasks.
1589
+ statuses: A list of statuses to cancel.
1590
+ types: A list of types to cancel.
1591
+ before_enqueued_at: Cancel tasks that were enqueued before the specified date time.
1592
+ after_enqueued_at: Cancel tasks that were enqueued after the specified date time.
1593
+ before_started_at: Cancel tasks that were started before the specified date time.
1594
+ after_finished_at: Cancel tasks that were finished after the specified date time.
1595
+
1596
+ Returns:
1597
+
1598
+ The details of the task
1599
+
1600
+ Raises:
1601
+
1602
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1603
+ MeilisearchApiError: If the Meilisearch API returned an error.
1604
+ MeilisearchTimeoutError: If the connection times out.
1605
+
1606
+ Examples:
1607
+
1608
+ >>> from meilisearch_python_sdk import Client
1609
+ >>>
1610
+ >>> client = Client("http://localhost.com", "masterKey")
1611
+ >>> client.delete_tasks(client, uids=[1, 2])
1612
+ """
1613
+ return _task.delete_tasks(
1614
+ self.http_client,
1615
+ uids=uids,
1616
+ index_uids=index_uids,
1617
+ statuses=statuses,
1618
+ types=types,
1619
+ before_enqueued_at=before_enqueued_at,
1620
+ after_enqueued_at=after_enqueued_at,
1621
+ before_started_at=before_started_at,
1622
+ after_finished_at=after_finished_at,
1623
+ )
1624
+
1625
+ def get_task(self, task_id: int) -> TaskResult:
1626
+ """Get a single task from it's task id.
1627
+
1628
+ Args:
1629
+
1630
+ task_id: Identifier of the task to retrieve.
1631
+
1632
+ Returns:
1633
+
1634
+ Results of a task.
1635
+
1636
+ Raises:
1637
+
1638
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1639
+ MeilisearchApiError: If the Meilisearch API returned an error.
1640
+ MeilisearchTimeoutError: If the connection times out.
1641
+
1642
+ Examples:
1643
+
1644
+ >>> from meilisearch_python_sdk import Client
1645
+ >>>
1646
+ >>> client = AsyncClient("http://localhost.com", "masterKey")
1647
+ >>> get_task(client, 1244)
1648
+ """
1649
+ return _task.get_task(self.http_client, task_id=task_id)
1650
+
1651
+ def get_tasks(
1652
+ self,
1653
+ *,
1654
+ index_ids: list[str] | None = None,
1655
+ types: str | list[str] | None = None,
1656
+ ) -> TaskStatus:
1657
+ """Get multiple tasks.
1658
+
1659
+ Args:
1660
+
1661
+ index_ids: A list of index UIDs for which to get the tasks. If provided this will get the
1662
+ tasks only for the specified indexes, if not all tasks will be returned. Default = None
1663
+ types: Specify specific task types to retrieve. Default = None
1664
+
1665
+ Returns:
1666
+
1667
+ Task statuses.
1668
+
1669
+ Raises:
1670
+
1671
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1672
+ MeilisearchApiError: If the Meilisearch API returned an error.
1673
+ MeilisearchTimeoutError: If the connection times out.
1674
+
1675
+ Examples:
1676
+
1677
+ >>> from meilisearch_python_sdk import Client
1678
+ >>>
1679
+ >>> client = Client("http://localhost.com", "masterKey")
1680
+ >>> client.get_tasks(client)
1681
+ """
1682
+ return _task.get_tasks(self.http_client, index_ids=index_ids, types=types)
1683
+
1684
+ def wait_for_task(
1685
+ self,
1686
+ task_id: int,
1687
+ *,
1688
+ timeout_in_ms: int | None = 5000,
1689
+ interval_in_ms: int = 50,
1690
+ raise_for_status: bool = False,
1691
+ ) -> TaskResult:
1692
+ """Wait until Meilisearch processes a task, and get its status.
1693
+
1694
+ Args:
1695
+
1696
+ client: An httpx HttpxClient or meilisearch_python_sdk Client instance.
1697
+ task_id: Identifier of the task to retrieve.
1698
+ timeout_in_ms: Amount of time in milliseconds to wait before raising a
1699
+ MeilisearchTimeoutError. `None` can also be passed to wait indefinitely. Be aware that
1700
+ if the `None` option is used the wait time could be very long. Defaults to 5000.
1701
+ interval_in_ms: Time interval in miliseconds to sleep between requests. Defaults to 50.
1702
+ raise_for_status: When set to `True` a MeilisearchTaskFailedError will be raised if a task
1703
+ has a failed status. Defaults to False.
1704
+
1705
+ Returns:
1706
+
1707
+ Details of the processed update status.
1708
+
1709
+ Raises:
1710
+
1711
+ MeilisearchCommunicationError: If there was an error communicating with the server.
1712
+ MeilisearchApiError: If the Meilisearch API returned an error.
1713
+ MeilisearchTimeoutError: If the connection times out.
1714
+ MeilisearchTaskFailedError: If `raise_for_status` is `True` and a task has a failed status.
1715
+
1716
+ Examples:
1717
+
1718
+ >>> from meilisearch_python_sdk import Client
1719
+ >>> >>> documents = [
1720
+ >>> {"id": 1, "title": "Movie 1", "genre": "comedy"},
1721
+ >>> {"id": 2, "title": "Movie 2", "genre": "drama"},
1722
+ >>> ]
1723
+ >>> client = Client("http://localhost.com", "masterKey")
1724
+ >>> index = client.index("movies")
1725
+ >>> response = await index.add_documents(documents)
1726
+ >>> client.wait_for_task(response.update_id)
1727
+ """
1728
+ return _task.wait_for_task(
1729
+ self.http_client,
1730
+ task_id=task_id,
1731
+ timeout_in_ms=timeout_in_ms,
1732
+ interval_in_ms=interval_in_ms,
1733
+ raise_for_status=raise_for_status,
1734
+ )
1735
+
1736
+
1737
+ def _build_offset_limit_url(base: str, offset: int | None, limit: int | None) -> str:
1738
+ if offset is not None and limit is not None:
1739
+ return f"{base}?offset={offset}&limit={limit}"
1740
+ elif offset is not None:
1741
+ return f"{base}?offset={offset}"
1742
+ elif limit is not None:
1743
+ return f"{base}?limit={limit}"
1744
+
1745
+ return base
1746
+
1747
+
1748
+ def _build_update_key_payload(key: KeyUpdate) -> dict[str, Any]:
1749
+ # The json.loads(key.json()) is because Pydantic can't serialize a date in a Python dict,
1750
+ # but can when converting to a json string.
1751
+ if is_pydantic_2():
1752
+ return { # type: ignore[attr-defined]
1753
+ k: v
1754
+ for k, v in json.loads(key.model_dump_json(by_alias=True)).items()
1755
+ if v is not None and k != "key"
1756
+ }
1757
+ else: # pragma: no cover
1758
+ return { # type: ignore[attr-defined]
1759
+ k: v
1760
+ for k, v in json.loads(key.json(by_alias=True)).items()
1761
+ if v is not None and k != "key"
1762
+ }