Collection API
A typed NoSQL document API built on top of CSharpDB's storage engine. Store, index, and query C# objects with zero SQL.
Getting Started
Collections serialize C# objects to JSON and store them in B+trees. Each document is keyed by a string identifier, and GetCollectionAsync<T>("name") opens or creates the collection.
public record Customer(
string Name,
string Email,
Address Address,
string[] Tags
);
public record Address(string City, string State, string Zip);
await using var db = await Database.OpenAsync("myapp.db");
var customers = await db.GetCollectionAsync<Customer>("customers");
CRUD Operations
// Put (insert or update)
await customers.PutAsync("customer:alice", new Customer(
"Alice", "alice@example.com",
new("Portland", "OR", "97201"),
["premium", "early-adopter"]
));
// Get by key
var alice = await customers.GetAsync("customer:alice");
// Delete
await customers.DeleteAsync("customer:alice");
// Scan all documents
await foreach (var entry in customers.ScanAsync())
Console.WriteLine($"{entry.Key}: {entry.Value.Name}");
Indexing
Collections support several indexing strategies for efficient queries without full scans.
Scalar Member Index
Index a top-level property for exact-match and range queries.
await customers.EnsureIndexAsync(c => c.Email);
await foreach (var match in customers.FindByIndexAsync(c => c.Email, "alice@example.com"))
Console.WriteLine(match.Key);
Nested Object Path
Index into nested objects using property path expressions like $.address.city.
await customers.EnsureIndexAsync("Address.City");
await foreach (var match in customers.FindByPathAsync("Address.City", "Portland"))
Console.WriteLine(match.Key);
Array Element Path
Index into terminal array elements for membership queries like $.tags[].
// Index array elements — one index entry per tag per document
await customers.EnsureIndexAsync("$.tags[]");
await foreach (var match in customers.FindByPathAsync("$.tags[]", "premium"))
Console.WriteLine(match.Key);
Range Queries
Query indexed properties with range bounds for ordered data.
public record Order(string Customer, double Total, string Date);
var orders = await db.GetCollectionAsync<Order>("orders");
await orders.EnsureIndexAsync(o => o.Total);
// Find orders with total between 100 and 500
await foreach (var match in orders.FindByPathRangeAsync(
o => o.Total, 100.0, 500.0))
{
Console.WriteLine(match.Key);
}
Index Types Summary
| Index Type | Path Pattern | Use Case |
|---|---|---|
| Scalar member | $.Name | Top-level property exact match |
| Nested object | $.Address.City | Nested property lookup |
| Terminal array | $.Tags[] | Array membership queries |
| Nested array-object | $.Orders[].Sku | Property within array elements |
Performance: Indexed lookups use B+tree secondary indexes for O(log n) access. Without an index, queries fall back to a full collection scan.