diff --git a/python/python/lancedb/query.py b/python/python/lancedb/query.py index 66aae06a6..644b6571f 100644 --- a/python/python/lancedb/query.py +++ b/python/python/lancedb/query.py @@ -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. diff --git a/python/python/tests/test_query.py b/python/python/tests/test_query.py index cef985aa6..3d7c01fc5 100644 --- a/python/python/tests/test_query.py +++ b/python/python/tests/test_query.py @@ -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 diff --git a/python/src/query.rs b/python/src/query.rs index bf267443c..c2bd6afb3 100644 --- a/python/src/query.rs +++ b/python/src/query.rs @@ -377,7 +377,7 @@ impl From 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