meilisearch-python-sdk 2.2.3__py3-none-any.whl → 2.4.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.

@@ -4,9 +4,9 @@ import asyncio
4
4
  import json
5
5
  from csv import DictReader
6
6
  from datetime import datetime
7
- from functools import partial
7
+ from functools import cached_property, partial
8
8
  from pathlib import Path
9
- from typing import Any, Generator, Mapping, Sequence
9
+ from typing import Any, Generator, MutableMapping, Sequence
10
10
  from urllib.parse import urlencode
11
11
  from warnings import warn
12
12
 
@@ -27,10 +27,22 @@ from meilisearch_python_sdk.models.settings import (
27
27
  TypoTolerance,
28
28
  )
29
29
  from meilisearch_python_sdk.models.task import TaskInfo
30
+ from meilisearch_python_sdk.plugins import (
31
+ AsyncDocumentPlugin,
32
+ AsyncEvent,
33
+ AsyncIndexPlugins,
34
+ AsyncPlugin,
35
+ AsyncPostSearchPlugin,
36
+ DocumentPlugin,
37
+ Event,
38
+ IndexPlugins,
39
+ Plugin,
40
+ PostSearchPlugin,
41
+ )
30
42
  from meilisearch_python_sdk.types import Filter, JsonDict, JsonMapping
31
43
 
32
44
 
33
- class BaseIndex:
45
+ class _BaseIndex:
34
46
  def __init__(
35
47
  self,
36
48
  uid: str,
@@ -62,7 +74,7 @@ class BaseIndex:
62
74
  self.updated_at = iso_to_date_time(updated_at_iso_str)
63
75
 
64
76
 
65
- class AsyncIndex(BaseIndex):
77
+ class AsyncIndex(_BaseIndex):
66
78
  """AsyncIndex class gives access to all indexes routes and child routes.
67
79
 
68
80
  https://docs.meilisearch.com/reference/api/indexes.html
@@ -75,6 +87,7 @@ class AsyncIndex(BaseIndex):
75
87
  primary_key: str | None = None,
76
88
  created_at: str | datetime | None = None,
77
89
  updated_at: str | datetime | None = None,
90
+ plugins: AsyncIndexPlugins | None = None,
78
91
  ):
79
92
  """Class initializer.
80
93
 
@@ -86,10 +99,374 @@ class AsyncIndex(BaseIndex):
86
99
  primary_key: The primary key of the documents. Defaults to None.
87
100
  created_at: The date and time the index was created. Defaults to None.
88
101
  updated_at: The date and time the index was last updated. Defaults to None.
102
+ plugins: Optional plugins can be provided to extend functionality.
89
103
  """
90
104
  super().__init__(uid, primary_key, created_at, updated_at)
91
105
  self.http_client = http_client
92
106
  self._http_requests = AsyncHttpRequests(http_client)
107
+ self.plugins = plugins
108
+
109
+ @cached_property
110
+ def _concurrent_add_documents_plugins(self) -> list[AsyncPlugin | AsyncDocumentPlugin] | None:
111
+ if not self.plugins or not self.plugins.add_documents_plugins:
112
+ return None
113
+
114
+ plugins = []
115
+ for plugin in self.plugins.add_documents_plugins:
116
+ if plugin.CONCURRENT_EVENT:
117
+ plugins.append(plugin)
118
+
119
+ if not plugins:
120
+ return None
121
+
122
+ return plugins
123
+
124
+ @cached_property
125
+ def _post_add_documents_plugins(self) -> list[AsyncPlugin | AsyncDocumentPlugin] | None:
126
+ if not self.plugins or not self.plugins.add_documents_plugins:
127
+ return None
128
+
129
+ plugins = []
130
+ for plugin in self.plugins.add_documents_plugins:
131
+ if plugin.POST_EVENT:
132
+ plugins.append(plugin)
133
+
134
+ if not plugins:
135
+ return None
136
+
137
+ return plugins
138
+
139
+ @cached_property
140
+ def _pre_add_documents_plugins(self) -> list[AsyncPlugin | AsyncDocumentPlugin] | None:
141
+ if not self.plugins or not self.plugins.add_documents_plugins:
142
+ return None
143
+
144
+ plugins = []
145
+ for plugin in self.plugins.add_documents_plugins:
146
+ if plugin.PRE_EVENT:
147
+ plugins.append(plugin)
148
+
149
+ if not plugins:
150
+ return None
151
+
152
+ return plugins
153
+
154
+ @cached_property
155
+ def _concurrent_delete_all_documents_plugins(self) -> list[AsyncPlugin] | None:
156
+ if not self.plugins or not self.plugins.delete_all_documents_plugins:
157
+ return None
158
+
159
+ plugins = []
160
+ for plugin in self.plugins.delete_all_documents_plugins:
161
+ if plugin.CONCURRENT_EVENT:
162
+ plugins.append(plugin)
163
+
164
+ if not plugins:
165
+ return None
166
+
167
+ return plugins
168
+
169
+ @cached_property
170
+ def _post_delete_all_documents_plugins(self) -> list[AsyncPlugin] | None:
171
+ if not self.plugins or not self.plugins.delete_all_documents_plugins:
172
+ return None
173
+
174
+ plugins = []
175
+ for plugin in self.plugins.delete_all_documents_plugins:
176
+ if plugin.POST_EVENT:
177
+ plugins.append(plugin)
178
+
179
+ if not plugins:
180
+ return None
181
+
182
+ return plugins
183
+
184
+ @cached_property
185
+ def _pre_delete_all_documents_plugins(self) -> list[AsyncPlugin] | None:
186
+ if not self.plugins or not self.plugins.delete_all_documents_plugins:
187
+ return None
188
+
189
+ plugins = []
190
+ for plugin in self.plugins.delete_all_documents_plugins:
191
+ if plugin.PRE_EVENT:
192
+ plugins.append(plugin)
193
+
194
+ if not plugins:
195
+ return None
196
+
197
+ return plugins
198
+
199
+ @cached_property
200
+ def _concurrent_delete_document_plugins(self) -> list[AsyncPlugin] | None:
201
+ if not self.plugins or not self.plugins.delete_document_plugins:
202
+ return None
203
+
204
+ plugins = []
205
+ for plugin in self.plugins.delete_document_plugins:
206
+ if plugin.CONCURRENT_EVENT:
207
+ plugins.append(plugin)
208
+
209
+ if not plugins:
210
+ return None
211
+
212
+ return plugins
213
+
214
+ @cached_property
215
+ def _post_delete_document_plugins(self) -> list[AsyncPlugin] | None:
216
+ if not self.plugins or not self.plugins.delete_document_plugins:
217
+ return None
218
+
219
+ plugins = []
220
+ for plugin in self.plugins.delete_document_plugins:
221
+ if plugin.POST_EVENT:
222
+ plugins.append(plugin)
223
+
224
+ if not plugins:
225
+ return None
226
+
227
+ return plugins
228
+
229
+ @cached_property
230
+ def _pre_delete_document_plugins(self) -> list[AsyncPlugin] | None:
231
+ if not self.plugins or not self.plugins.delete_document_plugins:
232
+ return None
233
+
234
+ plugins = []
235
+ for plugin in self.plugins.delete_document_plugins:
236
+ if plugin.PRE_EVENT:
237
+ plugins.append(plugin)
238
+
239
+ if not plugins:
240
+ return None
241
+
242
+ return plugins
243
+
244
+ @cached_property
245
+ def _concurrent_delete_documents_plugins(self) -> list[AsyncPlugin] | None:
246
+ if not self.plugins or not self.plugins.delete_documents_plugins:
247
+ return None
248
+
249
+ plugins = []
250
+ for plugin in self.plugins.delete_documents_plugins:
251
+ if plugin.CONCURRENT_EVENT:
252
+ plugins.append(plugin)
253
+
254
+ if not plugins:
255
+ return None
256
+
257
+ return plugins
258
+
259
+ @cached_property
260
+ def _post_delete_documents_plugins(self) -> list[AsyncPlugin] | None:
261
+ if not self.plugins or not self.plugins.delete_documents_plugins:
262
+ return None
263
+
264
+ plugins = []
265
+ for plugin in self.plugins.delete_documents_plugins:
266
+ if plugin.POST_EVENT:
267
+ plugins.append(plugin)
268
+
269
+ if not plugins:
270
+ return None
271
+
272
+ return plugins
273
+
274
+ @cached_property
275
+ def _pre_delete_documents_plugins(self) -> list[AsyncPlugin] | None:
276
+ if not self.plugins or not self.plugins.delete_documents_plugins:
277
+ return None
278
+
279
+ plugins = []
280
+ for plugin in self.plugins.delete_documents_plugins:
281
+ if plugin.PRE_EVENT:
282
+ plugins.append(plugin)
283
+
284
+ if not plugins:
285
+ return None
286
+
287
+ return plugins
288
+
289
+ @cached_property
290
+ def _concurrent_delete_documents_by_filter_plugins(self) -> list[AsyncPlugin] | None:
291
+ if not self.plugins or not self.plugins.delete_documents_by_filter_plugins:
292
+ return None
293
+
294
+ plugins = []
295
+ for plugin in self.plugins.delete_documents_by_filter_plugins:
296
+ if plugin.CONCURRENT_EVENT:
297
+ plugins.append(plugin)
298
+
299
+ if not plugins:
300
+ return None
301
+
302
+ return plugins
303
+
304
+ @cached_property
305
+ def _post_delete_documents_by_filter_plugins(self) -> list[AsyncPlugin] | None:
306
+ if not self.plugins or not self.plugins.delete_documents_by_filter_plugins:
307
+ return None
308
+
309
+ plugins = []
310
+ for plugin in self.plugins.delete_documents_by_filter_plugins:
311
+ if plugin.POST_EVENT:
312
+ plugins.append(plugin)
313
+
314
+ if not plugins:
315
+ return None
316
+
317
+ return plugins
318
+
319
+ @cached_property
320
+ def _pre_delete_documents_by_filter_plugins(self) -> list[AsyncPlugin] | None:
321
+ if not self.plugins or not self.plugins.delete_documents_by_filter_plugins:
322
+ return None
323
+
324
+ plugins = []
325
+ for plugin in self.plugins.delete_documents_by_filter_plugins:
326
+ if plugin.PRE_EVENT:
327
+ plugins.append(plugin)
328
+
329
+ if not plugins:
330
+ return None
331
+
332
+ return plugins
333
+
334
+ @cached_property
335
+ def _concurrent_facet_search_plugins(self) -> list[AsyncPlugin] | None:
336
+ if not self.plugins or not self.plugins.facet_search_plugins:
337
+ return None
338
+
339
+ plugins = []
340
+ for plugin in self.plugins.facet_search_plugins:
341
+ if plugin.CONCURRENT_EVENT:
342
+ plugins.append(plugin)
343
+
344
+ if not plugins:
345
+ return None
346
+
347
+ return plugins
348
+
349
+ @cached_property
350
+ def _post_facet_search_plugins(self) -> list[AsyncPlugin] | None:
351
+ if not self.plugins or not self.plugins.facet_search_plugins:
352
+ return None
353
+
354
+ plugins = []
355
+ for plugin in self.plugins.facet_search_plugins:
356
+ if plugin.POST_EVENT:
357
+ plugins.append(plugin)
358
+
359
+ if not plugins:
360
+ return None
361
+
362
+ return plugins
363
+
364
+ @cached_property
365
+ def _pre_facet_search_plugins(self) -> list[AsyncPlugin] | None:
366
+ if not self.plugins or not self.plugins.facet_search_plugins:
367
+ return None
368
+
369
+ plugins = []
370
+ for plugin in self.plugins.facet_search_plugins:
371
+ if plugin.PRE_EVENT:
372
+ plugins.append(plugin)
373
+
374
+ if not plugins:
375
+ return None
376
+
377
+ return plugins
378
+
379
+ @cached_property
380
+ def _concurrent_search_plugins(self) -> list[AsyncPlugin | AsyncPostSearchPlugin] | None:
381
+ if not self.plugins or not self.plugins.search_plugins:
382
+ return None
383
+
384
+ plugins = []
385
+ for plugin in self.plugins.search_plugins:
386
+ if plugin.CONCURRENT_EVENT:
387
+ plugins.append(plugin)
388
+
389
+ if not plugins:
390
+ return None
391
+
392
+ return plugins
393
+
394
+ @cached_property
395
+ def _post_search_plugins(self) -> list[AsyncPlugin | AsyncPostSearchPlugin] | None:
396
+ if not self.plugins or not self.plugins.search_plugins:
397
+ return None
398
+
399
+ plugins = []
400
+ for plugin in self.plugins.search_plugins:
401
+ if plugin.POST_EVENT:
402
+ plugins.append(plugin)
403
+
404
+ if not plugins:
405
+ return None
406
+
407
+ return plugins
408
+
409
+ @cached_property
410
+ def _pre_search_plugins(self) -> list[AsyncPlugin | AsyncPostSearchPlugin] | None:
411
+ if not self.plugins or not self.plugins.search_plugins:
412
+ return None
413
+
414
+ plugins = []
415
+ for plugin in self.plugins.search_plugins:
416
+ if plugin.PRE_EVENT:
417
+ plugins.append(plugin)
418
+
419
+ if not plugins:
420
+ return None
421
+
422
+ return plugins
423
+
424
+ @cached_property
425
+ def _concurrent_update_documents_plugins(
426
+ self,
427
+ ) -> list[AsyncPlugin | AsyncDocumentPlugin] | None:
428
+ if not self.plugins or not self.plugins.update_documents_plugins:
429
+ return None
430
+
431
+ plugins = []
432
+ for plugin in self.plugins.update_documents_plugins:
433
+ if plugin.CONCURRENT_EVENT:
434
+ plugins.append(plugin)
435
+
436
+ if not plugins:
437
+ return None
438
+
439
+ return plugins
440
+
441
+ @cached_property
442
+ def _post_update_documents_plugins(self) -> list[AsyncPlugin | AsyncDocumentPlugin] | None:
443
+ if not self.plugins or not self.plugins.update_documents_plugins:
444
+ return None
445
+
446
+ plugins = []
447
+ for plugin in self.plugins.update_documents_plugins:
448
+ if plugin.POST_EVENT:
449
+ plugins.append(plugin)
450
+
451
+ if not plugins:
452
+ return None
453
+
454
+ return plugins
455
+
456
+ @cached_property
457
+ def _pre_update_documents_plugins(self) -> list[AsyncPlugin | AsyncDocumentPlugin] | None:
458
+ if not self.plugins or not self.plugins.update_documents_plugins:
459
+ return None
460
+
461
+ plugins = []
462
+ for plugin in self.plugins.update_documents_plugins:
463
+ if plugin.PRE_EVENT:
464
+ plugins.append(plugin)
465
+
466
+ if not plugins:
467
+ return None
468
+
469
+ return plugins
93
470
 
94
471
  async def delete(self) -> TaskInfo:
95
472
  """Deletes the index.
@@ -223,7 +600,13 @@ class AsyncIndex(BaseIndex):
223
600
 
224
601
  @classmethod
225
602
  async def create(
226
- cls, http_client: AsyncClient, uid: str, primary_key: str | None = None
603
+ cls,
604
+ http_client: AsyncClient,
605
+ uid: str,
606
+ primary_key: str | None = None,
607
+ *,
608
+ settings: MeilisearchSettings | None = None,
609
+ plugins: AsyncIndexPlugins | None = None,
227
610
  ) -> AsyncIndex:
228
611
  """Creates a new index.
229
612
 
@@ -233,9 +616,15 @@ class AsyncIndex(BaseIndex):
233
616
  Args:
234
617
 
235
618
  http_client: An instance of the AsyncClient. This automatically gets passed by the
236
- Client when creating and AsyncIndex instance.
619
+ Client when creating an AsyncIndex instance.
237
620
  uid: The index's unique identifier.
238
621
  primary_key: The primary key of the documents. Defaults to None.
622
+ settings: Settings for the index. The settings can also be updated independently of
623
+ creating the index. The advantage to updating them here is updating the settings after
624
+ adding documents will cause the documents to be re-indexed. Because of this it will be
625
+ faster to update them before adding documents. Defaults to None (i.e. default
626
+ Meilisearch index settings).
627
+ plugins: Optional plugins can be provided to extend functionality.
239
628
 
240
629
  Returns:
241
630
 
@@ -261,16 +650,24 @@ class AsyncIndex(BaseIndex):
261
650
  http_request = AsyncHttpRequests(http_client)
262
651
  response = await http_request.post(url, payload)
263
652
  await async_wait_for_task(http_client, response.json()["taskUid"], timeout_in_ms=100000)
653
+
264
654
  index_response = await http_request.get(f"{url}/{uid}")
265
655
  index_dict = index_response.json()
266
- return cls(
656
+ index = cls(
267
657
  http_client=http_client,
268
658
  uid=index_dict["uid"],
269
659
  primary_key=index_dict["primaryKey"],
270
660
  created_at=index_dict["createdAt"],
271
661
  updated_at=index_dict["updatedAt"],
662
+ plugins=plugins,
272
663
  )
273
664
 
665
+ if settings:
666
+ settings_task = await index.update_settings(settings)
667
+ await async_wait_for_task(http_client, settings_task.task_uid, timeout_in_ms=100000)
668
+
669
+ return index
670
+
274
671
  async def get_stats(self) -> IndexStats:
275
672
  """Get stats of the index.
276
673
 
@@ -379,6 +776,7 @@ class AsyncIndex(BaseIndex):
379
776
  >>> index = client.index("movies")
380
777
  >>> search_results = await index.search("Tron")
381
778
  """
779
+
382
780
  body = _process_search_parameters(
383
781
  q=query,
384
782
  offset=offset,
@@ -402,10 +800,134 @@ class AsyncIndex(BaseIndex):
402
800
  show_ranking_score_details=show_ranking_score_details,
403
801
  vector=vector,
404
802
  )
803
+ search_url = f"{self._base_url_with_uid}/search"
804
+
805
+ if self._pre_search_plugins:
806
+ await AsyncIndex._run_plugins(
807
+ self._pre_search_plugins,
808
+ AsyncEvent.PRE,
809
+ query=query,
810
+ offset=offset,
811
+ limit=limit,
812
+ filter=filter,
813
+ facets=facets,
814
+ attributes_to_retrieve=attributes_to_retrieve,
815
+ attributes_to_crop=attributes_to_crop,
816
+ crop_length=crop_length,
817
+ attributes_to_highlight=attributes_to_highlight,
818
+ sort=sort,
819
+ show_matches_position=show_matches_position,
820
+ highlight_pre_tag=highlight_pre_tag,
821
+ highlight_post_tag=highlight_post_tag,
822
+ crop_marker=crop_marker,
823
+ matching_strategy=matching_strategy,
824
+ hits_per_page=hits_per_page,
825
+ page=page,
826
+ attributes_to_search_on=attributes_to_search_on,
827
+ show_ranking_score=show_ranking_score,
828
+ show_ranking_score_details=show_ranking_score_details,
829
+ vector=vector,
830
+ )
831
+
832
+ if self._concurrent_search_plugins:
833
+ if not use_task_groups():
834
+ concurrent_tasks: Any = []
835
+ for plugin in self._concurrent_search_plugins:
836
+ if _plugin_has_method(plugin, "run_plugin"):
837
+ concurrent_tasks.append(
838
+ plugin.run_plugin( # type: ignore[union-attr]
839
+ event=AsyncEvent.CONCURRENT,
840
+ query=query,
841
+ offset=offset,
842
+ limit=limit,
843
+ filter=filter,
844
+ facets=facets,
845
+ attributes_to_retrieve=attributes_to_retrieve,
846
+ attributes_to_crop=attributes_to_crop,
847
+ crop_length=crop_length,
848
+ attributes_to_highlight=attributes_to_highlight,
849
+ sort=sort,
850
+ show_matches_position=show_matches_position,
851
+ highlight_pre_tag=highlight_pre_tag,
852
+ highlight_post_tag=highlight_post_tag,
853
+ crop_marker=crop_marker,
854
+ matching_strategy=matching_strategy,
855
+ hits_per_page=hits_per_page,
856
+ page=page,
857
+ attributes_to_search_on=attributes_to_search_on,
858
+ show_ranking_score=show_ranking_score,
859
+ show_ranking_score_details=show_ranking_score_details,
860
+ vector=vector,
861
+ )
862
+ )
863
+
864
+ concurrent_tasks.append(self._http_requests.post(search_url, body=body))
405
865
 
406
- response = await self._http_requests.post(f"{self._base_url_with_uid}/search", body=body)
866
+ responses = await asyncio.gather(*concurrent_tasks)
867
+ result = SearchResults(**responses[-1].json())
868
+ if self._post_search_plugins:
869
+ post = await AsyncIndex._run_plugins(
870
+ self._post_search_plugins, AsyncEvent.POST, search_results=result
871
+ )
872
+ if post.get("search_result"):
873
+ result = post["search_result"]
874
+
875
+ return result
876
+
877
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
878
+ for plugin in self._concurrent_search_plugins:
879
+ if _plugin_has_method(plugin, "run_plugin"):
880
+ tg.create_task(
881
+ plugin.run_plugin( # type: ignore[union-attr]
882
+ event=AsyncEvent.CONCURRENT,
883
+ query=query,
884
+ offset=offset,
885
+ limit=limit,
886
+ filter=filter,
887
+ facets=facets,
888
+ attributes_to_retrieve=attributes_to_retrieve,
889
+ attributes_to_crop=attributes_to_crop,
890
+ crop_length=crop_length,
891
+ attributes_to_highlight=attributes_to_highlight,
892
+ sort=sort,
893
+ show_matches_position=show_matches_position,
894
+ highlight_pre_tag=highlight_pre_tag,
895
+ highlight_post_tag=highlight_post_tag,
896
+ crop_marker=crop_marker,
897
+ matching_strategy=matching_strategy,
898
+ hits_per_page=hits_per_page,
899
+ page=page,
900
+ attributes_to_search_on=attributes_to_search_on,
901
+ show_ranking_score=show_ranking_score,
902
+ show_ranking_score_details=show_ranking_score_details,
903
+ vector=vector,
904
+ )
905
+ )
906
+
907
+ response_coroutine = tg.create_task(self._http_requests.post(search_url, body=body))
908
+
909
+ response = await response_coroutine
910
+ result = SearchResults(**response.json())
911
+ if self._post_search_plugins:
912
+ post = await AsyncIndex._run_plugins(
913
+ self._post_search_plugins, AsyncEvent.POST, search_results=result
914
+ )
915
+ if post.get("search_result"):
916
+ result = post["search_result"]
917
+
918
+ return result
407
919
 
408
- return SearchResults(**response.json())
920
+ response = await self._http_requests.post(search_url, body=body)
921
+ result = SearchResults(**response.json())
922
+
923
+ if self._post_search_plugins:
924
+ post = await AsyncIndex._run_plugins(
925
+ self._post_search_plugins, AsyncEvent.POST, search_results=result
926
+ )
927
+ if post.get("search_result"):
928
+ result = post["search_result"]
929
+
930
+ return result
409
931
 
410
932
  async def facet_search(
411
933
  self,
@@ -525,12 +1047,132 @@ class AsyncIndex(BaseIndex):
525
1047
  show_ranking_score_details=show_ranking_score_details,
526
1048
  vector=vector,
527
1049
  )
1050
+ search_url = f"{self._base_url_with_uid}/facet-search"
1051
+
1052
+ if self._pre_facet_search_plugins:
1053
+ await AsyncIndex._run_plugins(
1054
+ self._pre_facet_search_plugins,
1055
+ AsyncEvent.PRE,
1056
+ query=query,
1057
+ offset=offset,
1058
+ limit=limit,
1059
+ filter=filter,
1060
+ facets=facets,
1061
+ attributes_to_retrieve=attributes_to_retrieve,
1062
+ attributes_to_crop=attributes_to_crop,
1063
+ crop_length=crop_length,
1064
+ attributes_to_highlight=attributes_to_highlight,
1065
+ sort=sort,
1066
+ show_matches_position=show_matches_position,
1067
+ highlight_pre_tag=highlight_pre_tag,
1068
+ highlight_post_tag=highlight_post_tag,
1069
+ crop_marker=crop_marker,
1070
+ matching_strategy=matching_strategy,
1071
+ hits_per_page=hits_per_page,
1072
+ page=page,
1073
+ attributes_to_search_on=attributes_to_search_on,
1074
+ show_ranking_score=show_ranking_score,
1075
+ show_ranking_score_details=show_ranking_score_details,
1076
+ vector=vector,
1077
+ )
528
1078
 
529
- response = await self._http_requests.post(
530
- f"{self._base_url_with_uid}/facet-search", body=body
531
- )
1079
+ if self._concurrent_facet_search_plugins:
1080
+ if not use_task_groups():
1081
+ tasks: Any = []
1082
+ for plugin in self._concurrent_facet_search_plugins:
1083
+ if _plugin_has_method(plugin, "run_plugin"):
1084
+ tasks.append(
1085
+ plugin.run_plugin( # type: ignore[union-attr]
1086
+ event=AsyncEvent.CONCURRENT,
1087
+ query=query,
1088
+ offset=offset,
1089
+ limit=limit,
1090
+ filter=filter,
1091
+ facets=facets,
1092
+ attributes_to_retrieve=attributes_to_retrieve,
1093
+ attributes_to_crop=attributes_to_crop,
1094
+ crop_length=crop_length,
1095
+ attributes_to_highlight=attributes_to_highlight,
1096
+ sort=sort,
1097
+ show_matches_position=show_matches_position,
1098
+ highlight_pre_tag=highlight_pre_tag,
1099
+ highlight_post_tag=highlight_post_tag,
1100
+ crop_marker=crop_marker,
1101
+ matching_strategy=matching_strategy,
1102
+ hits_per_page=hits_per_page,
1103
+ page=page,
1104
+ attributes_to_search_on=attributes_to_search_on,
1105
+ show_ranking_score=show_ranking_score,
1106
+ show_ranking_score_details=show_ranking_score_details,
1107
+ vector=vector,
1108
+ )
1109
+ )
532
1110
 
533
- return FacetSearchResults(**response.json())
1111
+ tasks.append(self._http_requests.post(search_url, body=body))
1112
+ responses = await asyncio.gather(*tasks)
1113
+ result = FacetSearchResults(**responses[-1].json())
1114
+ if self._post_facet_search_plugins:
1115
+ post = await AsyncIndex._run_plugins(
1116
+ self._post_facet_search_plugins, AsyncEvent.POST, result=result
1117
+ )
1118
+ if isinstance(post["generic_result"], FacetSearchResults):
1119
+ result = post["generic_result"]
1120
+
1121
+ return result
1122
+
1123
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
1124
+ for plugin in self._concurrent_facet_search_plugins:
1125
+ if _plugin_has_method(plugin, "run_plugin"):
1126
+ tg.create_task(
1127
+ plugin.run_plugin( # type: ignore[union-attr]
1128
+ event=AsyncEvent.CONCURRENT,
1129
+ query=query,
1130
+ offset=offset,
1131
+ limit=limit,
1132
+ filter=filter,
1133
+ facets=facets,
1134
+ attributes_to_retrieve=attributes_to_retrieve,
1135
+ attributes_to_crop=attributes_to_crop,
1136
+ crop_length=crop_length,
1137
+ attributes_to_highlight=attributes_to_highlight,
1138
+ sort=sort,
1139
+ show_matches_position=show_matches_position,
1140
+ highlight_pre_tag=highlight_pre_tag,
1141
+ highlight_post_tag=highlight_post_tag,
1142
+ crop_marker=crop_marker,
1143
+ matching_strategy=matching_strategy,
1144
+ hits_per_page=hits_per_page,
1145
+ page=page,
1146
+ attributes_to_search_on=attributes_to_search_on,
1147
+ show_ranking_score=show_ranking_score,
1148
+ show_ranking_score_details=show_ranking_score_details,
1149
+ vector=vector,
1150
+ )
1151
+ )
1152
+
1153
+ response_coroutine = tg.create_task(self._http_requests.post(search_url, body=body))
1154
+
1155
+ response = await response_coroutine
1156
+ result = FacetSearchResults(**response.json())
1157
+ if self._post_facet_search_plugins:
1158
+ post = await AsyncIndex._run_plugins(
1159
+ self._post_facet_search_plugins, AsyncEvent.POST, result=result
1160
+ )
1161
+ if isinstance(post["generic_result"], FacetSearchResults):
1162
+ result = post["generic_result"]
1163
+
1164
+ return result
1165
+
1166
+ response = await self._http_requests.post(search_url, body=body)
1167
+ result = FacetSearchResults(**response.json())
1168
+ if self._post_facet_search_plugins:
1169
+ post = await AsyncIndex._run_plugins(
1170
+ self._post_facet_search_plugins, AsyncEvent.POST, result=result
1171
+ )
1172
+ if isinstance(post["generic_result"], FacetSearchResults):
1173
+ result = post["generic_result"]
1174
+
1175
+ return result
534
1176
 
535
1177
  async def get_document(self, document_id: str) -> JsonDict:
536
1178
  """Get one document with given document identifier.
@@ -654,9 +1296,103 @@ class AsyncIndex(BaseIndex):
654
1296
  else:
655
1297
  url = self._documents_url
656
1298
 
1299
+ if self._pre_add_documents_plugins:
1300
+ pre = await AsyncIndex._run_plugins(
1301
+ self._pre_add_documents_plugins,
1302
+ AsyncEvent.PRE,
1303
+ documents=documents,
1304
+ primary_key=primary_key,
1305
+ )
1306
+ if pre.get("document_result"):
1307
+ documents = pre["document_result"]
1308
+
1309
+ if self._concurrent_add_documents_plugins:
1310
+ if not use_task_groups():
1311
+ tasks: Any = []
1312
+ for plugin in self._concurrent_add_documents_plugins:
1313
+ if _plugin_has_method(plugin, "run_plugin"):
1314
+ tasks.append(
1315
+ plugin.run_plugin( # type: ignore[union-attr]
1316
+ event=AsyncEvent.CONCURRENT,
1317
+ documents=documents,
1318
+ primary_key=primary_key,
1319
+ )
1320
+ )
1321
+ if _plugin_has_method(plugin, "run_document_plugin"):
1322
+ tasks.append(
1323
+ plugin.run_document_plugin( # type: ignore[union-attr]
1324
+ event=AsyncEvent.CONCURRENT,
1325
+ documents=documents,
1326
+ primary_key=primary_key,
1327
+ )
1328
+ )
1329
+
1330
+ tasks.append(self._http_requests.post(url, documents))
1331
+
1332
+ responses = await asyncio.gather(*tasks)
1333
+ result = TaskInfo(**responses[-1].json())
1334
+ if self._post_add_documents_plugins:
1335
+ post = await AsyncIndex._run_plugins(
1336
+ self._post_add_documents_plugins,
1337
+ AsyncEvent.POST,
1338
+ result=result,
1339
+ documents=documents,
1340
+ primary_key=primary_key,
1341
+ )
1342
+ if isinstance(post["generic_result"], TaskInfo):
1343
+ result = post["generic_result"]
1344
+ return result
1345
+
1346
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
1347
+ for plugin in self._concurrent_add_documents_plugins:
1348
+ if _plugin_has_method(plugin, "run_plugin"):
1349
+ tg.create_task(
1350
+ plugin.run_plugin( # type: ignore[union-attr]
1351
+ event=AsyncEvent.CONCURRENT,
1352
+ documents=documents,
1353
+ primary_key=primary_key,
1354
+ )
1355
+ )
1356
+ if _plugin_has_method(plugin, "run_document_plugin"):
1357
+ tg.create_task(
1358
+ plugin.run_document_plugin( # type: ignore[union-attr]
1359
+ event=AsyncEvent.CONCURRENT,
1360
+ documents=documents,
1361
+ primary_key=primary_key,
1362
+ )
1363
+ )
1364
+
1365
+ response_coroutine = tg.create_task(self._http_requests.post(url, documents))
1366
+
1367
+ response = await response_coroutine
1368
+ result = TaskInfo(**response.json())
1369
+ if self._post_add_documents_plugins:
1370
+ post = await AsyncIndex._run_plugins(
1371
+ self._post_add_documents_plugins,
1372
+ AsyncEvent.POST,
1373
+ result=result,
1374
+ documents=documents,
1375
+ primary_key=primary_key,
1376
+ )
1377
+ if isinstance(post["generic_result"], TaskInfo):
1378
+ result = post["generic_result"]
1379
+
1380
+ return result
1381
+
657
1382
  response = await self._http_requests.post(url, documents)
1383
+ result = TaskInfo(**response.json())
1384
+ if self._post_add_documents_plugins:
1385
+ post = await AsyncIndex._run_plugins(
1386
+ self._post_add_documents_plugins,
1387
+ AsyncEvent.POST,
1388
+ result=result,
1389
+ documents=documents,
1390
+ primary_key=primary_key,
1391
+ )
1392
+ if isinstance(post["generic_result"], TaskInfo):
1393
+ result = post["generic_result"]
658
1394
 
659
- return TaskInfo(**response.json())
1395
+ return result
660
1396
 
661
1397
  async def add_documents_in_batches(
662
1398
  self,
@@ -1095,9 +1831,105 @@ class AsyncIndex(BaseIndex):
1095
1831
  else:
1096
1832
  url = self._documents_url
1097
1833
 
1834
+ if self._pre_update_documents_plugins:
1835
+ pre = await AsyncIndex._run_plugins(
1836
+ self._pre_update_documents_plugins,
1837
+ AsyncEvent.PRE,
1838
+ documents=documents,
1839
+ primary_key=primary_key,
1840
+ )
1841
+ if pre.get("document_result"):
1842
+ documents = pre["document_result"]
1843
+
1844
+ if self._concurrent_update_documents_plugins:
1845
+ if not use_task_groups():
1846
+ tasks: Any = []
1847
+ for plugin in self._concurrent_update_documents_plugins:
1848
+ if _plugin_has_method(plugin, "run_plugin"):
1849
+ tasks.append(
1850
+ plugin.run_plugin( # type: ignore[union-attr]
1851
+ event=AsyncEvent.CONCURRENT,
1852
+ documents=documents,
1853
+ primary_key=primary_key,
1854
+ )
1855
+ )
1856
+ if _plugin_has_method(plugin, "run_document_plugin"):
1857
+ tasks.append(
1858
+ plugin.run_document_plugin( # type: ignore[union-attr]
1859
+ event=AsyncEvent.CONCURRENT,
1860
+ documents=documents,
1861
+ primary_key=primary_key,
1862
+ )
1863
+ )
1864
+
1865
+ tasks.append(self._http_requests.put(url, documents))
1866
+
1867
+ responses = await asyncio.gather(*tasks)
1868
+ result = TaskInfo(**responses[-1].json())
1869
+ if self._post_update_documents_plugins:
1870
+ post = await AsyncIndex._run_plugins(
1871
+ self._post_update_documents_plugins,
1872
+ AsyncEvent.POST,
1873
+ result=result,
1874
+ documents=documents,
1875
+ primary_key=primary_key,
1876
+ )
1877
+ if isinstance(post["generic_result"], TaskInfo):
1878
+ result = post["generic_result"]
1879
+
1880
+ return result
1881
+
1882
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
1883
+ for plugin in self._concurrent_update_documents_plugins:
1884
+ if _plugin_has_method(plugin, "run_plugin"):
1885
+ tg.create_task(
1886
+ plugin.run_plugin( # type: ignore[union-attr]
1887
+ event=AsyncEvent.CONCURRENT,
1888
+ documents=documents,
1889
+ primary_key=primary_key,
1890
+ )
1891
+ )
1892
+ if _plugin_has_method(plugin, "run_document_plugin"):
1893
+ tg.create_task(
1894
+ plugin.run_document_plugin( # type: ignore[union-attr]
1895
+ event=AsyncEvent.CONCURRENT,
1896
+ documents=documents,
1897
+ primary_key=primary_key,
1898
+ )
1899
+ )
1900
+
1901
+ response_coroutine = tg.create_task(self._http_requests.put(url, documents))
1902
+
1903
+ response = await response_coroutine
1904
+ result = TaskInfo(**response.json())
1905
+ if self._post_update_documents_plugins:
1906
+ post = await AsyncIndex._run_plugins(
1907
+ self._post_update_documents_plugins,
1908
+ AsyncEvent.POST,
1909
+ result=result,
1910
+ documents=documents,
1911
+ primary_key=primary_key,
1912
+ )
1913
+
1914
+ if isinstance(post["generic_result"], TaskInfo):
1915
+ result = post["generic_result"]
1916
+
1917
+ return result
1918
+
1098
1919
  response = await self._http_requests.put(url, documents)
1920
+ result = TaskInfo(**response.json())
1921
+ if self._post_update_documents_plugins:
1922
+ post = await AsyncIndex._run_plugins(
1923
+ self._post_update_documents_plugins,
1924
+ AsyncEvent.POST,
1925
+ result=result,
1926
+ documents=documents,
1927
+ primary_key=primary_key,
1928
+ )
1929
+ if isinstance(post["generic_result"], TaskInfo):
1930
+ result = post["generic_result"]
1099
1931
 
1100
- return TaskInfo(**response.json())
1932
+ return result
1101
1933
 
1102
1934
  async def update_documents_in_batches(
1103
1935
  self,
@@ -1536,9 +2368,61 @@ class AsyncIndex(BaseIndex):
1536
2368
  >>> index = client.index("movies")
1537
2369
  >>> await index.delete_document("1234")
1538
2370
  """
1539
- response = await self._http_requests.delete(f"{self._documents_url}/{document_id}")
2371
+ url = f"{self._documents_url}/{document_id}"
1540
2372
 
1541
- return TaskInfo(**response.json())
2373
+ if self._pre_delete_document_plugins:
2374
+ await AsyncIndex._run_plugins(
2375
+ self._pre_delete_document_plugins, AsyncEvent.PRE, document_id=document_id
2376
+ )
2377
+
2378
+ if self._concurrent_delete_document_plugins:
2379
+ if not use_task_groups():
2380
+ tasks: Any = []
2381
+ for plugin in self._concurrent_delete_document_plugins:
2382
+ tasks.append(
2383
+ plugin.run_plugin(event=AsyncEvent.CONCURRENT, document_id=document_id)
2384
+ )
2385
+
2386
+ tasks.append(self._http_requests.delete(url))
2387
+
2388
+ responses = await asyncio.gather(*tasks)
2389
+ result = TaskInfo(**responses[-1].json())
2390
+ if self._post_delete_document_plugins:
2391
+ post = await AsyncIndex._run_plugins(
2392
+ self._post_delete_document_plugins, AsyncEvent.POST, result=result
2393
+ )
2394
+ if isinstance(post.get("generic_result"), TaskInfo):
2395
+ result = post["generic_result"]
2396
+ return result
2397
+
2398
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
2399
+ for plugin in self._concurrent_delete_document_plugins:
2400
+ tg.create_task(
2401
+ plugin.run_plugin(event=AsyncEvent.CONCURRENT, document_id=document_id)
2402
+ )
2403
+
2404
+ response_coroutine = tg.create_task(self._http_requests.delete(url))
2405
+
2406
+ response = await response_coroutine
2407
+ result = TaskInfo(**response.json())
2408
+ if self._post_delete_document_plugins:
2409
+ post = await AsyncIndex._run_plugins(
2410
+ self._post_delete_document_plugins, event=AsyncEvent.POST, result=result
2411
+ )
2412
+ if isinstance(post["generic_result"], TaskInfo):
2413
+ result = post["generic_result"]
2414
+ return result
2415
+
2416
+ response = await self._http_requests.delete(url)
2417
+ result = TaskInfo(**response.json())
2418
+ if self._post_delete_document_plugins:
2419
+ post = await AsyncIndex._run_plugins(
2420
+ self._post_delete_document_plugins, AsyncEvent.POST, result=result
2421
+ )
2422
+ if isinstance(post["generic_result"], TaskInfo):
2423
+ result = post["generic_result"]
2424
+
2425
+ return result
1542
2426
 
1543
2427
  async def delete_documents(self, ids: list[str]) -> TaskInfo:
1544
2428
  """Delete multiple documents from the index.
@@ -1563,9 +2447,57 @@ class AsyncIndex(BaseIndex):
1563
2447
  >>> index = client.index("movies")
1564
2448
  >>> await index.delete_documents(["1234", "5678"])
1565
2449
  """
1566
- response = await self._http_requests.post(f"{self._documents_url}/delete-batch", ids)
2450
+ url = f"{self._documents_url}/delete-batch"
1567
2451
 
1568
- return TaskInfo(**response.json())
2452
+ if self._pre_delete_documents_plugins:
2453
+ await AsyncIndex._run_plugins(
2454
+ self._pre_delete_documents_plugins, AsyncEvent.PRE, ids=ids
2455
+ )
2456
+
2457
+ if self._concurrent_delete_documents_plugins:
2458
+ if not use_task_groups():
2459
+ tasks: Any = []
2460
+ for plugin in self._concurrent_delete_documents_plugins:
2461
+ tasks.append(plugin.run_plugin(event=AsyncEvent.CONCURRENT, ids=ids))
2462
+
2463
+ tasks.append(self._http_requests.post(url, ids))
2464
+
2465
+ responses = await asyncio.gather(*tasks)
2466
+ result = TaskInfo(**responses[-1].json())
2467
+ if self._post_delete_documents_plugins:
2468
+ post = await AsyncIndex._run_plugins(
2469
+ self._post_delete_documents_plugins, AsyncEvent.POST, result=result
2470
+ )
2471
+ if isinstance(post.get("generic_result"), TaskInfo):
2472
+ result = post["generic_result"]
2473
+ return result
2474
+
2475
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
2476
+ for plugin in self._concurrent_delete_documents_plugins:
2477
+ tg.create_task(plugin.run_plugin(event=AsyncEvent.CONCURRENT, ids=ids))
2478
+
2479
+ response_coroutine = tg.create_task(self._http_requests.post(url, ids))
2480
+
2481
+ response = await response_coroutine
2482
+ result = TaskInfo(**response.json())
2483
+ if self._post_delete_documents_plugins:
2484
+ post = await AsyncIndex._run_plugins(
2485
+ self._post_delete_documents_plugins, AsyncEvent.POST, result=result
2486
+ )
2487
+ if isinstance(post["generic_result"], TaskInfo):
2488
+ result = post["generic_result"]
2489
+ return result
2490
+
2491
+ response = await self._http_requests.post(url, ids)
2492
+ result = TaskInfo(**response.json())
2493
+ if self._post_delete_documents_plugins:
2494
+ post = await AsyncIndex._run_plugins(
2495
+ self._post_delete_documents_plugins, AsyncEvent.POST, result=result
2496
+ )
2497
+ if isinstance(post["generic_result"], TaskInfo):
2498
+ result = post["generic_result"]
2499
+
2500
+ return result
1569
2501
 
1570
2502
  async def delete_documents_by_filter(self, filter: Filter) -> TaskInfo:
1571
2503
  """Delete documents from the index by filter.
@@ -1585,16 +2517,66 @@ class AsyncIndex(BaseIndex):
1585
2517
 
1586
2518
  Examples:
1587
2519
 
1588
- >>> from meilisearch_python_sdk import AsyncClient
2520
+ >>> from meilisearch_pyrhon_sdk import AsyncClient
1589
2521
  >>> async with AsyncClient("http://localhost.com", "masterKey") as client:
1590
2522
  >>> index = client.index("movies")
1591
2523
  >>> await index.delete_documents_by_filter("genre=horor"))
1592
2524
  """
1593
- response = await self._http_requests.post(
1594
- f"{self._documents_url}/delete", body={"filter": filter}
1595
- )
2525
+ url = f"{self._documents_url}/delete"
1596
2526
 
1597
- return TaskInfo(**response.json())
2527
+ if self._pre_delete_documents_by_filter_plugins:
2528
+ await AsyncIndex._run_plugins(
2529
+ self._pre_delete_documents_by_filter_plugins, AsyncEvent.PRE, filter=filter
2530
+ )
2531
+
2532
+ if self._concurrent_delete_documents_by_filter_plugins:
2533
+ if not use_task_groups():
2534
+ tasks: Any = []
2535
+ for plugin in self._concurrent_delete_documents_by_filter_plugins:
2536
+ tasks.append(plugin.run_plugin(event=AsyncEvent.CONCURRENT, filter=filter))
2537
+
2538
+ tasks.append(self._http_requests.post(url, body={"filter": filter}))
2539
+
2540
+ responses = await asyncio.gather(*tasks)
2541
+ result = TaskInfo(**responses[-1].json())
2542
+ if self._post_delete_documents_by_filter_plugins:
2543
+ post = await AsyncIndex._run_plugins(
2544
+ self._post_delete_documents_by_filter_plugins,
2545
+ AsyncEvent.POST,
2546
+ result=result,
2547
+ )
2548
+ if isinstance(post["generic_result"], TaskInfo):
2549
+ result = post["generic_result"]
2550
+ return result
2551
+
2552
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
2553
+ for plugin in self._concurrent_delete_documents_by_filter_plugins:
2554
+ tg.create_task(plugin.run_plugin(event=AsyncEvent.CONCURRENT, filter=filter))
2555
+
2556
+ response_coroutine = tg.create_task(
2557
+ self._http_requests.post(url, body={"filter": filter})
2558
+ )
2559
+
2560
+ response = await response_coroutine
2561
+ result = TaskInfo(**response.json())
2562
+ if self._post_delete_documents_by_filter_plugins:
2563
+ post = await AsyncIndex._run_plugins(
2564
+ self._post_delete_documents_by_filter_plugins, AsyncEvent.POST, result=result
2565
+ )
2566
+ if isinstance(post["generic_result"], TaskInfo):
2567
+ result = post["generic_result"]
2568
+
2569
+ return result
2570
+
2571
+ response = await self._http_requests.post(url, body={"filter": filter})
2572
+ result = TaskInfo(**response.json())
2573
+ if self._post_delete_documents_by_filter_plugins:
2574
+ post = await AsyncIndex._run_plugins(
2575
+ self._post_delete_documents_by_filter_plugins, AsyncEvent.POST, result=result
2576
+ )
2577
+ if isinstance(post.get("generic_result"), TaskInfo):
2578
+ result = post["generic_result"]
2579
+ return result
1598
2580
 
1599
2581
  async def delete_documents_in_batches_by_filter(
1600
2582
  self, filters: list[str | list[str | list[str]]]
@@ -1656,9 +2638,52 @@ class AsyncIndex(BaseIndex):
1656
2638
  >>> index = client.index("movies")
1657
2639
  >>> await index.delete_all_document()
1658
2640
  """
1659
- response = await self._http_requests.delete(self._documents_url)
2641
+ if self._pre_delete_all_documents_plugins:
2642
+ await AsyncIndex._run_plugins(self._pre_delete_all_documents_plugins, AsyncEvent.PRE)
1660
2643
 
1661
- return TaskInfo(**response.json())
2644
+ if self._concurrent_delete_all_documents_plugins:
2645
+ if not use_task_groups():
2646
+ tasks: Any = []
2647
+ for plugin in self._concurrent_delete_all_documents_plugins:
2648
+ tasks.append(plugin.run_plugin(event=AsyncEvent.CONCURRENT))
2649
+
2650
+ tasks.append(self._http_requests.delete(self._documents_url))
2651
+
2652
+ responses = await asyncio.gather(*tasks)
2653
+ result = TaskInfo(**responses[-1].json())
2654
+ if self._post_delete_all_documents_plugins:
2655
+ post = await AsyncIndex._run_plugins(
2656
+ self._post_delete_all_documents_plugins, AsyncEvent.POST, result=result
2657
+ )
2658
+ if isinstance(post.get("generic_result"), TaskInfo):
2659
+ result = post["generic_result"]
2660
+ return result
2661
+
2662
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
2663
+ for plugin in self._concurrent_delete_all_documents_plugins:
2664
+ tg.create_task(plugin.run_plugin(event=AsyncEvent.CONCURRENT))
2665
+
2666
+ response_coroutine = tg.create_task(self._http_requests.delete(self._documents_url))
2667
+
2668
+ response = await response_coroutine
2669
+ result = TaskInfo(**response.json())
2670
+ if self._post_delete_all_documents_plugins:
2671
+ post = await AsyncIndex._run_plugins(
2672
+ self._post_delete_all_documents_plugins, AsyncEvent.POST, result=result
2673
+ )
2674
+ if isinstance(post.get("generic_result"), TaskInfo):
2675
+ result = post["generic_result"]
2676
+ return result
2677
+
2678
+ response = await self._http_requests.delete(self._documents_url)
2679
+ result = TaskInfo(**response.json())
2680
+ if self._post_delete_all_documents_plugins:
2681
+ post = await AsyncIndex._run_plugins(
2682
+ self._post_delete_all_documents_plugins, AsyncEvent.POST, result=result
2683
+ )
2684
+ if isinstance(post.get("generic_result"), TaskInfo):
2685
+ result = post["generic_result"]
2686
+ return result
1662
2687
 
1663
2688
  async def get_settings(self) -> MeilisearchSettings:
1664
2689
  """Get settings of the index.
@@ -2858,8 +3883,76 @@ class AsyncIndex(BaseIndex):
2858
3883
 
2859
3884
  return TaskInfo(**response.json())
2860
3885
 
3886
+ @staticmethod
3887
+ async def _run_plugins(
3888
+ plugins: Sequence[AsyncPlugin | AsyncDocumentPlugin | AsyncPostSearchPlugin],
3889
+ event: AsyncEvent,
3890
+ **kwargs: Any,
3891
+ ) -> dict[str, Any]:
3892
+ generic_plugins = []
3893
+ document_plugins = []
3894
+ search_plugins = []
3895
+ results: dict[str, Any] = {
3896
+ "generic_result": None,
3897
+ "document_result": None,
3898
+ "search_result": None,
3899
+ }
3900
+ if not use_task_groups():
3901
+ for plugin in plugins:
3902
+ if _plugin_has_method(plugin, "run_plugin"):
3903
+ generic_plugins.append(plugin.run_plugin(event=event, **kwargs)) # type: ignore[union-attr]
3904
+ if _plugin_has_method(plugin, "run_document_plugin"):
3905
+ document_plugins.append(plugin.run_document_plugin(event=event, **kwargs)) # type: ignore[union-attr]
3906
+ if _plugin_has_method(plugin, "run_post_search_plugin"):
3907
+ search_plugins.append(plugin.run_post_search_plugin(event=event, **kwargs)) # type: ignore[union-attr]
3908
+ if generic_plugins:
3909
+ generic_results = await asyncio.gather(*generic_plugins)
3910
+ if generic_results:
3911
+ results["generic_result"] = generic_results[-1]
3912
+
3913
+ if document_plugins:
3914
+ document_results = await asyncio.gather(*document_plugins)
3915
+ if document_results:
3916
+ results["document_result"] = document_results[-1]
3917
+ if search_plugins:
3918
+ search_results = await asyncio.gather(*search_plugins)
3919
+ if search_results:
3920
+ results["search_result"] = search_results[-1]
3921
+
3922
+ return results
2861
3923
 
2862
- class Index(BaseIndex):
3924
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
3925
+ generic_tasks = []
3926
+ document_tasks = []
3927
+ search_tasks = []
3928
+ for plugin in plugins:
3929
+ if _plugin_has_method(plugin, "run_plugin"):
3930
+ generic_tasks.append(tg.create_task(plugin.run_plugin(event=event, **kwargs))) # type: ignore[union-attr]
3931
+ if _plugin_has_method(plugin, "run_document_plugin"):
3932
+ document_tasks.append(
3933
+ tg.create_task(plugin.run_document_plugin(event=event, **kwargs)) # type: ignore[union-attr]
3934
+ )
3935
+ if _plugin_has_method(plugin, "run_post_search_plugin"):
3936
+ search_tasks.append(
3937
+ tg.create_task(plugin.run_post_search_plugin(event=event, **kwargs)) # type: ignore[union-attr]
3938
+ )
3939
+
3940
+ if generic_tasks:
3941
+ for result in reversed(generic_tasks):
3942
+ if result:
3943
+ results["generic_result"] = await result
3944
+ break
3945
+
3946
+ if document_tasks:
3947
+ results["document_result"] = await document_tasks[-1]
3948
+
3949
+ if search_tasks:
3950
+ results["search_result"] = await search_tasks[-1]
3951
+
3952
+ return results
3953
+
3954
+
3955
+ class Index(_BaseIndex):
2863
3956
  """Index class gives access to all indexes routes and child routes.
2864
3957
 
2865
3958
  https://docs.meilisearch.com/reference/api/indexes.html
@@ -2872,6 +3965,7 @@ class Index(BaseIndex):
2872
3965
  primary_key: str | None = None,
2873
3966
  created_at: str | datetime | None = None,
2874
3967
  updated_at: str | datetime | None = None,
3968
+ plugins: IndexPlugins | None = None,
2875
3969
  ):
2876
3970
  """Class initializer.
2877
3971
 
@@ -2883,10 +3977,252 @@ class Index(BaseIndex):
2883
3977
  primary_key: The primary key of the documents. Defaults to None.
2884
3978
  created_at: The date and time the index was created. Defaults to None.
2885
3979
  updated_at: The date and time the index was last updated. Defaults to None.
3980
+ plugins: Optional plugins can be provided to extend functionality.
2886
3981
  """
2887
3982
  super().__init__(uid, primary_key, created_at, updated_at)
2888
3983
  self.http_client = http_client
2889
3984
  self._http_requests = HttpRequests(http_client)
3985
+ self.plugins = plugins
3986
+
3987
+ @cached_property
3988
+ def _post_add_documents_plugins(self) -> list[Plugin | DocumentPlugin] | None:
3989
+ if not self.plugins or not self.plugins.add_documents_plugins:
3990
+ return None
3991
+
3992
+ plugins = []
3993
+ for plugin in self.plugins.add_documents_plugins:
3994
+ if plugin.POST_EVENT:
3995
+ plugins.append(plugin)
3996
+
3997
+ if not plugins:
3998
+ return None
3999
+
4000
+ return plugins
4001
+
4002
+ @cached_property
4003
+ def _pre_add_documents_plugins(self) -> list[Plugin | DocumentPlugin] | None:
4004
+ if not self.plugins or not self.plugins.add_documents_plugins:
4005
+ return None
4006
+
4007
+ plugins = []
4008
+ for plugin in self.plugins.add_documents_plugins:
4009
+ if plugin.PRE_EVENT:
4010
+ plugins.append(plugin)
4011
+
4012
+ if not plugins:
4013
+ return None
4014
+
4015
+ return plugins
4016
+
4017
+ @cached_property
4018
+ def _post_delete_all_documents_plugins(self) -> list[Plugin] | None:
4019
+ if not self.plugins or not self.plugins.delete_all_documents_plugins:
4020
+ return None
4021
+
4022
+ plugins = []
4023
+ for plugin in self.plugins.delete_all_documents_plugins:
4024
+ if plugin.POST_EVENT:
4025
+ plugins.append(plugin)
4026
+
4027
+ if not plugins:
4028
+ return None
4029
+
4030
+ return plugins
4031
+
4032
+ @cached_property
4033
+ def _pre_delete_all_documents_plugins(self) -> list[Plugin] | None:
4034
+ if not self.plugins or not self.plugins.delete_all_documents_plugins:
4035
+ return None
4036
+
4037
+ plugins = []
4038
+ for plugin in self.plugins.delete_all_documents_plugins:
4039
+ if plugin.PRE_EVENT:
4040
+ plugins.append(plugin)
4041
+
4042
+ if not plugins:
4043
+ return None
4044
+
4045
+ return plugins
4046
+
4047
+ @cached_property
4048
+ def _post_delete_document_plugins(self) -> list[Plugin] | None:
4049
+ if not self.plugins or not self.plugins.delete_document_plugins:
4050
+ return None
4051
+
4052
+ plugins = []
4053
+ for plugin in self.plugins.delete_document_plugins:
4054
+ if plugin.POST_EVENT:
4055
+ plugins.append(plugin)
4056
+
4057
+ if not plugins:
4058
+ return None
4059
+
4060
+ return plugins
4061
+
4062
+ @cached_property
4063
+ def _pre_delete_document_plugins(self) -> list[Plugin] | None:
4064
+ if not self.plugins or not self.plugins.delete_document_plugins:
4065
+ return None
4066
+
4067
+ plugins = []
4068
+ for plugin in self.plugins.delete_document_plugins:
4069
+ if plugin.PRE_EVENT:
4070
+ plugins.append(plugin)
4071
+
4072
+ if not plugins:
4073
+ return None
4074
+
4075
+ return plugins
4076
+
4077
+ @cached_property
4078
+ def _post_delete_documents_plugins(self) -> list[Plugin] | None:
4079
+ if not self.plugins or not self.plugins.delete_documents_plugins:
4080
+ return None
4081
+
4082
+ plugins = []
4083
+ for plugin in self.plugins.delete_documents_plugins:
4084
+ if plugin.POST_EVENT:
4085
+ plugins.append(plugin)
4086
+
4087
+ if not plugins:
4088
+ return None
4089
+
4090
+ return plugins
4091
+
4092
+ @cached_property
4093
+ def _pre_delete_documents_plugins(self) -> list[Plugin] | None:
4094
+ if not self.plugins or not self.plugins.delete_documents_plugins:
4095
+ return None
4096
+
4097
+ plugins = []
4098
+ for plugin in self.plugins.delete_documents_plugins:
4099
+ if plugin.PRE_EVENT:
4100
+ plugins.append(plugin)
4101
+
4102
+ if not plugins:
4103
+ return None
4104
+
4105
+ return plugins
4106
+
4107
+ @cached_property
4108
+ def _post_delete_documents_by_filter_plugins(self) -> list[Plugin] | None:
4109
+ if not self.plugins or not self.plugins.delete_documents_by_filter_plugins:
4110
+ return None
4111
+
4112
+ plugins = []
4113
+ for plugin in self.plugins.delete_documents_by_filter_plugins:
4114
+ if plugin.POST_EVENT:
4115
+ plugins.append(plugin)
4116
+
4117
+ if not plugins:
4118
+ return None
4119
+
4120
+ return plugins
4121
+
4122
+ @cached_property
4123
+ def _pre_delete_documents_by_filter_plugins(self) -> list[Plugin] | None:
4124
+ if not self.plugins or not self.plugins.delete_documents_by_filter_plugins:
4125
+ return None
4126
+
4127
+ plugins = []
4128
+ for plugin in self.plugins.delete_documents_by_filter_plugins:
4129
+ if plugin.PRE_EVENT:
4130
+ plugins.append(plugin)
4131
+
4132
+ if not plugins:
4133
+ return None
4134
+
4135
+ return plugins
4136
+
4137
+ @cached_property
4138
+ def _post_facet_search_plugins(self) -> list[Plugin] | None:
4139
+ if not self.plugins or not self.plugins.facet_search_plugins:
4140
+ return None
4141
+
4142
+ plugins = []
4143
+ for plugin in self.plugins.facet_search_plugins:
4144
+ if plugin.POST_EVENT:
4145
+ plugins.append(plugin)
4146
+
4147
+ if not plugins:
4148
+ return None
4149
+
4150
+ return plugins
4151
+
4152
+ @cached_property
4153
+ def _pre_facet_search_plugins(self) -> list[Plugin] | None:
4154
+ if not self.plugins or not self.plugins.facet_search_plugins:
4155
+ return None
4156
+
4157
+ plugins = []
4158
+ for plugin in self.plugins.facet_search_plugins:
4159
+ if plugin.PRE_EVENT:
4160
+ plugins.append(plugin)
4161
+
4162
+ if not plugins:
4163
+ return None
4164
+
4165
+ return plugins
4166
+
4167
+ @cached_property
4168
+ def _post_search_plugins(self) -> list[Plugin | PostSearchPlugin] | None:
4169
+ if not self.plugins or not self.plugins.search_plugins:
4170
+ return None
4171
+
4172
+ plugins = []
4173
+ for plugin in self.plugins.search_plugins:
4174
+ if plugin.POST_EVENT:
4175
+ plugins.append(plugin)
4176
+
4177
+ if not plugins:
4178
+ return None
4179
+
4180
+ return plugins
4181
+
4182
+ @cached_property
4183
+ def _pre_search_plugins(self) -> list[Plugin | PostSearchPlugin] | None:
4184
+ if not self.plugins or not self.plugins.search_plugins:
4185
+ return None
4186
+
4187
+ plugins = []
4188
+ for plugin in self.plugins.search_plugins:
4189
+ if plugin.PRE_EVENT:
4190
+ plugins.append(plugin)
4191
+
4192
+ if not plugins:
4193
+ return None
4194
+
4195
+ return plugins
4196
+
4197
+ @cached_property
4198
+ def _post_update_documents_plugins(self) -> list[Plugin | DocumentPlugin] | None:
4199
+ if not self.plugins or not self.plugins.update_documents_plugins:
4200
+ return None
4201
+
4202
+ plugins = []
4203
+ for plugin in self.plugins.update_documents_plugins:
4204
+ if plugin.POST_EVENT:
4205
+ plugins.append(plugin)
4206
+
4207
+ if not plugins:
4208
+ return None
4209
+
4210
+ return plugins
4211
+
4212
+ @cached_property
4213
+ def _pre_update_documents_plugins(self) -> list[Plugin | DocumentPlugin] | None:
4214
+ if not self.plugins or not self.plugins.update_documents_plugins:
4215
+ return None
4216
+
4217
+ plugins = []
4218
+ for plugin in self.plugins.update_documents_plugins:
4219
+ if plugin.PRE_EVENT:
4220
+ plugins.append(plugin)
4221
+
4222
+ if not plugins:
4223
+ return None
4224
+
4225
+ return plugins
2890
4226
 
2891
4227
  def delete(self) -> TaskInfo:
2892
4228
  """Deletes the index.
@@ -3015,7 +4351,15 @@ class Index(BaseIndex):
3015
4351
  return info.primary_key
3016
4352
 
3017
4353
  @classmethod
3018
- def create(cls, http_client: Client, uid: str, primary_key: str | None = None) -> Index:
4354
+ def create(
4355
+ cls,
4356
+ http_client: Client,
4357
+ uid: str,
4358
+ primary_key: str | None = None,
4359
+ *,
4360
+ settings: MeilisearchSettings | None = None,
4361
+ plugins: IndexPlugins | None = None,
4362
+ ) -> Index:
3019
4363
  """Creates a new index.
3020
4364
 
3021
4365
  In general this method should not be used directly and instead the index should be created
@@ -3024,9 +4368,15 @@ class Index(BaseIndex):
3024
4368
  Args:
3025
4369
 
3026
4370
  http_client: An instance of the Client. This automatically gets passed by the Client
3027
- when creating and Index instance.
4371
+ when creating an Index instance.
3028
4372
  uid: The index's unique identifier.
3029
4373
  primary_key: The primary key of the documents. Defaults to None.
4374
+ settings: Settings for the index. The settings can also be updated independently of
4375
+ creating the index. The advantage to updating them here is updating the settings after
4376
+ adding documents will cause the documents to be re-indexed. Because of this it will be
4377
+ faster to update them before adding documents. Defaults to None (i.e. default
4378
+ Meilisearch index settings).
4379
+ plugins: Optional plugins can be provided to extend functionality.
3030
4380
 
3031
4381
  Returns:
3032
4382
 
@@ -3054,14 +4404,21 @@ class Index(BaseIndex):
3054
4404
  wait_for_task(http_client, response.json()["taskUid"], timeout_in_ms=100000)
3055
4405
  index_response = http_request.get(f"{url}/{uid}")
3056
4406
  index_dict = index_response.json()
3057
- return cls(
4407
+ index = cls(
3058
4408
  http_client=http_client,
3059
4409
  uid=index_dict["uid"],
3060
4410
  primary_key=index_dict["primaryKey"],
3061
4411
  created_at=index_dict["createdAt"],
3062
4412
  updated_at=index_dict["updatedAt"],
4413
+ plugins=plugins,
3063
4414
  )
3064
4415
 
4416
+ if settings:
4417
+ settings_task = index.update_settings(settings)
4418
+ wait_for_task(http_client, settings_task.task_uid, timeout_in_ms=10000)
4419
+
4420
+ return index
4421
+
3065
4422
  def get_stats(self) -> IndexStats:
3066
4423
  """Get stats of the index.
3067
4424
 
@@ -3194,9 +4551,41 @@ class Index(BaseIndex):
3194
4551
  vector=vector,
3195
4552
  )
3196
4553
 
4554
+ if self._pre_search_plugins:
4555
+ Index._run_plugins(
4556
+ self._pre_search_plugins,
4557
+ Event.PRE,
4558
+ query=query,
4559
+ offset=offset,
4560
+ limit=limit,
4561
+ filter=filter,
4562
+ facets=facets,
4563
+ attributes_to_retrieve=attributes_to_retrieve,
4564
+ attributes_to_crop=attributes_to_crop,
4565
+ crop_length=crop_length,
4566
+ attributes_to_highlight=attributes_to_highlight,
4567
+ sort=sort,
4568
+ show_matches_position=show_matches_position,
4569
+ highlight_pre_tag=highlight_pre_tag,
4570
+ highlight_post_tag=highlight_post_tag,
4571
+ crop_marker=crop_marker,
4572
+ matching_strategy=matching_strategy,
4573
+ hits_per_page=hits_per_page,
4574
+ page=page,
4575
+ attributes_to_search_on=attributes_to_search_on,
4576
+ show_ranking_score=show_ranking_score,
4577
+ show_ranking_score_details=show_ranking_score_details,
4578
+ vector=vector,
4579
+ )
4580
+
3197
4581
  response = self._http_requests.post(f"{self._base_url_with_uid}/search", body=body)
4582
+ result = SearchResults(**response.json())
4583
+ if self._post_search_plugins:
4584
+ post = Index._run_plugins(self._post_search_plugins, Event.POST, search_results=result)
4585
+ if post.get("search_result"):
4586
+ result = post["search_result"]
3198
4587
 
3199
- return SearchResults(**response.json())
4588
+ return result
3200
4589
 
3201
4590
  def facet_search(
3202
4591
  self,
@@ -3317,9 +4706,41 @@ class Index(BaseIndex):
3317
4706
  vector=vector,
3318
4707
  )
3319
4708
 
4709
+ if self._pre_facet_search_plugins:
4710
+ Index._run_plugins(
4711
+ self._pre_facet_search_plugins,
4712
+ Event.PRE,
4713
+ query=query,
4714
+ offset=offset,
4715
+ limit=limit,
4716
+ filter=filter,
4717
+ facets=facets,
4718
+ attributes_to_retrieve=attributes_to_retrieve,
4719
+ attributes_to_crop=attributes_to_crop,
4720
+ crop_length=crop_length,
4721
+ attributes_to_highlight=attributes_to_highlight,
4722
+ sort=sort,
4723
+ show_matches_position=show_matches_position,
4724
+ highlight_pre_tag=highlight_pre_tag,
4725
+ highlight_post_tag=highlight_post_tag,
4726
+ crop_marker=crop_marker,
4727
+ matching_strategy=matching_strategy,
4728
+ hits_per_page=hits_per_page,
4729
+ page=page,
4730
+ attributes_to_search_on=attributes_to_search_on,
4731
+ show_ranking_score=show_ranking_score,
4732
+ show_ranking_score_details=show_ranking_score_details,
4733
+ vector=vector,
4734
+ )
4735
+
3320
4736
  response = self._http_requests.post(f"{self._base_url_with_uid}/facet-search", body=body)
4737
+ result = FacetSearchResults(**response.json())
4738
+ if self._post_facet_search_plugins:
4739
+ post = Index._run_plugins(self._post_facet_search_plugins, Event.POST, result=result)
4740
+ if isinstance(post["generic_result"], FacetSearchResults):
4741
+ result = post["generic_result"]
3321
4742
 
3322
- return FacetSearchResults(**response.json())
4743
+ return result
3323
4744
 
3324
4745
  def get_document(self, document_id: str) -> JsonDict:
3325
4746
  """Get one document with given document identifier.
@@ -3442,9 +4863,24 @@ class Index(BaseIndex):
3442
4863
  else:
3443
4864
  url = self._documents_url
3444
4865
 
4866
+ if self._pre_add_documents_plugins:
4867
+ pre = Index._run_plugins(
4868
+ self._pre_add_documents_plugins,
4869
+ Event.PRE,
4870
+ documents=documents,
4871
+ primary_key=primary_key,
4872
+ )
4873
+ if pre.get("document_result"):
4874
+ documents = pre["document_result"]
4875
+
3445
4876
  response = self._http_requests.post(url, documents)
4877
+ result = TaskInfo(**response.json())
4878
+ if self._post_add_documents_plugins:
4879
+ post = Index._run_plugins(self._post_add_documents_plugins, Event.POST, result=result)
4880
+ if isinstance(post.get("generic_result"), TaskInfo):
4881
+ result = post["generic_result"]
3446
4882
 
3447
- return TaskInfo(**response.json())
4883
+ return result
3448
4884
 
3449
4885
  def add_documents_in_batches(
3450
4886
  self,
@@ -3831,9 +5267,26 @@ class Index(BaseIndex):
3831
5267
  else:
3832
5268
  url = self._documents_url
3833
5269
 
5270
+ if self._pre_update_documents_plugins:
5271
+ pre = Index._run_plugins(
5272
+ self._pre_update_documents_plugins,
5273
+ Event.PRE,
5274
+ documents=documents,
5275
+ primary_key=primary_key,
5276
+ )
5277
+ if pre.get("document_result"):
5278
+ documents = pre["document_result"]
5279
+
3834
5280
  response = self._http_requests.put(url, documents)
5281
+ result = TaskInfo(**response.json())
5282
+ if self._post_update_documents_plugins:
5283
+ post = Index._run_plugins(
5284
+ self._post_update_documents_plugins, Event.POST, result=result
5285
+ )
5286
+ if isinstance(post.get("generic_result"), TaskInfo):
5287
+ result = post["generic_result"]
3835
5288
 
3836
- return TaskInfo(**response.json())
5289
+ return result
3837
5290
 
3838
5291
  def update_documents_in_batches(
3839
5292
  self,
@@ -4202,9 +5655,19 @@ class Index(BaseIndex):
4202
5655
  >>> index = client.index("movies")
4203
5656
  >>> index.delete_document("1234")
4204
5657
  """
5658
+ if self._pre_delete_document_plugins:
5659
+ Index._run_plugins(
5660
+ self._pre_delete_document_plugins, Event.PRE, document_id=document_id
5661
+ )
5662
+
4205
5663
  response = self._http_requests.delete(f"{self._documents_url}/{document_id}")
5664
+ result = TaskInfo(**response.json())
5665
+ if self._post_delete_document_plugins:
5666
+ post = Index._run_plugins(self._post_delete_document_plugins, Event.POST, result=result)
5667
+ if isinstance(post.get("generic_result"), TaskInfo):
5668
+ result = post["generic_result"]
4206
5669
 
4207
- return TaskInfo(**response.json())
5670
+ return result
4208
5671
 
4209
5672
  def delete_documents(self, ids: list[str]) -> TaskInfo:
4210
5673
  """Delete multiple documents from the index.
@@ -4229,9 +5692,19 @@ class Index(BaseIndex):
4229
5692
  >>> index = client.index("movies")
4230
5693
  >>> index.delete_documents(["1234", "5678"])
4231
5694
  """
5695
+ if self._pre_delete_documents_plugins:
5696
+ Index._run_plugins(self._pre_delete_documents_plugins, Event.PRE, ids=ids)
5697
+
4232
5698
  response = self._http_requests.post(f"{self._documents_url}/delete-batch", ids)
5699
+ result = TaskInfo(**response.json())
5700
+ if self._post_delete_documents_plugins:
5701
+ post = Index._run_plugins(
5702
+ self._post_delete_documents_plugins, Event.POST, result=result
5703
+ )
5704
+ if isinstance(post.get("generic_result"), TaskInfo):
5705
+ result = post["generic_result"]
4233
5706
 
4234
- return TaskInfo(**response.json())
5707
+ return result
4235
5708
 
4236
5709
  def delete_documents_by_filter(self, filter: Filter) -> TaskInfo:
4237
5710
  """Delete documents from the index by filter.
@@ -4256,11 +5729,23 @@ class Index(BaseIndex):
4256
5729
  >>> index = client.index("movies")
4257
5730
  >>> index.delete_documents_by_filter("genre=horor"))
4258
5731
  """
5732
+ if self._pre_delete_documents_by_filter_plugins:
5733
+ Index._run_plugins(
5734
+ self._pre_delete_documents_by_filter_plugins, Event.PRE, filter=filter
5735
+ )
5736
+
4259
5737
  response = self._http_requests.post(
4260
5738
  f"{self._documents_url}/delete", body={"filter": filter}
4261
5739
  )
5740
+ result = TaskInfo(**response.json())
5741
+ if self._post_delete_documents_by_filter_plugins:
5742
+ post = Index._run_plugins(
5743
+ self._post_delete_documents_by_filter_plugins, Event.POST, result=result
5744
+ )
5745
+ if isinstance(post.get("generic_result"), TaskInfo):
5746
+ result = post["generic_result"]
4262
5747
 
4263
- return TaskInfo(**response.json())
5748
+ return result
4264
5749
 
4265
5750
  def delete_documents_in_batches_by_filter(
4266
5751
  self, filters: list[str | list[str | list[str]]]
@@ -4313,9 +5798,19 @@ class Index(BaseIndex):
4313
5798
  >>> index = client.index("movies")
4314
5799
  >>> index.delete_all_document()
4315
5800
  """
5801
+ if self._pre_delete_all_documents_plugins:
5802
+ Index._run_plugins(self._pre_delete_all_documents_plugins, Event.PRE)
5803
+
4316
5804
  response = self._http_requests.delete(self._documents_url)
5805
+ result = TaskInfo(**response.json())
5806
+ if self._post_delete_all_documents_plugins:
5807
+ post = Index._run_plugins(
5808
+ self._post_delete_all_documents_plugins, Event.POST, result=result
5809
+ )
5810
+ if isinstance(post.get("generic_result"), TaskInfo):
5811
+ result = post["generic_result"]
4317
5812
 
4318
- return TaskInfo(**response.json())
5813
+ return result
4319
5814
 
4320
5815
  def get_settings(self) -> MeilisearchSettings:
4321
5816
  """Get settings of the index.
@@ -5509,6 +7004,46 @@ class Index(BaseIndex):
5509
7004
 
5510
7005
  return TaskInfo(**response.json())
5511
7006
 
7007
+ @staticmethod
7008
+ def _run_plugins(
7009
+ plugins: Sequence[Plugin | DocumentPlugin | PostSearchPlugin],
7010
+ event: Event,
7011
+ **kwargs: Any,
7012
+ ) -> dict[str, Any]:
7013
+ results: dict[str, Any] = {
7014
+ "generic_result": None,
7015
+ "document_result": None,
7016
+ "search_result": None,
7017
+ }
7018
+ generic_tasks = []
7019
+ document_tasks = []
7020
+ search_tasks = []
7021
+ for plugin in plugins:
7022
+ if _plugin_has_method(plugin, "run_plugin"):
7023
+ generic_tasks.append(plugin.run_plugin(event=event, **kwargs)) # type: ignore[union-attr]
7024
+ if _plugin_has_method(plugin, "run_document_plugin"):
7025
+ document_tasks.append(
7026
+ plugin.run_document_plugin(event=event, **kwargs) # type: ignore[union-attr]
7027
+ )
7028
+ if _plugin_has_method(plugin, "run_post_search_plugin"):
7029
+ search_tasks.append(
7030
+ plugin.run_post_search_plugin(event=event, **kwargs) # type: ignore[union-attr]
7031
+ )
7032
+
7033
+ if generic_tasks:
7034
+ for result in reversed(generic_tasks):
7035
+ if result:
7036
+ results["generic_result"] = result
7037
+ break
7038
+
7039
+ if document_tasks:
7040
+ results["document_result"] = document_tasks[-1]
7041
+
7042
+ if search_tasks:
7043
+ results["search_result"] = search_tasks[-1]
7044
+
7045
+ return results
7046
+
5512
7047
 
5513
7048
  async def _async_load_documents_from_file(
5514
7049
  file_path: Path | str,
@@ -5552,8 +7087,8 @@ async def _async_load_documents_from_file(
5552
7087
 
5553
7088
 
5554
7089
  def _batch(
5555
- documents: Sequence[Mapping], batch_size: int
5556
- ) -> Generator[Sequence[Mapping], None, None]:
7090
+ documents: Sequence[MutableMapping], batch_size: int
7091
+ ) -> Generator[Sequence[MutableMapping], None, None]:
5557
7092
  total_len = len(documents)
5558
7093
  for i in range(0, total_len, batch_size):
5559
7094
  yield documents[i : i + batch_size]
@@ -5563,6 +7098,22 @@ def _combine_documents(documents: list[list[Any]]) -> list[Any]:
5563
7098
  return [x for y in documents for x in y]
5564
7099
 
5565
7100
 
7101
+ def _plugin_has_method(
7102
+ plugin: AsyncPlugin
7103
+ | AsyncDocumentPlugin
7104
+ | AsyncPostSearchPlugin
7105
+ | Plugin
7106
+ | DocumentPlugin
7107
+ | PostSearchPlugin,
7108
+ method: str,
7109
+ ) -> bool:
7110
+ check = getattr(plugin, method, None)
7111
+ if callable(check):
7112
+ return True
7113
+
7114
+ return False
7115
+
7116
+
5566
7117
  def _load_documents_from_file(
5567
7118
  file_path: Path | str,
5568
7119
  csv_delimiter: str | None = None,