mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-12 14:52:26 +00:00
fix(python): accept automatic fuzziness in MatchQuery (#4467)
`MatchQuery(..., fuzziness=None)` raised a Pydantic validation error before a search could run because the field accepted only integers. Allow `Optional[int]`, retain the exact-match default of `0`, and make the documented parameter type explicit. The existing Rust binding already accepts and serializes the optional value. Regression coverage checks construction and JSON serialization for `None`, `0`, `1`, and `2`, plus native indexed searches with the default, explicit exact matching, automatic mode, and one- and two-edit controls. The two `None` regressions reproduced the original constructor error before the fix. Validation against the source-built extension in the locked uv environment: - `uv run --extra tests --extra dev maturin develop --extras tests,dev` - `uv run --no-sync --extra tests --extra dev pytest python/tests/test_fts.py -k 'match_query_fuzziness or fts_query_to_json' -q` — 10 passed. - `uv run --no-sync --extra tests --extra dev pytest python/tests/test_fts.py -q --disable-warnings` — 61 passed. - Locked-environment `ruff check .`, `ruff format .`, and `git diff --check` passed. The reported engine limitation remains observable with pinned Lance `14.0.0-beta.1`: its dataset planner compatibility gate rewrites `None` to zero, so a five-character AUTO query still omits one-edit matches. Automatic expansion for longer terms requires upstream Lance activation. The native acceptance regression uses a two-character term, whose documented automatic distance is zero, while the JSON regression verifies that Python forwards `None` as `null`. This keeps the repair within the issue's requested Python constructor scope. Fixes #4466 <!-- lance-gatekeeper-fix:v1 agent=f5f78166a4f0c7433e7890792b729c3d generation=1 --> Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com>
This commit is contained in:
@@ -479,7 +479,7 @@ class MatchQuery(FullTextQuery):
|
||||
boost : float, default 1.0
|
||||
The boost factor for the query.
|
||||
The score of each matching document is multiplied by this value.
|
||||
fuzziness : int, optional
|
||||
fuzziness : int or None, default 0
|
||||
The maximum edit distance for each term in the match query.
|
||||
Defaults to 0 (exact match).
|
||||
If None, fuzziness is applied automatically by the rules:
|
||||
@@ -506,7 +506,7 @@ class MatchQuery(FullTextQuery):
|
||||
query: str
|
||||
column: str
|
||||
boost: float = pydantic.Field(1.0, kw_only=True)
|
||||
fuzziness: int = pydantic.Field(0, kw_only=True)
|
||||
fuzziness: Optional[int] = pydantic.Field(0, kw_only=True)
|
||||
max_expansions: int = pydantic.Field(50, kw_only=True)
|
||||
operator: FullTextOperator = pydantic.Field(FullTextOperator.OR, kw_only=True)
|
||||
prefix_length: int = pydantic.Field(0, kw_only=True)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
@@ -1137,6 +1138,34 @@ def test_fts_lindera_tokenizer(
|
||||
assert [row["text"] for row in results] == ["成田国際空港"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fuzziness", [None, 0, 1, 2])
|
||||
def test_match_query_fuzziness_to_json(fuzziness):
|
||||
query = MatchQuery("puppy", "text", fuzziness=fuzziness)
|
||||
|
||||
assert query.fuzziness == fuzziness
|
||||
assert json.loads(query.to_json())["match"]["fuzziness"] == fuzziness
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"options, expected",
|
||||
[
|
||||
pytest.param({}, ["ab"], id="default-exact"),
|
||||
pytest.param({"fuzziness": 0}, ["ab"], id="explicit-exact"),
|
||||
pytest.param({"fuzziness": None}, ["ab"], id="automatic"),
|
||||
pytest.param({"fuzziness": 1}, ["ab", "ac"], id="one-edit"),
|
||||
pytest.param({"fuzziness": 2}, ["ab", "ac", "zz"], id="two-edits"),
|
||||
],
|
||||
)
|
||||
def test_match_query_fuzziness_search(tmp_db, options, expected):
|
||||
# Automatic fuzziness is zero for two-character terms.
|
||||
table = tmp_db.create_table("fuzzy", pa.table({"text": ["ab", "ac", "zz"]}))
|
||||
table.create_index("text", config=FTS(stem=False, remove_stop_words=False))
|
||||
|
||||
results = table.search(MatchQuery("ab", "text", **options)).to_list()
|
||||
|
||||
assert sorted(row["text"] for row in results) == sorted(expected)
|
||||
|
||||
|
||||
def test_fts_query_to_json():
|
||||
"""Test that FTS query to_json() produces valid JSON strings with exact format."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user