.NET SDK
The official Torque .NET SDK provides high-level clients for the HTTP search/management API, TCP binary ingest protocol, and TQBF binary file writing. Zero external NuGet dependencies, targeting .NET 10.
Installation
dotnet add package Torque.Http
HTTP Client
The TorqueHttpClient class is the main entry point for the HTTP API:
using Torque.Http;
using Torque.Http.Search;
var client = new TorqueHttpClient(new TorqueHttpClientOptions
{
ApiKey = "YOUR_API_KEY",
BaseUrl = "http://localhost:8108",
});
// Health check
var health = await client.HealthAsync();
// List collections
var collections = await client.Collections.ListAsync();
// Search
var results = await client.SearchAsync("products", new SearchParameters
{
Q = "running shoes",
QueryBy = "title,description",
FilterBy = "in_stock:true",
SortBy = "price:asc",
PerPage = 20
});
Multi-Node with Failover
var client = new TorqueHttpClient(new TorqueHttpClientOptions
{
ApiKey = "YOUR_API_KEY",
Nodes =
[
new Uri("https://node1.example.com:8108"),
new Uri("https://node2.example.com:8108"),
],
MaxRetries = 3,
RetryBackoff = TimeSpan.FromMilliseconds(100),
});
Sub-Clients
The TorqueClient exposes specialized sub-clients for each resource type:
| Property | Description |
|---|---|
client.Collections | Create, list, get, update, delete collections |
client.Documents | Import, export, create, get, update, delete documents |
client.Keys | Create, list, get, delete API keys |
client.Aliases | Create, list, get, delete aliases |
client.Synonyms | Manage synonym sets and items |
client.Presets | Create, list, get, delete presets |
client.Stopwords | Create, list, get, delete stopword sets |
Search
// Simple search
var results = await client.SearchAsync("products", new SearchParameters
{
Q = "shoes",
QueryBy = "title"
});
foreach (var hit in results.Hits)
{
Console.WriteLine($"{hit.Document["title"]} - {hit.Document["price"]}");
}
// Grouped search
var grouped = await client.SearchGroupedAsync("products", new SearchParameters
{
Q = "shoes",
QueryBy = "title",
GroupBy = "brand",
GroupLimit = 3
});
foreach (var group in grouped.GroupedHits)
{
Console.WriteLine($"Brand: {string.Join(", ", group.GroupKey)} ({group.Found} hits)");
}
// Multi-search
var multi = await client.MultiSearchAsync(new MultiSearchRequest
{
Searches =
[
new MultiSearchQuery { Collection = "products", Q = "shoes", QueryBy = "title" },
new MultiSearchQuery { Collection = "articles", Q = "shoes", QueryBy = "body" },
],
});
Collection Management
// Create a collection
await client.Collections.CreateAsync(new CollectionSchema
{
Name = "products",
Fields = new[]
{
new FieldDefinition { Name = "title", Type = "string" },
new FieldDefinition { Name = "price", Type = "float", Sort = true },
new FieldDefinition { Name = "category", Type = "string", Facet = true }
},
DefaultSortingField = "price"
});
// Update schema (add a field)
await client.Collections.UpdateAsync("products", new CollectionSchemaUpdate
{
Fields = new[]
{
new FieldDefinition { Name = "brand", Type = "string", Facet = true }
}
});
// Delete a collection
await client.Collections.DeleteAsync("products");
Dependency Injection
Register the client in your ASP.NET Core or Generic Host application using TorqueHttpClientFactory:
using Torque.Http;
builder.Services.AddSingleton<ITorqueHttpClient>(sp =>
TorqueHttpClientFactory.Create(options =>
{
options.ApiKey = "YOUR_API_KEY";
options.BaseUrl = "http://localhost:8108";
}));
Or with IHttpClientFactory:
builder.Services.AddHttpClient("Torque");
builder.Services.AddSingleton<ITorqueHttpClient>(sp =>
{
var factory = sp.GetRequiredService<IHttpClientFactory>();
var httpClient = factory.CreateClient("Torque");
return TorqueHttpClientFactory.Create(httpClient, options =>
{
options.ApiKey = "YOUR_API_KEY";
});
});
Then inject ITorqueHttpClient into your services:
public class MyService(ITorqueHttpClient torque)
{
public async Task SearchAsync()
{
var results = await torque.SearchAsync("products", new SearchParameters { Q = "shoes" });
}
}
TCP Binary Ingestion
The TorqueIngestClient provides high-throughput document streaming via the binary TCP protocol. Host and port are supplied via TorqueIngestOptions; operations run directly on the client (there is no separate session object).
using Torque.Http.Tcp;
using Torque.Http.Models;
// Fetch the collection schema (needed to build and encode documents)
var collection = await client.Collections.GetAsync("products");
var schema = new CollectionSchema
{
Name = collection.Name,
Fields = collection.Fields,
};
await using var ingest = new TorqueIngestClient(new TorqueIngestOptions
{
Host = "localhost",
Port = 8109,
});
await ingest.ConnectAsync();
await ingest.StartIngestAsync("products");
// Batch mode: stream documents, then commit
var batch = new List<TorqueDocument>(5000);
for (int i = 0; i < 100000; i++)
{
var doc = new TorqueDocumentBuilder()
.SetId($"product_{i}")
.Set("title", $"Product {i}")
.Set("price", 9.99 + i * 0.01)
.Set("category", "Electronics")
.Build(schema);
batch.Add(doc);
if (batch.Count == 5000)
{
await ingest.SendBatchAsync(batch, schema);
batch.Clear();
}
}
if (batch.Count > 0)
await ingest.SendBatchAsync(batch, schema);
var ack = await ingest.CommitAsync();
Console.WriteLine($"Committed: seq={ack.BatchSeq}, docs={ack.DocsReceived}");
Realtime Mode
For stateless upserts against a realtime collection, use UpsertAsync — no StartIngest/Commit flow needed:
var doc = new TorqueDocumentBuilder()
.SetId("product_new")
.Set("title", "New Product")
.Set("price", 29.99)
.Build(schema);
await ingest.UpsertAsync("products", doc, schema);
// Document is immediately searchable
Document Builder
The TorqueDocumentBuilder creates documents with typed field values. All field values go through overloaded Set methods that match the wire encoding for the matching schema type.
| Method | Schema Type |
|---|---|
SetId(string id) | Document external ID |
SetLanguage(Language lang) | Document language |
Set(name, string) | string |
Set(name, int) | int32 |
Set(name, long) | int64 |
Set(name, double) | float |
Set(name, bool) | bool |
SetGeoPoint(name, lat, lng) | geopoint |
Set(name, string[]) | string[] |
Set(name, int[]) | int32[] |
Set(name, long[]) | int64[] |
Set(name, float[]) | float[] (vector) |
SetNull(name) | Explicit null field |
Build(CollectionSchema) | Finalize the document for the given schema |
TQBF Binary File Writer
The TorqueBinaryWriter creates TQBF binary files for bulk import via POST /collections/{name}/documents/import-binary. Documents are encoded with ZLib compression, eliminating all parsing overhead at import time:
using Torque.Http.Tcp;
using Torque.Http.Models;
// Build a CollectionSchema that matches the target collection's field layout
var schema = new CollectionSchema
{
Name = "products",
Fields =
[
new() { Name = "title", Type = "string" },
new() { Name = "price", Type = "float", Sort = true },
new() { Name = "category", Type = "string", Facet = true },
],
};
await using var writer = new TorqueBinaryWriter(schema, "output.tqbf");
foreach (var doc in documents)
writer.Write(doc);
await writer.FinalizeAsync();
// The writer uploads itself via HTTP POST /collections/{name}/documents/import-binary
using var httpClient = new HttpClient { BaseAddress = new Uri("http://localhost:8108") };
var result = await writer.UploadAsync(httpClient, "products", "YOUR_API_KEY");
Console.WriteLine($"Imported {result.NumImported} documents");
Import & Export Options
// Import with upsert action and options
var results = await client.Documents.ImportAsync("products", jsonlLines,
ImportAction.Upsert, new ImportOptions { BatchSize = 100 });
// Export with field filtering (streaming)
await foreach (var doc in client.Documents.ExportStreamingAsync("products",
includeFields: "id,title,price"))
{
Console.WriteLine(doc);
}
Models
The SDK includes typed models for all API responses:
SearchResult,GroupedSearchResult,Hit— search responsesCollectionSchema,FieldDefinition— collection schemasApiKey,Alias,Synonym,Preset,Stopword— management resourcesImportResult,IngestResult— ingestion resultsAdminStats,Health— server status
All serialization uses System.Text.Json with source-generated serialization contexts for optimal performance.
Error Handling
HTTP API errors throw TorqueApiException with the HTTP status code and error message. TCP protocol errors are returned as IngestError with an error code and description.