QueryClient.*.cs ← Public API, one partial file per search type (BM25, NearText, etc.)
↓ delegates to
gRPC/Search.cs ← Executes gRPC requests (SearchBM25, SearchNearVector, etc.)
↓ builds via
gRPC/Search.Builders.cs ← BaseSearchRequest() constructs V1.SearchRequest proto
↓ transforms via
gRPC/Result.cs ← BuildResult*() maps V1.SearchReply → C# model types
↓ surfaces as
Models/Results.cs ← WeaviateResult<TObject>, GroupByResult<TObject, TGroup>
- Generic bases:
WeaviateResult<TObject>andGroupByResult<TObject, TGroup>hold all shared properties. - Concrete types:
WeaviateResult,GroupByResultextend the generics with typed objects. - Feature variants:
GenerativeWeaviateResult,GenerativeGroupByResultadd feature-specific properties. - Typed wrappers:
WeaviateResult<WeaviateObject<T>>viaToTyped<T>()inTypedResultConverter.cs. - Pattern: Add result-level features (like
Generative,QueryProfile) to the generic base so all variants inherit.
- Add C# model to
Models/ - Add
Feature? Feature { get; init; }toWeaviateResult<TObject>(and/orGroupByResult<TObject, TGroup>) - Map proto → model in
gRPC/Result.csBuildResult*methods - Propagate in
Models/Typed/TypedResultConverter.csToTypedmethods
- Add flag to
MetadataOptionsenum / computed property onMetadataQueryinModels/MetadataQuery.cs - Wire into
BaseSearchRequest()→V1.MetadataRequestingRPC/Search.Builders.cs - If it requires a new proto field: add to
gRPC/proto/v1/search_get.proto(Grpc.Tools auto-generates C# on build)
Grpc.Tools compiles *.proto → C# on every dotnet build. No manual step needed. Generated classes land in namespace Weaviate.Client.Grpc.Protobuf.V1.
Nested proto message Foo.Bar becomes Foo.Types.Bar in C#.
For optional proto3 message fields, the generated C# has a HasXxx bool property for presence detection.
gRPC/Extensions.cs adds implicit operator from SearchReply to all four result types. The operators delegate to WeaviateGrpcClient.BuildResult*(). Unit tests can use proto JSON deserialization + the implicit cast as a clean roundtrip pattern.
- Proto JSON → model roundtrip: use
JsonParserfromGoogle.Protobufto parse JSON into a proto message, then use the implicit conversion operator to get the C# result. - Model construction tests: directly instantiate model types to verify properties are accessible.
- Flag tests: verify
MetadataOptionsflags map correctly throughMetadataQuerycomputed properties.
var jsonParser = new JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true));
var reply = jsonParser.Parse<Grpc.Protobuf.V1.SearchReply>(json);
WeaviateResult result = reply; // implicit conversion- Use
CollectionFactory(name, description, properties, vectorConfig?)helper fromIntegrationTestsbase. - Join
[Collection("SearchTests")]+public partial class SearchTests : IntegrationTests— no new fixture needed. - Use
TestContext.Current.CancellationTokenfor all async calls. - No
RequiresWeaviateVersionguard needed for gRPC fields: older servers silently ignore unknown proto fields.
A live Weaviate server must be running. Use the CI scripts to start/stop it:
./ci/start_weaviate.sh 1.36.9 # start (replace version as needed)
dotnet test src/Weaviate.Client.Tests --filter "FullyQualifiedName~Integration"
./ci/stop_weaviate.sh 1.36.9 # stop when donesrc/Weaviate.Client/Rest/Dto/Models.g.cs is auto-generated — never edit it manually. To update it:
- Run
./tools/openapi_sync.shfollowed by the desired branch or tag to target from the github.com/weaviate/weaviate repo - Run
./tools/gen_rest_dto.shto regenerateModels.g.csvia NSwag
To customize the generated output (e.g., access modifiers), edit the Liquid templates in src/Weaviate.Client/Rest/Schema/Templates/. These override NSwag's built-in templates. File.liquid overrides the file-level template (controls FileResponse visibility, etc.).
After adding public types/members, run:
dotnet build src/Weaviate.Client/Weaviate.Client.csproj 2>&1 | grep "RS0016"Copy the RS0016 error messages (minus the file/error prefix) into PublicAPI.Unshipped.txt. C# records auto-generate many symbols (<Clone>$, EqualityContract, equality operators, PrintMembers, etc.) — all must be listed.
For methods that require a minimum Weaviate server version:
[RequiresWeaviateVersion(1, 36, 0)]
public async Task<T> MethodName(...)
{
await _client.EnsureVersion<ContainingClass>();
// ...
}The WEAVIATE008 Roslyn analyzer enforces that the EnsureVersion call is present. Only needed for standalone methods, not for feature flags added as optional parameters to existing query methods.
Feature branches use isolated git worktrees under .worktrees/. The worktree's working directory is separate but shares the same .git repo. Run all commands from the worktree directory, not the main repo root.