From 7527890607bfe55c2fc457766990d446dc29d4cb Mon Sep 17 00:00:00 2001 From: kid <19265318+u70b3@users.noreply.github.com> Date: Tue, 14 Jul 2026 03:28:26 +0800 Subject: [PATCH] fix(python): preserve zero distance bounds in hybrid search (#3652) ## Summary - preserve explicit `0.0` distance bounds in synchronous hybrid search - distinguish omitted `None` endpoints from zero-valued endpoints when configuring the vector child query - add a public end-to-end regression test for a zero upper bound ## Testing - `cd python && uv run --extra tests pytest python/tests/test_hybrid_query.py -q` - `uv run --project python ruff format --check python/python/lancedb/query.py python/python/tests/test_hybrid_query.py` - `uv run --project python ruff check .` Fixes #3651 --- python/python/lancedb/query.py | 2 +- python/python/tests/test_hybrid_query.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/python/python/lancedb/query.py b/python/python/lancedb/query.py index 97be089c6..992463f0c 100644 --- a/python/python/lancedb/query.py +++ b/python/python/lancedb/query.py @@ -2634,7 +2634,7 @@ class LanceHybridQueryBuilder(LanceQueryBuilder): self._vector_query.ef(self._ef) if self._bypass_vector_index: self._vector_query.bypass_vector_index() - if self._lower_bound or self._upper_bound: + if self._lower_bound is not None or self._upper_bound is not None: self._vector_query.distance_range( lower_bound=self._lower_bound, upper_bound=self._upper_bound ) diff --git a/python/python/tests/test_hybrid_query.py b/python/python/tests/test_hybrid_query.py index d0712f16c..f2bbf3180 100644 --- a/python/python/tests/test_hybrid_query.py +++ b/python/python/tests/test_hybrid_query.py @@ -139,6 +139,20 @@ def test_hybrid_query_distance_range(sync_table: Table): assert 0.2 <= dist.as_py() <= 0.5 +def test_hybrid_query_applies_zero_upper_distance_bound(sync_table: Table): + result = ( + sync_table.search(query_type="hybrid") + .vector([0.0, 0.4]) + .text("elephant") + .distance_range(upper_bound=0.0) + .rerank(RRFReranker(return_score="all")) + .limit(4) + .to_arrow() + ) + + assert len(result) == 0 + + @pytest.mark.asyncio async def test_hybrid_query_distance_range_async(table: AsyncTable): reranker = RRFReranker(return_score="all")