Repository navigation
Delete keys and documents without reading the records first, add WithDocumentDeletionKey, WithEmptyDocumentDeletions and WithReadsBeyondKeyQuota, and let a collection client choose once how it deletes documents - #126
Merged
Conversation
… so that concurrent writes of a record do not mix
…s a document copy key
… without a document copy key, so no write reads the documents first
…ads before it writes only in some runs
…ly beyond its quota of 32 keys
…rite, which deletes its keys
…nulls too, and Chroma stores it without the keys
…etadata key instead of copying every document
…n empty one, read back as an empty string
…t the three ways in the error of NullDocumentsDelete
…cs/metadata.md, with the version
…, and throw only for a change of way
There was a problem hiding this comment.
🟡 Changes recommended
Cloud quota filtering miscounts duplicate deletion keys and breaks ordered behavior for repeated IDs.
2 open findings
What changed in this PR
This PR makes metadata/document deletions atomic with their writes and adds configurable document-deletion strategies.
Changes:
- Sends deletion values without preliminary reads, except for Chroma Cloud quota handling.
- Adds deletion-key and empty-document strategies alongside document-copy keys.
- Expands concurrency, serialization, and integration coverage.
| File | Description |
|---|---|
docs/metadata.md |
Documents deletion behavior and strategies. |
ChromaDB.Client/PublicAPI.Unshipped.txt |
Registers new public methods. |
ChromaDB.Client/Common/DocumentReader.cs |
Generalizes document reconstruction. |
ChromaDB.Client/Common/DocumentCopyReader.cs |
Removes superseded reader. |
ChromaDB.Client/Common/CollectionQueryEntryMapper.cs |
Uses generalized reader. |
ChromaDB.Client/Common/CollectionEntryMapper.cs |
Uses generalized reader. |
ChromaDB.Client/ChromaRecords.cs |
Updates deletion documentation. |
ChromaDB.Client/ChromaCollectionClient.cs |
Implements deletion configuration and write behavior. |
ChromaDB.Client.Tests/RequestValuesTests.cs |
Verifies direct deletion writes. |
ChromaDB.Client.Tests/MetadataTests.cs |
Covers strategies and Cloud quotas. |
ChromaDB.Client.Tests/DocumentCopyReadTests.cs |
Tests deletion-marker reads. |
ChromaDB.Client.Tests/DeletionsInWritesTests.cs |
Tests request construction and strategy selection. |
ChromaDB.Client.Tests/ConcurrentWritesTests.cs |
Tests concurrent deletion semantics. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…tor that the caller gives with the null of its text
…oud, and read the record first only with WithReadsBeyondKeyQuota
…at the client computes, which go with the write
…e with nulls, and that Chroma Cloud rejects a record with more than 32 values
…he client adds, and name ChromaConfigurationOptions.WithReadsBeyondKeyQuota() on the ChromaClient
…keys, as the client returns it
AnisaHaxhialushi
approved these changes
Oct 12, 2026
…he sparse vectors go before the deletions
…so that the vector of the old text does not stay
AnisaHaxhialushi
approved these changes
Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Updates and upserts that delete a metadata key or a document no longer read the records first, so two writes of the same record at the same time leave the record of one of them. Two new methods of the collection client,
WithDocumentDeletionKeyandWithEmptyDocumentDeletions, joinWithDocumentCopyKeyas the ways to delete a document, and a collection client chooses one of them once. On Chroma Cloud a write beyond the quota of 32 metadata keys throws, unless the new optionWithReadsBeyondKeyQuota().UpdateAsyncandUpsertAsyncread the records and sent the null only to the ones that had the key. To delete a document withNullDocumentsDelete, they read the records and wrote an empty document only to the ones that had one. A write of another client between the read and the write could stay in the record: A writes{s: "A", x: null}while B writes{s: "B", x: 2}, and the record ends up{s: "A", x: 2}.NullDocumentsDelete, in one of three ways. In each, the empty document goes in the same write, without a read:WithDocumentCopyKey(key), which already copied each document into a metadata key: the null of the copy goes with the empty document, which reads back as null.WithDocumentDeletionKey(key), new:trueunder the key goes with the empty document, which reads back as null. Any other document that an update or an upsert writes deletes the mark with a null.WithEmptyDocumentDeletions(), new: the empty document goes alone, and reads back as an empty string.where_documentfilter that matches an empty text now sees it as one with an empty document: on Chroma 1.5.9$contains ""and$regex "^$"find it and$not_regex "^$"does not; on Chroma Cloud$contains ""finds it.NullDocumentsDeletethrows anArgumentExceptionbefore any request, which lists them. WithoutNullDocumentsDelete, a null document keeps the stored one, as before.InvalidOperationExceptionthat says the way the client has. Up to 2.12.0,WithDocumentCopyKeywith another key replaced the key; the same key again works as before.ArgumentExceptionbefore it goes, without a read. The count has the keys that the client adds too, like the BM25 sparse vectors, which Chroma Cloud counts. A record with more than 32 values and no null goes, and Chroma Cloud rejects it with422. The new optionWithReadsBeyondKeyQuota()ofChromaConfigurationOptionsreads that record first instead, with two risks that the docs say: a write of another client between the read and the write can mix with this one, and with the same id more than once in one write, a key to delete can stay.ConcurrentWritesTests: a write that lands between the start of a deletion and its write, for a key and for a document in each of the three ways. Also 8 writes of one record at the same time, 20 rounds; its comment says that it catches a client that reads first only in some runs.DeletionsInWritesTests: no get before the write, on a fake server, for the keys and for each way; beyond the 32 keys of Chroma Cloud the error without any request by default, and the read withReadsBeyondKeyQuota, which stays through the other options; neither within the quota or on a single server; the null of a sparse vector and of its text counted once; the sparse vectors that the client computes counted too, while an empty list given for one still deletes it; a record with 33 values and no null sent as it is; the error without a way, also from a client made by name; the same call again, which changes nothing in the writes and in the reads, and each change of way, which throws.DocumentCopyReadTests: the mark in a get, a query and a search, and the reads of empty document deletions.MetadataTests:DocumentCopy,DocumentDeletionKeyandEmptyDocumentDeletionson a server; on Chroma Cloud 32 keys with the nulls, and 33: the error without a request by default, the read with the option; a computed sparse vector that takes a write to 33 keys; 33 values without nulls, which Chroma Cloud rejects.AnEmptyTextGetsAnEmptyVectoron a fake server, andAnEmptyTextReplacesItsVectorOnChromaCloudon Chroma Cloud.ApiV2CompleteTests.QuotasOfChromaCloudexpects the emptyMetadataof a record without keys, as the client returns it: it failed on main on Chroma Cloud, which the CI does not run.NullDocumentsDelete, the three ways, update and upsert, on records that do not exist, are empty, are full or have a deleted document. 4,160 on Chroma 1.5.9, 16,640 and 6,656 on Chroma Cloud with the index on a metadata key and on the documents, and 880 around the quota of keys on each. No difference.docs/metadata.md,docs/chroma-cloud.md,docs/search.md, the XML comments ofUpdateAsync,UpsertAsync,WithDocumentCopyKey,WithDocumentDeletionKey,WithEmptyDocumentDeletions,ChromaRecords.NullDocumentsDeleteandChromaConfigurationOptions.ReadsBeyondKeyQuota, andPublicAPI.Unshipped.txt.