Skip to content

Commit 9c3b31a

Browse files
authored
Merge branch 'master' into allow-timeout-customization-when-using-redis-cluster
2 parents 25c6d82 + 43ce2a4 commit 9c3b31a

File tree

5 files changed

+88
-2
lines changed

5 files changed

+88
-2
lines changed

.github/workflows/integration.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
max-parallel: 15
7575
fail-fast: false
7676
matrix:
77-
redis-version: ['8.2-RC1-pre', '${{ needs.redis_version.outputs.CURRENT }}', '7.4.4', '7.2.9']
77+
redis-version: ['8.2', '${{ needs.redis_version.outputs.CURRENT }}', '7.4.4', '7.2.9']
7878
python-version: ['3.9', '3.13']
7979
parser-backend: ['plain']
8080
event-loop: ['asyncio']
@@ -99,7 +99,7 @@ jobs:
9999
fail-fast: false
100100
matrix:
101101
redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}' ]
102-
python-version: ['3.9', '3.10', '3.11', '3.12', 'pypy-3.9', 'pypy-3.10']
102+
python-version: ['3.10', '3.11', '3.12', 'pypy-3.9', 'pypy-3.10']
103103
parser-backend: [ 'plain' ]
104104
event-loop: [ 'asyncio' ]
105105
env:

redis/commands/vectorset/commands.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def vsim(
129129
filter_ef: Optional[str] = None,
130130
truth: Optional[bool] = False,
131131
no_thread: Optional[bool] = False,
132+
epsilon: Optional[Number] = None,
132133
) -> Union[
133134
Awaitable[Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]]],
134135
Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]],
@@ -152,6 +153,9 @@ def vsim(
152153
``no_thread`` when enabled forces the command to execute the search
153154
on the data structure in the main thread.
154155
156+
``epsilon`` floating point between 0 and 1, if specified will return
157+
only elements with distance no further than the specified one.
158+
155159
For more information see https://redis.io/commands/vsim
156160
"""
157161

@@ -176,6 +180,9 @@ def vsim(
176180
if count:
177181
pieces.extend(["COUNT", count])
178182

183+
if epsilon:
184+
pieces.extend(["EPSILON", epsilon])
185+
179186
if ef:
180187
pieces.extend(["EF", ef])
181188

tests/test_asyncio/test_vsets.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,21 @@ async def test_vsim_truth_no_thread_enabled(d_client):
423423
assert isinstance(sim_no_thread, dict)
424424

425425

426+
@skip_if_server_version_lt("8.2.0")
427+
async def test_vsim_epsilon(d_client):
428+
await d_client.vset().vadd("myset", [2, 1, 1], "a")
429+
await d_client.vset().vadd("myset", [2, 0, 1], "b")
430+
await d_client.vset().vadd("myset", [2, 0, 0], "c")
431+
await d_client.vset().vadd("myset", [2, 0, -1], "d")
432+
await d_client.vset().vadd("myset", [2, -1, -1], "e")
433+
434+
res1 = await d_client.vset().vsim("myset", [2, 1, 1])
435+
assert 5 == len(res1)
436+
437+
res2 = await d_client.vset().vsim("myset", [2, 1, 1], epsilon=0.5)
438+
assert 4 == len(res2)
439+
440+
426441
@skip_if_server_version_lt("7.9.0")
427442
async def test_vdim(d_client):
428443
float_array = [1, 4.32, 0.11, 0.5, 0.9, 0.1, 0.2]

tests/test_search.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3802,3 +3802,52 @@ def test_svs_vamana_vector_search_with_parameters(client):
38023802
else:
38033803
assert res["total_results"] == 3
38043804
assert "doc0" == res["results"][0]["id"]
3805+
3806+
3807+
@pytest.mark.redismod
3808+
@skip_ifmodversion_lt("2.4.3", "search")
3809+
@skip_if_server_version_lt("8.1.224")
3810+
def test_svs_vamana_vector_search_with_parameters_leanvec(client):
3811+
client.ft().create_index(
3812+
(
3813+
VectorField(
3814+
"v",
3815+
"SVS-VAMANA",
3816+
{
3817+
"TYPE": "FLOAT32",
3818+
"DIM": 8,
3819+
"DISTANCE_METRIC": "L2",
3820+
"COMPRESSION": "LeanVec8x8", # LeanVec compression required for REDUCE
3821+
"CONSTRUCTION_WINDOW_SIZE": 200,
3822+
"GRAPH_MAX_DEGREE": 32,
3823+
"SEARCH_WINDOW_SIZE": 15,
3824+
"EPSILON": 0.01,
3825+
"TRAINING_THRESHOLD": 1024,
3826+
"REDUCE": 4, # Half of DIM (8/2 = 4)
3827+
},
3828+
),
3829+
)
3830+
)
3831+
3832+
# Create test vectors (8-dimensional to match DIM)
3833+
vectors = [
3834+
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
3835+
[2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
3836+
[3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
3837+
[4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0],
3838+
[5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0],
3839+
]
3840+
3841+
for i, vec in enumerate(vectors):
3842+
client.hset(f"doc{i}", "v", np.array(vec, dtype=np.float32).tobytes())
3843+
3844+
query = Query("*=>[KNN 3 @v $vec as score]").no_content()
3845+
query_params = {"vec": np.array(vectors[0], dtype=np.float32).tobytes()}
3846+
3847+
res = client.ft().search(query, query_params=query_params)
3848+
if is_resp2_connection(client):
3849+
assert res.total == 3
3850+
assert "doc0" == res.docs[0].id
3851+
else:
3852+
assert res["total_results"] == 3
3853+
assert "doc0" == res["results"][0]["id"]

tests/test_vsets.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,21 @@ def test_vsim_truth_no_thread_enabled(d_client):
425425
assert isinstance(sim_no_thread, dict)
426426

427427

428+
@skip_if_server_version_lt("8.2.0")
429+
def test_vsim_epsilon(d_client):
430+
d_client.vset().vadd("myset", [2, 1, 1], "a")
431+
d_client.vset().vadd("myset", [2, 0, 1], "b")
432+
d_client.vset().vadd("myset", [2, 0, 0], "c")
433+
d_client.vset().vadd("myset", [2, 0, -1], "d")
434+
d_client.vset().vadd("myset", [2, -1, -1], "e")
435+
436+
res1 = d_client.vset().vsim("myset", [2, 1, 1])
437+
assert 5 == len(res1)
438+
439+
res2 = d_client.vset().vsim("myset", [2, 1, 1], epsilon=0.5)
440+
assert 4 == len(res2)
441+
442+
428443
@skip_if_server_version_lt("7.9.0")
429444
def test_vdim(d_client):
430445
float_array = [1, 4.32, 0.11, 0.5, 0.9, 0.1, 0.2]

0 commit comments

Comments
 (0)