Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pinecone/async_client/async_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,8 @@ async def update(
namespace (str): Namespace to target. Defaults to the default namespace.
filter (dict[str, Any] | None): Metadata filter expression selecting vectors to update.
dry_run (bool): If True, return the count of records that would be
affected without applying changes.
affected without applying changes. Only applies to filter-based
updates.

Returns:
:class:`UpdateResponse` with matched_records count (when available).
Expand Down Expand Up @@ -942,6 +943,8 @@ async def update(
raise ValidationError("Exactly one of id or filter must be provided, not both")
if not has_id and not has_filter:
raise ValidationError("Exactly one of id or filter must be provided, got neither")
if dry_run and has_id:
raise ValidationError("dry_run is only supported for filter-based updates")

body: dict[str, Any] = {"namespace": namespace}
if id is not None:
Expand Down
5 changes: 4 additions & 1 deletion pinecone/grpc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,8 @@ def update(
namespace (str): Namespace to target. Defaults to the default namespace.
filter (dict[str, Any] | None): Metadata filter expression selecting vectors to update.
dry_run (bool): If True, return the count of records that would be
affected without applying changes.
affected without applying changes. Only applies to filter-based
updates.
timeout (float | None): Per-call timeout in seconds. None uses the client-level default.

Returns:
Expand Down Expand Up @@ -795,6 +796,8 @@ def update(
raise ValidationError("Exactly one of id or filter must be provided, not both")
if not has_id and not has_filter:
raise ValidationError("Exactly one of id or filter must be provided, got neither")
if dry_run and has_id:
raise ValidationError("dry_run is only supported for filter-based updates")

# Convert SparseValues model to dict for GrpcChannel
sv_dict: Mapping[str, Any] | None = None
Expand Down
2 changes: 2 additions & 0 deletions pinecone/index/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,8 @@ def update(
raise ValidationError("Exactly one of id or filter must be provided, not both")
if not has_id and not has_filter:
raise ValidationError("Exactly one of id or filter must be provided, got neither")
if dry_run and has_id:
raise ValidationError("dry_run is only supported for filter-based updates")

body: dict[str, Any] = {"namespace": namespace}
if id is not None:
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/test_async_index_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,12 @@ async def test_update_both_id_and_filter_raises(self) -> None:
with pytest.raises(ValidationError, match="not both"):
await idx.update(id="vec1", filter={"x": 1})

@pytest.mark.asyncio
async def test_update_dry_run_with_id_raises(self) -> None:
idx = _make_async_index()
with pytest.raises(ValidationError, match="dry_run is only supported"):
await idx.update(id="vec1", set_metadata={"genre": "comedy"}, dry_run=True)

@respx.mock
@pytest.mark.asyncio
async def test_update_dry_run(self) -> None:
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/test_grpc_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ def test_update_validates_neither_id_nor_filter(self, grpc_index: GrpcIndex) ->
with pytest.raises(ValidationError, match="got neither"):
grpc_index.update(values=[0.1])

def test_update_validates_dry_run_with_id(self, grpc_index: GrpcIndex) -> None:
with pytest.raises(ValidationError, match="dry_run is only supported"):
grpc_index.update(id="v1", set_metadata={"key": "new_val"}, dry_run=True)

def test_update_by_filter_with_dry_run(
self, grpc_index: GrpcIndex, mock_channel: MagicMock
) -> None:
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/test_index_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ def test_neither_id_nor_filter_raises(self) -> None:
with pytest.raises(ValidationError, match="got neither"):
idx.update(values=[0.1])

def test_dry_run_with_id_raises(self) -> None:
idx = _make_index()
with pytest.raises(ValidationError, match="dry_run is only supported"):
idx.update(id="vec1", set_metadata={"genre": "comedy"}, dry_run=True)

@respx.mock
def test_dry_run_not_in_body_when_false(self) -> None:
route = respx.post(UPDATE_URL).mock(
Expand Down