redis 6.4.0__py3-none-any.whl → 7.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. redis/__init__.py +1 -1
  2. redis/_parsers/base.py +193 -8
  3. redis/_parsers/helpers.py +64 -6
  4. redis/_parsers/hiredis.py +16 -10
  5. redis/_parsers/resp3.py +11 -5
  6. redis/asyncio/client.py +65 -8
  7. redis/asyncio/cluster.py +57 -5
  8. redis/asyncio/connection.py +62 -2
  9. redis/asyncio/http/__init__.py +0 -0
  10. redis/asyncio/http/http_client.py +265 -0
  11. redis/asyncio/multidb/__init__.py +0 -0
  12. redis/asyncio/multidb/client.py +530 -0
  13. redis/asyncio/multidb/command_executor.py +339 -0
  14. redis/asyncio/multidb/config.py +210 -0
  15. redis/asyncio/multidb/database.py +69 -0
  16. redis/asyncio/multidb/event.py +84 -0
  17. redis/asyncio/multidb/failover.py +125 -0
  18. redis/asyncio/multidb/failure_detector.py +38 -0
  19. redis/asyncio/multidb/healthcheck.py +285 -0
  20. redis/background.py +204 -0
  21. redis/cache.py +1 -0
  22. redis/client.py +97 -16
  23. redis/cluster.py +14 -3
  24. redis/commands/core.py +348 -313
  25. redis/commands/helpers.py +0 -20
  26. redis/commands/json/commands.py +2 -2
  27. redis/commands/search/__init__.py +2 -2
  28. redis/commands/search/aggregation.py +24 -26
  29. redis/commands/search/commands.py +10 -10
  30. redis/commands/search/field.py +2 -2
  31. redis/commands/search/query.py +23 -23
  32. redis/commands/vectorset/__init__.py +1 -1
  33. redis/commands/vectorset/commands.py +43 -25
  34. redis/commands/vectorset/utils.py +40 -4
  35. redis/connection.py +1257 -83
  36. redis/data_structure.py +81 -0
  37. redis/event.py +84 -10
  38. redis/exceptions.py +8 -0
  39. redis/http/__init__.py +0 -0
  40. redis/http/http_client.py +425 -0
  41. redis/maint_notifications.py +810 -0
  42. redis/multidb/__init__.py +0 -0
  43. redis/multidb/circuit.py +144 -0
  44. redis/multidb/client.py +526 -0
  45. redis/multidb/command_executor.py +350 -0
  46. redis/multidb/config.py +207 -0
  47. redis/multidb/database.py +130 -0
  48. redis/multidb/event.py +89 -0
  49. redis/multidb/exception.py +17 -0
  50. redis/multidb/failover.py +125 -0
  51. redis/multidb/failure_detector.py +104 -0
  52. redis/multidb/healthcheck.py +282 -0
  53. redis/retry.py +14 -1
  54. redis/utils.py +34 -0
  55. {redis-6.4.0.dist-info → redis-7.0.0.dist-info}/METADATA +7 -4
  56. redis-7.0.0.dist-info/RECORD +105 -0
  57. redis-6.4.0.dist-info/RECORD +0 -78
  58. {redis-6.4.0.dist-info → redis-7.0.0.dist-info}/WHEEL +0 -0
  59. {redis-6.4.0.dist-info → redis-7.0.0.dist-info}/licenses/LICENSE +0 -0
redis/commands/helpers.py CHANGED
@@ -72,26 +72,6 @@ def parse_to_list(response):
72
72
  return res
73
73
 
74
74
 
75
- def parse_list_to_dict(response):
76
- res = {}
77
- for i in range(0, len(response), 2):
78
- if isinstance(response[i], list):
79
- res["Child iterators"].append(parse_list_to_dict(response[i]))
80
- try:
81
- if isinstance(response[i + 1], list):
82
- res["Child iterators"].append(parse_list_to_dict(response[i + 1]))
83
- except IndexError:
84
- pass
85
- elif isinstance(response[i + 1], list):
86
- res["Child iterators"] = [parse_list_to_dict(response[i + 1])]
87
- else:
88
- try:
89
- res[response[i]] = float(response[i + 1])
90
- except (TypeError, ValueError):
91
- res[response[i]] = response[i + 1]
92
- return res
93
-
94
-
95
75
  def random_string(length=10):
96
76
  """
97
77
  Returns a random N character long string.
@@ -14,7 +14,7 @@ class JSONCommands:
14
14
  """json commands."""
15
15
 
16
16
  def arrappend(
17
- self, name: str, path: Optional[str] = Path.root_path(), *args: List[JsonType]
17
+ self, name: str, path: Optional[str] = Path.root_path(), *args: JsonType
18
18
  ) -> List[Optional[int]]:
19
19
  """Append the objects ``args`` to the array under the
20
20
  ``path` in key ``name``.
@@ -52,7 +52,7 @@ class JSONCommands:
52
52
  return self.execute_command("JSON.ARRINDEX", *pieces, keys=[name])
53
53
 
54
54
  def arrinsert(
55
- self, name: str, path: str, index: int, *args: List[JsonType]
55
+ self, name: str, path: str, index: int, *args: JsonType
56
56
  ) -> List[Optional[int]]:
57
57
  """Insert the objects ``args`` to the array at index ``index``
58
58
  under the ``path` in key ``name``.
@@ -1,4 +1,4 @@
1
- import redis
1
+ from redis.client import Pipeline as RedisPipeline
2
2
 
3
3
  from ...asyncio.client import Pipeline as AsyncioPipeline
4
4
  from .commands import (
@@ -181,7 +181,7 @@ class AsyncSearch(Search, AsyncSearchCommands):
181
181
  return p
182
182
 
183
183
 
184
- class Pipeline(SearchCommands, redis.client.Pipeline):
184
+ class Pipeline(SearchCommands, RedisPipeline):
185
185
  """Pipeline for the module."""
186
186
 
187
187
 
@@ -1,4 +1,4 @@
1
- from typing import List, Union
1
+ from typing import List, Optional, Tuple, Union
2
2
 
3
3
  from redis.commands.search.dialect import DEFAULT_DIALECT
4
4
 
@@ -27,9 +27,9 @@ class Reducer:
27
27
  NAME = None
28
28
 
29
29
  def __init__(self, *args: str) -> None:
30
- self._args = args
31
- self._field = None
32
- self._alias = None
30
+ self._args: Tuple[str, ...] = args
31
+ self._field: Optional[str] = None
32
+ self._alias: Optional[str] = None
33
33
 
34
34
  def alias(self, alias: str) -> "Reducer":
35
35
  """
@@ -49,13 +49,14 @@ class Reducer:
49
49
  if alias is FIELDNAME:
50
50
  if not self._field:
51
51
  raise ValueError("Cannot use FIELDNAME alias with no field")
52
- # Chop off initial '@'
53
- alias = self._field[1:]
52
+ else:
53
+ # Chop off initial '@'
54
+ alias = self._field[1:]
54
55
  self._alias = alias
55
56
  return self
56
57
 
57
58
  @property
58
- def args(self) -> List[str]:
59
+ def args(self) -> Tuple[str, ...]:
59
60
  return self._args
60
61
 
61
62
 
@@ -64,7 +65,7 @@ class SortDirection:
64
65
  This special class is used to indicate sort direction.
65
66
  """
66
67
 
67
- DIRSTRING = None
68
+ DIRSTRING: Optional[str] = None
68
69
 
69
70
  def __init__(self, field: str) -> None:
70
71
  self.field = field
@@ -104,17 +105,17 @@ class AggregateRequest:
104
105
  All member methods (except `build_args()`)
105
106
  return the object itself, making them useful for chaining.
106
107
  """
107
- self._query = query
108
- self._aggregateplan = []
109
- self._loadfields = []
110
- self._loadall = False
111
- self._max = 0
112
- self._with_schema = False
113
- self._verbatim = False
114
- self._cursor = []
115
- self._dialect = DEFAULT_DIALECT
116
- self._add_scores = False
117
- self._scorer = "TFIDF"
108
+ self._query: str = query
109
+ self._aggregateplan: List[str] = []
110
+ self._loadfields: List[str] = []
111
+ self._loadall: bool = False
112
+ self._max: int = 0
113
+ self._with_schema: bool = False
114
+ self._verbatim: bool = False
115
+ self._cursor: List[str] = []
116
+ self._dialect: int = DEFAULT_DIALECT
117
+ self._add_scores: bool = False
118
+ self._scorer: str = "TFIDF"
118
119
 
119
120
  def load(self, *fields: str) -> "AggregateRequest":
120
121
  """
@@ -133,7 +134,7 @@ class AggregateRequest:
133
134
  return self
134
135
 
135
136
  def group_by(
136
- self, fields: List[str], *reducers: Union[Reducer, List[Reducer]]
137
+ self, fields: Union[str, List[str]], *reducers: Reducer
137
138
  ) -> "AggregateRequest":
138
139
  """
139
140
  Specify by which fields to group the aggregation.
@@ -147,7 +148,6 @@ class AggregateRequest:
147
148
  `aggregation` module.
148
149
  """
149
150
  fields = [fields] if isinstance(fields, str) else fields
150
- reducers = [reducers] if isinstance(reducers, Reducer) else reducers
151
151
 
152
152
  ret = ["GROUPBY", str(len(fields)), *fields]
153
153
  for reducer in reducers:
@@ -251,12 +251,10 @@ class AggregateRequest:
251
251
  .sort_by(Desc("@paid"), max=10)
252
252
  ```
253
253
  """
254
- if isinstance(fields, (str, SortDirection)):
255
- fields = [fields]
256
254
 
257
255
  fields_args = []
258
256
  for f in fields:
259
- if isinstance(f, SortDirection):
257
+ if isinstance(f, (Asc, Desc)):
260
258
  fields_args += [f.field, f.DIRSTRING]
261
259
  else:
262
260
  fields_args += [f]
@@ -356,7 +354,7 @@ class AggregateRequest:
356
354
  ret.extend(self._loadfields)
357
355
 
358
356
  if self._dialect:
359
- ret.extend(["DIALECT", self._dialect])
357
+ ret.extend(["DIALECT", str(self._dialect)])
360
358
 
361
359
  ret.extend(self._aggregateplan)
362
360
 
@@ -393,7 +391,7 @@ class AggregateResult:
393
391
  self.cursor = cursor
394
392
  self.schema = schema
395
393
 
396
- def __repr__(self) -> (str, str):
394
+ def __repr__(self) -> str:
397
395
  cid = self.cursor.cid if self.cursor else -1
398
396
  return (
399
397
  f"<{self.__class__.__name__} at 0x{id(self):x} "
@@ -221,7 +221,7 @@ class SearchCommands:
221
221
 
222
222
  return self.execute_command(*args)
223
223
 
224
- def alter_schema_add(self, fields: List[str]):
224
+ def alter_schema_add(self, fields: Union[Field, List[Field]]):
225
225
  """
226
226
  Alter the existing search index by adding new fields. The index
227
227
  must already exist.
@@ -336,11 +336,11 @@ class SearchCommands:
336
336
  doc_id: str,
337
337
  nosave: bool = False,
338
338
  score: float = 1.0,
339
- payload: bool = None,
339
+ payload: Optional[bool] = None,
340
340
  replace: bool = False,
341
341
  partial: bool = False,
342
342
  language: Optional[str] = None,
343
- no_create: str = False,
343
+ no_create: bool = False,
344
344
  **fields: List[str],
345
345
  ):
346
346
  """
@@ -464,7 +464,7 @@ class SearchCommands:
464
464
  return self._parse_results(INFO_CMD, res)
465
465
 
466
466
  def get_params_args(
467
- self, query_params: Union[Dict[str, Union[str, int, float, bytes]], None]
467
+ self, query_params: Optional[Dict[str, Union[str, int, float, bytes]]]
468
468
  ):
469
469
  if query_params is None:
470
470
  return []
@@ -478,7 +478,7 @@ class SearchCommands:
478
478
  return args
479
479
 
480
480
  def _mk_query_args(
481
- self, query, query_params: Union[Dict[str, Union[str, int, float, bytes]], None]
481
+ self, query, query_params: Optional[Dict[str, Union[str, int, float, bytes]]]
482
482
  ):
483
483
  args = [self.index_name]
484
484
 
@@ -528,7 +528,7 @@ class SearchCommands:
528
528
  def explain(
529
529
  self,
530
530
  query: Union[str, Query],
531
- query_params: Dict[str, Union[str, int, float]] = None,
531
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
532
532
  ):
533
533
  """Returns the execution plan for a complex query.
534
534
 
@@ -543,7 +543,7 @@ class SearchCommands:
543
543
  def aggregate(
544
544
  self,
545
545
  query: Union[AggregateRequest, Cursor],
546
- query_params: Dict[str, Union[str, int, float]] = None,
546
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
547
547
  ):
548
548
  """
549
549
  Issue an aggregation query.
@@ -598,7 +598,7 @@ class SearchCommands:
598
598
  self,
599
599
  query: Union[Query, AggregateRequest],
600
600
  limited: bool = False,
601
- query_params: Optional[Dict[str, Union[str, int, float]]] = None,
601
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
602
602
  ):
603
603
  """
604
604
  Performs a search or aggregate command and collects performance
@@ -936,7 +936,7 @@ class AsyncSearchCommands(SearchCommands):
936
936
  async def search(
937
937
  self,
938
938
  query: Union[str, Query],
939
- query_params: Dict[str, Union[str, int, float]] = None,
939
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
940
940
  ):
941
941
  """
942
942
  Search the index for a given query, and return a result of documents
@@ -968,7 +968,7 @@ class AsyncSearchCommands(SearchCommands):
968
968
  async def aggregate(
969
969
  self,
970
970
  query: Union[AggregateResult, Cursor],
971
- query_params: Dict[str, Union[str, int, float]] = None,
971
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
972
972
  ):
973
973
  """
974
974
  Issue an aggregation query.
@@ -52,14 +52,14 @@ class Field:
52
52
  self.args_suffix = list()
53
53
  self.as_name = as_name
54
54
 
55
- if sortable:
56
- self.args_suffix.append(Field.SORTABLE)
57
55
  if no_index:
58
56
  self.args_suffix.append(Field.NOINDEX)
59
57
  if index_missing:
60
58
  self.args_suffix.append(Field.INDEX_MISSING)
61
59
  if index_empty:
62
60
  self.args_suffix.append(Field.INDEX_EMPTY)
61
+ if sortable:
62
+ self.args_suffix.append(Field.SORTABLE)
63
63
 
64
64
  if no_index and not sortable:
65
65
  raise ValueError("Non-Sortable non-Indexable fields are ignored")
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Union
1
+ from typing import List, Optional, Tuple, Union
2
2
 
3
3
  from redis.commands.search.dialect import DEFAULT_DIALECT
4
4
 
@@ -9,7 +9,7 @@ class Query:
9
9
  the query string. The query string is set in the constructor, and other
10
10
  options have setter functions.
11
11
 
12
- The setter functions return the query object, so they can be chained,
12
+ The setter functions return the query object so they can be chained.
13
13
  i.e. `Query("foo").verbatim().filter(...)` etc.
14
14
  """
15
15
 
@@ -31,7 +31,7 @@ class Query:
31
31
  self._with_scores: bool = False
32
32
  self._scorer: Optional[str] = None
33
33
  self._filters: List = list()
34
- self._ids: Optional[List[str]] = None
34
+ self._ids: Optional[Tuple[str, ...]] = None
35
35
  self._slop: int = -1
36
36
  self._timeout: Optional[float] = None
37
37
  self._in_order: bool = False
@@ -81,7 +81,7 @@ class Query:
81
81
  self._return_fields += ("AS", as_field)
82
82
  return self
83
83
 
84
- def _mk_field_list(self, fields: List[str]) -> List:
84
+ def _mk_field_list(self, fields: Optional[Union[List[str], str]]) -> List:
85
85
  if not fields:
86
86
  return []
87
87
  return [fields] if isinstance(fields, str) else list(fields)
@@ -95,12 +95,12 @@ class Query:
95
95
  ) -> "Query":
96
96
  """
97
97
  Return an abridged format of the field, containing only the segments of
98
- the field which contain the matching term(s).
98
+ the field that contain the matching term(s).
99
99
 
100
100
  If `fields` is specified, then only the mentioned fields are
101
- summarized; otherwise all results are summarized.
101
+ summarized; otherwise, all results are summarized.
102
102
 
103
- Server side defaults are used for each option (except `fields`)
103
+ Server-side defaults are used for each option (except `fields`)
104
104
  if not specified
105
105
 
106
106
  - **fields** List of fields to summarize. All fields are summarized
@@ -126,11 +126,11 @@ class Query:
126
126
 
127
127
  def highlight(
128
128
  self, fields: Optional[List[str]] = None, tags: Optional[List[str]] = None
129
- ) -> None:
129
+ ) -> "Query":
130
130
  """
131
131
  Apply specified markup to matched term(s) within the returned field(s).
132
132
 
133
- - **fields** If specified then only those mentioned fields are
133
+ - **fields** If specified, then only those mentioned fields are
134
134
  highlighted, otherwise all fields are highlighted
135
135
  - **tags** A list of two strings to surround the match.
136
136
  """
@@ -154,7 +154,7 @@ class Query:
154
154
  return self
155
155
 
156
156
  def slop(self, slop: int) -> "Query":
157
- """Allow a maximum of N intervening non matched terms between
157
+ """Allow a maximum of N intervening non-matched terms between
158
158
  phrase terms (0 means exact phrase).
159
159
  """
160
160
  self._slop = slop
@@ -169,7 +169,7 @@ class Query:
169
169
  """
170
170
  Match only documents where the query terms appear in
171
171
  the same order in the document.
172
- i.e. for the query "hello world", we do not match "world hello"
172
+ i.e., for the query "hello world", we do not match "world hello"
173
173
  """
174
174
  self._in_order = True
175
175
  return self
@@ -187,16 +187,16 @@ class Query:
187
187
  self._scorer = scorer
188
188
  return self
189
189
 
190
- def get_args(self) -> List[str]:
190
+ def get_args(self) -> List[Union[str, int, float]]:
191
191
  """Format the redis arguments for this query and return them."""
192
- args = [self._query_string]
192
+ args: List[Union[str, int, float]] = [self._query_string]
193
193
  args += self._get_args_tags()
194
194
  args += self._summarize_fields + self._highlight_fields
195
195
  args += ["LIMIT", self._offset, self._num]
196
196
  return args
197
197
 
198
- def _get_args_tags(self) -> List[str]:
199
- args = []
198
+ def _get_args_tags(self) -> List[Union[str, int, float]]:
199
+ args: List[Union[str, int, float]] = []
200
200
  if self._no_content:
201
201
  args.append("NOCONTENT")
202
202
  if self._fields:
@@ -258,7 +258,7 @@ class Query:
258
258
  return self
259
259
 
260
260
  def verbatim(self) -> "Query":
261
- """Set the query to be verbatim, i.e. use no query expansion
261
+ """Set the query to be verbatim, i.e., use no query expansion
262
262
  or stemming.
263
263
  """
264
264
  self._verbatim = True
@@ -288,20 +288,20 @@ class Query:
288
288
  self._with_scores = True
289
289
  return self
290
290
 
291
- def limit_fields(self, *fields: List[str]) -> "Query":
291
+ def limit_fields(self, *fields: str) -> "Query":
292
292
  """
293
293
  Limit the search to specific TEXT fields only.
294
294
 
295
- - **fields**: A list of strings, case sensitive field names
295
+ - **fields**: Each element should be a string, case sensitive field name
296
296
  from the defined schema.
297
297
  """
298
- self._fields = fields
298
+ self._fields = list(fields)
299
299
  return self
300
300
 
301
301
  def add_filter(self, flt: "Filter") -> "Query":
302
302
  """
303
303
  Add a numeric or geo filter to the query.
304
- **Currently only one of each filter is supported by the engine**
304
+ **Currently, only one of each filter is supported by the engine**
305
305
 
306
306
  - **flt**: A NumericFilter or GeoFilter object, used on a
307
307
  corresponding field
@@ -315,14 +315,14 @@ class Query:
315
315
  Add a sortby field to the query.
316
316
 
317
317
  - **field** - the name of the field to sort by
318
- - **asc** - when `True`, sorting will be done in asceding order
318
+ - **asc** - when `True`, sorting will be done in ascending order
319
319
  """
320
320
  self._sortby = SortbyField(field, asc)
321
321
  return self
322
322
 
323
323
  def expander(self, expander: str) -> "Query":
324
324
  """
325
- Add a expander field to the query.
325
+ Add an expander field to the query.
326
326
 
327
327
  - **expander** - the name of the expander
328
328
  """
@@ -340,7 +340,7 @@ class Query:
340
340
 
341
341
 
342
342
  class Filter:
343
- def __init__(self, keyword: str, field: str, *args: List[str]) -> None:
343
+ def __init__(self, keyword: str, field: str, *args: Union[str, float]) -> None:
344
344
  self.args = [keyword, field] + list(args)
345
345
 
346
346
 
@@ -24,12 +24,12 @@ class VectorSet(VectorSetCommands):
24
24
  # Set the module commands' callbacks
25
25
  self._MODULE_CALLBACKS = {
26
26
  VEMB_CMD: parse_vemb_result,
27
+ VSIM_CMD: parse_vsim_result,
27
28
  VGETATTR_CMD: lambda r: r and json.loads(r) or None,
28
29
  }
29
30
 
30
31
  self._RESP2_MODULE_CALLBACKS = {
31
32
  VINFO_CMD: lambda r: r and pairs_to_dict(r) or None,
32
- VSIM_CMD: parse_vsim_result,
33
33
  VLINKS_CMD: parse_vlinks_result,
34
34
  }
35
35
  self._RESP3_MODULE_CALLBACKS = {}
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from enum import Enum
3
- from typing import Awaitable, Dict, List, Optional, Union
3
+ from typing import Any, Awaitable, Dict, List, Optional, Union
4
4
 
5
5
  from redis.client import NEVER_DECODE
6
6
  from redis.commands.helpers import get_protocol_version
@@ -19,6 +19,15 @@ VSETATTR_CMD = "VSETATTR"
19
19
  VGETATTR_CMD = "VGETATTR"
20
20
  VRANDMEMBER_CMD = "VRANDMEMBER"
21
21
 
22
+ # Return type for vsim command
23
+ VSimResult = Optional[
24
+ List[
25
+ Union[
26
+ List[EncodableT], Dict[EncodableT, Number], Dict[EncodableT, Dict[str, Any]]
27
+ ]
28
+ ]
29
+ ]
30
+
22
31
 
23
32
  class QuantizationOptions(Enum):
24
33
  """Quantization options for the VADD command."""
@@ -33,6 +42,7 @@ class CallbacksOptions(Enum):
33
42
 
34
43
  RAW = "RAW"
35
44
  WITHSCORES = "WITHSCORES"
45
+ WITHATTRIBS = "WITHATTRIBS"
36
46
  ALLOW_DECODING = "ALLOW_DECODING"
37
47
  RESP3 = "RESP3"
38
48
 
@@ -77,7 +87,7 @@ class VectorSetCommands(CommandsProtocol):
77
87
  ``numlinks`` sets the number of links to create for the vector.
78
88
  If not provided, the default number of links is used.
79
89
 
80
- For more information see https://redis.io/commands/vadd
90
+ For more information, see https://redis.io/commands/vadd.
81
91
  """
82
92
  if not vector or not element:
83
93
  raise DataError("Both vector and element must be provided")
@@ -123,6 +133,7 @@ class VectorSetCommands(CommandsProtocol):
123
133
  key: KeyT,
124
134
  input: Union[List[float], bytes, str],
125
135
  with_scores: Optional[bool] = False,
136
+ with_attribs: Optional[bool] = False,
126
137
  count: Optional[int] = None,
127
138
  ef: Optional[Number] = None,
128
139
  filter: Optional[str] = None,
@@ -130,25 +141,24 @@ class VectorSetCommands(CommandsProtocol):
130
141
  truth: Optional[bool] = False,
131
142
  no_thread: Optional[bool] = False,
132
143
  epsilon: Optional[Number] = None,
133
- ) -> Union[
134
- Awaitable[Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]]],
135
- Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]],
136
- ]:
144
+ ) -> Union[Awaitable[VSimResult], VSimResult]:
137
145
  """
138
146
  Compare a vector or element ``input`` with the other vectors in a vector set ``key``.
139
147
 
140
- ``with_scores`` sets if the results should be returned with the
141
- similarity scores of the elements in the result.
148
+ ``with_scores`` sets if similarity scores should be returned for each element in the result.
149
+
150
+ ``with_attribs`` ``with_attribs`` sets if the results should be returned with the
151
+ attributes of the elements in the result, or None when no attributes are present.
142
152
 
143
153
  ``count`` sets the number of results to return.
144
154
 
145
155
  ``ef`` sets the exploration factor.
146
156
 
147
- ``filter`` sets filter that should be applied for the search.
157
+ ``filter`` sets the filter that should be applied for the search.
148
158
 
149
159
  ``filter_ef`` sets the max filtering effort.
150
160
 
151
- ``truth`` when enabled forces the command to perform linear scan.
161
+ ``truth`` when enabled, forces the command to perform a linear scan.
152
162
 
153
163
  ``no_thread`` when enabled forces the command to execute the search
154
164
  on the data structure in the main thread.
@@ -156,7 +166,7 @@ class VectorSetCommands(CommandsProtocol):
156
166
  ``epsilon`` floating point between 0 and 1, if specified will return
157
167
  only elements with distance no further than the specified one.
158
168
 
159
- For more information see https://redis.io/commands/vsim
169
+ For more information, see https://redis.io/commands/vsim.
160
170
  """
161
171
 
162
172
  if not input:
@@ -173,9 +183,17 @@ class VectorSetCommands(CommandsProtocol):
173
183
  else:
174
184
  pieces.extend(["ELE", input])
175
185
 
176
- if with_scores:
177
- pieces.append("WITHSCORES")
178
- options[CallbacksOptions.WITHSCORES.value] = True
186
+ if with_scores or with_attribs:
187
+ if get_protocol_version(self.client) in ["3", 3]:
188
+ options[CallbacksOptions.RESP3.value] = True
189
+
190
+ if with_scores:
191
+ pieces.append("WITHSCORES")
192
+ options[CallbacksOptions.WITHSCORES.value] = True
193
+
194
+ if with_attribs:
195
+ pieces.append("WITHATTRIBS")
196
+ options[CallbacksOptions.WITHATTRIBS.value] = True
179
197
 
180
198
  if count:
181
199
  pieces.extend(["COUNT", count])
@@ -210,7 +228,7 @@ class VectorSetCommands(CommandsProtocol):
210
228
 
211
229
  Raises `redis.exceptions.ResponseError` if the vector set doesn't exist.
212
230
 
213
- For more information see https://redis.io/commands/vdim
231
+ For more information, see https://redis.io/commands/vdim.
214
232
  """
215
233
  return self.execute_command(VDIM_CMD, key)
216
234
 
@@ -220,7 +238,7 @@ class VectorSetCommands(CommandsProtocol):
220
238
 
221
239
  Raises `redis.exceptions.ResponseError` if the vector set doesn't exist.
222
240
 
223
- For more information see https://redis.io/commands/vcard
241
+ For more information, see https://redis.io/commands/vcard.
224
242
  """
225
243
  return self.execute_command(VCARD_CMD, key)
226
244
 
@@ -228,7 +246,7 @@ class VectorSetCommands(CommandsProtocol):
228
246
  """
229
247
  Remove an element from a vector set.
230
248
 
231
- For more information see https://redis.io/commands/vrem
249
+ For more information, see https://redis.io/commands/vrem.
232
250
  """
233
251
  return self.execute_command(VREM_CMD, key, element)
234
252
 
@@ -242,10 +260,10 @@ class VectorSetCommands(CommandsProtocol):
242
260
  Get the approximated vector of an element ``element`` from vector set ``key``.
243
261
 
244
262
  ``raw`` is a boolean flag that indicates whether to return the
245
- interal representation used by the vector.
263
+ internal representation used by the vector.
246
264
 
247
265
 
248
- For more information see https://redis.io/commands/vembed
266
+ For more information, see https://redis.io/commands/vemb.
249
267
  """
250
268
  options = {}
251
269
  pieces = []
@@ -293,7 +311,7 @@ class VectorSetCommands(CommandsProtocol):
293
311
  If the ``WITHSCORES`` option is provided, the result is a list of dicts,
294
312
  where each dict contains the neighbors for one level, with the scores as values.
295
313
 
296
- For more information see https://redis.io/commands/vlinks
314
+ For more information, see https://redis.io/commands/vlinks
297
315
  """
298
316
  options = {}
299
317
  pieces = []
@@ -309,7 +327,7 @@ class VectorSetCommands(CommandsProtocol):
309
327
  """
310
328
  Get information about a vector set.
311
329
 
312
- For more information see https://redis.io/commands/vinfo
330
+ For more information, see https://redis.io/commands/vinfo.
313
331
  """
314
332
  return self.execute_command(VINFO_CMD, key)
315
333
 
@@ -320,7 +338,7 @@ class VectorSetCommands(CommandsProtocol):
320
338
  Associate or remove JSON attributes ``attributes`` of element ``element``
321
339
  for vector set ``key``.
322
340
 
323
- For more information see https://redis.io/commands/vsetattr
341
+ For more information, see https://redis.io/commands/vsetattr
324
342
  """
325
343
  if attributes is None:
326
344
  attributes_json = "{}"
@@ -336,12 +354,12 @@ class VectorSetCommands(CommandsProtocol):
336
354
  self, key: KeyT, element: str
337
355
  ) -> Union[Optional[Awaitable[dict]], Optional[dict]]:
338
356
  """
339
- Retrieve the JSON attributes of an element ``elemet`` for vector set ``key``.
357
+ Retrieve the JSON attributes of an element ``element `` for vector set ``key``.
340
358
 
341
359
  If the element does not exist, or if the vector set does not exist, None is
342
360
  returned.
343
361
 
344
- For more information see https://redis.io/commands/vgetattr
362
+ For more information, see https://redis.io/commands/vgetattr.
345
363
  """
346
364
  return self.execute_command(VGETATTR_CMD, key, element)
347
365
 
@@ -365,7 +383,7 @@ class VectorSetCommands(CommandsProtocol):
365
383
 
366
384
  If the vector set does not exist, ``None`` is returned.
367
385
 
368
- For more information see https://redis.io/commands/vrandmember
386
+ For more information, see https://redis.io/commands/vrandmember.
369
387
  """
370
388
  pieces = []
371
389
  pieces.append(key)