Skip to content

Commit 932de36

Browse files
committed
Fix vector type fields should not be encoded as strings (redis#2772)
1 parent 0d47d65 commit 932de36

File tree

4 files changed

+50
-13
lines changed

4 files changed

+50
-13
lines changed

dev_requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ ujson>=4.2.0
1616
wheel>=0.30.0
1717
urllib3<2
1818
uvloop
19+
numpy>=1.24.4

redis/_parsers/encoders.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,9 @@ def decode(self, value, force=False):
4040
if isinstance(value, memoryview):
4141
value = value.tobytes()
4242
if isinstance(value, bytes):
43-
value = value.decode(self.encoding, self.encoding_errors)
43+
try:
44+
value = value.decode(self.encoding, self.encoding_errors)
45+
except UnicodeDecodeError as e:
46+
# Return the bytes unmodified
47+
return value
4448
return value

redis/commands/search/result.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,11 @@ def __init__(
3939

4040
fields = {}
4141
if hascontent and res[i + fields_offset] is not None:
42-
fields = (
43-
dict(
44-
dict(
45-
zip(
46-
map(to_string, res[i + fields_offset][::2]),
47-
map(to_string, res[i + fields_offset][1::2]),
48-
)
49-
)
50-
)
51-
if hascontent
52-
else {}
53-
)
42+
for j in range(0, len(res[i + fields_offset]), 2):
43+
key = to_string(res[i + fields_offset][j])
44+
value = res[i + fields_offset][j + 1]
45+
fields[key] = value
46+
5447
try:
5548
del fields["id"]
5649
except KeyError:

tests/test_search.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import time
55
from io import TextIOWrapper
66

7+
import numpy as np
8+
79
import pytest
810
import redis
911
import redis.commands.search
@@ -2282,3 +2284,40 @@ def test_geoshape(client: redis.Redis):
22822284
assert result.docs[0]["id"] == "small"
22832285
result = client.ft().search(q2, query_params=qp2)
22842286
assert len(result.docs) == 2
2287+
2288+
@pytest.mark.redismod
2289+
def test_vector_storage_and_retrieval(client):
2290+
# Constants
2291+
INDEX_NAME = "vector_index"
2292+
DOC_PREFIX = "doc:"
2293+
VECTOR_DIMENSIONS = 4
2294+
VECTOR_FIELD_NAME = "my_vector"
2295+
2296+
# Create index
2297+
client.ft(INDEX_NAME).create_index((
2298+
VectorField(VECTOR_FIELD_NAME,
2299+
"FLAT", {
2300+
"TYPE": "FLOAT32",
2301+
"DIM": VECTOR_DIMENSIONS,
2302+
"DISTANCE_METRIC": "COSINE",
2303+
}
2304+
),
2305+
), definition=IndexDefinition(prefix=[DOC_PREFIX], index_type=IndexType.HASH))
2306+
2307+
# Add a document with a vector value
2308+
vector_data = [0.1, 0.2, 0.3, 0.4]
2309+
client.hset(f"{DOC_PREFIX}1", mapping={
2310+
VECTOR_FIELD_NAME: np.array(vector_data, dtype=np.float32).tobytes()
2311+
})
2312+
2313+
# Perform a search to retrieve the document
2314+
query = (
2315+
Query("*").return_fields(VECTOR_FIELD_NAME).dialect(2)
2316+
)
2317+
res = client.ft(INDEX_NAME).search(query)
2318+
2319+
# Assert that the document is retrieved and the vector matches the original data
2320+
assert res.total == 1
2321+
assert res.docs[0].id == f"{DOC_PREFIX}1"
2322+
retrieved_vector_data = np.frombuffer(res.docs[0].__dict__[VECTOR_FIELD_NAME], dtype=np.float32)
2323+
assert np.allclose(retrieved_vector_data, vector_data)

0 commit comments

Comments
 (0)