mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-28 22:46:14 +00:00
fix(python): preserve async query search options during serialization (#4659)
Async `to_query_object()` dropped enabled `fast_search` on every query type and `postfilter` on plain and full-text queries. Re-executing a serialized fast search could therefore return unindexed rows. Copy enabled `fast_search` into the Python `Query` and serialize enabled plain/FTS postfilter from the Rust request's prefilter setting. Preserve the existing unset defaults: `None` for these options, with the vector query's existing `False` postfilter default. Validation: - Added 12 serialization cases across plain, FTS, and vector queries, covering both enabled and unset options, plus two FTS execution round-trip cases. Before the fix, nine cases failed, including returning an unindexed row after serialization. - Rebuilt the Python extension using the uv-managed environment. - Query tests and existing FTS fast-search tests: 192 passed, 1 slow test deselected. - Rust formatting and Python package lint/format checks pass. Repository-wide `ruff check .` reports 49 pre-existing errors in unchanged `docs/web-tests` files. Fixes #4658 <!-- lance-gatekeeper-fix:v1 agent=f828b24871a2dc928668be2de4f85078 generation=1 --> Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Co-authored-by: Xuanwo <github@xuanwo.io>
This commit is contained in:
co-authored by
Xuanwo
parent
1edfa6077b
commit
7fca4d666a
@@ -910,6 +910,8 @@ class Query(pydantic.BaseModel):
|
||||
query.filter = req.filter
|
||||
query.full_text_query = req.full_text_search
|
||||
query.columns = req.select
|
||||
# Keep the unset value for this opt-in flag, as in the sync builders.
|
||||
query.fast_search = req.fast_search or None
|
||||
query.with_row_id = req.with_row_id
|
||||
# use_rte is a genuine tri-state (None / True / False); preserve it as-is
|
||||
# so a round-tripped query keeps an explicit False.
|
||||
|
||||
@@ -1977,6 +1977,56 @@ def test_query_serialization_sync(table: lancedb.table.Table):
|
||||
check_set_props(q, full_text_query=FullTextSearchQuery(columns=[], query="foo"))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("query_type", ["plain", "fts", "vector"])
|
||||
@pytest.mark.parametrize("fast_search", [False, True])
|
||||
@pytest.mark.parametrize("postfilter", [False, True])
|
||||
async def test_query_serialization_async_search_options(
|
||||
table_async: AsyncTable, query_type, fast_search, postfilter
|
||||
):
|
||||
query = table_async.query().where("id = 1")
|
||||
if query_type == "fts":
|
||||
query = query.nearest_to_text(MatchQuery("dog", "text"))
|
||||
elif query_type == "vector":
|
||||
query = query.nearest_to([1.0, 2.0])
|
||||
if fast_search:
|
||||
query = query.fast_search()
|
||||
if postfilter:
|
||||
query = query.postfilter()
|
||||
|
||||
serialized = query.to_query_object()
|
||||
assert serialized.fast_search is (True if fast_search else None)
|
||||
default_postfilter = False if query_type == "vector" else None
|
||||
assert serialized.postfilter is (True if postfilter else default_postfilter)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("fast_search", [False, True])
|
||||
async def test_query_serialization_async_fast_search_execution(
|
||||
table_async, fast_search
|
||||
):
|
||||
await table_async.create_index("text", config=FTS())
|
||||
await table_async.add(
|
||||
[
|
||||
{
|
||||
"id": 3,
|
||||
"vector": [5.0, 6.0],
|
||||
"str_field": "c",
|
||||
"float_field": 3.0,
|
||||
"text": "unindexed dog",
|
||||
}
|
||||
]
|
||||
)
|
||||
query = table_async.query().nearest_to_text(MatchQuery("unindexed", "text"))
|
||||
if fast_search:
|
||||
query = query.fast_search()
|
||||
|
||||
expected_ids = [] if fast_search else [3]
|
||||
assert [row["id"] for row in await query.to_list()] == expected_ids
|
||||
restored = table_async._sync_query_to_async(query.to_query_object())
|
||||
assert [row["id"] for row in await restored.to_list()] == expected_ids
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_serialization_async(table_async: AsyncTable):
|
||||
# Simple queries
|
||||
|
||||
+1
-1
@@ -377,7 +377,7 @@ impl From<AnyQuery> for PyQueryRequest {
|
||||
refine_factor: None,
|
||||
distance_type: None,
|
||||
bypass_vector_index: None,
|
||||
postfilter: None,
|
||||
postfilter: (!query_request.prefilter).then_some(true),
|
||||
norm: None,
|
||||
order_by: query_request
|
||||
.order_by
|
||||
|
||||
Reference in New Issue
Block a user