Skip to content
Merged
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
31 changes: 24 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## Features

### Helpers

- [x] Connection helpers: Local, Cloud, Custom, FromEnvironment

### Collections

- [x] List collections.
Expand All @@ -14,16 +18,29 @@
### Objects

- [x] Insert data.
- [ ] Add object with vector data.
- [X] Add object with named vector data.
- [x] Delete data.
- [ ] Update data.
- [ ] Get object by ID.
- [X] Update data.
- [X] Get object by ID.
- [x] Get objects.
- [x] Get object metadata (vectors, schema, etc.)

### Search

- [ ] Query objects over gRPC.
- [ ] Perform a search with:
- Search mode: BM5, Hybrid, Near vector.
- Pagination: Limit, Offset.
- [X] Query objects over gRPC.
- [X] Perform a search with:
- Search mode:
- [X] BM5
- [X] Hybrid
- [X] Near vector
- Pagination:
- [X] Limit
- [ ] Offset.
- Filters
- [X] Property
- [X] Property Length
- [X] Creation/Update Time
- [X] Single-Target References
- [X] Reference Count
- [ ] Multi-Target References
- [ ] Geo Coordinates
115 changes: 50 additions & 65 deletions src/Example/Program.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@

using System.Text.Json;
using Weaviate.Client;
using System.Text.Json;
using Weaviate.Client.Models;

using CatDataWithVectors = (Example.Cat Data, float[] Vector);

namespace Example;

class Program
{
static private ClientConfiguration GetConfiguration()
{
var instanceUrl = Environment.GetEnvironmentVariable("WEAVIATE_CLUSTER_URL");
if (string.IsNullOrEmpty(instanceUrl))
{
throw new Exception("Required environment variable WEAVIATE_CLUSTER_URL is missing.");
}

var apiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
throw new Exception("Required environment variable WEAVIATE_API_KEY is missing.");
}

return new ClientConfiguration
{
Host = new Uri(instanceUrl),
ApiKey = apiKey
};
}
private record CatDataWithVectors(float[] Vector, Cat Data);

static async Task<IEnumerable<CatDataWithVectors>> GetCatsAsync(string filename)
static async Task<List<CatDataWithVectors>> GetCatsAsync(string filename)
{
try
{
Expand All @@ -40,13 +17,19 @@ static async Task<IEnumerable<CatDataWithVectors>> GetCatsAsync(string filename)
return []; // Return an empty list if the file doesn't exist
}

using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))
{
// Deserialize directly from the stream for better performance, especially with large files
var data = await JsonSerializer.DeserializeAsync<IList<CatDataWithVectors>>(fs) ?? [];
using FileStream fs = new FileStream(
filename,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize: 4096,
useAsync: true
);

return data;
}
// Deserialize directly from the stream for better performance, especially with large files
var data = await JsonSerializer.DeserializeAsync<List<CatDataWithVectors>>(fs) ?? [];

return data;
}
catch (JsonException ex)
{
Expand All @@ -62,9 +45,13 @@ static async Task<IEnumerable<CatDataWithVectors>> GetCatsAsync(string filename)

static async Task Main()
{
//var config = GetConfiguration();
// Read 250 cats from JSON file and unmarshal into Cat class
var cats = await GetCatsAsync("cats.json");

// Use the C# client to store all cats with a cat class
Console.WriteLine("Cats to store: " + cats.Count);

var weaviate = new WeaviateClient();
var weaviate = Weaviate.Client.Connect.Local();

var collection = weaviate.Collections.Use<Cat>("Cat");

Expand Down Expand Up @@ -93,22 +80,25 @@ static async Task Main()
{
Vectorizer = new Dictionary<string, object>
{
{ "none", new object { } }
{
"none",
new object { }
},
},
VectorIndexType = "hnsw",
};

var VectorConfigs = new Dictionary<string, VectorConfig>
{
{ "default", vectorizerConfigNone }
{ "default", vectorizerConfigNone },
};

var catCollection = new Collection()
{
Name = "Cat",
Description = "Lots of Cats of multiple breeds",
Properties = [Property.Text("Name"), Property.Text("Color"), Property.Text("Breed"), Property.Int("Counter")],
VectorConfig = VectorConfigs
Properties = Property.FromType<Cat>(),
VectorConfig = VectorConfigs,
};

collection = await weaviate.Collections.Create<Cat>(catCollection);
Expand All @@ -118,17 +108,9 @@ static async Task Main()
Console.WriteLine($"Collection: {c.Name}");
}

// // Read 250 cats from JSON file and unmarshal into Cat class
var cats = await GetCatsAsync("cats.json");

// Use the C# client to store all cats with a cat class
Console.WriteLine("Cats to store: " + cats.Count());
foreach (var cat in cats)
{
var vectors = new NamedVectors()
{
{ "default", cat.Vector }
};
var vectors = new NamedVectors() { { "default", cat.Vector } };

var inserted = await collection.Data.Insert(cat.Data, vectors: vectors);
}
Expand All @@ -154,37 +136,40 @@ static async Task Main()
if (firstObj.ID is Guid id2)
{
var fetched = await collection.Query.FetchObjectByID(id: id2);
Console.WriteLine("Cat retrieved via gRPC matches: " + ((fetched?.Objects.First().ID ?? Guid.Empty) == id2));
Console.WriteLine(
"Cat retrieved via gRPC matches: "
+ ((fetched?.Objects.First().ID ?? Guid.Empty) == id2)
);
}

{
var idList = retrieved
.Where(c => c.ID.HasValue)
.Take(10)
.Select(c => c.ID!.Value)
.ToHashSet();
.Where(c => c.ID.HasValue)
.Take(10)
.Select(c => c.ID!.Value)
.ToHashSet();

var fetched = await collection.Query.FetchObjectsByIDs(idList);
Console.WriteLine($"Cats retrieved via gRPC matches:{Environment.NewLine} {JsonSerializer.Serialize(fetched.Objects, new JsonSerializerOptions { WriteIndented = true })}");
Console.WriteLine(
$"Cats retrieved via gRPC matches:{Environment.NewLine} {JsonSerializer.Serialize(fetched.Objects, new JsonSerializerOptions { WriteIndented = true })}"
);
}

var queryNearVector =
await collection
.Query
.NearVector(
vector: [20f, 21f, 22f],
distance: 0.5f,
limit: 5,
fields: ["name", "breed", "color", "counter"],
metadata: MetadataOptions.Score | MetadataOptions.Distance
);
var queryNearVector = await collection.Query.NearVector(
vector: [20f, 21f, 22f],
distance: 0.5f,
limit: 5,
fields: ["name", "breed", "color", "counter"],
metadata: MetadataOptions.Score | MetadataOptions.Distance
);

foreach (var cat in queryNearVector.Objects)
{
Console.WriteLine(JsonSerializer.Serialize(cat, new JsonSerializerOptions { WriteIndented = true }));
Console.WriteLine(
JsonSerializer.Serialize(cat, new JsonSerializerOptions { WriteIndented = true })
);
}


// Cursor API
// var objects = collection.Iterator<Cat>();
// var sum = await objects.SumAsync(c => c.Counter);
Expand Down
29 changes: 29 additions & 0 deletions src/Weaviate.Client.Tests/Integration/Connection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Weaviate.Client.Tests.Integration;

public partial class BasicTests
{
[Fact]
public async Task ConnectToLocal()
{
var client = Connect.Local();

var ex = await Record.ExceptionAsync(() =>
client.Collections.List().ToListAsync(TestContext.Current.CancellationToken).AsTask()
);
Assert.Null(ex);
}

[Fact]
public async Task ConnectToCloud()
{
var WCS_HOST = "piblpmmdsiknacjnm1ltla.c1.europe-west3.gcp.weaviate.cloud";
var WCS_CREDS = "cy4ua772mBlMdfw3YnclqAWzFhQt0RLIN0sl";

var client = Connect.Cloud(WCS_HOST, WCS_CREDS);

var ex = await Record.ExceptionAsync(() =>
client.Collections.List().ToListAsync(TestContext.Current.CancellationToken).AsTask()
);
Assert.Null(ex);
}
}
70 changes: 70 additions & 0 deletions src/Weaviate.Client/Helpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
namespace Weaviate.Client;

public static class Connect
{
public static ClientConfiguration LocalConfig(
ushort restPort,
ushort grpcPort,
bool useSsl,
string? apiKey
) => new("localhost", "localhost", restPort, grpcPort, useSsl, apiKey);

public static WeaviateClient Local(
ushort restPort = 8080,
ushort grpcPort = 50051,
bool useSsl = false,
string? apiKey = null
) => LocalConfig(restPort, grpcPort, useSsl, apiKey).Client();

public static ClientConfiguration CloudConfig(string restEndpoint, string? apiKey = null) =>
new ClientConfiguration(restEndpoint, $"grpc-{restEndpoint}", 443, 443, true, apiKey);

public static WeaviateClient Cloud(string restEndpoint, string? apiKey = null) =>
CloudConfig(restEndpoint, apiKey).Client();

public static WeaviateClient FromEnvironment(string prefix = "WEAVIATE_")
{
var restEndpoint = Environment.GetEnvironmentVariable($"{prefix}REST_ENDPOINT");
var grpcEndpoint = Environment.GetEnvironmentVariable($"{prefix}GRPC_ENDPOINT");
var restPort = Environment.GetEnvironmentVariable($"{prefix}REST_PORT") ?? "8080";
var grpcPort = Environment.GetEnvironmentVariable($"{prefix}GRPC_PORT") ?? "50051";
var useSsl = Environment.GetEnvironmentVariable($"{prefix}USE_SSL")?.ToLower() == "true";
var apiKey = Environment.GetEnvironmentVariable($"{prefix}API_KEY");

if (restEndpoint is null && grpcEndpoint is null)
{
throw new InvalidOperationException("No REST or GRPC endpoint provided.");
}
else if (restEndpoint is not null && grpcEndpoint is null)
{
grpcEndpoint = restEndpoint;
}
else if (restEndpoint is null && grpcEndpoint is not null)
{
restEndpoint = grpcEndpoint;
}

return Custom(restEndpoint!, grpcEndpoint!, restPort, grpcPort, useSsl, apiKey);
}

private static WeaviateClient Custom(
string restEndpoint,
string grpcEndpoint,
string restPort,
string grpcPort,
bool useSsl,
string? apiKey
)
{
return new(
new ClientConfiguration(
restEndpoint,
grpcEndpoint,
Convert.ToUInt16(restPort),
Convert.ToUInt16(grpcPort),
useSsl,
apiKey
)
);
}
}
10 changes: 2 additions & 8 deletions src/Weaviate.Client/Rest/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public class WeaviateRestClient : IDisposable
private readonly bool _ownershipClient;
private readonly HttpClient _httpClient;

internal WeaviateRestClient(WeaviateClient client, HttpClient? httpClient = null)
internal WeaviateRestClient(Uri restUri, HttpClient? httpClient = null)
{
if (httpClient is null)
{
Expand All @@ -97,13 +97,7 @@ internal WeaviateRestClient(WeaviateClient client, HttpClient? httpClient = null
}

_httpClient = httpClient;

var ub = new UriBuilder(client.Configuration.Host);

ub.Port = client.Configuration.RestPort;
ub.Path = "v1/";

_httpClient.BaseAddress = ub.Uri;
_httpClient.BaseAddress = restUri;
}

public void Dispose()
Expand Down
Loading
Loading