coredis 5.5.0__cp313-cp313-macosx_11_0_arm64.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.
Files changed (100) hide show
  1. 22fe76227e35f92ab5c3__mypyc.cpython-313-darwin.so +0 -0
  2. coredis/__init__.py +42 -0
  3. coredis/_enum.py +42 -0
  4. coredis/_json.py +11 -0
  5. coredis/_packer.cpython-313-darwin.so +0 -0
  6. coredis/_packer.py +71 -0
  7. coredis/_protocols.py +50 -0
  8. coredis/_py_311_typing.py +20 -0
  9. coredis/_py_312_typing.py +17 -0
  10. coredis/_sidecar.py +114 -0
  11. coredis/_utils.cpython-313-darwin.so +0 -0
  12. coredis/_utils.py +440 -0
  13. coredis/_version.py +34 -0
  14. coredis/_version.pyi +1 -0
  15. coredis/cache.py +801 -0
  16. coredis/client/__init__.py +6 -0
  17. coredis/client/basic.py +1240 -0
  18. coredis/client/cluster.py +1265 -0
  19. coredis/commands/__init__.py +64 -0
  20. coredis/commands/_key_spec.py +517 -0
  21. coredis/commands/_utils.py +108 -0
  22. coredis/commands/_validators.py +159 -0
  23. coredis/commands/_wrappers.py +175 -0
  24. coredis/commands/bitfield.py +110 -0
  25. coredis/commands/constants.py +662 -0
  26. coredis/commands/core.py +8484 -0
  27. coredis/commands/function.py +408 -0
  28. coredis/commands/monitor.py +168 -0
  29. coredis/commands/pubsub.py +905 -0
  30. coredis/commands/request.py +108 -0
  31. coredis/commands/script.py +296 -0
  32. coredis/commands/sentinel.py +246 -0
  33. coredis/config.py +50 -0
  34. coredis/connection.py +906 -0
  35. coredis/constants.cpython-313-darwin.so +0 -0
  36. coredis/constants.py +37 -0
  37. coredis/credentials.py +45 -0
  38. coredis/exceptions.py +360 -0
  39. coredis/experimental/__init__.py +1 -0
  40. coredis/globals.py +23 -0
  41. coredis/modules/__init__.py +121 -0
  42. coredis/modules/autocomplete.py +138 -0
  43. coredis/modules/base.py +262 -0
  44. coredis/modules/filters.py +1319 -0
  45. coredis/modules/graph.py +362 -0
  46. coredis/modules/json.py +691 -0
  47. coredis/modules/response/__init__.py +0 -0
  48. coredis/modules/response/_callbacks/__init__.py +0 -0
  49. coredis/modules/response/_callbacks/autocomplete.py +42 -0
  50. coredis/modules/response/_callbacks/graph.py +237 -0
  51. coredis/modules/response/_callbacks/json.py +21 -0
  52. coredis/modules/response/_callbacks/search.py +221 -0
  53. coredis/modules/response/_callbacks/timeseries.py +158 -0
  54. coredis/modules/response/types.py +179 -0
  55. coredis/modules/search.py +1089 -0
  56. coredis/modules/timeseries.py +1139 -0
  57. coredis/parser.cpython-313-darwin.so +0 -0
  58. coredis/parser.py +344 -0
  59. coredis/pipeline.py +1225 -0
  60. coredis/pool/__init__.py +11 -0
  61. coredis/pool/basic.py +453 -0
  62. coredis/pool/cluster.py +517 -0
  63. coredis/pool/nodemanager.py +340 -0
  64. coredis/py.typed +0 -0
  65. coredis/recipes/__init__.py +0 -0
  66. coredis/recipes/credentials/__init__.py +5 -0
  67. coredis/recipes/credentials/iam_provider.py +63 -0
  68. coredis/recipes/locks/__init__.py +5 -0
  69. coredis/recipes/locks/extend.lua +17 -0
  70. coredis/recipes/locks/lua_lock.py +281 -0
  71. coredis/recipes/locks/release.lua +10 -0
  72. coredis/response/__init__.py +5 -0
  73. coredis/response/_callbacks/__init__.py +538 -0
  74. coredis/response/_callbacks/acl.py +32 -0
  75. coredis/response/_callbacks/cluster.py +183 -0
  76. coredis/response/_callbacks/command.py +86 -0
  77. coredis/response/_callbacks/connection.py +31 -0
  78. coredis/response/_callbacks/geo.py +58 -0
  79. coredis/response/_callbacks/hash.py +85 -0
  80. coredis/response/_callbacks/keys.py +59 -0
  81. coredis/response/_callbacks/module.py +33 -0
  82. coredis/response/_callbacks/script.py +85 -0
  83. coredis/response/_callbacks/sentinel.py +179 -0
  84. coredis/response/_callbacks/server.py +241 -0
  85. coredis/response/_callbacks/sets.py +44 -0
  86. coredis/response/_callbacks/sorted_set.py +204 -0
  87. coredis/response/_callbacks/streams.py +185 -0
  88. coredis/response/_callbacks/strings.py +70 -0
  89. coredis/response/_callbacks/vector_sets.py +159 -0
  90. coredis/response/_utils.py +33 -0
  91. coredis/response/types.py +416 -0
  92. coredis/retry.py +233 -0
  93. coredis/sentinel.py +477 -0
  94. coredis/stream.py +369 -0
  95. coredis/tokens.py +2286 -0
  96. coredis/typing.py +593 -0
  97. coredis-5.5.0.dist-info/METADATA +211 -0
  98. coredis-5.5.0.dist-info/RECORD +100 -0
  99. coredis-5.5.0.dist-info/WHEEL +6 -0
  100. coredis-5.5.0.dist-info/licenses/LICENSE +23 -0
@@ -0,0 +1,1089 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import itertools
5
+ from collections import OrderedDict
6
+ from datetime import timedelta
7
+
8
+ from deprecated.sphinx import versionadded
9
+
10
+ from ..commands._utils import normalized_milliseconds, normalized_seconds
11
+ from ..commands._wrappers import ClusterCommandConfig
12
+ from ..commands.constants import CommandGroup, CommandName, NodeFlag
13
+ from ..commands.request import CommandRequest
14
+ from ..response._callbacks import (
15
+ AnyStrCallback,
16
+ ClusterEnsureConsistent,
17
+ ClusterMergeSets,
18
+ DictCallback,
19
+ IntCallback,
20
+ SetCallback,
21
+ SimpleStringCallback,
22
+ )
23
+ from ..tokens import PrefixToken, PureToken
24
+ from ..typing import (
25
+ AnyStr,
26
+ CommandArgList,
27
+ KeyT,
28
+ Literal,
29
+ Mapping,
30
+ Parameters,
31
+ ResponsePrimitive,
32
+ ResponseType,
33
+ StringT,
34
+ ValueT,
35
+ )
36
+ from .base import Module, ModuleGroup, module_command
37
+ from .response._callbacks.search import (
38
+ AggregationResultCallback,
39
+ SearchConfigCallback,
40
+ SearchResultCallback,
41
+ SpellCheckCallback,
42
+ )
43
+ from .response.types import SearchAggregationResult, SearchResult
44
+
45
+
46
+ class RediSearch(Module[AnyStr]):
47
+ NAME = "search"
48
+ FULL_NAME = "RediSearch"
49
+ DESCRIPTION = """RedisSearch is a Redis module that enables querying, secondary
50
+ indexing, and full-text search for Redis. These features enable multi-field queries,
51
+ aggregation, exact phrase matching, numeric filtering, geo filtering and vector
52
+ similarity semantic search on top of text queries."""
53
+ DOCUMENTATION_URL = "https://redis.io/docs/stack/search"
54
+
55
+
56
+ @dataclasses.dataclass
57
+ class Field:
58
+ """
59
+ Field definition to be used in :meth:`~coredis.modules.Search.create` &
60
+ :meth:`~coredis.modules.Search.alter`
61
+
62
+ For more details refer to the documentation for
63
+ `FT.CREATE <https://redis.io/commands/ft.create/>`__
64
+ """
65
+
66
+ #: Name of the field. For hashes, this is a field name within the hash.
67
+ #: For JSON, this is a JSON Path expression.
68
+ name: StringT
69
+ #: Type of field
70
+ type: Literal[
71
+ PureToken.TEXT,
72
+ PureToken.TAG,
73
+ PureToken.NUMERIC,
74
+ PureToken.GEO,
75
+ PureToken.VECTOR,
76
+ ]
77
+
78
+ #: Defines the alias associated to :paramref:`name`.
79
+ #: For example, you can use this feature to alias a complex
80
+ #: JSONPath expression with more memorable (and easier to type) name.
81
+ alias: StringT | None = None
82
+ #: Whether to optimize for sorting.
83
+ sortable: bool | None = None
84
+ #: Whether to use the unnormalized form of the field for sorting.
85
+ unf: bool | None = None
86
+ #: Whether to disable stemming for this field.
87
+ nostem: bool | None = None
88
+ #: Skip indexing this field
89
+ noindex: bool | None = None
90
+ #: Phonetic algorithm to use for this field.
91
+ phonetic: StringT | None = None
92
+ #: Weight of this field in the document's ranking. The default is 1.0.
93
+ weight: int | float | None = None
94
+ #: Separator to use for splitting tags if the field is of
95
+ #: type :attr:`~coredis.PureToken.TAG`.
96
+ separator: StringT | None = None
97
+ #: For fields of type :attr:`~coredis.PureToken.TAG`,
98
+ #: keeps the original letter cases of the tags. If not specified,
99
+ #: the characters are converted to lowercase.
100
+ casesensitive: bool | None = None
101
+ #: For fields of type :attr:`~coredis.PureToken.TAG` &
102
+ #: :attr:`~coredis.PureToken.TEXT`, keeps a suffix trie with all
103
+ #: terms which match the suffix. It is used to optimize contains ``(foo)``
104
+ #: and suffix ``(*foo)`` queries. Otherwise, a brute-force search on the trie
105
+ #: is performed. If suffix trie exists for some fields, these queries will
106
+ #: be disabled for other fields
107
+ withsuffixtrie: bool | None = None
108
+ #: The algorithm to use for indexing if the field is of type
109
+ #: :attr:`~coredis.PureToken.VECTOR`.
110
+ #: For more details refer to the
111
+ #: `Vector similarity <https://redis.io/docs/stack/search/reference/vectors/>`__
112
+ #: section of the RediSearch documentation.
113
+ algorithm: Literal["FLAT", "HSNW"] | None = None
114
+ #: A dictionary of attributes to be used with the :paramref:`algorithm` specified.
115
+ #: For more details refer to the
116
+ #: `Creation attributes per algorithm <https://redis.io/docs/stack/search/reference/vectors/#creation-attributes-per-algorithm>`__
117
+ #: section of the RediSearch documentation.
118
+ attributes: dict[StringT, ValueT] | None = None
119
+
120
+ @property
121
+ def args(self) -> tuple[ValueT, ...]:
122
+ args: CommandArgList = [self.name]
123
+ if self.alias:
124
+ args += [PrefixToken.AS, self.alias]
125
+ args += [self.type]
126
+ if self.type == PureToken.VECTOR:
127
+ assert self.algorithm
128
+
129
+ args += [self.algorithm]
130
+ if self.attributes:
131
+ _attributes: list[ValueT] = list(itertools.chain(*self.attributes.items()))
132
+ args += [len(_attributes)]
133
+ args += _attributes
134
+
135
+ if self.sortable:
136
+ args += [PureToken.SORTABLE]
137
+ if self.unf:
138
+ args += [PureToken.UNF]
139
+ if self.nostem:
140
+ args += [b"NOSTEM"]
141
+ if self.noindex:
142
+ args += [PureToken.NOINDEX]
143
+ if self.phonetic:
144
+ args += [b"PHONETIC", self.phonetic]
145
+ if self.weight:
146
+ args += [b"WEIGHT", self.weight]
147
+ if self.separator:
148
+ args += [PrefixToken.SEPARATOR, self.separator]
149
+ if self.casesensitive:
150
+ args += [b"CASESENSITIVE"]
151
+ if self.withsuffixtrie:
152
+ args += [PureToken.WITHSUFFIXTRIE]
153
+ return tuple(args)
154
+
155
+
156
+ @dataclasses.dataclass
157
+ class Reduce:
158
+ """
159
+ Reduce definition to be used with :paramref:`~coredis.modules.Search.aggregate.transformations`
160
+ to define ``REDUCE`` steps in :meth:`~coredis.modules.Search.aggregate`
161
+
162
+ For more details refer to `GroupBy Reducers <https://redis.io/docs/stack/search/reference/aggregations/#groupby-reducers>`__
163
+ in the RediSearch documentation.
164
+ """
165
+
166
+ #: The name of the reducer function
167
+ function: StringT
168
+ #: The arguments to the reducer function
169
+ parameters: Parameters[ValueT] | None = None
170
+ #: The alias to assign to the result of the reducer function
171
+ alias: StringT | None = None
172
+
173
+ @property
174
+ def args(self) -> CommandArgList:
175
+ args: CommandArgList = [self.function]
176
+ if self.parameters:
177
+ args.extend(self.parameters)
178
+ if self.alias:
179
+ args.extend([PrefixToken.AS, self.alias])
180
+ return args
181
+
182
+
183
+ @dataclasses.dataclass
184
+ class Group:
185
+ """
186
+ Group definition to be used with :paramref:`~coredis.modules.Search.aggregate.transformations`
187
+ to specify ``GROUPBY`` steps in :meth:`~coredis.modules.Search.aggregate`
188
+
189
+ For more details refer to
190
+ `Aggregations <https://redis.io/docs/stack/search/reference/aggregations>`__
191
+ in the RediSearch documentation.
192
+ """
193
+
194
+ #: The field to group by
195
+ by: StringT | Parameters[StringT]
196
+ #: The reducers to apply to each group
197
+ reducers: Parameters[Reduce] | None = None
198
+
199
+ @property
200
+ def args(self) -> CommandArgList:
201
+ args: CommandArgList = [PrefixToken.GROUPBY]
202
+ if isinstance(self.by, (bytes, str)):
203
+ args.extend([1, self.by])
204
+ else:
205
+ bies: list[StringT] = list(self.by)
206
+ args.extend([len(bies), *bies])
207
+ for reducer in self.reducers or []:
208
+ args.append(PureToken.REDUCE)
209
+ args.extend(reducer.args)
210
+
211
+ return args
212
+
213
+
214
+ @dataclasses.dataclass
215
+ class Apply:
216
+ """
217
+ Apply definition to be used with :paramref:`~coredis.modules.Search.aggregate.transformations`
218
+ to specify ``APPLY`` steps in :meth:`~coredis.modules.Search.aggregate`
219
+
220
+ For more details refer to
221
+ `APPLY expressions <https://redis.io/docs/stack/search/reference/aggregations/#apply-expressions>`__
222
+ in the RediSearch documentation.
223
+ """
224
+
225
+ #: The expression to apply
226
+ function: StringT
227
+ #: The alias to assign to the result of the expression
228
+ alias: StringT
229
+
230
+ @property
231
+ def args(self) -> CommandArgList:
232
+ return [PrefixToken.APPLY, self.function, PrefixToken.AS, self.alias]
233
+
234
+
235
+ @dataclasses.dataclass
236
+ class Filter:
237
+ """
238
+ Filter definition to be used with :paramref:`~coredis.modules.Search.aggregate.transformations`
239
+ to specify ``FILTER`` steps in :meth:`~coredis.modules.Search.aggregate`
240
+
241
+ For more details refer to
242
+ `FILTER expressions <https://redis.io/docs/stack/search/reference/aggregations/#filter-expressions>`__
243
+ in the RediSearch documentation.
244
+ """
245
+
246
+ #: The filter expression
247
+ expression: StringT
248
+
249
+ @property
250
+ def args(self) -> CommandArgList:
251
+ return [PrefixToken.FILTER, self.expression]
252
+
253
+
254
+ @versionadded(version="4.12")
255
+ class Search(ModuleGroup[AnyStr]):
256
+ MODULE = RediSearch
257
+ COMMAND_GROUP = CommandGroup.SEARCH
258
+
259
+ @module_command(
260
+ CommandName.FT_CREATE,
261
+ module=MODULE,
262
+ version_introduced="1.0.0",
263
+ group=COMMAND_GROUP,
264
+ )
265
+ def create(
266
+ self,
267
+ index: KeyT,
268
+ schema: Parameters[Field],
269
+ *,
270
+ on: Literal[PureToken.HASH, PureToken.JSON] | None = None,
271
+ prefixes: Parameters[StringT] | None = None,
272
+ filter_expression: StringT | None = None,
273
+ language: StringT | None = None,
274
+ language_field: StringT | None = None,
275
+ score: int | float | None = None,
276
+ score_field: StringT | None = None,
277
+ payload_field: StringT | None = None,
278
+ maxtextfields: bool | None = None,
279
+ temporary: int | timedelta | None = None,
280
+ nooffsets: bool | None = None,
281
+ nohl: bool | None = None,
282
+ nofields: bool | None = None,
283
+ nofreqs: bool | None = None,
284
+ stopwords: Parameters[StringT] | None = None,
285
+ skipinitialscan: bool | None = None,
286
+ ) -> CommandRequest[bool]:
287
+ """
288
+ Creates an index with the given spec
289
+
290
+
291
+ :param index: The name of the index to create.
292
+ :param schema: A list of schema fields
293
+ :param on: The type of Redis key to index.
294
+ :param prefixes: A list of key prefixes to index.
295
+ :param filter_expression: A filter expression to apply to the index.
296
+ :param language: The default language to use for text fields.
297
+ :param language_field: The name of the field to use for language detection.
298
+ :param score: The default score to use for documents.
299
+ :param score_field: The name of the field to use for document scoring.
300
+ :param payload_field: The name of the field to use for document payloads.
301
+ :param maxtextfields: If ``True``, the maximum number of text fields will be used.
302
+ :param temporary: If specified, the index will be temporary and
303
+ will expire after the given number of seconds.
304
+ :param nooffsets: If ``True``, term offsets will not be stored.
305
+ :param nohl: If ``True``, search results will not include highlighted snippets.
306
+ :param nofields: If ``True``, search results will not include field values.
307
+ :param nofreqs: If ``True``, term frequencies will not be stored.
308
+ :param stopwords: A list of stopwords to ignore.
309
+ :param skipinitialscan: If ``True``, the initial scan of the index will be skipped.
310
+ """
311
+ command_arguments: CommandArgList = [index]
312
+ if on:
313
+ command_arguments.extend([PrefixToken.ON, on])
314
+
315
+ if prefixes:
316
+ _prefixes: list[StringT] = list(prefixes)
317
+ command_arguments.extend([PrefixToken.PREFIX, len(_prefixes), *_prefixes])
318
+ if filter_expression:
319
+ command_arguments.extend([PrefixToken.FILTER, filter_expression])
320
+ if language:
321
+ command_arguments.extend([PrefixToken.LANGUAGE, language])
322
+ if language_field:
323
+ command_arguments.extend([PrefixToken.LANGUAGE_FIELD, language_field])
324
+ if score:
325
+ command_arguments.extend([PrefixToken.SCORE, score])
326
+ if score_field:
327
+ command_arguments.extend([PrefixToken.SCORE_FIELD, score_field])
328
+ if payload_field:
329
+ command_arguments.extend([PrefixToken.PAYLOAD_FIELD, payload_field])
330
+ if maxtextfields:
331
+ command_arguments.append(PureToken.MAXTEXTFIELDS)
332
+ if temporary:
333
+ command_arguments.extend([PrefixToken.TEMPORARY, normalized_seconds(temporary)])
334
+ if nooffsets:
335
+ command_arguments.append(PureToken.NOOFFSETS)
336
+ if nohl:
337
+ command_arguments.append(PureToken.NOHL)
338
+ if nofields:
339
+ command_arguments.append(PureToken.NOFIELDS)
340
+ if nofreqs:
341
+ command_arguments.append(PureToken.NOFREQS)
342
+ if stopwords:
343
+ _stop: list[StringT] = list(stopwords)
344
+ command_arguments.extend([PrefixToken.STOPWORDS, len(_stop), *_stop])
345
+ if skipinitialscan:
346
+ command_arguments.append(PureToken.SKIPINITIALSCAN)
347
+
348
+ field_args: CommandArgList = [PureToken.SCHEMA]
349
+ for field in schema:
350
+ field_args.extend(field.args)
351
+ command_arguments.extend(field_args)
352
+
353
+ return self.client.create_request(
354
+ CommandName.FT_CREATE, *command_arguments, callback=SimpleStringCallback()
355
+ )
356
+
357
+ @module_command(
358
+ CommandName.FT_INFO,
359
+ module=MODULE,
360
+ version_introduced="1.0.0",
361
+ group=COMMAND_GROUP,
362
+ )
363
+ def info(self, index: KeyT) -> CommandRequest[dict[AnyStr, ResponseType]]:
364
+ """
365
+ Returns information and statistics on the index
366
+
367
+ :param index: The name of the index.
368
+ """
369
+ return self.client.create_request(
370
+ CommandName.FT_INFO,
371
+ index,
372
+ callback=DictCallback[AnyStr, ResponseType](
373
+ recursive=[
374
+ "attributes",
375
+ "index_definition",
376
+ "gc_stats",
377
+ "cursor_stats",
378
+ "dialect_stats",
379
+ ]
380
+ ),
381
+ )
382
+
383
+ @module_command(
384
+ CommandName.FT_EXPLAIN,
385
+ module=MODULE,
386
+ version_introduced="1.0.0",
387
+ group=COMMAND_GROUP,
388
+ arguments={"dialect": {"version_introduced": "2.4.3"}},
389
+ )
390
+ def explain(
391
+ self, index: KeyT, query: StringT, dialect: int | None = None
392
+ ) -> CommandRequest[AnyStr]:
393
+ """
394
+ Returns the execution plan for a complex query
395
+
396
+ :param index: The name of the index to query.
397
+ :param query: The query to explain.
398
+ :param dialect: Query dialect to use.
399
+ """
400
+ command_arguments: CommandArgList = [index, query]
401
+ if dialect:
402
+ command_arguments.extend([PrefixToken.DIALECT, dialect])
403
+ return self.client.create_request(
404
+ CommandName.FT_EXPLAIN,
405
+ *command_arguments,
406
+ callback=AnyStrCallback[AnyStr](),
407
+ )
408
+
409
+ @module_command(
410
+ CommandName.FT_ALTER,
411
+ module=MODULE,
412
+ version_introduced="1.0.0",
413
+ group=COMMAND_GROUP,
414
+ )
415
+ def alter(
416
+ self,
417
+ index: KeyT,
418
+ field: Field,
419
+ skipinitialscan: bool | None = None,
420
+ ) -> CommandRequest[bool]:
421
+ """
422
+ Adds a new field to the index
423
+
424
+ :param index: The name of the index to alter.
425
+ :param field: The new field to add
426
+ :param skipinitialscan: If ``True``, skip the initial scan and indexing.
427
+
428
+ """
429
+ command_arguments: CommandArgList = [index]
430
+ if skipinitialscan:
431
+ command_arguments.append(PureToken.SKIPINITIALSCAN)
432
+ command_arguments.extend([PureToken.SCHEMA, PureToken.ADD, *field.args])
433
+
434
+ return self.client.create_request(
435
+ CommandName.FT_ALTER, *command_arguments, callback=SimpleStringCallback()
436
+ )
437
+
438
+ @module_command(
439
+ CommandName.FT_DROPINDEX,
440
+ module=MODULE,
441
+ version_introduced="2.0.0",
442
+ group=COMMAND_GROUP,
443
+ )
444
+ def dropindex(self, index: KeyT, delete_docs: bool | None = None) -> CommandRequest[bool]:
445
+ """
446
+ Deletes the index
447
+
448
+ :param index: The name of the index to delete.
449
+ :param delete_docs: If ``True``, delete the documents associated with the index.
450
+ """
451
+ command_arguments: CommandArgList = [index]
452
+ if delete_docs:
453
+ command_arguments.append(PureToken.DELETE_DOCS)
454
+
455
+ return self.client.create_request(
456
+ CommandName.FT_DROPINDEX,
457
+ *command_arguments,
458
+ callback=SimpleStringCallback(),
459
+ )
460
+
461
+ @module_command(
462
+ CommandName.FT_ALIASADD,
463
+ module=MODULE,
464
+ version_introduced="1.0.0",
465
+ group=COMMAND_GROUP,
466
+ )
467
+ def aliasadd(self, alias: StringT, index: KeyT) -> CommandRequest[bool]:
468
+ """
469
+ Adds an alias to the index
470
+
471
+ :param alias: The alias to be added to the index.
472
+ :param index: The index to which the alias will be added.
473
+ """
474
+
475
+ return self.client.create_request(
476
+ CommandName.FT_ALIASADD, alias, index, callback=SimpleStringCallback()
477
+ )
478
+
479
+ @module_command(
480
+ CommandName.FT_ALIASUPDATE,
481
+ module=MODULE,
482
+ version_introduced="1.0.0",
483
+ group=COMMAND_GROUP,
484
+ )
485
+ def aliasupdate(self, alias: StringT, index: KeyT) -> CommandRequest[bool]:
486
+ """
487
+ Adds or updates an alias to the index
488
+
489
+ :param alias: The alias to be added to an index.
490
+ :param index: The index to which the alias will be added.
491
+ """
492
+
493
+ return self.client.create_request(
494
+ CommandName.FT_ALIASUPDATE, alias, index, callback=SimpleStringCallback()
495
+ )
496
+
497
+ @module_command(
498
+ CommandName.FT_ALIASDEL,
499
+ module=MODULE,
500
+ version_introduced="1.0.0",
501
+ group=COMMAND_GROUP,
502
+ )
503
+ def aliasdel(self, alias: StringT) -> CommandRequest[bool]:
504
+ """
505
+ Deletes an alias from the index
506
+
507
+ :param alias: The index alias to be removed.
508
+ """
509
+
510
+ return self.client.create_request(
511
+ CommandName.FT_ALIASDEL, alias, callback=SimpleStringCallback()
512
+ )
513
+
514
+ @module_command(
515
+ CommandName.FT_TAGVALS,
516
+ module=MODULE,
517
+ version_introduced="1.0.0",
518
+ group=COMMAND_GROUP,
519
+ )
520
+ def tagvals(self, index: KeyT, field_name: StringT) -> CommandRequest[set[AnyStr]]:
521
+ """
522
+ Returns the distinct tags indexed in a Tag field
523
+
524
+ :param index: The name of the index.
525
+ :param field_name: Name of a Tag field defined in the schema.
526
+ """
527
+
528
+ return self.client.create_request(
529
+ CommandName.FT_TAGVALS, index, field_name, callback=SetCallback[AnyStr]()
530
+ )
531
+
532
+ @module_command(
533
+ CommandName.FT_SYNUPDATE,
534
+ module=MODULE,
535
+ version_introduced="1.2.0",
536
+ group=COMMAND_GROUP,
537
+ )
538
+ def synupdate(
539
+ self,
540
+ index: KeyT,
541
+ synonym_group: StringT,
542
+ terms: Parameters[StringT],
543
+ skipinitialscan: bool | None = None,
544
+ ) -> CommandRequest[bool]:
545
+ """
546
+ Creates or updates a synonym group with additional terms
547
+
548
+ :param index: The name of the index.
549
+ :param synonym_group: The ID of the synonym group to update.
550
+ :param terms: A list of terms to add to the synonym group.
551
+ :param skipinitialscan: If ``True``, only documents indexed after the
552
+ update will be affected.
553
+
554
+ """
555
+ command_arguments: CommandArgList = [index, synonym_group]
556
+ if skipinitialscan:
557
+ command_arguments.append(PureToken.SKIPINITIALSCAN)
558
+ command_arguments.extend(terms)
559
+ return self.client.create_request(
560
+ CommandName.FT_SYNUPDATE,
561
+ *command_arguments,
562
+ callback=SimpleStringCallback(),
563
+ )
564
+
565
+ @module_command(
566
+ CommandName.FT_SYNDUMP,
567
+ module=MODULE,
568
+ version_introduced="1.2.0",
569
+ group=COMMAND_GROUP,
570
+ )
571
+ def syndump(self, index: KeyT) -> CommandRequest[dict[AnyStr, list[AnyStr]]]:
572
+ """
573
+ Dumps the contents of a synonym group
574
+
575
+ :param index: The name of the index.
576
+ """
577
+
578
+ return self.client.create_request(
579
+ CommandName.FT_SYNDUMP,
580
+ index,
581
+ callback=DictCallback[AnyStr, list[AnyStr]](),
582
+ )
583
+
584
+ @module_command(
585
+ CommandName.FT_SPELLCHECK,
586
+ module=MODULE,
587
+ version_introduced="1.4.0",
588
+ group=COMMAND_GROUP,
589
+ arguments={"dialect": {"version_introduced": "2.4.3"}},
590
+ )
591
+ def spellcheck(
592
+ self,
593
+ index: KeyT,
594
+ query: StringT,
595
+ distance: int | None = None,
596
+ include: StringT | None = None,
597
+ exclude: StringT | None = None,
598
+ dialect: int | None = None,
599
+ ) -> CommandRequest[dict[AnyStr, OrderedDict[AnyStr, float]]]:
600
+ """
601
+ Performs spelling correction on a query, returning suggestions for misspelled terms
602
+
603
+ :param index: The name of the index with the indexed terms.
604
+ :param query: The search query.
605
+ :param distance: Maximum Levenshtein distance for spelling suggestions
606
+ :param include: Specifies an inclusion of a custom dictionary
607
+ :param exclude: Specifies an exclusion of a custom dictionary
608
+ :param dialect: The query dialect to use.
609
+ """
610
+ command_arguments: CommandArgList = [index, query]
611
+ if distance:
612
+ command_arguments.extend([PrefixToken.DISTANCE, distance])
613
+ if exclude:
614
+ command_arguments.extend([PrefixToken.TERMS, PureToken.EXCLUDE, exclude])
615
+ if include:
616
+ command_arguments.extend([PrefixToken.TERMS, PureToken.INCLUDE, include])
617
+ if dialect:
618
+ command_arguments.extend([PrefixToken.DIALECT, dialect])
619
+ return self.client.create_request(
620
+ CommandName.FT_SPELLCHECK,
621
+ *command_arguments,
622
+ callback=SpellCheckCallback[AnyStr](),
623
+ )
624
+
625
+ @module_command(
626
+ CommandName.FT_DICTADD,
627
+ module=MODULE,
628
+ version_introduced="1.4.0",
629
+ group=COMMAND_GROUP,
630
+ )
631
+ def dictadd(
632
+ self,
633
+ name: StringT,
634
+ terms: Parameters[StringT],
635
+ ) -> CommandRequest[int]:
636
+ """
637
+ Adds terms to a dictionary
638
+
639
+ :param name: The name of the dictionary.
640
+ :param terms: The terms to add to the dictionary.
641
+ """
642
+ command_arguments: CommandArgList = [name, *terms]
643
+
644
+ return self.client.create_request(
645
+ CommandName.FT_DICTADD, *command_arguments, callback=IntCallback()
646
+ )
647
+
648
+ @module_command(
649
+ CommandName.FT_DICTDEL,
650
+ module=MODULE,
651
+ version_introduced="1.4.0",
652
+ group=COMMAND_GROUP,
653
+ )
654
+ def dictdel(
655
+ self,
656
+ name: StringT,
657
+ terms: Parameters[StringT],
658
+ ) -> CommandRequest[int]:
659
+ """
660
+ Deletes terms from a dictionary
661
+
662
+ :param name: The name of the dictionary.
663
+ :param terms: The terms to delete from the dictionary.
664
+ """
665
+ command_arguments: CommandArgList = [name, *terms]
666
+
667
+ return self.client.create_request(
668
+ CommandName.FT_DICTDEL, *command_arguments, callback=IntCallback()
669
+ )
670
+
671
+ @module_command(
672
+ CommandName.FT_DICTDUMP,
673
+ module=MODULE,
674
+ version_introduced="1.4.0",
675
+ group=COMMAND_GROUP,
676
+ )
677
+ def dictdump(self, name: StringT) -> CommandRequest[set[AnyStr]]:
678
+ """
679
+ Dumps all terms in the given dictionary
680
+
681
+ :param name: The name of the dictionary to dump.
682
+ """
683
+
684
+ return self.client.create_request(
685
+ CommandName.FT_DICTDUMP, name, callback=SetCallback[AnyStr]()
686
+ )
687
+
688
+ @module_command(
689
+ CommandName.FT__LIST,
690
+ module=MODULE,
691
+ version_introduced="2.0.0",
692
+ group=COMMAND_GROUP,
693
+ cluster=ClusterCommandConfig(
694
+ route=NodeFlag.PRIMARIES,
695
+ combine=ClusterMergeSets[AnyStr](),
696
+ ),
697
+ )
698
+ def list(self) -> CommandRequest[set[AnyStr]]:
699
+ """
700
+ Returns a list of all existing indexes
701
+ """
702
+ return self.client.create_request(CommandName.FT__LIST, callback=SetCallback[AnyStr]())
703
+
704
+ @module_command(
705
+ CommandName.FT_CONFIG_SET,
706
+ module=MODULE,
707
+ version_introduced="1.0.0",
708
+ version_deprecated="8.0.0",
709
+ group=COMMAND_GROUP,
710
+ cluster=ClusterCommandConfig(
711
+ route=NodeFlag.PRIMARIES,
712
+ combine=ClusterEnsureConsistent[bool](),
713
+ ),
714
+ )
715
+ def config_set(self, option: StringT, value: ValueT) -> CommandRequest[bool]:
716
+ """
717
+ Sets runtime configuration options
718
+ """
719
+
720
+ return self.client.create_request(
721
+ CommandName.FT_CONFIG_SET, option, value, callback=SimpleStringCallback()
722
+ )
723
+
724
+ @module_command(
725
+ CommandName.FT_CONFIG_GET,
726
+ module=MODULE,
727
+ version_introduced="1.0.0",
728
+ version_deprecated="8.0.0",
729
+ group=COMMAND_GROUP,
730
+ cluster=ClusterCommandConfig(
731
+ route=NodeFlag.RANDOM,
732
+ ),
733
+ )
734
+ def config_get(self, option: StringT) -> CommandRequest[dict[AnyStr, ResponsePrimitive]]:
735
+ """
736
+ Retrieves runtime configuration options
737
+ """
738
+
739
+ return self.client.create_request(
740
+ CommandName.FT_CONFIG_GET,
741
+ option,
742
+ callback=SearchConfigCallback[AnyStr](),
743
+ )
744
+
745
+ @module_command(
746
+ CommandName.FT_SEARCH,
747
+ module=MODULE,
748
+ version_introduced="1.0.0",
749
+ group=COMMAND_GROUP,
750
+ arguments={"dialect": {"version_introduced": "2.4.3"}},
751
+ )
752
+ def search(
753
+ self,
754
+ index: KeyT,
755
+ query: StringT,
756
+ *,
757
+ nocontent: bool | None = None,
758
+ verbatim: bool | None = None,
759
+ nostopwords: bool | None = None,
760
+ withscores: bool | None = None,
761
+ withpayloads: bool | None = None,
762
+ withsortkeys: bool | None = None,
763
+ numeric_filters: None
764
+ | (Mapping[StringT, tuple[int | float | StringT, int | float | StringT]]) = None,
765
+ geo_filters: None
766
+ | (
767
+ Mapping[
768
+ StringT,
769
+ tuple[
770
+ tuple[int | float, int | float],
771
+ int | float,
772
+ Literal[PureToken.KM, PureToken.M, PureToken.MI, PureToken.FT],
773
+ ],
774
+ ]
775
+ ) = None,
776
+ in_keys: Parameters[StringT] | None = None,
777
+ in_fields: Parameters[StringT] | None = None,
778
+ returns: Mapping[StringT, StringT | None] | None = None,
779
+ summarize_fields: Parameters[StringT] | None = None,
780
+ summarize_frags: int | None = None,
781
+ summarize_length: int | None = None,
782
+ summarize_separator: StringT | None = None,
783
+ highlight_fields: Parameters[StringT] | None = None,
784
+ highlight_tags: tuple[StringT, StringT] | None = None,
785
+ slop: int | None = None,
786
+ timeout: int | timedelta | None = None,
787
+ inorder: bool | None = None,
788
+ language: StringT | None = None,
789
+ expander: StringT | None = None,
790
+ scorer: StringT | None = None,
791
+ explainscore: bool | None = None,
792
+ payload: StringT | None = None,
793
+ sortby: StringT | None = None,
794
+ sort_order: Literal[PureToken.ASC, PureToken.DESC] | None = None,
795
+ offset: int | None = 0,
796
+ limit: int | None = None,
797
+ parameters: Mapping[StringT, ValueT] | None = None,
798
+ dialect: int | None = None,
799
+ ) -> CommandRequest[SearchResult[AnyStr]]:
800
+ """
801
+ Searches the index with a textual query, returning either documents or just ids
802
+
803
+ :param index: The name of the index to search.
804
+ :param query: The text query to search.
805
+ :param nocontent: If ``True``, returns the document ids and not the content.
806
+ :param verbatim: If ``True``, does not try to use stemming for query expansion
807
+ but searches the query terms verbatim.
808
+ :param nostopwords: If ``True``, disables stopword filtering.
809
+ :param withscores: If ``True``, also returns the relative internal score of each document.
810
+ :param withpayloads: If ``True``, retrieves optional document payloads.
811
+ :param withsortkeys: If ``True``, returns the value of the sorting key
812
+ :param numeric_filters: A dictionary of numeric filters.
813
+ Limits results to those having numeric values ranging between min and max,
814
+ :param geo_filters: A dictionary of geo filters.
815
+ Filters the results to a given radius from lon and lat.
816
+ Radius is given as a number and units.
817
+ :param in_keys: A list of keys to limit the result to.
818
+ Non-existent keys are ignored, unless all the keys are non-existent.
819
+ :param in_fields: Filters the results to those appearing only in specific
820
+ attributes of the document, like title or URL.
821
+ :param returns: A dictionary of attributes to return from the document. The values
822
+ in the dictionary are used as an alias for the attribute name and if ``None``,
823
+ the attribute will be returned as is.
824
+ :param summarize_fields: A list of fields to summarize. If not ``None``,
825
+ returns a summary of the fields.
826
+ :param summarize_frags: The number of fragments to return in the summary.
827
+ :param summarize_length: The length of each fragment in the summary.
828
+ :param summarize_separator: The separator to use between fragments in the summary.
829
+ :param highlight_fields: A list of fields to highlight.
830
+ :param highlight_tags: A tuple of opening and closing tags to use for highlighting.
831
+ :param slop: The number of words allowed between query terms.
832
+ :param timeout: The timeout for the search query.
833
+ :param inorder: If ``True``, requires that the query terms appear in the same order
834
+ as in the query.
835
+ :param language: The language to use for stemming.
836
+ :param expander: The query expander to use.
837
+ :param scorer: The scorer to use.
838
+ :param explainscore: If ``True``, returns an explanation of the score.
839
+ :param payload: The payload to return.
840
+ :param sortby: The field to sort by.
841
+ :param sort_order: The order to sort by.
842
+ :param offset: The offset to start returning results from.
843
+ :param limit: The maximum number of results to return.
844
+ :param parameters: A dictionary of parameters to pass to the query.
845
+ :param dialect: The query dialect to use.
846
+
847
+ """
848
+ command_arguments: CommandArgList = [index, query]
849
+ if nocontent:
850
+ command_arguments.append(PureToken.NOCONTENT)
851
+ if verbatim:
852
+ command_arguments.append(PureToken.VERBATIM)
853
+ if nostopwords:
854
+ command_arguments.append(PureToken.NOSTOPWORDS)
855
+ if withscores:
856
+ command_arguments.append(PureToken.WITHSCORES)
857
+ if withpayloads:
858
+ command_arguments.append(PureToken.WITHPAYLOADS)
859
+ if withsortkeys:
860
+ command_arguments.append(PureToken.WITHSORTKEYS)
861
+ if numeric_filters:
862
+ for field, numeric_filter in numeric_filters.items():
863
+ command_arguments.extend(
864
+ [PrefixToken.FILTER, field, numeric_filter[0], numeric_filter[1]]
865
+ )
866
+ if geo_filters:
867
+ for field, gfilter in geo_filters.items():
868
+ command_arguments.extend(
869
+ [
870
+ PrefixToken.GEOFILTER,
871
+ field,
872
+ gfilter[0][0],
873
+ gfilter[0][1],
874
+ gfilter[1],
875
+ gfilter[2],
876
+ ]
877
+ )
878
+ if in_keys:
879
+ _in_keys: list[StringT] = list(in_keys)
880
+ command_arguments.extend([PrefixToken.INKEYS, len(_in_keys), *_in_keys])
881
+ if in_fields:
882
+ _in_fields: list[StringT] = list(in_fields)
883
+ command_arguments.extend([PrefixToken.INFIELDS, len(_in_fields), *_in_fields])
884
+ if returns:
885
+ return_items: CommandArgList = []
886
+ for identifier, property in returns.items():
887
+ return_items.append(identifier)
888
+ if property:
889
+ return_items.extend([PrefixToken.AS, property])
890
+ command_arguments.extend([PrefixToken.RETURN, len(return_items), *return_items])
891
+ if sortby:
892
+ command_arguments.extend([PrefixToken.SORTBY, sortby])
893
+ if sort_order:
894
+ command_arguments.append(sort_order)
895
+ if summarize_fields or summarize_frags or summarize_length or summarize_separator:
896
+ command_arguments.append(PureToken.SUMMARIZE)
897
+ if summarize_fields:
898
+ _fields: list[StringT] = list(summarize_fields)
899
+ command_arguments.extend([PrefixToken.FIELDS, len(_fields), *_fields])
900
+ if summarize_frags:
901
+ command_arguments.extend([PrefixToken.FRAGS, summarize_frags])
902
+ if summarize_length:
903
+ command_arguments.extend([PrefixToken.LEN, summarize_length])
904
+ if summarize_separator:
905
+ command_arguments.extend([PrefixToken.SEPARATOR, summarize_separator])
906
+ if highlight_fields or highlight_tags:
907
+ command_arguments.append(PureToken.HIGHLIGHT)
908
+ if highlight_fields:
909
+ _fields = list(highlight_fields)
910
+ command_arguments.extend([PrefixToken.FIELDS, len(_fields), *_fields])
911
+ if highlight_tags:
912
+ command_arguments.extend([PureToken.TAGS, highlight_tags[0], highlight_tags[1]])
913
+ if slop is not None:
914
+ command_arguments.extend([PrefixToken.SLOP, slop])
915
+ if timeout:
916
+ command_arguments.extend([PrefixToken.TIMEOUT, normalized_milliseconds(timeout)])
917
+ if inorder:
918
+ command_arguments.append(PureToken.INORDER)
919
+ if language:
920
+ command_arguments.extend([PrefixToken.LANGUAGE, language])
921
+ if expander: # noqa
922
+ command_arguments.extend([PrefixToken.EXPANDER, expander])
923
+ if scorer: # noqa
924
+ command_arguments.extend([PrefixToken.SCORER, scorer])
925
+ if explainscore:
926
+ command_arguments.append(PureToken.EXPLAINSCORE)
927
+ if payload:
928
+ command_arguments.extend([PrefixToken.PAYLOAD, payload])
929
+ if limit is not None:
930
+ command_arguments.extend([PrefixToken.LIMIT, offset or 0, limit])
931
+ if parameters:
932
+ _parameters: list[ValueT] = list(itertools.chain(*parameters.items()))
933
+ command_arguments.extend([PureToken.PARAMS, len(_parameters), *_parameters])
934
+ if dialect:
935
+ command_arguments.extend([PrefixToken.DIALECT, dialect])
936
+
937
+ return self.client.create_request(
938
+ CommandName.FT_SEARCH,
939
+ *command_arguments,
940
+ callback=SearchResultCallback[AnyStr](
941
+ withscores=withscores,
942
+ withpayloads=withpayloads,
943
+ withsortkeys=withsortkeys,
944
+ explainscore=explainscore,
945
+ nocontent=nocontent,
946
+ ),
947
+ )
948
+
949
+ @module_command(
950
+ CommandName.FT_AGGREGATE,
951
+ module=MODULE,
952
+ version_introduced="1.1.0",
953
+ group=COMMAND_GROUP,
954
+ arguments={"dialect": {"version_introduced": "2.4.3"}},
955
+ )
956
+ def aggregate(
957
+ self,
958
+ index: KeyT,
959
+ query: StringT,
960
+ *,
961
+ verbatim: bool | None = None,
962
+ load: None | (Literal["*"] | Parameters[StringT | tuple[StringT, StringT]]) = None,
963
+ timeout: int | timedelta | None = None,
964
+ transforms: Parameters[Group | Apply | Filter] | None = None,
965
+ sortby: Mapping[StringT, Literal[PureToken.ASC, PureToken.DESC]] | None = None,
966
+ sortby_max: int | None = None,
967
+ offset: int | None = 0,
968
+ limit: int | None = None,
969
+ with_cursor: bool | None = None,
970
+ cursor_read_size: int | None = None,
971
+ cursor_maxidle: int | timedelta | None = None,
972
+ parameters: Mapping[StringT, StringT] | None = None,
973
+ dialect: int | None = None,
974
+ ) -> CommandRequest[SearchAggregationResult[AnyStr]]:
975
+ """
976
+ Perform aggregate transformations on search results from a Redis index.
977
+
978
+ :param index: Name of the Redis index to search.
979
+ :param query: Base filtering query to retrieve documents.
980
+ :param verbatim: If ``True``, search query terms verbatim.
981
+ :param load: Load document attributes from the source document.
982
+ :param timeout: Maximum time to wait for the query to complete.
983
+ :param transforms: List of transformations to apply to the results.
984
+ :param sortby: Sort the pipeline up to the point of SORTBY.
985
+ :param sortby_max: Optimize sorting by sorting only for the n-largest elements.
986
+ :param offset: Number of results to skip.
987
+ :param limit: Maximum number of results to return.
988
+ :param with_cursor: If ``True``, return a cursor for large result sets.
989
+ :param cursor_read_size: Number of results to read from the cursor at a time.
990
+ :param cursor_maxidle: Maximum idle time for the cursor.
991
+ :param parameters: Additional parameters to pass to the query.
992
+ :param dialect: Query dialect to use.
993
+
994
+ :return: Aggregated search results from the Redis index.
995
+
996
+ """
997
+ command_arguments: CommandArgList = [index, query]
998
+ if verbatim:
999
+ command_arguments.append(PureToken.VERBATIM)
1000
+ if timeout:
1001
+ command_arguments.extend([PrefixToken.TIMEOUT, normalized_milliseconds(timeout)])
1002
+ if load:
1003
+ command_arguments.append(PrefixToken.LOAD)
1004
+ if isinstance(load, (bytes, str)):
1005
+ command_arguments.append(load)
1006
+ else:
1007
+ _load_fields: list[StringT] = []
1008
+ for field in load:
1009
+ if isinstance(field, (bytes, str)):
1010
+ _load_fields.append(field)
1011
+ else:
1012
+ _load_fields.extend([field[0], PrefixToken.AS, field[1]])
1013
+
1014
+ command_arguments.extend([len(_load_fields), *_load_fields])
1015
+
1016
+ if transforms:
1017
+ for step in transforms:
1018
+ command_arguments.extend(step.args)
1019
+
1020
+ if sortby:
1021
+ command_arguments.append(PrefixToken.SORTBY)
1022
+ command_arguments.append(len(sortby) * 2)
1023
+ for field, order in sortby.items():
1024
+ command_arguments.extend([field, order])
1025
+ if sortby_max:
1026
+ command_arguments.extend([PrefixToken.MAX, sortby_max])
1027
+
1028
+ if limit is not None:
1029
+ command_arguments.extend([PrefixToken.LIMIT, offset or 0, limit])
1030
+
1031
+ if with_cursor:
1032
+ command_arguments.append(PureToken.WITHCURSOR)
1033
+ if cursor_read_size:
1034
+ command_arguments.extend([PrefixToken.COUNT, cursor_read_size])
1035
+ if cursor_maxidle:
1036
+ command_arguments.extend(
1037
+ [PrefixToken.MAXIDLE, normalized_milliseconds(cursor_maxidle)]
1038
+ )
1039
+ if parameters:
1040
+ _parameters: list[StringT] = list(itertools.chain(*parameters.items()))
1041
+ command_arguments.extend([PureToken.PARAMS, len(_parameters), *_parameters])
1042
+ if dialect:
1043
+ command_arguments.extend([PrefixToken.DIALECT, dialect])
1044
+ return self.client.create_request(
1045
+ CommandName.FT_AGGREGATE,
1046
+ *command_arguments,
1047
+ callback=AggregationResultCallback[AnyStr](with_cursor=with_cursor, dialect=dialect),
1048
+ )
1049
+
1050
+ @module_command(
1051
+ CommandName.FT_CURSOR_READ,
1052
+ module=MODULE,
1053
+ version_introduced="1.1.0",
1054
+ group=COMMAND_GROUP,
1055
+ )
1056
+ def cursor_read(
1057
+ self, index: KeyT, cursor_id: int, count: int | None = None
1058
+ ) -> CommandRequest[SearchAggregationResult[AnyStr]]:
1059
+ """
1060
+ Reads from a cursor
1061
+ """
1062
+ command_arguments: CommandArgList = [index, cursor_id]
1063
+ if count:
1064
+ command_arguments.extend([PrefixToken.COUNT, count])
1065
+
1066
+ return self.client.create_request(
1067
+ CommandName.FT_CURSOR_READ,
1068
+ *command_arguments,
1069
+ callback=AggregationResultCallback[AnyStr](with_cursor=True),
1070
+ )
1071
+
1072
+ @module_command(
1073
+ CommandName.FT_CURSOR_DEL,
1074
+ module=MODULE,
1075
+ version_introduced="1.1.0",
1076
+ group=COMMAND_GROUP,
1077
+ )
1078
+ def cursor_del(self, index: KeyT, cursor_id: int) -> CommandRequest[bool]:
1079
+ """
1080
+ Deletes a cursor
1081
+
1082
+ """
1083
+ command_arguments: CommandArgList = [index, cursor_id]
1084
+
1085
+ return self.client.create_request(
1086
+ CommandName.FT_CURSOR_DEL,
1087
+ *command_arguments,
1088
+ callback=SimpleStringCallback(),
1089
+ )