Skip to content
This repository was archived by the owner on Oct 9, 2026. It is now read-only.
Closed
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
8 changes: 8 additions & 0 deletions src/ChromaDB.VectorData/ChromaModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ protected override void ValidateProperty(PropertyModel propertyModel, VectorStor
{
base.ValidateProperty(propertyModel, definition);

// Chroma reserves the metadata keys that start with #, like #id and #document of the Search API: a filter on #id selects
// the record with that id.
if (propertyModel is DataPropertyModel && propertyModel.StorageName.StartsWith("#", StringComparison.Ordinal))
{
throw new NotSupportedException(
$"Property '{propertyModel.ModelName}' has the storage name '{propertyModel.StorageName}', but Chroma reserves the metadata keys that start with #.");
}

switch (propertyModel)
{
case VectorPropertyModel vectorProperty:
Expand Down
74 changes: 74 additions & 0 deletions test/ChromaDB.VectorData.UnitTests/ChromaCollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,68 @@ public ChromaCollectionTests()
public void RejectsADictionaryRecord()
=> Assert.Throws<NotSupportedException>(() => new ChromaCollection<object, Dictionary<string, object?>>(this._chromaClientMock.Object, TestCollectionName, ownsClient: false));

[Theory]
[InlineData("#id")]
[InlineData("#document")]
[InlineData("#embedding")]
[InlineData("#metadata")]
[InlineData("#score")]
[InlineData("#other")]
public void RejectsADataPropertyWhoseStorageNameStartsWithAHash(string storageName)
{
// Chroma reserves the metadata keys that start with #: a filter on #id would select the record with that id.
var definition = new VectorStoreCollectionDefinition
{
Properties =
[
new VectorStoreKeyProperty("Key", typeof(string)),
new VectorStoreDataProperty("Tag", typeof(string)) { StorageName = storageName },
new VectorStoreVectorProperty("Vector", typeof(ReadOnlyMemory<float>), 4),
]
};

var exception = Assert.Throws<NotSupportedException>(() => new ChromaDynamicCollection(this._chromaClientMock.Object, TestCollectionName, ownsClient: false, new() { Definition = definition }));
Assert.Contains($"'{storageName}'", exception.Message);
}

[Fact]
public void RejectsAStorageNameThatStartsWithAHashOnARecordType()
=> Assert.Throws<NotSupportedException>(() => new ChromaCollection<string, HashStorageNameRecord>(this._chromaClientMock.Object, TestCollectionName, ownsClient: false));

[Fact]
public void RejectsAFullTextPropertyWhoseStorageNameStartsWithAHash()
{
// The full-text property stored as the document is also a metadata key: the client copies the document into it.
var definition = new VectorStoreCollectionDefinition
{
Properties =
[
new VectorStoreKeyProperty("Key", typeof(string)),
new VectorStoreDataProperty("Text", typeof(string)) { StorageName = "#document", IsFullTextIndexed = true },
new VectorStoreVectorProperty("Vector", typeof(ReadOnlyMemory<float>), 4),
]
};

Assert.Throws<NotSupportedException>(() => new ChromaDynamicCollection(this._chromaClientMock.Object, TestCollectionName, ownsClient: false, new() { Definition = definition }));
}

[Fact]
public void AcceptsAHashInsideAStorageName()
{
var definition = new VectorStoreCollectionDefinition
{
Properties =
[
new VectorStoreKeyProperty("Key", typeof(string)),
new VectorStoreDataProperty("Tag", typeof(string)) { StorageName = "tag#1" },
new VectorStoreVectorProperty("Vector", typeof(ReadOnlyMemory<float>), 4),
]
};

using var sut = new ChromaDynamicCollection(this._chromaClientMock.Object, TestCollectionName, ownsClient: false, new() { Definition = definition });
Assert.Equal(TestCollectionName, sut.Name);
}

[Fact]
public void DynamicCollectionsTakeAClientAndADefinition()
{
Expand Down Expand Up @@ -514,4 +576,16 @@ private static ChromaCollection HybridChromaCollection(params string[] indexes)
private static string Bm25Index(string key, string sourceKey)
=> "\"" + key + "\":{\"sparse_vector\":{\"sparse_vector_index\":{\"enabled\":true,\"config\":{\"source_key\":\"" + sourceKey + "\",\"bm25\":true,"
+ "\"embedding_function\":{\"type\":\"known\",\"name\":\"chroma_bm25\",\"config\":{\"k\":1.2,\"b\":0.75,\"avg_doc_length\":256,\"token_max_length\":40}}}}}}";

private sealed class HashStorageNameRecord
{
[VectorStoreKey]
public string Key { get; set; } = "";

[VectorStoreData(StorageName = "#id")]
public string? Tag { get; set; }

[VectorStoreVector(4)]
public ReadOnlyMemory<float> Vector { get; set; }
}
}
Loading