diff --git a/pinecone/preview/models/score_by.py b/pinecone/preview/models/score_by.py index a5c6fc53..ce5e4308 100644 --- a/pinecone/preview/models/score_by.py +++ b/pinecone/preview/models/score_by.py @@ -15,7 +15,7 @@ ] -class PreviewTextQuery(Struct, tag="text", tag_field="type", kw_only=True): +class PreviewTextQuery(Struct, tag="text", tag_field="type", kw_only=True, omit_defaults=True): """Full-text search query for scoring documents. .. admonition:: Preview @@ -29,10 +29,33 @@ class PreviewTextQuery(Struct, tag="text", tag_field="type", kw_only=True): Attributes: fields: One or more text field names to search across. query: Search query string. + field: Deprecated alias for ``fields``. If provided, it is migrated + to ``fields=[field]`` and a ``DeprecationWarning`` is emitted. + Cleared to ``None`` after migration; read ``fields`` instead. """ - fields: list[str] query: str + fields: list[str] | None = None + field: str | None = None + + def __post_init__(self) -> None: + if self.field is not None: + if self.fields is not None: + raise ValueError( + "PreviewTextQuery accepts `fields=[...]` or the deprecated " + "`field=...`, but not both." + ) + import warnings + + warnings.warn( + "PreviewTextQuery `field` is deprecated; use `fields=[...]`.", + DeprecationWarning, + stacklevel=2, + ) + self.fields = [self.field] + self.field = None + elif self.fields is None: + raise ValueError("PreviewTextQuery requires `fields=[...]`.") class PreviewQueryStringQuery(Struct, tag="query_string", tag_field="type", kw_only=True): diff --git a/tests/unit/preview/models/test_score_by.py b/tests/unit/preview/models/test_score_by.py index 79ecd420..31649140 100644 --- a/tests/unit/preview/models/test_score_by.py +++ b/tests/unit/preview/models/test_score_by.py @@ -2,7 +2,10 @@ from __future__ import annotations +import warnings + import msgspec +import pytest from pinecone.preview.models.score_by import ( PreviewDenseVectorQuery, @@ -52,6 +55,55 @@ def test_text_query_no_extra_fields() -> None: assert not hasattr(q, "sparse_values") +# --------------------------------------------------------------------------- +# PreviewTextQuery — deprecated `field=` alias +# --------------------------------------------------------------------------- + + +def test_text_query_deprecated_field_kwarg_migrates_to_fields() -> None: + with pytest.warns(DeprecationWarning, match=r"`field` is deprecated"): + q = PreviewTextQuery(field="title", query="hello") + assert q.fields == ["title"] + assert q.field is None + + +def test_text_query_deprecated_field_kwarg_keeps_wire_shape_clean() -> None: + with pytest.warns(DeprecationWarning, match=r"`field` is deprecated"): + q = PreviewTextQuery(field="title", query="hello") + decoded = msgspec.json.decode(msgspec.json.encode(q)) + assert decoded == {"type": "text", "fields": ["title"], "query": "hello"} + assert "field" not in decoded + + +def test_text_query_canonical_fields_emits_no_warning() -> None: + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + q = PreviewTextQuery(fields=["title"], query="hello") + assert q.fields == ["title"] + assert q.field is None + + +def test_text_query_rejects_both_field_and_fields() -> None: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + with pytest.raises(ValueError, match="not both"): + PreviewTextQuery(field="title", fields=["body"], query="hello") + + +def test_text_query_rejects_neither_field_nor_fields() -> None: + with pytest.raises(ValueError, match="requires `fields"): + PreviewTextQuery(query="hello") + + +def test_text_query_decodes_backend_field_variant() -> None: + """Backend may return the legacy single-field form; decode should migrate.""" + raw = b'{"type": "text", "field": "body", "query": "hello"}' + with pytest.warns(DeprecationWarning, match=r"`field` is deprecated"): + result = msgspec.json.decode(raw, type=PreviewTextQuery) + assert result.fields == ["body"] + assert result.field is None + + # --------------------------------------------------------------------------- # PreviewQueryStringQuery # ---------------------------------------------------------------------------