redis 6.3.0__py3-none-any.whl → 7.0.0b1__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.
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.
@@ -1,3 +1,5 @@
1
- from typing import Any, Dict, List, Union
1
+ from typing import List, Mapping, Union
2
2
 
3
- JsonType = Union[str, int, float, bool, None, Dict[str, Any], List[Any]]
3
+ JsonType = Union[
4
+ str, int, float, bool, None, Mapping[str, "JsonType"], List["JsonType"]
5
+ ]
@@ -26,7 +26,7 @@ class Reducer:
26
26
 
27
27
  NAME = None
28
28
 
29
- def __init__(self, *args: List[str]) -> None:
29
+ def __init__(self, *args: str) -> None:
30
30
  self._args = args
31
31
  self._field = None
32
32
  self._alias = None
@@ -116,7 +116,7 @@ class AggregateRequest:
116
116
  self._add_scores = False
117
117
  self._scorer = "TFIDF"
118
118
 
119
- def load(self, *fields: List[str]) -> "AggregateRequest":
119
+ def load(self, *fields: str) -> "AggregateRequest":
120
120
  """
121
121
  Indicate the fields to be returned in the response. These fields are
122
122
  returned in addition to any others implicitly specified.
@@ -223,7 +223,7 @@ class AggregateRequest:
223
223
  self._aggregateplan.extend(_limit.build_args())
224
224
  return self
225
225
 
226
- def sort_by(self, *fields: List[str], **kwargs) -> "AggregateRequest":
226
+ def sort_by(self, *fields: str, **kwargs) -> "AggregateRequest":
227
227
  """
228
228
  Indicate how the results should be sorted. This can also be used for
229
229
  *top-N* style queries
@@ -542,7 +542,7 @@ class SearchCommands:
542
542
 
543
543
  def aggregate(
544
544
  self,
545
- query: Union[str, Query],
545
+ query: Union[AggregateRequest, Cursor],
546
546
  query_params: Dict[str, Union[str, int, float]] = None,
547
547
  ):
548
548
  """
@@ -573,7 +573,7 @@ class SearchCommands:
573
573
  )
574
574
 
575
575
  def _get_aggregate_result(
576
- self, raw: List, query: Union[str, Query, AggregateRequest], has_cursor: bool
576
+ self, raw: List, query: Union[AggregateRequest, Cursor], has_cursor: bool
577
577
  ):
578
578
  if has_cursor:
579
579
  if isinstance(query, Cursor):
@@ -967,7 +967,7 @@ class AsyncSearchCommands(SearchCommands):
967
967
 
968
968
  async def aggregate(
969
969
  self,
970
- query: Union[str, Query],
970
+ query: Union[AggregateResult, Cursor],
971
971
  query_params: Dict[str, Union[str, int, float]] = None,
972
972
  ):
973
973
  """
@@ -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
 
@@ -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
@@ -130,7 +130,7 @@ class 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
@@ -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
@@ -292,7 +292,7 @@ class 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**: A list of strings; case-sensitive field names
296
296
  from the defined schema.
297
297
  """
298
298
  self._fields = fields
@@ -301,7 +301,7 @@ class Query:
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
  """
@@ -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,36 +133,40 @@ 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,
129
140
  filter_ef: Optional[str] = None,
130
141
  truth: Optional[bool] = False,
131
142
  no_thread: Optional[bool] = False,
132
- ) -> Union[
133
- Awaitable[Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]]],
134
- Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]],
135
- ]:
143
+ epsilon: Optional[Number] = None,
144
+ ) -> Union[Awaitable[VSimResult], VSimResult]:
136
145
  """
137
146
  Compare a vector or element ``input`` with the other vectors in a vector set ``key``.
138
147
 
139
- ``with_scores`` sets if the results should be returned with the
140
- 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.
141
152
 
142
153
  ``count`` sets the number of results to return.
143
154
 
144
155
  ``ef`` sets the exploration factor.
145
156
 
146
- ``filter`` sets filter that should be applied for the search.
157
+ ``filter`` sets the filter that should be applied for the search.
147
158
 
148
159
  ``filter_ef`` sets the max filtering effort.
149
160
 
150
- ``truth`` when enabled forces the command to perform linear scan.
161
+ ``truth`` when enabled, forces the command to perform a linear scan.
151
162
 
152
163
  ``no_thread`` when enabled forces the command to execute the search
153
164
  on the data structure in the main thread.
154
165
 
155
- For more information see https://redis.io/commands/vsim
166
+ ``epsilon`` floating point between 0 and 1, if specified will return
167
+ only elements with distance no further than the specified one.
168
+
169
+ For more information, see https://redis.io/commands/vsim.
156
170
  """
157
171
 
158
172
  if not input:
@@ -169,13 +183,24 @@ class VectorSetCommands(CommandsProtocol):
169
183
  else:
170
184
  pieces.extend(["ELE", input])
171
185
 
172
- if with_scores:
173
- pieces.append("WITHSCORES")
174
- 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
175
197
 
176
198
  if count:
177
199
  pieces.extend(["COUNT", count])
178
200
 
201
+ if epsilon:
202
+ pieces.extend(["EPSILON", epsilon])
203
+
179
204
  if ef:
180
205
  pieces.extend(["EF", ef])
181
206
 
@@ -203,7 +228,7 @@ class VectorSetCommands(CommandsProtocol):
203
228
 
204
229
  Raises `redis.exceptions.ResponseError` if the vector set doesn't exist.
205
230
 
206
- For more information see https://redis.io/commands/vdim
231
+ For more information, see https://redis.io/commands/vdim.
207
232
  """
208
233
  return self.execute_command(VDIM_CMD, key)
209
234
 
@@ -213,7 +238,7 @@ class VectorSetCommands(CommandsProtocol):
213
238
 
214
239
  Raises `redis.exceptions.ResponseError` if the vector set doesn't exist.
215
240
 
216
- For more information see https://redis.io/commands/vcard
241
+ For more information, see https://redis.io/commands/vcard.
217
242
  """
218
243
  return self.execute_command(VCARD_CMD, key)
219
244
 
@@ -221,7 +246,7 @@ class VectorSetCommands(CommandsProtocol):
221
246
  """
222
247
  Remove an element from a vector set.
223
248
 
224
- For more information see https://redis.io/commands/vrem
249
+ For more information, see https://redis.io/commands/vrem.
225
250
  """
226
251
  return self.execute_command(VREM_CMD, key, element)
227
252
 
@@ -235,10 +260,10 @@ class VectorSetCommands(CommandsProtocol):
235
260
  Get the approximated vector of an element ``element`` from vector set ``key``.
236
261
 
237
262
  ``raw`` is a boolean flag that indicates whether to return the
238
- interal representation used by the vector.
263
+ internal representation used by the vector.
239
264
 
240
265
 
241
- For more information see https://redis.io/commands/vembed
266
+ For more information, see https://redis.io/commands/vemb.
242
267
  """
243
268
  options = {}
244
269
  pieces = []
@@ -286,7 +311,7 @@ class VectorSetCommands(CommandsProtocol):
286
311
  If the ``WITHSCORES`` option is provided, the result is a list of dicts,
287
312
  where each dict contains the neighbors for one level, with the scores as values.
288
313
 
289
- For more information see https://redis.io/commands/vlinks
314
+ For more information, see https://redis.io/commands/vlinks
290
315
  """
291
316
  options = {}
292
317
  pieces = []
@@ -302,7 +327,7 @@ class VectorSetCommands(CommandsProtocol):
302
327
  """
303
328
  Get information about a vector set.
304
329
 
305
- For more information see https://redis.io/commands/vinfo
330
+ For more information, see https://redis.io/commands/vinfo.
306
331
  """
307
332
  return self.execute_command(VINFO_CMD, key)
308
333
 
@@ -313,7 +338,7 @@ class VectorSetCommands(CommandsProtocol):
313
338
  Associate or remove JSON attributes ``attributes`` of element ``element``
314
339
  for vector set ``key``.
315
340
 
316
- For more information see https://redis.io/commands/vsetattr
341
+ For more information, see https://redis.io/commands/vsetattr
317
342
  """
318
343
  if attributes is None:
319
344
  attributes_json = "{}"
@@ -329,12 +354,12 @@ class VectorSetCommands(CommandsProtocol):
329
354
  self, key: KeyT, element: str
330
355
  ) -> Union[Optional[Awaitable[dict]], Optional[dict]]:
331
356
  """
332
- 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``.
333
358
 
334
359
  If the element does not exist, or if the vector set does not exist, None is
335
360
  returned.
336
361
 
337
- For more information see https://redis.io/commands/vgetattr
362
+ For more information, see https://redis.io/commands/vgetattr.
338
363
  """
339
364
  return self.execute_command(VGETATTR_CMD, key, element)
340
365
 
@@ -358,7 +383,7 @@ class VectorSetCommands(CommandsProtocol):
358
383
 
359
384
  If the vector set does not exist, ``None`` is returned.
360
385
 
361
- For more information see https://redis.io/commands/vrandmember
386
+ For more information, see https://redis.io/commands/vrandmember.
362
387
  """
363
388
  pieces = []
364
389
  pieces.append(key)
@@ -1,3 +1,5 @@
1
+ import json
2
+
1
3
  from redis._parsers.helpers import pairs_to_dict
2
4
  from redis.commands.vectorset.commands import CallbacksOptions
3
5
 
@@ -75,19 +77,53 @@ def parse_vsim_result(response, **options):
75
77
  structures depending on input options.
76
78
  Parsing VSIM result into:
77
79
  - List[List[str]]
78
- - List[Dict[str, Number]]
80
+ - List[Dict[str, Number]] - when with_scores is used (without attributes)
81
+ - List[Dict[str, Mapping[str, Any]]] - when with_attribs is used (without scores)
82
+ - List[Dict[str, Union[Number, Mapping[str, Any]]]] - when with_scores and with_attribs are used
83
+
79
84
  """
80
85
  if response is None:
81
86
  return response
82
87
 
83
- if options.get(CallbacksOptions.WITHSCORES.value):
88
+ withscores = bool(options.get(CallbacksOptions.WITHSCORES.value))
89
+ withattribs = bool(options.get(CallbacksOptions.WITHATTRIBS.value))
90
+
91
+ # Exactly one of withscores or withattribs is True
92
+ if (withscores and not withattribs) or (not withscores and withattribs):
84
93
  # Redis will return a list of list of pairs.
85
94
  # This list have to be transformed to dict
86
95
  result_dict = {}
87
- for key, value in pairs_to_dict(response).items():
88
- value = float(value)
96
+ if options.get(CallbacksOptions.RESP3.value):
97
+ resp_dict = response
98
+ else:
99
+ resp_dict = pairs_to_dict(response)
100
+ for key, value in resp_dict.items():
101
+ if withscores:
102
+ value = float(value)
103
+ else:
104
+ value = json.loads(value) if value else None
105
+
89
106
  result_dict[key] = value
90
107
  return result_dict
108
+ elif withscores and withattribs:
109
+ it = iter(response)
110
+ result_dict = {}
111
+ if options.get(CallbacksOptions.RESP3.value):
112
+ for elem, data in response.items():
113
+ if data[1] is not None:
114
+ attribs_dict = json.loads(data[1])
115
+ else:
116
+ attribs_dict = None
117
+ result_dict[elem] = {"score": data[0], "attributes": attribs_dict}
118
+ else:
119
+ for elem, score, attribs in zip(it, it, it):
120
+ if attribs is not None:
121
+ attribs_dict = json.loads(attribs)
122
+ else:
123
+ attribs_dict = None
124
+
125
+ result_dict[elem] = {"score": float(score), "attributes": attribs_dict}
126
+ return result_dict
91
127
  else:
92
128
  # return the list of elements for each level
93
129
  # list of lists