@prisma-next/sql-orm-client 0.9.0-dev.2 → 0.9.0-dev.4
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/dist/index.d.mts +543 -8
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +461 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -20
- package/src/collection.ts +538 -31
- package/src/query-plan-aggregate.ts +3 -0
- package/src/query-plan-meta.ts +1 -1
- package/src/where-binding.ts +3 -0
package/dist/index.mjs
CHANGED
|
@@ -441,7 +441,7 @@ function capabilityFlag(contract, flag) {
|
|
|
441
441
|
//#endregion
|
|
442
442
|
//#region src/query-plan-meta.ts
|
|
443
443
|
function deriveParamsFromAst(ast) {
|
|
444
|
-
return { params: collectOrderedParamRefs(ast).map((p) => p.value) };
|
|
444
|
+
return { params: collectOrderedParamRefs(ast).map((p) => p.kind === "param-ref" ? p.value : void 0) };
|
|
445
445
|
}
|
|
446
446
|
function resolveTableColumns(contract, tableName) {
|
|
447
447
|
const table = contract.storage.tables[tableName];
|
|
@@ -557,6 +557,9 @@ function validateGroupedHavingExpr(expr) {
|
|
|
557
557
|
param() {
|
|
558
558
|
throw new Error("ParamRef is not supported in grouped having expressions");
|
|
559
559
|
},
|
|
560
|
+
preparedParam() {
|
|
561
|
+
throw new Error("PreparedParamRef is not supported in grouped having expressions");
|
|
562
|
+
},
|
|
560
563
|
list: rejectHavingExpr,
|
|
561
564
|
and(expr) {
|
|
562
565
|
return AndExpr.of(expr.exprs.map((child) => validateGroupedHavingExpr(child)));
|
|
@@ -767,6 +770,9 @@ function bindWhereExprNode(contract, expr) {
|
|
|
767
770
|
param(expr) {
|
|
768
771
|
return expr;
|
|
769
772
|
},
|
|
773
|
+
preparedParam(expr) {
|
|
774
|
+
return expr;
|
|
775
|
+
},
|
|
770
776
|
list(expr) {
|
|
771
777
|
return bindExpression(contract, expr);
|
|
772
778
|
},
|
|
@@ -2218,6 +2224,25 @@ var Collection = class Collection {
|
|
|
2218
2224
|
if (!filter) return this;
|
|
2219
2225
|
return this.#clone({ filters: [...this.state.filters, filter] });
|
|
2220
2226
|
}
|
|
2227
|
+
/**
|
|
2228
|
+
* Narrow a polymorphic model to a specific variant. The returned
|
|
2229
|
+
* collection has the variant's row shape and a discriminator filter
|
|
2230
|
+
* is automatically applied. Chaining `.variant(...)` again replaces
|
|
2231
|
+
* the previous variant filter.
|
|
2232
|
+
*
|
|
2233
|
+
* ```typescript
|
|
2234
|
+
* // Read only admin users (STI):
|
|
2235
|
+
* const admins = await db.orm.User.variant('Admin').all();
|
|
2236
|
+
*
|
|
2237
|
+
* // Iterate the rows:
|
|
2238
|
+
* for await (const admin of db.orm.User.variant('Admin').all()) {
|
|
2239
|
+
* console.log(admin.role);
|
|
2240
|
+
* }
|
|
2241
|
+
*
|
|
2242
|
+
* // Insert under a variant — discriminator is injected automatically:
|
|
2243
|
+
* await db.orm.User.variant('Admin').create({ name: 'Ada', role: 'super' });
|
|
2244
|
+
* ```
|
|
2245
|
+
*/
|
|
2221
2246
|
variant(variantName) {
|
|
2222
2247
|
const model = this.contract.models[this.modelName];
|
|
2223
2248
|
const discriminator = model?.["discriminator"];
|
|
@@ -2233,6 +2258,34 @@ var Collection = class Collection {
|
|
|
2233
2258
|
variantName
|
|
2234
2259
|
});
|
|
2235
2260
|
}
|
|
2261
|
+
/**
|
|
2262
|
+
* Eagerly load a related model. The relation appears on every
|
|
2263
|
+
* returned row under its declared name; to-one relations are mapped
|
|
2264
|
+
* to a single object (or `null`), to-many relations to an array.
|
|
2265
|
+
*
|
|
2266
|
+
* An optional refinement callback receives a child collection that
|
|
2267
|
+
* can be further constrained, projected, ordered, paginated, or
|
|
2268
|
+
* reduced to scalars via `count()`/`sum()`/etc. or to multiple
|
|
2269
|
+
* sub-aggregates via `combine()`.
|
|
2270
|
+
*
|
|
2271
|
+
* ```typescript
|
|
2272
|
+
* // Simple include — every user comes back with its posts array:
|
|
2273
|
+
* const users = await db.orm.User.include('posts').all();
|
|
2274
|
+
*
|
|
2275
|
+
* // Refine the related collection:
|
|
2276
|
+
* const withRecent = await db.orm.User.include('posts', (posts) =>
|
|
2277
|
+
* posts.where({ published: true }).orderBy((p) => p.createdAt.desc()).take(5),
|
|
2278
|
+
* ).all();
|
|
2279
|
+
*
|
|
2280
|
+
* // Reduce a to-many relation to a scalar value:
|
|
2281
|
+
* const withCounts = await db.orm.User.include('posts', (posts) => posts.count()).all();
|
|
2282
|
+
*
|
|
2283
|
+
* // Multiple sub-views via combine():
|
|
2284
|
+
* const overview = await db.orm.User.include('posts', (posts) =>
|
|
2285
|
+
* posts.combine({ recent: posts.take(3), total: posts.count() }),
|
|
2286
|
+
* ).all();
|
|
2287
|
+
* ```
|
|
2288
|
+
*/
|
|
2236
2289
|
include(relationName, refineFn) {
|
|
2237
2290
|
const relation = resolveIncludeRelation(this.contract, this.modelName, relationName);
|
|
2238
2291
|
let nestedState = emptyState();
|
|
@@ -2267,16 +2320,60 @@ var Collection = class Collection {
|
|
|
2267
2320
|
};
|
|
2268
2321
|
return this.#cloneWithRow({ includes: [...this.state.includes, includeExpr] });
|
|
2269
2322
|
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Project the row down to a subset of scalar fields. Previously
|
|
2325
|
+
* included relations are preserved on the resulting row shape; only
|
|
2326
|
+
* scalar columns are narrowed.
|
|
2327
|
+
*
|
|
2328
|
+
* ```typescript
|
|
2329
|
+
* const summaries = await db.orm.User.select('id', 'email').all();
|
|
2330
|
+
* // typeof summaries[number] === { id: ...; email: ... }
|
|
2331
|
+
*
|
|
2332
|
+
* for await (const row of db.orm.User.select('id', 'email').all()) {
|
|
2333
|
+
* console.log(row.id, row.email);
|
|
2334
|
+
* }
|
|
2335
|
+
* ```
|
|
2336
|
+
*/
|
|
2270
2337
|
select(...fields) {
|
|
2271
2338
|
const selectedFields = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2272
2339
|
return this.#cloneWithRow({ selectedFields });
|
|
2273
2340
|
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Append an `ORDER BY` clause. Pass a single selector callback or an
|
|
2343
|
+
* array of callbacks; each receives a typed model accessor whose
|
|
2344
|
+
* columns expose `.asc()` and `.desc()`. Multiple calls append to the
|
|
2345
|
+
* existing list (left-to-right ordering preserved).
|
|
2346
|
+
*
|
|
2347
|
+
* Calling `orderBy(...)` unlocks `cursor(...)` and `distinctOn(...)`,
|
|
2348
|
+
* which both require a defined sort order.
|
|
2349
|
+
*
|
|
2350
|
+
* ```typescript
|
|
2351
|
+
* const newest = await db.orm.User.orderBy((u) => u.createdAt.desc()).all();
|
|
2352
|
+
*
|
|
2353
|
+
* const byName = await db.orm.User
|
|
2354
|
+
* .orderBy([(u) => u.lastName.asc(), (u) => u.firstName.asc()])
|
|
2355
|
+
* .all();
|
|
2356
|
+
* ```
|
|
2357
|
+
*/
|
|
2274
2358
|
orderBy(selection) {
|
|
2275
2359
|
const accessor = createModelAccessor(this.ctx.context, this.modelName);
|
|
2276
2360
|
const nextOrders = (Array.isArray(selection) ? selection : [selection]).map((selector) => selector(accessor));
|
|
2277
2361
|
const existing = this.state.orderBy ?? [];
|
|
2278
2362
|
return this.#clone({ orderBy: [...existing, ...nextOrders] });
|
|
2279
2363
|
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Switch to grouped-aggregate mode. Returns a `GroupedCollection`
|
|
2366
|
+
* whose `.aggregate(...)` terminal produces one row per group with
|
|
2367
|
+
* the chosen key columns plus the requested aggregates.
|
|
2368
|
+
*
|
|
2369
|
+
* ```typescript
|
|
2370
|
+
* const stats = await db.orm.Post
|
|
2371
|
+
* .where({ published: true })
|
|
2372
|
+
* .groupBy('userId')
|
|
2373
|
+
* .aggregate((agg) => ({ count: agg.count(), totalViews: agg.sum('views') }));
|
|
2374
|
+
* // [{ userId: 1, count: 3, totalViews: 120 }, ...]
|
|
2375
|
+
* ```
|
|
2376
|
+
*/
|
|
2280
2377
|
groupBy(...fields) {
|
|
2281
2378
|
const groupByColumns = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2282
2379
|
return new GroupedCollection(this.ctx, this.modelName, {
|
|
@@ -2287,30 +2384,105 @@ var Collection = class Collection {
|
|
|
2287
2384
|
havingFilters: []
|
|
2288
2385
|
});
|
|
2289
2386
|
}
|
|
2387
|
+
/**
|
|
2388
|
+
* Scalar reducer — reduces a to-many relation to the number of
|
|
2389
|
+
* related rows. Use inside an `include(...)` refinement callback as
|
|
2390
|
+
* `include(..., (rel) => rel.count())`; throws if called elsewhere.
|
|
2391
|
+
* The parent row's relation field becomes that count instead of an
|
|
2392
|
+
* array.
|
|
2393
|
+
*
|
|
2394
|
+
* ```typescript
|
|
2395
|
+
* const users = await db.orm.User.include('posts', (posts) => posts.count()).all();
|
|
2396
|
+
* // each user row: { ...user, posts: number }
|
|
2397
|
+
* ```
|
|
2398
|
+
*/
|
|
2290
2399
|
count() {
|
|
2291
2400
|
this.#assertIncludeRefinementMode("count()");
|
|
2292
2401
|
return createIncludeScalar("count", this.state);
|
|
2293
2402
|
}
|
|
2403
|
+
/**
|
|
2404
|
+
* Scalar reducer — reduces a to-many relation to the sum of `field`
|
|
2405
|
+
* across related rows. Returns `null` when there are no related
|
|
2406
|
+
* rows. Use inside an `include(...)` refinement callback; throws if
|
|
2407
|
+
* called elsewhere.
|
|
2408
|
+
*
|
|
2409
|
+
* ```typescript
|
|
2410
|
+
* const users = await db.orm.User.include('posts', (posts) => posts.sum('views')).all();
|
|
2411
|
+
* // each user row: { ...user, posts: number | null }
|
|
2412
|
+
* ```
|
|
2413
|
+
*/
|
|
2294
2414
|
sum(field) {
|
|
2295
2415
|
this.#assertIncludeRefinementMode("sum()");
|
|
2296
2416
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2297
2417
|
return createIncludeScalar("sum", this.state, columnName);
|
|
2298
2418
|
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Scalar reducer — reduces a to-many relation to the average of
|
|
2421
|
+
* `field` across related rows. Returns `null` when there are no
|
|
2422
|
+
* related rows. Use inside an `include(...)` refinement callback;
|
|
2423
|
+
* throws if called elsewhere.
|
|
2424
|
+
*
|
|
2425
|
+
* ```typescript
|
|
2426
|
+
* const users = await db.orm.User.include('posts', (posts) => posts.avg('views')).all();
|
|
2427
|
+
* // each user row: { ...user, posts: number | null }
|
|
2428
|
+
* ```
|
|
2429
|
+
*/
|
|
2299
2430
|
avg(field) {
|
|
2300
2431
|
this.#assertIncludeRefinementMode("avg()");
|
|
2301
2432
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2302
2433
|
return createIncludeScalar("avg", this.state, columnName);
|
|
2303
2434
|
}
|
|
2435
|
+
/**
|
|
2436
|
+
* Scalar reducer — reduces a to-many relation to the minimum value
|
|
2437
|
+
* of `field` across related rows. Returns `null` when there are no
|
|
2438
|
+
* related rows. Use inside an `include(...)` refinement callback;
|
|
2439
|
+
* throws if called elsewhere.
|
|
2440
|
+
*
|
|
2441
|
+
* ```typescript
|
|
2442
|
+
* const users = await db.orm.User.include('posts', (posts) => posts.min('views')).all();
|
|
2443
|
+
* ```
|
|
2444
|
+
*/
|
|
2304
2445
|
min(field) {
|
|
2305
2446
|
this.#assertIncludeRefinementMode("min()");
|
|
2306
2447
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2307
2448
|
return createIncludeScalar("min", this.state, columnName);
|
|
2308
2449
|
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Scalar reducer — reduces a to-many relation to the maximum value
|
|
2452
|
+
* of `field` across related rows. Returns `null` when there are no
|
|
2453
|
+
* related rows. Use inside an `include(...)` refinement callback;
|
|
2454
|
+
* throws if called elsewhere.
|
|
2455
|
+
*
|
|
2456
|
+
* ```typescript
|
|
2457
|
+
* const users = await db.orm.User.include('posts', (posts) => posts.max('views')).all();
|
|
2458
|
+
* ```
|
|
2459
|
+
*/
|
|
2309
2460
|
max(field) {
|
|
2310
2461
|
this.#assertIncludeRefinementMode("max()");
|
|
2311
2462
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2312
2463
|
return createIncludeScalar("max", this.state, columnName);
|
|
2313
2464
|
}
|
|
2465
|
+
/**
|
|
2466
|
+
* Produce multiple named sub-views of a to-many relation in a
|
|
2467
|
+
* single `include(...)`. Each branch is either another refined
|
|
2468
|
+
* collection (mapped to a row array on the parent) or a scalar
|
|
2469
|
+
* reducer such as `count()`/`sum(...)`. Only valid inside an
|
|
2470
|
+
* `include(...)` refinement callback for to-many relations.
|
|
2471
|
+
*
|
|
2472
|
+
* ```typescript
|
|
2473
|
+
* const users = await db.orm.User.include('posts', (posts) =>
|
|
2474
|
+
* posts.combine({
|
|
2475
|
+
* recent: posts.where({ published: true }).take(3),
|
|
2476
|
+
* total: posts.count(),
|
|
2477
|
+
* averageViews: posts.avg('views'),
|
|
2478
|
+
* }),
|
|
2479
|
+
* ).all();
|
|
2480
|
+
* // each user row: {
|
|
2481
|
+
* // ...user,
|
|
2482
|
+
* // posts: { recent: Post[]; total: number; averageViews: number | null };
|
|
2483
|
+
* // }
|
|
2484
|
+
* ```
|
|
2485
|
+
*/
|
|
2314
2486
|
combine(spec) {
|
|
2315
2487
|
this.#assertIncludeRefinementMode("combine()");
|
|
2316
2488
|
const branches = {};
|
|
@@ -2333,11 +2505,39 @@ var Collection = class Collection {
|
|
|
2333
2505
|
}
|
|
2334
2506
|
return createIncludeCombine(branches);
|
|
2335
2507
|
}
|
|
2508
|
+
/**
|
|
2509
|
+
* Resume pagination from a known cursor position. Requires a prior
|
|
2510
|
+
* `orderBy(...)` so the cursor has a stable basis; provide a value
|
|
2511
|
+
* for every column referenced by the active `orderBy(...)` so each
|
|
2512
|
+
* ordered axis has a defined boundary.
|
|
2513
|
+
*
|
|
2514
|
+
* ```typescript
|
|
2515
|
+
* const page1 = await db.orm.Post
|
|
2516
|
+
* .orderBy((p) => p.createdAt.desc())
|
|
2517
|
+
* .take(20)
|
|
2518
|
+
* .all();
|
|
2519
|
+
*
|
|
2520
|
+
* const last = page1[page1.length - 1]!;
|
|
2521
|
+
* const page2 = await db.orm.Post
|
|
2522
|
+
* .orderBy((p) => p.createdAt.desc())
|
|
2523
|
+
* .cursor({ createdAt: last.createdAt })
|
|
2524
|
+
* .take(20)
|
|
2525
|
+
* .all();
|
|
2526
|
+
* ```
|
|
2527
|
+
*/
|
|
2336
2528
|
cursor(cursorValues) {
|
|
2337
2529
|
const mappedCursor = mapCursorValuesToColumns(this.contract, this.modelName, cursorValues);
|
|
2338
2530
|
if (Object.keys(mappedCursor).length === 0) return this;
|
|
2339
2531
|
return this.#clone({ cursor: mappedCursor });
|
|
2340
2532
|
}
|
|
2533
|
+
/**
|
|
2534
|
+
* Emit `SELECT DISTINCT` keyed on the given fields. Replaces any
|
|
2535
|
+
* previous `distinct(...)` / `distinctOn(...)` selection.
|
|
2536
|
+
*
|
|
2537
|
+
* ```typescript
|
|
2538
|
+
* const groups = await db.orm.User.distinct('country', 'role').all();
|
|
2539
|
+
* ```
|
|
2540
|
+
*/
|
|
2341
2541
|
distinct(...fields) {
|
|
2342
2542
|
const distinctFields = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2343
2543
|
return this.#clone({
|
|
@@ -2345,6 +2545,20 @@ var Collection = class Collection {
|
|
|
2345
2545
|
distinctOn: void 0
|
|
2346
2546
|
});
|
|
2347
2547
|
}
|
|
2548
|
+
/**
|
|
2549
|
+
* Emit `SELECT DISTINCT ON (fields)` — keep the first row per
|
|
2550
|
+
* distinct key according to the current `orderBy(...)`. Requires a
|
|
2551
|
+
* prior `orderBy(...)`; replaces any previous `distinct(...)` /
|
|
2552
|
+
* `distinctOn(...)` selection.
|
|
2553
|
+
*
|
|
2554
|
+
* ```typescript
|
|
2555
|
+
* // Latest post per user:
|
|
2556
|
+
* const latestPerUser = await db.orm.Post
|
|
2557
|
+
* .orderBy([(p) => p.userId.asc(), (p) => p.createdAt.desc()])
|
|
2558
|
+
* .distinctOn('userId')
|
|
2559
|
+
* .all();
|
|
2560
|
+
* ```
|
|
2561
|
+
*/
|
|
2348
2562
|
distinctOn(...fields) {
|
|
2349
2563
|
const distinctOnFields = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2350
2564
|
return this.#clone({
|
|
@@ -2352,44 +2566,93 @@ var Collection = class Collection {
|
|
|
2352
2566
|
distinctOn: distinctOnFields
|
|
2353
2567
|
});
|
|
2354
2568
|
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Apply `LIMIT n`. Replaces any previous limit set on this collection.
|
|
2571
|
+
*
|
|
2572
|
+
* ```typescript
|
|
2573
|
+
* const firstTen = await db.orm.User.orderBy((u) => u.id.asc()).take(10).all();
|
|
2574
|
+
* ```
|
|
2575
|
+
*/
|
|
2355
2576
|
take(n) {
|
|
2356
2577
|
return this.#clone({ limit: n });
|
|
2357
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Apply `OFFSET n`. Replaces any previous offset set on this collection.
|
|
2581
|
+
*
|
|
2582
|
+
* ```typescript
|
|
2583
|
+
* const page2 = await db.orm.User
|
|
2584
|
+
* .orderBy((u) => u.id.asc())
|
|
2585
|
+
* .skip(10)
|
|
2586
|
+
* .take(10)
|
|
2587
|
+
* .all();
|
|
2588
|
+
* ```
|
|
2589
|
+
*/
|
|
2358
2590
|
skip(n) {
|
|
2359
2591
|
return this.#clone({ offset: n });
|
|
2360
2592
|
}
|
|
2361
2593
|
/**
|
|
2362
|
-
* Read terminal:
|
|
2594
|
+
* Read terminal: execute the query and stream every matching row.
|
|
2595
|
+
*
|
|
2596
|
+
* The returned `AsyncIterableResult<Row>` is BOTH a thenable that
|
|
2597
|
+
* resolves to `Row[]` (so `await` collects all rows into an array)
|
|
2598
|
+
* AND an async iterable (so `for await` streams rows as they
|
|
2599
|
+
* arrive, without buffering the whole result set in memory). Pick
|
|
2600
|
+
* whichever fits the caller. A single result can only be consumed
|
|
2601
|
+
* once.
|
|
2602
|
+
*
|
|
2603
|
+
* Streaming is the default and the expected execution model. The
|
|
2604
|
+
* only scenarios that fall back to buffering internally before
|
|
2605
|
+
* yielding are drivers that cannot expose a cursor to the
|
|
2606
|
+
* underlying database, and — for queries with `include(...)` —
|
|
2607
|
+
* targets whose SQL dialect supports neither lateral joins nor
|
|
2608
|
+
* correlated subqueries (so child rows cannot be stitched in a
|
|
2609
|
+
* single streaming query). These are implementation details below
|
|
2610
|
+
* the public API; the iteration shape itself is genuinely
|
|
2611
|
+
* streaming whenever the driver and plan allow it.
|
|
2612
|
+
*
|
|
2613
|
+
* ```typescript
|
|
2614
|
+
* // Thenable — collect to an array:
|
|
2615
|
+
* const users = await db.orm.User.all();
|
|
2616
|
+
* for (const user of users) console.log(user.id);
|
|
2617
|
+
*
|
|
2618
|
+
* // Async iterable — stream rows as they arrive:
|
|
2619
|
+
* for await (const user of db.orm.User.all()) {
|
|
2620
|
+
* console.log(user.id);
|
|
2621
|
+
* }
|
|
2622
|
+
* ```
|
|
2363
2623
|
*
|
|
2364
2624
|
* Accepts an optional `configure` callback that receives a
|
|
2365
2625
|
* `MetaBuilder<'read'>` so the caller can attach typed user
|
|
2366
2626
|
* annotations to the executed plan. `meta.annotate(...)` enforces
|
|
2367
2627
|
* applicability at the type level and at runtime; annotations are
|
|
2368
2628
|
* merged into `plan.meta.annotations` at compile time.
|
|
2369
|
-
*/
|
|
2370
|
-
all(configure) {
|
|
2371
|
-
return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
|
|
2372
|
-
}
|
|
2373
|
-
/**
|
|
2374
|
-
* Read terminal: return the first matching row, or `null`.
|
|
2375
|
-
*
|
|
2376
|
-
* Accepts an optional `filter` (function or shorthand) followed by an
|
|
2377
|
-
* optional `configure` callback that receives a `MetaBuilder<'read'>`
|
|
2378
|
-
* for attaching typed annotations. To attach annotations without
|
|
2379
|
-
* narrowing further, pass `undefined` as the filter (or chain
|
|
2380
|
-
* `.where(...)` first):
|
|
2381
2629
|
*
|
|
2382
2630
|
* ```typescript
|
|
2383
|
-
* await db.User.
|
|
2384
|
-
* await db.User.first(undefined, (meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
|
|
2631
|
+
* await db.orm.User.all((meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
|
|
2385
2632
|
* ```
|
|
2386
2633
|
*/
|
|
2634
|
+
all(configure) {
|
|
2635
|
+
return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
|
|
2636
|
+
}
|
|
2387
2637
|
async first(filter, configure) {
|
|
2388
2638
|
return (await (filter === void 0 ? this : typeof filter === "function" ? this.where(filter) : this.where(filter)).take(1).#withAnnotationsFromMeta(configure, "first").#dispatch().toArray())[0] ?? null;
|
|
2389
2639
|
}
|
|
2390
2640
|
/**
|
|
2391
2641
|
* Read terminal: run an aggregate query (count, sum, avg, min, max)
|
|
2392
|
-
* built via the `AggregateBuilder` callback.
|
|
2642
|
+
* built via the `AggregateBuilder` callback. Returns one object
|
|
2643
|
+
* with the requested aggregate values keyed by the aliases supplied
|
|
2644
|
+
* in the spec.
|
|
2645
|
+
*
|
|
2646
|
+
* ```typescript
|
|
2647
|
+
* const stats = await db.orm.Post
|
|
2648
|
+
* .where({ published: true })
|
|
2649
|
+
* .aggregate((agg) => ({
|
|
2650
|
+
* total: agg.count(),
|
|
2651
|
+
* averageViews: agg.avg('views'),
|
|
2652
|
+
* maxViews: agg.max('views'),
|
|
2653
|
+
* }));
|
|
2654
|
+
* // { total: 42, averageViews: 17.3, maxViews: 9001 }
|
|
2655
|
+
* ```
|
|
2393
2656
|
*
|
|
2394
2657
|
* Accepts an optional `configure` callback that receives a
|
|
2395
2658
|
* `MetaBuilder<'read'>` for attaching typed annotations.
|
|
@@ -2404,20 +2667,6 @@ var Collection = class Collection {
|
|
|
2404
2667
|
const compiled = mergeAnnotations(compileAggregate(this.contract, this.tableName, this.state.filters, aggregateSpec), annotationsMap);
|
|
2405
2668
|
return normalizeAggregateResult(aggregateSpec, (await executeQueryPlan(this.ctx.runtime, compiled).toArray())[0] ?? {});
|
|
2406
2669
|
}
|
|
2407
|
-
/**
|
|
2408
|
-
* Write terminal: insert one row and return it.
|
|
2409
|
-
*
|
|
2410
|
-
* Accepts an optional `configure` callback that receives a
|
|
2411
|
-
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
2412
|
-
* Annotations are merged into the compiled mutation plan's
|
|
2413
|
-
* `meta.annotations`.
|
|
2414
|
-
*
|
|
2415
|
-
* Note: when the input contains nested-mutation callbacks, the
|
|
2416
|
-
* operation is executed as a graph of internal queries via
|
|
2417
|
-
* `withMutationScope`. In that path, annotations apply to the
|
|
2418
|
-
* logical `create()` call but do not currently flow into each
|
|
2419
|
-
* constituent SQL statement issued for the related rows.
|
|
2420
|
-
*/
|
|
2421
2670
|
async create(data, configure) {
|
|
2422
2671
|
assertReturningCapability(this.contract, "create()");
|
|
2423
2672
|
const annotationsMap = this.#collectAnnotationsFromMeta(configure, "write", "create");
|
|
@@ -2437,6 +2686,33 @@ var Collection = class Collection {
|
|
|
2437
2686
|
if (created) return created;
|
|
2438
2687
|
throw new Error(`create() for model "${this.modelName}" did not return a row`);
|
|
2439
2688
|
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Write terminal: insert many rows and stream the inserted rows.
|
|
2691
|
+
*
|
|
2692
|
+
* The returned `AsyncIterableResult<Row>` is BOTH a thenable that
|
|
2693
|
+
* resolves to `Row[]` AND an async iterable that streams inserted
|
|
2694
|
+
* rows as they arrive. Use whichever shape fits the caller — but
|
|
2695
|
+
* only consume it once. Streaming is the default; some
|
|
2696
|
+
* driver/plan combinations may still buffer internally before
|
|
2697
|
+
* yielding.
|
|
2698
|
+
*
|
|
2699
|
+
* ```typescript
|
|
2700
|
+
* // Thenable — collect all inserted rows into an array:
|
|
2701
|
+
* const created = await db.orm.User.createAll([
|
|
2702
|
+
* { email: 'a@example.com' },
|
|
2703
|
+
* { email: 'b@example.com' },
|
|
2704
|
+
* ]);
|
|
2705
|
+
*
|
|
2706
|
+
* // Async iterable — stream inserted rows as they arrive:
|
|
2707
|
+
* for await (const row of db.orm.User.createAll(seedUsers)) {
|
|
2708
|
+
* console.log('inserted', row.id);
|
|
2709
|
+
* }
|
|
2710
|
+
* ```
|
|
2711
|
+
*
|
|
2712
|
+
* Accepts an optional `configure` callback that receives a
|
|
2713
|
+
* `MetaBuilder<'write'>` for attaching typed annotations to the
|
|
2714
|
+
* compiled insert plan.
|
|
2715
|
+
*/
|
|
2440
2716
|
createAll(data, configure) {
|
|
2441
2717
|
return this.#createAllWithAnnotations(data, this.#collectAnnotationsFromMeta(configure, "write", "createAll"));
|
|
2442
2718
|
}
|
|
@@ -2568,6 +2844,24 @@ var Collection = class Collection {
|
|
|
2568
2844
|
return mapped;
|
|
2569
2845
|
});
|
|
2570
2846
|
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Write terminal: insert many rows without materializing the
|
|
2849
|
+
* inserted rows, returning the number of inserted records.
|
|
2850
|
+
*
|
|
2851
|
+
* Prefer `createAll(...)` when you need the returned rows; prefer
|
|
2852
|
+
* this when you only need to know how many rows were inserted (the
|
|
2853
|
+
* compiled plan skips `RETURNING`).
|
|
2854
|
+
*
|
|
2855
|
+
* ```typescript
|
|
2856
|
+
* const inserted = await db.orm.User.createCount([
|
|
2857
|
+
* { email: 'a@example.com' },
|
|
2858
|
+
* { email: 'b@example.com' },
|
|
2859
|
+
* ]);
|
|
2860
|
+
* // inserted === 2
|
|
2861
|
+
* ```
|
|
2862
|
+
*
|
|
2863
|
+
* Not supported on MTI variants — use `createAll(...)` instead.
|
|
2864
|
+
*/
|
|
2571
2865
|
async createCount(data, configure) {
|
|
2572
2866
|
if (data.length === 0) return 0;
|
|
2573
2867
|
this.#assertNotMtiVariant("createCount()");
|
|
@@ -2585,9 +2879,36 @@ var Collection = class Collection {
|
|
|
2585
2879
|
return data.length;
|
|
2586
2880
|
}
|
|
2587
2881
|
/**
|
|
2588
|
-
*
|
|
2589
|
-
*
|
|
2590
|
-
*
|
|
2882
|
+
* Write terminal: insert a row, or update the existing row on
|
|
2883
|
+
* conflict. Returns the resulting row (the inserted one or the
|
|
2884
|
+
* updated/existing one).
|
|
2885
|
+
*
|
|
2886
|
+
* `conflictOn` selects which unique constraint drives the conflict
|
|
2887
|
+
* resolution — omit to use the model's primary key.
|
|
2888
|
+
*
|
|
2889
|
+
* ```typescript
|
|
2890
|
+
* // Insert-or-update on email uniqueness:
|
|
2891
|
+
* await db.orm.User.upsert({
|
|
2892
|
+
* create: { email: 'alice@example.com', name: 'Alice' },
|
|
2893
|
+
* update: { name: 'Alice (updated)' },
|
|
2894
|
+
* conflictOn: { email: 'alice@example.com' },
|
|
2895
|
+
* });
|
|
2896
|
+
*
|
|
2897
|
+
* // Conditional create — `update: {}` keeps the existing row
|
|
2898
|
+
* // unchanged. `conflictOn` must reference the constraint that
|
|
2899
|
+
* // makes the row "already exist"; omit only when the conflict is
|
|
2900
|
+
* // on the primary key. On conflict,
|
|
2901
|
+
* // `ON CONFLICT DO NOTHING RETURNING ...` may return zero rows,
|
|
2902
|
+
* // so a follow-up reload is issued to fetch and return the
|
|
2903
|
+
* // existing row.
|
|
2904
|
+
* await db.orm.User.upsert({
|
|
2905
|
+
* create: { email: 'alice@example.com', name: 'Alice' },
|
|
2906
|
+
* update: {},
|
|
2907
|
+
* conflictOn: { email: 'alice@example.com' },
|
|
2908
|
+
* });
|
|
2909
|
+
* ```
|
|
2910
|
+
*
|
|
2911
|
+
* Not supported on MTI variants.
|
|
2591
2912
|
*/
|
|
2592
2913
|
async upsert(input, configure) {
|
|
2593
2914
|
assertReturningCapability(this.contract, "upsert()");
|
|
@@ -2623,7 +2944,30 @@ var Collection = class Collection {
|
|
|
2623
2944
|
}
|
|
2624
2945
|
/**
|
|
2625
2946
|
* Write terminal: update matching rows and return the first one (or
|
|
2626
|
-
* null when no row matched).
|
|
2947
|
+
* `null` when no row matched). Requires a prior `.where(...)` —
|
|
2948
|
+
* calling `update(...)` on an unfiltered collection is a type error.
|
|
2949
|
+
*
|
|
2950
|
+
* Related rows can be created or relinked through relation
|
|
2951
|
+
* callbacks on parent/child-owned relations (one-to-one or
|
|
2952
|
+
* one-to-many). The callback receives a mutator exposing
|
|
2953
|
+
* `create(...)`, `connect(...)`, and `disconnect(...)`. Nested
|
|
2954
|
+
* updates against existing related rows, and many-to-many relations
|
|
2955
|
+
* as nested-mutation targets, are not supported through this API.
|
|
2956
|
+
*
|
|
2957
|
+
* ```typescript
|
|
2958
|
+
* // Update one row by id:
|
|
2959
|
+
* const updated = await db.orm.User
|
|
2960
|
+
* .where({ id: 1 })
|
|
2961
|
+
* .update({ name: 'Alice Renamed' });
|
|
2962
|
+
*
|
|
2963
|
+
* // Update + relink — runs as a graph of internal mutations:
|
|
2964
|
+
* await db.orm.User
|
|
2965
|
+
* .where({ id: 1 })
|
|
2966
|
+
* .update({
|
|
2967
|
+
* name: 'Alice',
|
|
2968
|
+
* posts: (posts) => posts.connect([{ id: 5 }]),
|
|
2969
|
+
* });
|
|
2970
|
+
* ```
|
|
2627
2971
|
*
|
|
2628
2972
|
* Accepts an optional `configure` callback that receives a
|
|
2629
2973
|
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
@@ -2656,6 +3000,31 @@ var Collection = class Collection {
|
|
|
2656
3000
|
return (await scoped.#clone({ filters: [identityWhere] }).#updateAllWithAnnotations(data, annotationsMap))[0] ?? null;
|
|
2657
3001
|
});
|
|
2658
3002
|
}
|
|
3003
|
+
/**
|
|
3004
|
+
* Write terminal: update every matching row and stream the updated
|
|
3005
|
+
* rows. Requires a prior `.where(...)` filter.
|
|
3006
|
+
*
|
|
3007
|
+
* The returned `AsyncIterableResult<Row>` is BOTH a thenable that
|
|
3008
|
+
* resolves to `Row[]` AND an async iterable that streams updated
|
|
3009
|
+
* rows as they arrive. Use whichever fits; a result can only be
|
|
3010
|
+
* consumed once. Streaming is the default; some driver/plan
|
|
3011
|
+
* combinations may still buffer internally before yielding.
|
|
3012
|
+
*
|
|
3013
|
+
* ```typescript
|
|
3014
|
+
* // Thenable — collect updated rows into an array:
|
|
3015
|
+
* const updated = await db.orm.Post
|
|
3016
|
+
* .where({ published: false })
|
|
3017
|
+
* .updateAll({ published: true });
|
|
3018
|
+
*
|
|
3019
|
+
* // Async iterable — stream updated rows as they arrive:
|
|
3020
|
+
* for await (const row of db.orm.Post.where({ draft: true }).updateAll({ draft: false })) {
|
|
3021
|
+
* console.log('published', row.id);
|
|
3022
|
+
* }
|
|
3023
|
+
* ```
|
|
3024
|
+
*
|
|
3025
|
+
* Accepts an optional `configure` callback that receives a
|
|
3026
|
+
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
3027
|
+
*/
|
|
2659
3028
|
updateAll(data, configure) {
|
|
2660
3029
|
return this.#updateAllWithAnnotations(data, this.#collectAnnotationsFromMeta(configure, "write", "updateAll"));
|
|
2661
3030
|
}
|
|
@@ -2680,6 +3049,20 @@ var Collection = class Collection {
|
|
|
2680
3049
|
mapRow: (mapped) => mapped
|
|
2681
3050
|
});
|
|
2682
3051
|
}
|
|
3052
|
+
/**
|
|
3053
|
+
* Write terminal: update every matching row without returning them,
|
|
3054
|
+
* resolving to the count of rows that were updated. Requires a prior
|
|
3055
|
+
* `.where(...)` filter.
|
|
3056
|
+
*
|
|
3057
|
+
* Prefer `updateAll(...)` when you need the updated rows; prefer
|
|
3058
|
+
* this when you only need the affected-row count.
|
|
3059
|
+
*
|
|
3060
|
+
* ```typescript
|
|
3061
|
+
* const count = await db.orm.Post
|
|
3062
|
+
* .where({ published: false })
|
|
3063
|
+
* .updateCount({ published: true });
|
|
3064
|
+
* ```
|
|
3065
|
+
*/
|
|
2683
3066
|
async updateCount(data, configure) {
|
|
2684
3067
|
const mappedData = mapModelDataToStorageRow(this.contract, this.modelName, data);
|
|
2685
3068
|
if (Object.keys(mappedData).length === 0) return 0;
|
|
@@ -2698,8 +3081,14 @@ var Collection = class Collection {
|
|
|
2698
3081
|
return matchingRows.length;
|
|
2699
3082
|
}
|
|
2700
3083
|
/**
|
|
2701
|
-
* Write terminal: delete matching rows and return the first
|
|
2702
|
-
* null when no row matched).
|
|
3084
|
+
* Write terminal: delete matching rows and return the first deleted
|
|
3085
|
+
* row (or `null` when no row matched). Requires a prior `.where(...)`
|
|
3086
|
+
* — calling `delete()` on an unfiltered collection is a type error.
|
|
3087
|
+
*
|
|
3088
|
+
* ```typescript
|
|
3089
|
+
* const deleted = await db.orm.User.where({ id: 1 }).delete();
|
|
3090
|
+
* if (deleted) console.log('deleted', deleted.email);
|
|
3091
|
+
* ```
|
|
2703
3092
|
*
|
|
2704
3093
|
* Accepts an optional `configure` callback that receives a
|
|
2705
3094
|
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
@@ -2714,6 +3103,29 @@ var Collection = class Collection {
|
|
|
2714
3103
|
return (await scoped.#clone({ filters: [identityWhere] }).#executeDeleteReturning(annotationsMap).toArray())[0] ?? null;
|
|
2715
3104
|
});
|
|
2716
3105
|
}
|
|
3106
|
+
/**
|
|
3107
|
+
* Write terminal: delete every matching row and stream the deleted
|
|
3108
|
+
* rows. Requires a prior `.where(...)` filter.
|
|
3109
|
+
*
|
|
3110
|
+
* The returned `AsyncIterableResult<Row>` is BOTH a thenable that
|
|
3111
|
+
* resolves to `Row[]` AND an async iterable that streams deleted
|
|
3112
|
+
* rows as they arrive. Use whichever fits; a result can only be
|
|
3113
|
+
* consumed once. Streaming is the default; some driver/plan
|
|
3114
|
+
* combinations may still buffer internally before yielding.
|
|
3115
|
+
*
|
|
3116
|
+
* ```typescript
|
|
3117
|
+
* // Thenable — collect the deleted rows into an array:
|
|
3118
|
+
* const deleted = await db.orm.Post.where({ archived: true }).deleteAll();
|
|
3119
|
+
*
|
|
3120
|
+
* // Async iterable — stream deleted rows as they arrive:
|
|
3121
|
+
* for await (const row of db.orm.Post.where({ archived: true }).deleteAll()) {
|
|
3122
|
+
* console.log('removed', row.id);
|
|
3123
|
+
* }
|
|
3124
|
+
* ```
|
|
3125
|
+
*
|
|
3126
|
+
* Accepts an optional `configure` callback that receives a
|
|
3127
|
+
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
3128
|
+
*/
|
|
2717
3129
|
deleteAll(configure) {
|
|
2718
3130
|
return this.#deleteAllWithAnnotations(this.#collectAnnotationsFromMeta(configure, "write", "deleteAll"));
|
|
2719
3131
|
}
|
|
@@ -2735,6 +3147,18 @@ var Collection = class Collection {
|
|
|
2735
3147
|
mapRow: (mapped) => mapped
|
|
2736
3148
|
});
|
|
2737
3149
|
}
|
|
3150
|
+
/**
|
|
3151
|
+
* Write terminal: delete every matching row without returning them,
|
|
3152
|
+
* resolving to the count of rows that were deleted. Requires a prior
|
|
3153
|
+
* `.where(...)` filter.
|
|
3154
|
+
*
|
|
3155
|
+
* Prefer `deleteAll(...)` when you need the deleted rows; prefer
|
|
3156
|
+
* this when you only need the affected-row count.
|
|
3157
|
+
*
|
|
3158
|
+
* ```typescript
|
|
3159
|
+
* const removed = await db.orm.Post.where({ archived: true }).deleteCount();
|
|
3160
|
+
* ```
|
|
3161
|
+
*/
|
|
2738
3162
|
async deleteCount(configure) {
|
|
2739
3163
|
const annotationsMap = this.#collectAnnotationsFromMeta(configure, "write", "deleteCount");
|
|
2740
3164
|
const primaryKeyColumn = resolvePrimaryKeyColumn(this.contract, this.tableName);
|