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 TypePath PatternUse Case
Scalar member$.NameTop-level property exact match
Nested object$.Address.CityNested property lookup
Terminal array$.Tags[]Array membership queries
Nested array-object$.Orders[].SkuProperty 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.