kitcn 0.32.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/auth/index.js +1 -1
- package/dist/{capabilities-Ctcw2VRq.d.ts → capabilities-Bxzoofl4.d.ts} +195 -9
- package/dist/orm/aggregate-index/index.d.ts +1 -1
- package/dist/orm/aggregate-index/index.js +226 -96
- package/dist/orm/index.d.ts +2 -2
- package/dist/orm/index.js +440 -113
- package/dist/orm/migrations/index.d.ts +2 -2
- package/dist/{query-context-D4z1CnDH.js → query-context-DQLv58LH.js} +34 -1
- package/dist/react/index.js +4 -3
- package/dist/{schema-Bh7AmJwY.js → schema-BF4P0ZjS.js} +322 -1
- package/dist/{where-clause-compiler-BHGHbfb9.d.ts → where-clause-compiler-DrV6lQg0.d.ts} +126 -126
- package/package.json +1 -1
- package/skills/kitcn/references/features/aggregates.md +13 -0
- package/skills/kitcn/references/features/orm.md +24 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,119 @@
|
|
|
1
1
|
# kitcn
|
|
2
2
|
|
|
3
|
+
## 0.33.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#456](https://github.com/udecode/kitcn/pull/456) [`5b0bd5a`](https://github.com/udecode/kitcn/commit/5b0bd5a1debb4a0e0bb87f79ef1a18f357b614c1) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Breaking changes
|
|
8
|
+
|
|
9
|
+
- Support index-ordered pagination for indexed filters with more than 64 values. Pages follow index order, grouped by the filtered value, rather than creation order. Add `orderBy` and `maxScan` to preserve newest-first paging.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// Before
|
|
13
|
+
const page = await db.query.users.withIndex("by_status").findMany({
|
|
14
|
+
where: { status: { in: manyStatuses } },
|
|
15
|
+
cursor: null,
|
|
16
|
+
limit: 20,
|
|
17
|
+
maxScan: 500,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// After
|
|
21
|
+
const page = await db.query.users.withIndex("by_status").findMany({
|
|
22
|
+
where: { status: { in: manyStatuses } },
|
|
23
|
+
orderBy: { createdAt: "desc" },
|
|
24
|
+
cursor: null,
|
|
25
|
+
limit: 20,
|
|
26
|
+
maxScan: 500,
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Patches
|
|
31
|
+
|
|
32
|
+
- Fix authenticated cRPC query results disappearing when a server-rendered page
|
|
33
|
+
hydrates.
|
|
34
|
+
- Fix unnecessary full-table reads for `select()` filters containing more than 64 values.
|
|
35
|
+
- Fix unnecessary full-table reads for long `in` lists combined with another condition, such as `name: { contains: 'x' }`.
|
|
36
|
+
- Improve limited reads with additional conditions so they stop after enough matching rows are found when index order satisfies the requested sort.
|
|
37
|
+
- Support index-bounded reads for indexed `in`, `notIn`, `ne`, and same-field equality `OR` filters regardless of list length.
|
|
38
|
+
- Support pagination without `maxScan` for wide filters whose requested order follows their indexed values; cross-value sorting, such as `orderBy: { createdAt: 'desc' }`, still requires `maxScan` past 64 values.
|
|
39
|
+
|
|
40
|
+
- [#449](https://github.com/udecode/kitcn/pull/449) [`b8df2da`](https://github.com/udecode/kitcn/commit/b8df2da49a8733b545f02b84bb538b1cbef275b0) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Breaking changes
|
|
41
|
+
|
|
42
|
+
- Read a `select().flatMap(relation, { where })` stage through an index that extends the relation's foreign key when the schema declares one. Children arrive in that index's order, so a lowered range field now orders them ahead of creation time, and outstanding page cursors for those queries do not carry over.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// Before: every post by the author is read, then filtered, in creation order.
|
|
46
|
+
// After: only the by_author_likes range is read, in numLikes order.
|
|
47
|
+
index("by_author_likes").on(t.authorId, t.numLikes);
|
|
48
|
+
|
|
49
|
+
await ctx.orm.query.users
|
|
50
|
+
.select()
|
|
51
|
+
.flatMap("posts", { includeParent: false, where: { numLikes: { gt: 10 } } })
|
|
52
|
+
.paginate({ cursor: null, limit: 20 });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Patches
|
|
56
|
+
|
|
57
|
+
- Compile a `select().union([{ where }])` source `where` against the table's indexes instead of filtering every scanned row, so an object `where` on an indexed field bounds the read. A source keeps its unanchored read when the lowered one could not supply `interleaveBy`, and a `where` never displaces an index the source or the chain pinned with a range.
|
|
58
|
+
- Report what a caller can actually do when a `predicate(...)` `where` runs over an unbounded pipeline read: a union source names its own `index` option, and a `flatMap` stage names the relation index it needs.
|
|
59
|
+
- Resolve a `select()` union source or `flatMap` stage `where` once per read. A callback `where` runs a single time instead of twice, and an object `where` compiles once instead of once per row.
|
|
60
|
+
|
|
61
|
+
### Patch Changes
|
|
62
|
+
|
|
63
|
+
- [#455](https://github.com/udecode/kitcn/pull/455) [`a73de28`](https://github.com/udecode/kitcn/commit/a73de28bacf3e518a38762442270d2c54f6989e8) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
64
|
+
|
|
65
|
+
- Fix `findMany` losing its read bound when a `limit` is combined with `in`, `ne`,
|
|
66
|
+
`notIn` or a same-field `OR` on a table that has RLS enabled, or alongside a
|
|
67
|
+
filter Convex cannot evaluate such as `contains`. Either one used to make the
|
|
68
|
+
query read every row it matched against, so
|
|
69
|
+
`findMany({ where: { ownerId: { in: [a, b] } }, limit: 3 })` read 500 documents
|
|
70
|
+
on a 500-row table and 200 on a 200-row table. It now reads 6 at either size,
|
|
71
|
+
and the count no longer grows with the table.
|
|
72
|
+
- Improve how that `limit` is counted, so it bounds rows the caller can actually
|
|
73
|
+
see: with 80 rows an RLS policy hides sitting in front of the matches,
|
|
74
|
+
`limit: 3` still returns three rows and reads 86 documents instead of 200.
|
|
75
|
+
- Support that bound for an `in` list of any length.
|
|
76
|
+
|
|
77
|
+
Rows and their order are unchanged. A `where` that filters through a relation
|
|
78
|
+
keeps its previous read cost, as does `ne`, `notIn` or `isNotNull` ordered by a
|
|
79
|
+
field no index can serve.
|
|
80
|
+
|
|
81
|
+
- [#450](https://github.com/udecode/kitcn/pull/450) [`781af65`](https://github.com/udecode/kitcn/commit/781af65c6e2f9128362378f0cf358d2c8e509b7d) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
82
|
+
|
|
83
|
+
- Improve update read costs on `aggregateIndex` and `rankIndex` tables: read
|
|
84
|
+
each row once unless a user `update.before` hook requires a fresh read.
|
|
85
|
+
- Fix CLEARING checks for deletes of already-deleted rows: report the
|
|
86
|
+
transient aggregate-index state instead of `Delete on non-existent doc`.
|
|
87
|
+
|
|
88
|
+
- [#451](https://github.com/udecode/kitcn/pull/451) [`80f7609`](https://github.com/udecode/kitcn/commit/80f7609cb107d23e4688b6877f35787107dd0a39) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
89
|
+
|
|
90
|
+
- Improve `aggregateIndex` bulk-write read costs by reusing bucket and member
|
|
91
|
+
reads within uninterrupted ORM statements. User hooks and policy callbacks
|
|
92
|
+
end reuse so nested mutation writes remain visible to later maintenance.
|
|
93
|
+
|
|
94
|
+
- [#454](https://github.com/udecode/kitcn/pull/454) [`f399843`](https://github.com/udecode/kitcn/commit/f399843c6bfb46e080558bc22d2cc4a77842cd16) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
95
|
+
|
|
96
|
+
- Improve bulk aggregate writes by folding shared bucket and extrema updates
|
|
97
|
+
within uninterrupted statements. A 40-row key migration writes two shared
|
|
98
|
+
buckets and two extrema entries, plus forty membership rows.
|
|
99
|
+
- Preserve read-your-own-writes through aggregate reads and nested functions
|
|
100
|
+
called by lifecycle hooks or RLS policies. Reads and callbacks flush pending
|
|
101
|
+
writes; callbacks suspend batching until they settle.
|
|
102
|
+
- Fix mid-statement aggregate reads through `withoutTriggers()` and an ORM
|
|
103
|
+
rebuilt from a hook context to observe the same rows as `ctx.orm`.
|
|
104
|
+
|
|
105
|
+
## 0.32.2
|
|
106
|
+
|
|
107
|
+
### Patch Changes
|
|
108
|
+
|
|
109
|
+
- [#448](https://github.com/udecode/kitcn/pull/448) [`75ceda6`](https://github.com/udecode/kitcn/commit/75ceda65a12b4dfdd525c97a9401a31c54fd89d0) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
110
|
+
|
|
111
|
+
- Improve the read cost of a relation `where` on a relation joined on a column
|
|
112
|
+
other than the primary id, including `through` targets and `_count` on a
|
|
113
|
+
`through` relation.
|
|
114
|
+
- Fix relation targets sharing one loaded document, which could add fields to a
|
|
115
|
+
relation that only another relation in the same query requested.
|
|
116
|
+
|
|
3
117
|
## 0.32.1
|
|
4
118
|
|
|
5
119
|
### Patch Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Vn as ConvexTextBuilderInitial, Zt as ConvexTableWithColumns } from "../capabilities-
|
|
2
|
-
import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-
|
|
1
|
+
import { Vn as ConvexTextBuilderInitial, Zt as ConvexTableWithColumns } from "../capabilities-Bxzoofl4.js";
|
|
2
|
+
import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-DrV6lQg0.js";
|
|
3
3
|
import * as convex_values0 from "convex/values";
|
|
4
4
|
import { GenericId, Infer, Value } from "convex/values";
|
|
5
5
|
import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
|
package/dist/auth/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { i as defineAuth, n as createDisabledAuthRuntime, r as getGeneratedAuthD
|
|
|
4
4
|
import { n as createGeneratedFunctionReference, o as isQueryCtx, s as isRunMutationCtx } from "../api-entry-Buvjl9hn.js";
|
|
5
5
|
import { i as customQuery, n as customCtx, r as customMutation } from "../customFunctions-X_B4kET8.js";
|
|
6
6
|
import { C as eq, o as getAggregateIndexes } from "../index-utils-DvK7P6Q1.js";
|
|
7
|
-
import {
|
|
7
|
+
import { c as stream, f as unsetToken, s as mergedStream, t as getByIdWithOrmQueryFallback } from "../query-context-DQLv58LH.js";
|
|
8
8
|
import { n as convex } from "../convex-plugin-AbNQl1Sg.js";
|
|
9
9
|
import { v } from "convex/values";
|
|
10
10
|
import { internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
|
|
@@ -2149,7 +2149,7 @@ declare const createAggregateError: (code: AggregateErrorCode, message: string)
|
|
|
2149
2149
|
declare const ensureCountAllowedForRls: (tableConfig: TableRelationalConfig, rlsMode: "skip" | "default" | undefined) => void;
|
|
2150
2150
|
declare const ensureAggregateAllowedForRls: (tableConfig: TableRelationalConfig, rlsMode: "skip" | "default" | undefined, methodName: string) => void;
|
|
2151
2151
|
declare namespace runtime_d_exports$1 {
|
|
2152
|
-
export { AGGREGATE_ERROR, AGGREGATE_STATE_KIND_METRIC, AGGREGATE_STATE_KIND_RANK, AggregateBucketDelta, AggregateErrorCode, AggregateExtremaDelta, AggregateIndexDefinition, AggregateMemberWrite, AggregateMembershipDelta, AggregateQueryPlan, AggregateRangeConstraint, COUNT_ERROR, COUNT_STATUS_BUILDING, COUNT_STATUS_CLEARING, COUNT_STATUS_READY, ClearIndexChunkResult, CountErrorCode, CountIndexDefinition, CountQueryPlan, CountState, NULLISH_PROBE_VALUES, PlanBucketReadCache, applyAggregateIndexesForChange, applyCountIndexesForChange, asUnknownArray, assertAggregateIndexesWritable, clearCountIndexChunk, compileAggregateQueryPlan, compileCountFieldQueryPlan, compileCountQueryPlan, computeAggregateMembershipDelta, computeAggregateMetricValues, computeCountKeyParts, createAggregateError, createCountError, ensureAggregateAllowedForRls, ensureAggregateIndexReady, ensureCountAllowedForRls, ensureCountIndexReady, flushAggregateMembershipDeltas, getAggregateIndexDefinitions, getCountIndexDefinitions, getCountIndexValuesForFields, getCountState, isAggregatePlanZero, isIndexCountZero, listCountStates, listSchemaAggregateIndexes, listSchemaCountIndexes, parseAggregateWhere, parseCountWhere, readAverageFromBuckets, readCountFieldFromBuckets, readCountFromBuckets, readExtremaFromBuckets, readPlanBuckets, readSumFromBuckets, reconcileAggregateMembership, serializeCountKeyParts, setCountState, setCountStateError };
|
|
2152
|
+
export { AGGREGATE_ERROR, AGGREGATE_STATE_KIND_METRIC, AGGREGATE_STATE_KIND_RANK, AggregateBucketDelta, AggregateErrorCode, AggregateExtremaDelta, AggregateIndexDefinition, AggregateMemberWrite, AggregateMembershipDelta, AggregateQueryPlan, AggregateRangeConstraint, COUNT_ERROR, COUNT_STATUS_BUILDING, COUNT_STATUS_CLEARING, COUNT_STATUS_READY, ClearIndexChunkResult, CountErrorCode, CountIndexDefinition, CountMemberRow, CountQueryPlan, CountState, NULLISH_PROBE_VALUES, PlanBucketReadCache, applyAggregateIndexesForChange, applyCountIndexesForChange, asUnknownArray, assertAggregateIndexesWritable, clearCountIndexChunk, compileAggregateQueryPlan, compileCountFieldQueryPlan, compileCountQueryPlan, computeAggregateMembershipDelta, computeAggregateMetricValues, computeCountKeyParts, createAggregateError, createCountError, ensureAggregateAllowedForRls, ensureAggregateIndexReady, ensureCountAllowedForRls, ensureCountIndexReady, flushAggregateMembershipDeltas, getAggregateIndexDefinitions, getCountIndexDefinitions, getCountIndexValuesForFields, getCountState, isAggregatePlanZero, isIndexCountZero, listCountStates, listSchemaAggregateIndexes, listSchemaCountIndexes, parseAggregateWhere, parseCountWhere, readAverageFromBuckets, readCountFieldFromBuckets, readCountFromBuckets, readExtremaFromBuckets, readPlanBuckets, readSumFromBuckets, reconcileAggregateMembership, serializeCountKeyParts, setCountState, setCountStateError };
|
|
2153
2153
|
}
|
|
2154
2154
|
declare const AGGREGATE_STATE_KIND_METRIC = "metric";
|
|
2155
2155
|
declare const AGGREGATE_STATE_KIND_RANK = "rank";
|
|
@@ -2188,6 +2188,21 @@ type CountState = {
|
|
|
2188
2188
|
completedAt?: number | null;
|
|
2189
2189
|
lastError?: string | null;
|
|
2190
2190
|
};
|
|
2191
|
+
type CountMemberRow = {
|
|
2192
|
+
_id: GenericId<any>;
|
|
2193
|
+
kind: string;
|
|
2194
|
+
tableKey: string;
|
|
2195
|
+
indexName: string;
|
|
2196
|
+
docId: string;
|
|
2197
|
+
keyHash: string;
|
|
2198
|
+
keyParts: unknown[];
|
|
2199
|
+
sumValues: Record<string, number>;
|
|
2200
|
+
nonNullCountValues: Record<string, number>;
|
|
2201
|
+
extremaValues: Record<string, unknown>;
|
|
2202
|
+
rankNamespace?: unknown;
|
|
2203
|
+
rankKey?: unknown;
|
|
2204
|
+
rankSumValue?: number;
|
|
2205
|
+
};
|
|
2191
2206
|
type CountBucketRow = {
|
|
2192
2207
|
_id: GenericId<any>;
|
|
2193
2208
|
tableKey: string;
|
|
@@ -2261,6 +2276,11 @@ type AggregateExtremaDelta = {
|
|
|
2261
2276
|
value: unknown;
|
|
2262
2277
|
delta: number;
|
|
2263
2278
|
};
|
|
2279
|
+
/**
|
|
2280
|
+
* `post` is what the write leaves in the member table, so the flush can bring
|
|
2281
|
+
* the member memo forward without reading the row back. The `_id` is missing
|
|
2282
|
+
* because an insert only learns it once the insert returns.
|
|
2283
|
+
*/
|
|
2264
2284
|
type AggregateMemberWrite = {
|
|
2265
2285
|
kind: 'none';
|
|
2266
2286
|
} | {
|
|
@@ -2270,11 +2290,13 @@ type AggregateMemberWrite = {
|
|
|
2270
2290
|
kind: 'patch';
|
|
2271
2291
|
id: GenericId<any>;
|
|
2272
2292
|
doc: Record<string, unknown>;
|
|
2293
|
+
post: Omit<CountMemberRow, '_id'>;
|
|
2273
2294
|
} | {
|
|
2274
2295
|
kind: 'insert';
|
|
2275
|
-
doc:
|
|
2296
|
+
doc: Omit<CountMemberRow, '_id'>;
|
|
2276
2297
|
};
|
|
2277
2298
|
type AggregateMembershipDelta = {
|
|
2299
|
+
/** The document this delta reconciles, and the member memo's key. */docId: string;
|
|
2278
2300
|
buckets: AggregateBucketDelta[];
|
|
2279
2301
|
extrema: AggregateExtremaDelta[];
|
|
2280
2302
|
member: AggregateMemberWrite;
|
|
@@ -2293,8 +2315,28 @@ declare const computeAggregateMembershipDelta: (db: GenericDatabaseWriter<any>,
|
|
|
2293
2315
|
*/
|
|
2294
2316
|
declare const flushAggregateMembershipDeltas: (db: GenericDatabaseWriter<any>, tableName: string, indexName: string, deltas: AggregateMembershipDelta[]) => Promise<void>;
|
|
2295
2317
|
/**
|
|
2296
|
-
* Single-document reconciliation.
|
|
2297
|
-
*
|
|
2318
|
+
* Single-document reconciliation.
|
|
2319
|
+
*
|
|
2320
|
+
* The bucket and extrema writes always go on the transaction's write queue,
|
|
2321
|
+
* never straight to storage: `applyBucketDelta` writes an absolute count
|
|
2322
|
+
* computed from the row it just read, so two of them interleaving would be a
|
|
2323
|
+
* lost update, and routing every one of them through a single drain is what
|
|
2324
|
+
* keeps them serialized. Inside a mutation statement the queue is held to the
|
|
2325
|
+
* end of the statement, which is what lets the fold collapse a page of
|
|
2326
|
+
* documents into one write per key tuple; outside one it is drained
|
|
2327
|
+
* immediately, so a raw `ctx.db` write behaves exactly as it did before.
|
|
2328
|
+
*
|
|
2329
|
+
* The member row is written per document either way, because it is the
|
|
2330
|
+
* pre-image the next reconciliation of this document subtracts. Outside a
|
|
2331
|
+
* statement it is still written after the bucket, preserving the previous
|
|
2332
|
+
* order; inside one it necessarily lands first, and a flush that throws
|
|
2333
|
+
* part-way leaves the index needing a backfill — which a partially applied fold
|
|
2334
|
+
* would anyway, whichever order the two halves ran in.
|
|
2335
|
+
*
|
|
2336
|
+
* Deferral is invisible to aggregate readers: every bucket- and extrema-backed
|
|
2337
|
+
* read path drains the queue first, so user code reading a count later in the
|
|
2338
|
+
* same mutation — including from a trigger firing mid-statement — still sees
|
|
2339
|
+
* its own writes.
|
|
2298
2340
|
*/
|
|
2299
2341
|
declare const reconcileAggregateMembership: (db: GenericDatabaseWriter<any>, params: {
|
|
2300
2342
|
tableName: string;
|
|
@@ -2755,6 +2797,7 @@ declare class ConvexDeleteBuilder<TTable extends ConvexTable<any>, TReturning ex
|
|
|
2755
2797
|
}): this;
|
|
2756
2798
|
executeAsync(this: ConvexDeleteExecutableThis<TTable, TReturning, TMode>, ...args: TMode extends 'single' ? [config?: Omit<MutationExecuteConfig, 'mode'>] : [config: never]): Promise<TMode extends 'single' ? MutationExecuteResult<TTable, TReturning, 'single'> : never>;
|
|
2757
2799
|
execute(this: ConvexDeleteExecutableThis<TTable, TReturning, TMode>, ...args: TMode extends 'single' ? [config?: MutationExecuteConfig] : [config?: never]): Promise<MutationExecuteResult<TTable, TReturning, TMode>>;
|
|
2800
|
+
private _runStatement;
|
|
2758
2801
|
}
|
|
2759
2802
|
//#endregion
|
|
2760
2803
|
//#region src/orm/insert.d.ts
|
|
@@ -2795,6 +2838,7 @@ declare class ConvexInsertBuilder<TTable extends ConvexTable<any>, TReturning ex
|
|
|
2795
2838
|
onConflictDoNothing(config?: InsertOnConflictDoNothingConfig<TTable>): ConvexInsertWithout<this, 'onConflictDoNothing' | 'onConflictDoUpdate'>;
|
|
2796
2839
|
onConflictDoUpdate(config: InsertOnConflictDoUpdateConfig<TTable>): ConvexInsertWithout<this, 'onConflictDoNothing' | 'onConflictDoUpdate'>;
|
|
2797
2840
|
execute(): Promise<MutationResult<TTable, TReturning>>;
|
|
2841
|
+
private _runStatement;
|
|
2798
2842
|
private resolveReturningRow;
|
|
2799
2843
|
private handleConflict;
|
|
2800
2844
|
private findConflictRow;
|
|
@@ -2922,6 +2966,20 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
2922
2966
|
* intervening write would make it stale.
|
|
2923
2967
|
*/
|
|
2924
2968
|
private readonly _documentByNormalizedId;
|
|
2969
|
+
/**
|
|
2970
|
+
* Single target documents resolved by an eq-pinned key during one execution,
|
|
2971
|
+
* keyed on the read itself: table, index, join columns and their values.
|
|
2972
|
+
*
|
|
2973
|
+
* `_documentByNormalizedId` only covers a join on the primary id. A relation
|
|
2974
|
+
* joined on any other column resolves its target with `.first()` instead, and
|
|
2975
|
+
* the same one-row-at-a-time membership predicate re-issues that read once per
|
|
2976
|
+
* drain. The index is fixed for the whole relation and the key is pinned by
|
|
2977
|
+
* `eq`, so the first row is a pure function of this key within an execution.
|
|
2978
|
+
*
|
|
2979
|
+
* Same scope and staleness argument as `_documentByNormalizedId`: not handed
|
|
2980
|
+
* to the next run by `_forExecution`, so an intervening write is still seen.
|
|
2981
|
+
*/
|
|
2982
|
+
private readonly _firstDocumentByFieldKey;
|
|
2925
2983
|
constructor(schema: TSchema, tableConfig: TTableConfig, edgeMetadata: EdgeMetadata[], db: GenericDatabaseReader<any>, config: DBQueryConfig<'one' | 'many', boolean, TSchema, TTableConfig>, mode: 'many' | 'first' | 'firstOrThrow' | 'count' | 'aggregate' | 'groupBy', _allEdges?: EdgeMetadata[] | undefined, // M6.5 Phase 2: All edges for nested loading
|
|
2926
2984
|
rls?: RlsContext | undefined, relationLoading?: {
|
|
2927
2985
|
concurrency?: number;
|
|
@@ -3051,8 +3109,41 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
3051
3109
|
private _finalizeRows;
|
|
3052
3110
|
private _getSchemaDefinitionOrThrow;
|
|
3053
3111
|
private _applyEqBounds;
|
|
3054
|
-
|
|
3055
|
-
|
|
3112
|
+
/**
|
|
3113
|
+
* The row filter for an already-resolved pipeline stage `where`.
|
|
3114
|
+
*
|
|
3115
|
+
* Takes the resolved shape rather than the clause so the rows that are read
|
|
3116
|
+
* and the rows that are kept come from the same resolution. Resolving twice
|
|
3117
|
+
* would let a `where` callback that closes over changing state — a clock, a
|
|
3118
|
+
* counter — bound the read with one expression and filter it with another,
|
|
3119
|
+
* dropping rows that the second expression matched but the first never read.
|
|
3120
|
+
*/
|
|
3121
|
+
private _buildResolvedWherePredicate;
|
|
3122
|
+
/**
|
|
3123
|
+
* What a pipeline stage `where` is, resolved once.
|
|
3124
|
+
*
|
|
3125
|
+
* Object clauses and callbacks that return an expression can be compiled and
|
|
3126
|
+
* therefore index-lowered. `predicate(...)` is opaque JavaScript and never
|
|
3127
|
+
* can be. Both the anchoring check and the index lowering need to tell those
|
|
3128
|
+
* apart, and a callback `where` should be run once per read rather than once
|
|
3129
|
+
* per caller that asks.
|
|
3130
|
+
*/
|
|
3131
|
+
private _resolvePipelineWhere;
|
|
3132
|
+
/**
|
|
3133
|
+
* Reject a `predicate(...)` stage `where` on a read nothing bounds.
|
|
3134
|
+
*
|
|
3135
|
+
* A predicate can only run after the row is read, so it costs one read per
|
|
3136
|
+
* candidate row. That is fine over an index range and unbounded over a table
|
|
3137
|
+
* scan, which is the same line `findMany` draws for a chain-level
|
|
3138
|
+
* `predicate(...)`. An object `where` is not held to it: the compiler
|
|
3139
|
+
* lowers what it can and post-filters the rest, exactly as the chain-level
|
|
3140
|
+
* read does.
|
|
3141
|
+
*
|
|
3142
|
+
* `remedy` is per call site because the anchor a caller can add differs: a
|
|
3143
|
+
* union source takes an index, a `flatMap` stage has no index option at all
|
|
3144
|
+
* and is anchored by the relation's own declared index.
|
|
3145
|
+
*/
|
|
3146
|
+
private _assertPipelineWhereIsAnchored;
|
|
3056
3147
|
private _isFilterExpressionNode;
|
|
3057
3148
|
private _isPredicateWhereClause;
|
|
3058
3149
|
private _createFilterOperators;
|
|
@@ -3085,14 +3176,31 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
3085
3176
|
/**
|
|
3086
3177
|
* The compiled index union, as one ordered stream.
|
|
3087
3178
|
*
|
|
3088
|
-
* Each probe is its own index range, so the union is only usable where
|
|
3089
|
-
*
|
|
3090
|
-
*
|
|
3179
|
+
* Each probe is its own index range, so the union is only usable where its
|
|
3180
|
+
* order is the order the read has to produce. `mergedStream` orders by a
|
|
3181
|
+
* suffix of the index key, and a suffix may only drop key components the
|
|
3091
3182
|
* probes all pin to a single value — so the requested field has to sit inside
|
|
3092
3183
|
* the pinned run or immediately after it. Returns null when it does not, and
|
|
3093
3184
|
* the caller falls back to the plan's plain index range or a bounded scan.
|
|
3185
|
+
*
|
|
3186
|
+
* Two executors, because a union that is too wide to merge is not too wide to
|
|
3187
|
+
* read. A merge registers every probe up front and holds them open for the
|
|
3188
|
+
* life of the read, which is what `MAX_INDEX_UNION_PROBES` refuses; past that
|
|
3189
|
+
* width the probes are concatenated instead, one open query at a time. What
|
|
3190
|
+
* the cap picks is therefore fan-out versus sequential — never index versus
|
|
3191
|
+
* table scan, which is a trade no read wins.
|
|
3094
3192
|
*/
|
|
3095
3193
|
private _buildProbeUnionStream;
|
|
3194
|
+
/**
|
|
3195
|
+
* `probeFilters` in the order a scan of `indexField` reaches them, or null
|
|
3196
|
+
* when they cannot be proven to be pairwise-disjoint ranges on that field.
|
|
3197
|
+
*
|
|
3198
|
+
* Concatenation stands in for a merge only under those two properties, and
|
|
3199
|
+
* both have to come from the filters themselves rather than from trust in the
|
|
3200
|
+
* compiler that emitted them: overlapping probes would emit a row twice, and
|
|
3201
|
+
* probes handed over out of order would emit index keys backwards.
|
|
3202
|
+
*/
|
|
3203
|
+
private _orderDisjointProbes;
|
|
3096
3204
|
/**
|
|
3097
3205
|
* The read the compiled plan describes, as a stream, with nothing filtered
|
|
3098
3206
|
* yet.
|
|
@@ -3124,7 +3232,41 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
3124
3232
|
* the caller then falls back to its plain-query path.
|
|
3125
3233
|
*/
|
|
3126
3234
|
private _buildResidualFilterStream;
|
|
3235
|
+
/**
|
|
3236
|
+
* One `pipeline.union` source, as a stream.
|
|
3237
|
+
*
|
|
3238
|
+
* The source's own `where` is compiled into an index plan first, so an
|
|
3239
|
+
* object `where` bounds the read instead of being discarded row by row after
|
|
3240
|
+
* it. Two rules keep that from changing what the union produces:
|
|
3241
|
+
*
|
|
3242
|
+
* - `_compileQueryPlan` discards a plan that would displace an index the
|
|
3243
|
+
* caller pinned, so a source's own range (a tenant scope, say) still wins.
|
|
3244
|
+
* - A merged union is ordered by `interleaveBy`, which only some index
|
|
3245
|
+
* shapes can supply. The lowered read is checked against that before it is
|
|
3246
|
+
* committed to, and the unlowered scan is used when it cannot serve the
|
|
3247
|
+
* merge — an index that saves reads is not worth an ordering that throws.
|
|
3248
|
+
*
|
|
3249
|
+
* The `where` stays applied in JS either way. The index range is a bound on
|
|
3250
|
+
* what is read, not a replacement for the predicate.
|
|
3251
|
+
*/
|
|
3127
3252
|
private _buildUnionSourceStream;
|
|
3253
|
+
/**
|
|
3254
|
+
* The index a `flatMap` stage `where` can ride on top of the join keys.
|
|
3255
|
+
*
|
|
3256
|
+
* The join is only correct while `targetFields` stay pinned by equality, and
|
|
3257
|
+
* a Convex range builder pins fields in index-key order with no gaps, so the
|
|
3258
|
+
* candidates are exactly the declared indexes that lead with `targetFields`
|
|
3259
|
+
* and carry at least one more field. Those trailing fields are what the
|
|
3260
|
+
* stage `where` is compiled against — the compiler never sees the join keys,
|
|
3261
|
+
* because their values are per parent and the index choice has to be made
|
|
3262
|
+
* once for all of them.
|
|
3263
|
+
*
|
|
3264
|
+
* Returns null when nothing is gained, and the caller keeps the relation's
|
|
3265
|
+
* own index plus the post-filter. A multi-probe plan is refused: each probe
|
|
3266
|
+
* is its own range, and a union of ranges per parent is a different stream
|
|
3267
|
+
* shape than the one `flatMap` was handed as `mappedIndexFields`.
|
|
3268
|
+
*/
|
|
3269
|
+
private _resolveFlatMapStageIndex;
|
|
3128
3270
|
private _applyFlatMapStage;
|
|
3129
3271
|
private _applyPipelineStages;
|
|
3130
3272
|
private _tryNativeUnfilteredCount;
|
|
@@ -3189,6 +3331,17 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
3189
3331
|
* Phase 4 implementation with WhereClauseCompiler
|
|
3190
3332
|
*/
|
|
3191
3333
|
private _toConvexQuery;
|
|
3334
|
+
/**
|
|
3335
|
+
* The read plan for one `where` on this table.
|
|
3336
|
+
*
|
|
3337
|
+
* Split out of `_toConvexQuery` because the chain-level `where` is not the
|
|
3338
|
+
* only one that has to be index-lowered: a `pipeline.union` source carries
|
|
3339
|
+
* its own `where` and its own pinned index, and compiles against the same
|
|
3340
|
+
* table with a different ordering contract (`interleaveBy`, not `orderBy`).
|
|
3341
|
+
* Both go through here so a plain-object `where` is planned the same way
|
|
3342
|
+
* wherever it is written.
|
|
3343
|
+
*/
|
|
3344
|
+
private _compileQueryPlan;
|
|
3192
3345
|
private _buildRelationKey;
|
|
3193
3346
|
/**
|
|
3194
3347
|
* How many leading fields of the scanned index are pinned to a single value.
|
|
@@ -3219,7 +3372,39 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
3219
3372
|
* Helper for recursive relation loading
|
|
3220
3373
|
*/
|
|
3221
3374
|
private _getTargetTableEdges;
|
|
3375
|
+
/**
|
|
3376
|
+
* A memo entry is a snapshot, so every caller has to get its own document.
|
|
3377
|
+
*
|
|
3378
|
+
* Relation loaders write nested `with` results and `extras` straight onto the
|
|
3379
|
+
* target they were handed, and `hydrateDateFieldsForRead` copies every own key
|
|
3380
|
+
* it finds. Two loads that share one entry would therefore publish each
|
|
3381
|
+
* other's fields — a relation asked for as `true` coming back carrying a
|
|
3382
|
+
* nested relation only the `where` requested. Those writes are all top-level,
|
|
3383
|
+
* so a shallow copy is exactly as much isolation as they need.
|
|
3384
|
+
*/
|
|
3385
|
+
private _ownedCopy;
|
|
3222
3386
|
private _getById;
|
|
3387
|
+
/**
|
|
3388
|
+
* Identity of one `_firstByFields` read, or null when the join values cannot
|
|
3389
|
+
* be encoded losslessly.
|
|
3390
|
+
*
|
|
3391
|
+
* `JSON.stringify` alone is not safe here. It renders every `ArrayBuffer` as
|
|
3392
|
+
* `{}` and `NaN`/`Infinity`/`-Infinity` as `null`, so two distinct join values
|
|
3393
|
+
* would share one entry, and it throws outright on `int64`. The relation
|
|
3394
|
+
* loader's own per-batch key map cannot catch that, because the residual
|
|
3395
|
+
* relation `where` hands it one row at a time — a map of one never compares
|
|
3396
|
+
* two values. This key is the only thing that tells them apart.
|
|
3397
|
+
*
|
|
3398
|
+
* `convexToJson` is the same wire encoding the client uses for query keys, so
|
|
3399
|
+
* every Convex value round-trips distinctly. Anything it rejects is not a
|
|
3400
|
+
* Convex value and simply goes unmemoized.
|
|
3401
|
+
*/
|
|
3402
|
+
private _firstByFieldsMemoKey;
|
|
3403
|
+
/**
|
|
3404
|
+
* `_getById` for a relation joined on something other than the primary id:
|
|
3405
|
+
* resolve one target document by an eq-pinned key, memoized per execution.
|
|
3406
|
+
*/
|
|
3407
|
+
private _firstByFields;
|
|
3223
3408
|
private _getRelationConcurrency;
|
|
3224
3409
|
private _getRelationFanOutKeyCap;
|
|
3225
3410
|
private _enforceRelationFanOutKeyCap;
|
|
@@ -3588,6 +3773,7 @@ declare class ConvexUpdateBuilder<TTable extends ConvexTable<any>, TReturning ex
|
|
|
3588
3773
|
allowFullScan(): ConvexUpdateBuilder<TTable, TReturning, TMode, true>;
|
|
3589
3774
|
executeAsync(this: ConvexUpdateExecutableThis<TTable, TReturning, TMode>, ...args: TMode extends 'single' ? [config?: Omit<MutationExecuteConfig, 'mode'>] : [config: never]): Promise<TMode extends 'single' ? MutationExecuteResult<TTable, TReturning, 'single'> : never>;
|
|
3590
3775
|
execute(this: ConvexUpdateExecutableThis<TTable, TReturning, TMode>, ...args: TMode extends 'single' ? [config?: MutationExecuteConfig] : [config?: never]): Promise<MutationExecuteResult<TTable, TReturning, TMode>>;
|
|
3776
|
+
private _runStatement;
|
|
3591
3777
|
}
|
|
3592
3778
|
//#endregion
|
|
3593
3779
|
//#region src/orm/database.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as RankIndexDefinition, G as CountBackfillChunkArgs, J as CountBackfillStatusArgs, K as CountBackfillKickoffArgs, Q as CountIndexDefinition, X as CountQueryPlan, Y as AggregateQueryPlan, Z as AggregateIndexDefinition, et as RankOrderField, q as CountBackfillMode, r as OrmCapability } from "../../capabilities-
|
|
1
|
+
import { $ as RankIndexDefinition, G as CountBackfillChunkArgs, J as CountBackfillStatusArgs, K as CountBackfillKickoffArgs, Q as CountIndexDefinition, X as CountQueryPlan, Y as AggregateQueryPlan, Z as AggregateIndexDefinition, et as RankOrderField, q as CountBackfillMode, r as OrmCapability } from "../../capabilities-Bxzoofl4.js";
|
|
2
2
|
|
|
3
3
|
//#region src/orm/aggregate-index/capability.d.ts
|
|
4
4
|
/**
|