diff --git a/pinecone/models/vectors/search.py b/pinecone/models/vectors/search.py index 05e0cc95..3a334127 100644 --- a/pinecone/models/vectors/search.py +++ b/pinecone/models/vectors/search.py @@ -83,6 +83,9 @@ class SearchUsage(StructDictMixin, Struct, kw_only=True): rerank_units: int | None = None +_WIRE_KEY_ALIASES = {"_id": "id", "_score": "score"} + + class Hit(StructDictMixin, Struct, kw_only=True, rename={"id_": "_id", "score_": "_score"}): """A single search result hit. @@ -117,7 +120,15 @@ def __getitem__(self, key: str) -> Any: if key == "score": return self.score_ if key not in self.__struct_fields__: - raise KeyError(key) + alias = _WIRE_KEY_ALIASES.get(key) + if alias is not None: + raise KeyError( + f"{key!r} is the wire name for this field; use hit[{alias!r}] or hit.{alias}" + ) + raise KeyError( + f"{key!r}; available keys are 'id', 'score', and 'fields' " + "(record fields live in hit['fields'])" + ) return getattr(self, key) def __contains__(self, key: object) -> bool: diff --git a/tests/unit/test_search_models.py b/tests/unit/test_search_models.py index b157ffc0..40ed3e12 100644 --- a/tests/unit/test_search_models.py +++ b/tests/unit/test_search_models.py @@ -35,6 +35,29 @@ def test_hit_bracket_missing_key(self) -> None: with pytest.raises(KeyError, match="nonexistent"): hit["nonexistent"] + def test_hit_bracket_missing_key_lists_available_keys(self) -> None: + hit = Hit(id_="r1", score_=0.5) + with pytest.raises(KeyError) as excinfo: + hit["nonexistent"] + assert "available keys" in str(excinfo.value) + + def test_hit_bracket_wire_name_id_teaches_alias(self) -> None: + """The wire format calls the field '_id'; the error must point at hit['id'].""" + hit = Hit(id_="r1", score_=0.5) + with pytest.raises(KeyError) as excinfo: + hit["_id"] + message = str(excinfo.value) + assert "wire name" in message + assert "hit['id']" in message + + def test_hit_bracket_wire_name_score_teaches_alias(self) -> None: + hit = Hit(id_="r1", score_=0.5) + with pytest.raises(KeyError) as excinfo: + hit["_score"] + message = str(excinfo.value) + assert "wire name" in message + assert "hit['score']" in message + class TestSearchUsage: def test_search_usage_required_only(self) -> None: