Skip to content

Commit 9a0cc99

Browse files
committed
Update docs
1 parent bb62ad7 commit 9a0cc99

8 files changed

Lines changed: 292 additions & 189 deletions

File tree

src/content/docs/release-notes/sqlite-documentdb.mdx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,26 @@ tableOfContents: true
55

66
import RN from '/src/components/ReleaseNote.astro';
77

8-
## 1.2 - February 24, 2026
9-
<RN type="feature">Expression-based OrderBy (ascending/descending) on all GetAll, Query, GetAllStream, and QueryStream methods — translates to ORDER BY json_extract SQL</RN>
10-
<RN type="feature">Scalar aggregates: Max, Min, Sum, Average with optional predicate filters</RN>
11-
<RN type="feature">Aggregate projections with automatic GROUP BY via Sql.Count(), Sql.Max(), Sql.Min(), Sql.Sum(), Sql.Avg() marker methods</RN>
12-
<RN type="feature">Collection-level aggregates in projections: Sum, Min, Max, Average on child collections (e.g. o.Lines.Sum(l => l.Quantity))</RN>
13-
14-
## 1.1 - February 23, 2026
15-
<RN type="feature">SetProperty — update a single scalar JSON field via json_set without deserializing the document. Supports nested paths (e.g. o.ShippingAddress.City)</RN>
16-
<RN type="feature">RemoveProperty — strip a field from the stored JSON via json_remove. Works on any property type including collections and nested objects</RN>
17-
<RN type="feature">Both SetProperty and RemoveProperty have reflection-based and AOT-safe overloads, and work inside transactions</RN>
18-
19-
## 1.0 - February 22, 2026
20-
<RN type="feature">Initial Public Release</RN>
8+
## 1.0 - February 2026
219
<RN type="feature">Schema-free JSON document storage on top of SQLite</RN>
2210
<RN type="feature">LINQ expression queries translated to json_extract SQL with support for equality, comparisons, logical operators, null checks, string methods, nested properties, and collection queries</RN>
23-
<RN type="feature">SQL-level projections via json_object for extracting specific fields without full deserialization</RN>
24-
<RN type="feature">IAsyncEnumerable streaming with GetAllStream and QueryStream</RN>
11+
<RN type="feature">Fluent query builder (IDocumentQuery) — chain .Where(), .OrderBy(), .OrderByDescending(), .GroupBy(), .Paginate(), .Select() and terminate with .ToList(), .ToAsyncEnumerable(), .Count(), .Any(), .Remove(), .Max(), .Min(), .Sum(), .Average()</RN>
12+
<RN type="feature">Pagination via .Paginate(offset, take) — translates to SQL LIMIT/OFFSET</RN>
13+
<RN type="feature">Expression-based ordering — .OrderBy(u => u.Age) and .OrderByDescending(u => u.Name) on the fluent query builder</RN>
14+
<RN type="feature">SQL-level projections via .Select() using json_object for extracting specific fields without full deserialization</RN>
15+
<RN type="feature">IAsyncEnumerable streaming via .ToAsyncEnumerable() — yield results one-at-a-time without buffering</RN>
2516
<RN type="feature">Expression-based JSON indexes for up to 30x faster queries on indexed properties</RN>
26-
<RN type="feature">Full AOT and trimming support with JsonTypeInfo overloads on every API</RN>
27-
<RN type="feature">Auto-resolving type info from configured JsonSerializerContext</RN>
17+
<RN type="feature">Full AOT and trimming support — all JsonTypeInfo parameters are optional and auto-resolve from configured JsonSerializerContext</RN>
18+
<RN type="feature">Scalar aggregates: .Max(), .Min(), .Sum(), .Average() as terminal methods on the query builder</RN>
19+
<RN type="feature">Aggregate projections with automatic GROUP BY via Sql.Count(), Sql.Max(), Sql.Min(), Sql.Sum(), Sql.Avg() marker methods</RN>
20+
<RN type="feature">Collection-level aggregates in projections: Sum, Min, Max, Average on child collections (e.g. o.Lines.Sum(l => l.Quantity))</RN>
21+
<RN type="feature">JSON Merge Patch (Upsert) — deep-merge partial objects into existing documents via json_patch (RFC 7396)</RN>
22+
<RN type="feature">SetProperty — update a single scalar JSON field via json_set without deserializing the document. Supports nested paths</RN>
23+
<RN type="feature">RemoveProperty — strip a field from the stored JSON via json_remove. Works on any property type</RN>
24+
<RN type="feature">Bulk delete via query builder — .Where(predicate).Remove() returns count of deleted documents</RN>
2825
<RN type="feature">Transactions with automatic commit/rollback via RunInTransaction</RN>
2926
<RN type="feature">Dependency injection registration via AddSqliteDocumentStore</RN>
3027
<RN type="feature">Configurable type name resolution (ShortName or FullName)</RN>
3128
<RN type="feature">UseReflectionFallback option for strict AOT enforcement</RN>
29+
<RN type="feature">SQL logging callback via DocumentStoreOptions.Logging</RN>
30+
<RN type="feature">Raw SQL query and streaming support via store.Query(whereClause) and store.QueryStream(whereClause)</RN>

src/content/docs/sqlite-docdb/aggregates.mdx

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ title: Aggregates
44

55
## Scalar Aggregates
66

7-
Compute a single aggregate value across all documents of a type, or filtered by a predicate. All scalar aggregates are AOT-safe.
7+
Compute a single aggregate value across documents using terminal methods on the fluent query builder.
88

99
### Max / Min
1010

1111
```csharp
12-
var maxAge = await store.Max<User, int>(u => u.Age, ctx.User);
13-
var minAge = await store.Min<User, int>(u => u.Age, ctx.User);
12+
var maxAge = await store.Query<User>().Max(u => u.Age);
13+
var minAge = await store.Query<User>().Min(u => u.Age);
1414

1515
// With predicate filter
16-
var maxAge = await store.Max<User, int>(u => u.Age < 35, u => u.Age, ctx.User);
16+
var maxAge = await store.Query<User>().Where(u => u.Age < 35).Max(u => u.Age);
1717
```
1818

1919
Generated SQL:
@@ -24,20 +24,19 @@ SELECT MAX(json_extract(Data, '$.age')) FROM documents WHERE TypeName = @typeNam
2424
### Sum
2525

2626
```csharp
27-
var totalAge = await store.Sum<User, int>(u => u.Age, ctx.User);
28-
var totalPrice = await store.Sum<Product, decimal>(p => p.Price, ctx.Product);
27+
var totalAge = await store.Query<User>().Sum(u => u.Age);
2928

3029
// With predicate filter
31-
var over25 = await store.Sum<User, int>(u => u.Age > 25, u => u.Age, ctx.User);
30+
var over25 = await store.Query<User>().Where(u => u.Age > 25).Sum(u => u.Age);
3231
```
3332

3433
### Average
3534

3635
```csharp
37-
var avgAge = await store.Average<User>(u => u.Age, ctx.User);
36+
var avgAge = await store.Query<User>().Average(u => u.Age);
3837

3938
// With predicate filter
40-
var avgAge = await store.Average<User>(u => u.Age > 25, u => u.Age, ctx.User);
39+
var avgAge = await store.Query<User>().Where(u => u.Age > 25).Average(u => u.Age);
4140
```
4241

4342
Returns `double`. Returns `0d` for empty result sets.
@@ -46,7 +45,7 @@ Returns `double`. Returns `0d` for empty result sets.
4645

4746
## Aggregate Projections (GROUP BY)
4847

49-
Use the `Sql` marker class to build aggregate projections with automatic GROUP BY. Non-aggregate columns are automatically grouped.
48+
Use the `Sql` marker class to build aggregate projections with automatic GROUP BY via `.Select()`. Non-aggregate columns are automatically grouped.
5049

5150
### Available Sql Markers
5251

@@ -61,14 +60,13 @@ Use the `Sql` marker class to build aggregate projections with automatic GROUP B
6160
### Group By with Count
6261

6362
```csharp
64-
var results = await store.Aggregate<Order, OrderStats>(
65-
o => new OrderStats
63+
var results = await store.Query<Order>()
64+
.Select(o => new OrderStats
6665
{
6766
Status = o.Status, // GROUP BY column
6867
OrderCount = Sql.Count(), // COUNT(*)
69-
},
70-
ctx.Order,
71-
ctx.OrderStats);
68+
})
69+
.ToList();
7270
// Status = "Shipped", OrderCount = 2
7371
// Status = "Pending", OrderCount = 1
7472
```
@@ -78,50 +76,56 @@ var results = await store.Aggregate<Order, OrderStats>(
7876
When every column uses a `Sql.*` marker, no GROUP BY is generated — the query returns a single summary row.
7977

8078
```csharp
81-
var results = await store.Aggregate<Product, PriceSummary>(
82-
p => new PriceSummary
79+
var results = await store.Query<Product>()
80+
.Select(p => new PriceSummary
8381
{
8482
TotalCount = Sql.Count(),
8583
MaxPrice = Sql.Max(p.Price),
8684
MinPrice = Sql.Min(p.Price),
8785
SumPrice = Sql.Sum(p.Price),
8886
AvgPrice = Sql.Avg(p.Price),
89-
},
90-
ctx.Product,
91-
ctx.PriceSummary);
87+
})
88+
.ToList();
9289
```
9390

9491
### With Predicate Filter
9592

9693
```csharp
97-
var results = await store.Aggregate<Order, OrderStats>(
98-
o => o.Status == "Shipped", // WHERE filter
99-
o => new OrderStats
94+
var results = await store.Query<Order>()
95+
.Where(o => o.Status == "Shipped") // WHERE filter
96+
.Select(o => new OrderStats
10097
{
10198
Status = o.Status,
10299
OrderCount = Sql.Count(),
103-
},
104-
ctx.Order,
105-
ctx.OrderStats);
100+
})
101+
.ToList();
102+
```
103+
104+
### Explicit GroupBy
105+
106+
```csharp
107+
var results = await store.Query<Order>()
108+
.GroupBy(o => o.Status)
109+
.Select(o => new OrderStats { Status = o.Status, OrderCount = Sql.Count() })
110+
.ToList();
106111
```
107112

108113
---
109114

110115
## Collection Aggregates in Projections
111116

112-
Aggregate over child collections within a single document using standard LINQ methods in projection expressions.
117+
Aggregate over child collections within a single document using standard LINQ methods in `.Select()` projection expressions.
113118

114119
### Sum on Collection
115120

116121
```csharp
117-
var results = await store.GetAll<Order, OrderLineAggregates>(
118-
o => new OrderLineAggregates
122+
var results = await store.Query<Order>()
123+
.Select(o => new OrderLineAggregates
119124
{
120125
Customer = o.CustomerName,
121126
TotalQty = o.Lines.Sum(l => l.Quantity)
122-
},
123-
ctx.Order,
124-
ctx.OrderLineAggregates);
127+
})
128+
.ToList();
125129
```
126130

127131
Generated SQL:
@@ -135,30 +139,31 @@ SELECT json_object(
135139
### Max / Min on Collection
136140

137141
```csharp
138-
o => new OrderLineAggregates
139-
{
140-
Customer = o.CustomerName,
141-
MaxPrice = o.Lines.Max(l => l.UnitPrice),
142-
MinPrice = o.Lines.Min(l => l.UnitPrice)
143-
}
142+
var results = await store.Query<Order>()
143+
.Select(o => new OrderLineAggregates
144+
{
145+
Customer = o.CustomerName,
146+
MaxPrice = o.Lines.Max(l => l.UnitPrice),
147+
MinPrice = o.Lines.Min(l => l.UnitPrice)
148+
})
149+
.ToList();
144150
```
145151

146152
### Multiple Collection Aggregates
147153

148154
Combine multiple collection aggregates in a single projection:
149155

150156
```csharp
151-
var results = await store.Query<Order, OrderLineAggregates>(
152-
o => o.CustomerName == "Alice",
153-
o => new OrderLineAggregates
157+
var results = await store.Query<Order>()
158+
.Where(o => o.CustomerName == "Alice")
159+
.Select(o => new OrderLineAggregates
154160
{
155161
Customer = o.CustomerName,
156162
TotalQty = o.Lines.Sum(l => l.Quantity),
157163
MaxPrice = o.Lines.Max(l => l.UnitPrice),
158164
MinPrice = o.Lines.Min(l => l.UnitPrice),
159-
},
160-
ctx.Order,
161-
ctx.OrderLineAggregates);
165+
})
166+
.ToList();
162167
```
163168

164169
### Collection Aggregate Reference

src/content/docs/sqlite-docdb/aot.mdx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,42 +52,37 @@ var store = new SqliteDocumentStore(new DocumentStoreOptions
5252
});
5353
```
5454

55-
## Auto-resolving type info from the context
55+
## Optional JsonTypeInfo&lt;T&gt; parameters
5656

57-
When a `JsonSerializerContext` is attached to `JsonSerializerOptions`, the reflection-marked overloads (without `JsonTypeInfo<T>`) automatically resolve type info from the configured resolver. You can configure the context once and skip passing `JsonTypeInfo<T>` on every call — while retaining full AOT safety.
57+
All `JsonTypeInfo<T>` parameters across the entire API are optional (`= null` default). When omitted, type info is automatically resolved from the configured `JsonSerializerOptions.TypeInfoResolver`. You can configure a `JsonSerializerContext` once and skip passing `JsonTypeInfo<T>` on every call — while retaining full AOT safety.
5858

59-
| Without resolver (explicit `JsonTypeInfo<T>`) | With resolver (auto-resolved) |
59+
| With explicit `JsonTypeInfo<T>` | With auto-resolution (recommended) |
6060
|---|---|
6161
| `store.Set(user, ctx.User)` | `store.Set(user)` |
6262
| `store.Set("id", user, ctx.User)` | `store.Set("id", user)` |
63-
| `store.Get<User>("id", ctx.User)` | `store.Get<User>("id")` |
64-
| `store.GetAll<User>(ctx.User)` | `store.GetAll<User>()` |
63+
| `store.Get("id", ctx.User)` | `store.Get<User>("id")` |
6564
| `store.Upsert("id", patch, ctx.User)` | `store.Upsert("id", patch)` |
6665
| `store.SetProperty("id", (User u) => u.Age, 31, ctx.User)` | `store.SetProperty<User>("id", u => u.Age, 31)` |
6766
| `store.RemoveProperty("id", (User u) => u.Email, ctx.User)` | `store.RemoveProperty<User>("id", u => u.Email)` |
68-
| `store.Query<User>(sql, ctx.User, parms)` | `store.Query<User>(sql, parms)` |
69-
| `store.GetAllStream<User>(ctx.User)` | `store.GetAllStream<User>()` |
70-
| `store.QueryStream<User>(sql, ctx.User, parms)` | `store.QueryStream<User>(sql, parms)` |
67+
| `store.Query(ctx.User)` | `store.Query<User>()` |
68+
| `store.Query<User>("sql", ctx.User, parms)` | `store.Query<User>("sql", parameters: parms)` |
69+
| `store.QueryStream<User>("sql", ctx.User, parms)` | `store.QueryStream<User>("sql", parameters: parms)` |
7170

7271
```csharp
7372
// All of these are AOT-safe when ctx.Options is configured
7473
var id = await store.Set(new User { Name = "Alice", Age = 25 });
7574
var user = await store.Get<User>(id);
76-
var all = await store.GetAll<User>();
75+
var all = await store.Query<User>().ToList();
7776
await store.Upsert("user-1", new User { Name = "Alice", Age = 30 });
7877

7978
var results = await store.Query<User>(
8079
"json_extract(Data, '$.age') > @minAge",
81-
new { minAge = 30 });
80+
parameters: new { minAge = 30 });
8281

83-
await foreach (var u in store.GetAllStream<User>())
82+
await foreach (var u in store.Query<User>().ToAsyncEnumerable())
8483
Console.WriteLine(u.Name);
8584
```
8685

87-
:::note
88-
Expression-based queries (`store.Query(u => u.Age > 30, ctx.User)`) and projections always require explicit `JsonTypeInfo<T>` because the LINQ expression visitor needs type metadata to resolve JSON property names. Auto-resolution applies to the 10 methods in the table above.
89-
:::
90-
9186
## Disabling reflection fallback
9287

9388
By default (`UseReflectionFallback = true`), if no resolver is configured or the type isn't registered, methods fall back to reflection-based serialization. This preserves backwards compatibility.

src/content/docs/sqlite-docdb/crud.mdx

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ title: CRUD Operations
66

77
```csharp
88
// Auto-generated GUID key — returns the ID
9-
var id = await store.Set(new User { Name = "Alice", Age = 25 }, ctx.User);
9+
var id = await store.Set(new User { Name = "Alice", Age = 25 });
1010

1111
// Explicit key
12-
await store.Set("user-1", new User { Name = "Alice", Age = 25 }, ctx.User);
12+
await store.Set("user-1", new User { Name = "Alice", Age = 25 });
1313
```
1414

1515
## Upsert with JSON Merge Patch
@@ -18,12 +18,12 @@ await store.Set("user-1", new User { Name = "Alice", Age = 25 }, ctx.User);
1818

1919
```csharp
2020
// Insert a full document
21-
await store.Set("user-1", new User { Name = "Alice", Age = 25, Email = "alice@test.com" }, ctx.User);
21+
await store.Set("user-1", new User { Name = "Alice", Age = 25, Email = "alice@test.com" });
2222

2323
// Merge patch — only update Name and Age, preserve Email
24-
await store.Upsert("user-1", new User { Name = "Alice", Age = 30 }, ctx.User);
24+
await store.Upsert("user-1", new User { Name = "Alice", Age = 30 });
2525

26-
var user = await store.Get<User>("user-1", ctx.User);
26+
var user = await store.Get<User>("user-1");
2727
// user.Name == "Alice", user.Age == 30, user.Email == "alice@test.com" (preserved)
2828
```
2929

@@ -42,19 +42,19 @@ For true partial updates, use nullable properties in your patch type so that uns
4242

4343
```csharp
4444
// Update a scalar field
45-
await store.SetProperty<User>("user-1", u => u.Age, 31, ctx.User);
45+
await store.SetProperty<User>("user-1", u => u.Age, 31);
4646

4747
// Update a string field
48-
await store.SetProperty<User>("user-1", u => u.Email, "newemail@test.com", ctx.User);
48+
await store.SetProperty<User>("user-1", u => u.Email, "newemail@test.com");
4949

5050
// Set a field to null
51-
await store.SetProperty<User>("user-1", u => u.Email, null, ctx.User);
51+
await store.SetProperty<User>("user-1", u => u.Email, null);
5252

5353
// Nested property
54-
await store.SetProperty<Order>("order-1", o => o.ShippingAddress.City, "Portland", ctx.Order);
54+
await store.SetProperty<Order>("order-1", o => o.ShippingAddress.City, "Portland");
5555

5656
// Check if document existed
57-
bool updated = await store.SetProperty<User>("user-1", u => u.Age, 31, ctx.User);
57+
bool updated = await store.SetProperty<User>("user-1", u => u.Age, 31);
5858
```
5959

6060
**How it works:** The expression `u => u.Age` is resolved to the JSON path `$.age` (respecting `[JsonPropertyName]` attributes and naming policies). The SQL executed is:
@@ -73,13 +73,13 @@ WHERE Id = @id AND TypeName = @typeName;
7373

7474
```csharp
7575
// Remove a nullable field
76-
await store.RemoveProperty<User>("user-1", u => u.Email, ctx.User);
76+
await store.RemoveProperty<User>("user-1", u => u.Email);
7777

7878
// Remove a nested property
79-
await store.RemoveProperty<Order>("order-1", o => o.ShippingAddress.City, ctx.Order);
79+
await store.RemoveProperty<Order>("order-1", o => o.ShippingAddress.City);
8080

8181
// Remove a collection property (removes the entire array)
82-
await store.RemoveProperty<Order>("order-1", o => o.Tags, ctx.Order);
82+
await store.RemoveProperty<Order>("order-1", o => o.Tags);
8383
```
8484

8585
Unlike `SetProperty`, `RemoveProperty` works on any property type — scalar, nested object, or collection — because it simply removes the key from the JSON.
@@ -96,25 +96,31 @@ Unlike `SetProperty`, `RemoveProperty` works on any property type — scalar, ne
9696
## Get a document
9797

9898
```csharp
99-
var user = await store.Get<User>("user-1", ctx.User);
99+
var user = await store.Get<User>("user-1");
100100
```
101101

102102
## Get all documents of a type
103103

104104
```csharp
105-
var users = await store.GetAll<User>(ctx.User);
105+
var users = await store.Query<User>().ToList();
106106
```
107107

108108
## Remove a document
109109

110110
```csharp
111111
// By ID
112112
bool deleted = await store.Remove<User>("user-1");
113+
```
114+
115+
## Remove documents matching a predicate
113116

114-
// By expression — returns number deleted
115-
int deleted = await store.Remove<User>(u => u.Age < 18, ctx.User);
117+
```csharp
118+
// Returns number of deleted rows
119+
int deleted = await store.Query<User>().Where(u => u.Age < 18).Remove();
116120
```
117121

122+
See [Querying](/sqlite-docdb/querying) for more examples of removing with expressions.
123+
118124
## Clear all documents of a type
119125

120126
```csharp

0 commit comments

Comments
 (0)