Skip to content

Commit

Permalink
Implement create collection. (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
cincuranet authored Jun 24, 2024
1 parent 850da9c commit 2730ca3
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 7 deletions.
2 changes: 1 addition & 1 deletion ChromaDB.Client.Tests/ChromaDBAuthTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ public async Task NoAuth()
{
using var httpClient = new ChromaDBHttpClient(ConfigurationOptions);
var client = new ChromaDBClient(ConfigurationOptions, httpClient);
await Assert.ThatAsync(client.Heartbeat, Throws.Nothing);
await Assert.ThatAsync(client.Heartbeat, Throws.Nothing);
}
}
67 changes: 67 additions & 0 deletions ChromaDB.Client.Tests/ChromaDBCollectionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using ChromaDB.Client.Models.Requests;
using ChromaDB.Client.Services.Implementations;
using NUnit.Framework;

namespace ChromaDB.Client.Tests;

[TestFixture]
public class ChromaDBCollectionTests : ChromaDBTestsBase
{
[Test]
public async Task CreateCollectionWithoutMetadata()
{
var name = $"collection{Random.Shared.Next()}";

using var httpClient = new ChromaDBHttpClient(ConfigurationOptions);
var client = new ChromaDBClient(ConfigurationOptions, httpClient);
var result = await client.CreateCollection(new DBCreateCollectionRequest()
{
Name = name,
});
Assert.That(result.Success, Is.True);
Assert.That(result.Data, Is.Not.Null);
Assert.That(result.Data.Name, Is.EqualTo(name));
}

[Test]
public async Task CreateCollectionWithMetadata()
{
var name = $"collection{Random.Shared.Next()}";
var metadata = new Dictionary<string, object>()
{
{ "test", "foo" },
{ "test2", 10 },
};

using var httpClient = new ChromaDBHttpClient(ConfigurationOptions);
var client = new ChromaDBClient(ConfigurationOptions, httpClient);
var result = await client.CreateCollection(new DBCreateCollectionRequest()
{
Name = name,
Metadata = metadata,
});
Assert.That(result.Success, Is.True);
Assert.That(result.Data, Is.Not.Null);
Assert.That(result.Data.Name, Is.EqualTo(name));
Assert.That(result.Data.Metadata, Is.Not.Null);
Assert.That(result.Data.Metadata["test"], Is.EqualTo(metadata["test"]));
Assert.That(result.Data.Metadata["test2"], Is.EqualTo(metadata["test2"]));
}

[Test]
public async Task CreateCollectionAlreadyExists()
{
using var httpClient = new ChromaDBHttpClient(ConfigurationOptions);
var client = new ChromaDBClient(ConfigurationOptions, httpClient);
await client.CreateCollection(new DBCreateCollectionRequest()
{
Name = $"collection_exists",
});
var result = await client.CreateCollection(new DBCreateCollectionRequest()
{
Name = $"collection_exists",
});
Assert.That(result.Success, Is.False);
Assert.That(result.ReasonPhrase, Is.Not.Null.And.Not.Empty);
}
}
16 changes: 12 additions & 4 deletions ChromaDB.Client/Common/Helpers/HttpClientHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ public static partial class HttpClientHelpers
{
AllowTrailingCommas = false,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
ReadCommentHandling = JsonCommentHandling.Skip
ReadCommentHandling = JsonCommentHandling.Skip,
};

private static readonly JsonSerializerOptions DeserializerJsonSerializerOptions = new()
{
Converters =
{
new ObjectToInferredTypesJsonConverter(),
},
};

public static async Task<BaseResponse<TResponse>> Get<TSource, TResponse>(this IChromaDBHttpClient httpClient, RequestQueryParams? queryParams = null) where TSource : class
Expand All @@ -42,7 +50,7 @@ public static async Task<BaseResponse<TResponse>> Get<TSource, TResponse>(this I

return new BaseResponse<TResponse>(
data: responseBody is not null and not []
? JsonSerializer.Deserialize<TResponse>(responseBody)
? JsonSerializer.Deserialize<TResponse>(responseBody, DeserializerJsonSerializerOptions)
: default,
statusCode: httpResponseMessage.StatusCode);
}
Expand Down Expand Up @@ -93,7 +101,7 @@ public static async Task<BaseResponse<TResponse>> Post<TSource, TInput, TRespons

return new BaseResponse<TResponse>(
data: responseBody is not null and not []
? JsonSerializer.Deserialize<TResponse>(responseBody)
? JsonSerializer.Deserialize<TResponse>(responseBody, DeserializerJsonSerializerOptions)
: default,
statusCode: httpResponseMessage.StatusCode);
}
Expand All @@ -120,7 +128,7 @@ public static async Task<BaseResponse<TResponse>> Post<TSource, TInput, TRespons
{
try
{
GeneralError deserialized = JsonSerializer.Deserialize<GeneralError>(errorMessageBody)!;
GeneralError deserialized = JsonSerializer.Deserialize<GeneralError>(errorMessageBody, DeserializerJsonSerializerOptions)!;
Match match = ParseErrorMessageBodyRegex().Match(deserialized?.ErrorMessage ?? string.Empty);

return match.Success
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ChromaDB.Client.Common.Helpers;

public class ObjectToInferredTypesJsonConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number when reader.TryGetInt64(out long l) => l,
JsonTokenType.Number => reader.GetDouble(),
JsonTokenType.String when reader.TryGetDateTime(out DateTime datetime) => datetime,
JsonTokenType.String => reader.GetString()!,
_ => JsonDocument.ParseValue(ref reader).RootElement.Clone()
};

public override void Write(Utf8JsonWriter writer, object objectToWrite, JsonSerializerOptions options) => throw new NotSupportedException();
}
1 change: 1 addition & 0 deletions ChromaDB.Client/Models/Collection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace ChromaDB.Client.Models;
[ChromaGetRoute(Endpoint = "collections?tenant={tenant}&database={database}", Source = typeof(Collection), ResponseType = typeof(List<Collection>))]
[ChromaPostRoute(Endpoint = "collections/{collection_id}/get", Source = typeof(Collection), RequestType = typeof(CollectionGetRequest), ResponseType = typeof(CollectionEntriesResponse))]
[ChromaPostRoute(Endpoint = "collections/{collection_id}/query", Source = typeof(Collection), RequestType = typeof(CollectionQueryRequest), ResponseType = typeof(CollectionEntriesQueryResponse))]
[ChromaPostRoute(Endpoint = "collections", Source = typeof(Collection), RequestType = typeof(DBCreateCollectionRequest), ResponseType = typeof(Collection))]
public class Collection
{
[JsonPropertyName("id")]
Expand Down
12 changes: 12 additions & 0 deletions ChromaDB.Client/Models/Requests/DBCreateCollectionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace ChromaDB.Client.Models.Requests;

public class DBCreateCollectionRequest
{
[JsonPropertyName("name")]
public required string Name { get; init; }

[JsonPropertyName("metadata")]
public IDictionary<string, object>? Metadata { get; init; }
}
10 changes: 10 additions & 0 deletions ChromaDB.Client/Services/Implementations/ChromaDBClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,14 @@ public async Task<BaseResponse<Heartbeat>> Heartbeat()
{
return await _httpClient.Get<Heartbeat, Heartbeat>(new RequestQueryParams());
}

public async Task<BaseResponse<Collection>> CreateCollection(DBCreateCollectionRequest request, string? tenant = null, string? database = null)
{
tenant = tenant is not null and not [] ? tenant : _currentTenant.Name;
database = database is not null and not [] ? database : _currentDatabase.Name;
RequestQueryParams requestParams = new RequestQueryParams()
.Add("{tenant}", tenant)
.Add("{database}", database);
return await _httpClient.Post<Collection, DBCreateCollectionRequest, Collection>(request, requestParams);
}
}
2 changes: 2 additions & 0 deletions ChromaDB.Client/Services/Interfaces/IChromaDBClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ChromaDB.Client.Models;
using ChromaDB.Client.Models.Requests;

namespace ChromaDB.Client.Services.Interfaces;

Expand All @@ -7,4 +8,5 @@ public interface IChromaDBClient
Task<BaseResponse<Collection>> GetCollectionByName(string name, string? tenant = null, string? database = null);
Task<BaseResponse<List<Collection>>> GetCollections(string? tenant = null, string? database = null);
Task<BaseResponse<Heartbeat>> Heartbeat();
Task<BaseResponse<Collection>> CreateCollection(DBCreateCollectionRequest request, string? tenant = null, string? database = null);
}
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ With ChromaDB.Client, you can easily connect to a ChromaDB instance, create and
## Features

- [x] Basic connection and authentication (partially done)
- [ ] Collection creation and deletion
- [x] Collection creation (partially done)
- [ ] Collection deletion
- [x] Collection retrieval and modification (partially done)
- [ ] Document insertion, deletion, and update
- [ ] Document retrieval by ID or filter
Expand Down
7 changes: 6 additions & 1 deletion Samples/ChromaDB.Client.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
using IChromaDBHttpClient httpClient = new ChromaDBHttpClient(configOptions);
IChromaDBClient client = new ChromaDBClient(configOptions, httpClient);

BaseResponse<Collection> createCollectionResponse = await client.CreateCollection(new DBCreateCollectionRequest()
{
Name = "string5",
}, database: "test", tenant: "nedeljko");

BaseResponse<List<Collection>> collections = await client.GetCollections(database: "test", tenant: "nedeljko");

BaseResponse<Collection> collection1 = await client.GetCollectionByName("string5", database: "test", tenant: "nedeljko");
Expand All @@ -18,7 +23,7 @@
BaseResponse<List<CollectionEntry>> getResponse = await string5Client.Get(new CollectionGetRequest()
{
Ids = ["340a36ad-c38a-406c-be38-250174aee5a4"],
Include = ["metadatas", "documents", "embeddings"]
Include = ["metadatas", "documents", "embeddings"],
});

BaseResponse<CollectionEntriesQueryResponse> queryResponse = await string5Client.Query(new CollectionQueryRequest()
Expand Down

0 comments on commit 2730ca3

Please sign in to comment.