Outmatic
Outmatic

Vespa is one of the few engines that will run full-text search, vector search, and a machine-learned ranking model over the same query, at scale, in under 100 milliseconds. Reaching it from .NET is another matter. You get an HTTP API, and everything above it is yours to build: JSON payloads by hand, YQL assembled from string concatenation, schemas maintained in a .sd file that has no relationship to the C# types they describe.
That was our starting point too. We've been running Vespa in production for the last 8 months, powering AI search, recommendations, and hybrid vector + keyword workloads inside our own solutions. Vespa.NET is the client we built to stop writing that plumbing. We open-sourced it in April under MIT.
Four things, in the order you meet them: define your document model in C#, deploy the schema, feed the data, query it. Most clients cover the last one.
Annotate a C# class with attributes and Vespa.NET generates the .sd schema and the application package from it, then deploys them through the config server with DeploySchemaAsync<T>(). Indexing modes, tensor fields and their types, rank profiles, struct and map fields, garbage-collection selections, multi-node layouts.
The generator fails at generation time for the shapes the config server would reject at deploy: enum and byte properties, predicate fields declared without arity, recursive structs, a redundancy higher than the node count. You find out in your build, not in a deployment log.
The feed pipeline is parallel with backpressure, so a slow cluster throttles the producer rather than filling memory. Per-document retry with exponential backoff handles 429 and 503, and honors the server's Retry-After rather than guessing. When a pipeline faults, it throws with the partial FeedResult attached: you keep the progress you made and know exactly where it stopped.
A typed YQL builder covers full-text, nearest-neighbor, weakAnd and wand, hybrid combinations of all of them, and geo search. Term annotations, negation, near and onear exclusions, sameElement, parameter substitution. There is a Raw(...) escape hatch for the day the builder doesn't have what you need, and identifier validation on every entry point so a field name from user input can't become a YQL injection.
The grouping DSL builds all(...) expressions with composable filters, buckets with open and closed endpoints, aliases, parallel each() blocks, and the documented expression functions. Streaming search and the ranking DSL are there. Grouping results come back as typed objects, with RawAggregations for the non-numeric outputs.
The full document/v1 surface is covered too: conditional puts, partial updates, tensor modify/add/remove operations, selection-based update and delete, and visiting with automatic continuation.
mTLS for Vespa Cloud, inside DI or out of it. API key auth. Retry and circuit breaker through Microsoft.Extensions.Http.Resilience. Health checks you can register by name.
Telemetry is emitted through ActivitySource and Meter, the BCL primitives, so it flows into any OpenTelemetry pipeline you already run without Vespa.NET dragging an OpenTelemetry dependency into your dependency tree. Metrics are tagged with method, host, operation, and status. Query text and document ids stay out of traces unless you opt in.
For testing, Vespa.NET.Testcontainers spins up a real Vespa container. Which brings us to the part worth saying out loud.
dotnet add package Vespa.NET
Targets .NET 8 LTS and .NET 10. MIT licensed. Source at github.com/outmatic/Vespa.NET.
The current release is 3.0.0. It is a major version with breaking changes, all of them documented with their reasons in the changelog. If you are upgrading from 2.0.0, read it first.