fix(python): allow phrase queries without a column (#4653)

Quoted async full-text queries without `columns` carry an unset phrase
column, but converting them to Python unwrapped it and raised
`PanicException`. Python also required `PhraseQuery.column`, preventing
callers from constructing the equivalent query object.

Make the Python column optional and preserve `Option<String>` in both
Rust conversion directions, leaving column selection to query planning.
Regression tests cover quoted strings and structured queries with
omitted, `None`, and explicit columns, query reuse, JSON serialization,
slop, and document granularity.

Validation:

- Reproduced both missing-column failures before the fix;
explicit-column controls passed.
- Rebuilt the extension with `uv run --extra tests --extra dev maturin
develop --extras tests,dev`.
- `cd python && uv run --no-sync --extra tests pytest
python/tests/test_fts.py
python/tests/test_query.py::test_query_serialization_async -q`: 85
passed.
- `cargo fmt --all -- --check` and `ruff check python` pass; touched
Python files pass formatting checks.
- `cargo clippy --quiet -p lancedb-python --lib --features remote`
passes.
- Root-wide `ruff check .` reports 49 existing findings in unchanged
documentation examples.

Fixes #4651

<!-- lance-gatekeeper-fix:v1 agent=62d36fad70c8717236283b8e97dd6b30
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:
lancedb-gatefixer[bot]
2026-10-27 15:53:14 +08:00
committed by GitHub
co-authored by Xuanwo
parent 8887c82789
commit 1edfa6077b
3 changed files with 52 additions and 8 deletions
+3 -5
View File
@@ -103,7 +103,7 @@ impl<'a, 'py> FromPyObject<'a, 'py> for PyLanceDB<FtsQuery> {
}
"PhraseQuery" => {
let query = ob.getattr("query")?.extract()?;
let column = ob.getattr("column")?.extract()?;
let column = ob.getattr("column")?.extract::<Option<String>>()?;
let slop = ob.getattr("slop")?.extract()?;
let document_granularity = ob
.getattr("document_granularity")?
@@ -114,9 +114,7 @@ impl<'a, 'py> FromPyObject<'a, 'py> for PyLanceDB<FtsQuery> {
})
.transpose()?;
let mut query = PhraseQuery::new(query)
.with_column(Some(column))
.with_slop(slop);
let mut query = PhraseQuery::new(query).with_column(column).with_slop(slop);
if let Some(document_granularity) = document_granularity {
query = query.with_document_granularity(document_granularity);
}
@@ -213,7 +211,7 @@ impl<'py> IntoPyObject<'py> for PyLanceDB<FtsQuery> {
}
namespace
.getattr(intern!(py, "PhraseQuery"))?
.call((query.terms, query.column.unwrap()), Some(&kwargs))
.call((query.terms, query.column), Some(&kwargs))
}
FtsQuery::Boost(query) => {
let positive = Self(query.positive.as_ref().clone()).into_pyobject(py)?;