@prisma-next/sql-orm-client 0.9.0-dev.3 → 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 +454 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -20
- package/src/collection.ts +538 -31
package/dist/index.mjs
CHANGED
|
@@ -2224,6 +2224,25 @@ var Collection = class Collection {
|
|
|
2224
2224
|
if (!filter) return this;
|
|
2225
2225
|
return this.#clone({ filters: [...this.state.filters, filter] });
|
|
2226
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
|
+
*/
|
|
2227
2246
|
variant(variantName) {
|
|
2228
2247
|
const model = this.contract.models[this.modelName];
|
|
2229
2248
|
const discriminator = model?.["discriminator"];
|
|
@@ -2239,6 +2258,34 @@ var Collection = class Collection {
|
|
|
2239
2258
|
variantName
|
|
2240
2259
|
});
|
|
2241
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
|
+
*/
|
|
2242
2289
|
include(relationName, refineFn) {
|
|
2243
2290
|
const relation = resolveIncludeRelation(this.contract, this.modelName, relationName);
|
|
2244
2291
|
let nestedState = emptyState();
|
|
@@ -2273,16 +2320,60 @@ var Collection = class Collection {
|
|
|
2273
2320
|
};
|
|
2274
2321
|
return this.#cloneWithRow({ includes: [...this.state.includes, includeExpr] });
|
|
2275
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
|
+
*/
|
|
2276
2337
|
select(...fields) {
|
|
2277
2338
|
const selectedFields = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2278
2339
|
return this.#cloneWithRow({ selectedFields });
|
|
2279
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
|
+
*/
|
|
2280
2358
|
orderBy(selection) {
|
|
2281
2359
|
const accessor = createModelAccessor(this.ctx.context, this.modelName);
|
|
2282
2360
|
const nextOrders = (Array.isArray(selection) ? selection : [selection]).map((selector) => selector(accessor));
|
|
2283
2361
|
const existing = this.state.orderBy ?? [];
|
|
2284
2362
|
return this.#clone({ orderBy: [...existing, ...nextOrders] });
|
|
2285
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
|
+
*/
|
|
2286
2377
|
groupBy(...fields) {
|
|
2287
2378
|
const groupByColumns = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2288
2379
|
return new GroupedCollection(this.ctx, this.modelName, {
|
|
@@ -2293,30 +2384,105 @@ var Collection = class Collection {
|
|
|
2293
2384
|
havingFilters: []
|
|
2294
2385
|
});
|
|
2295
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
|
+
*/
|
|
2296
2399
|
count() {
|
|
2297
2400
|
this.#assertIncludeRefinementMode("count()");
|
|
2298
2401
|
return createIncludeScalar("count", this.state);
|
|
2299
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
|
+
*/
|
|
2300
2414
|
sum(field) {
|
|
2301
2415
|
this.#assertIncludeRefinementMode("sum()");
|
|
2302
2416
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2303
2417
|
return createIncludeScalar("sum", this.state, columnName);
|
|
2304
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
|
+
*/
|
|
2305
2430
|
avg(field) {
|
|
2306
2431
|
this.#assertIncludeRefinementMode("avg()");
|
|
2307
2432
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2308
2433
|
return createIncludeScalar("avg", this.state, columnName);
|
|
2309
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
|
+
*/
|
|
2310
2445
|
min(field) {
|
|
2311
2446
|
this.#assertIncludeRefinementMode("min()");
|
|
2312
2447
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2313
2448
|
return createIncludeScalar("min", this.state, columnName);
|
|
2314
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
|
+
*/
|
|
2315
2460
|
max(field) {
|
|
2316
2461
|
this.#assertIncludeRefinementMode("max()");
|
|
2317
2462
|
const columnName = resolveFieldToColumn(this.contract, this.modelName, field);
|
|
2318
2463
|
return createIncludeScalar("max", this.state, columnName);
|
|
2319
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
|
+
*/
|
|
2320
2486
|
combine(spec) {
|
|
2321
2487
|
this.#assertIncludeRefinementMode("combine()");
|
|
2322
2488
|
const branches = {};
|
|
@@ -2339,11 +2505,39 @@ var Collection = class Collection {
|
|
|
2339
2505
|
}
|
|
2340
2506
|
return createIncludeCombine(branches);
|
|
2341
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
|
+
*/
|
|
2342
2528
|
cursor(cursorValues) {
|
|
2343
2529
|
const mappedCursor = mapCursorValuesToColumns(this.contract, this.modelName, cursorValues);
|
|
2344
2530
|
if (Object.keys(mappedCursor).length === 0) return this;
|
|
2345
2531
|
return this.#clone({ cursor: mappedCursor });
|
|
2346
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
|
+
*/
|
|
2347
2541
|
distinct(...fields) {
|
|
2348
2542
|
const distinctFields = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2349
2543
|
return this.#clone({
|
|
@@ -2351,6 +2545,20 @@ var Collection = class Collection {
|
|
|
2351
2545
|
distinctOn: void 0
|
|
2352
2546
|
});
|
|
2353
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
|
+
*/
|
|
2354
2562
|
distinctOn(...fields) {
|
|
2355
2563
|
const distinctOnFields = mapFieldsToColumns(this.contract, this.modelName, fields);
|
|
2356
2564
|
return this.#clone({
|
|
@@ -2358,44 +2566,93 @@ var Collection = class Collection {
|
|
|
2358
2566
|
distinctOn: distinctOnFields
|
|
2359
2567
|
});
|
|
2360
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
|
+
*/
|
|
2361
2576
|
take(n) {
|
|
2362
2577
|
return this.#clone({ limit: n });
|
|
2363
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
|
+
*/
|
|
2364
2590
|
skip(n) {
|
|
2365
2591
|
return this.#clone({ offset: n });
|
|
2366
2592
|
}
|
|
2367
2593
|
/**
|
|
2368
|
-
* 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
|
+
* ```
|
|
2369
2623
|
*
|
|
2370
2624
|
* Accepts an optional `configure` callback that receives a
|
|
2371
2625
|
* `MetaBuilder<'read'>` so the caller can attach typed user
|
|
2372
2626
|
* annotations to the executed plan. `meta.annotate(...)` enforces
|
|
2373
2627
|
* applicability at the type level and at runtime; annotations are
|
|
2374
2628
|
* merged into `plan.meta.annotations` at compile time.
|
|
2375
|
-
*/
|
|
2376
|
-
all(configure) {
|
|
2377
|
-
return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
|
|
2378
|
-
}
|
|
2379
|
-
/**
|
|
2380
|
-
* Read terminal: return the first matching row, or `null`.
|
|
2381
|
-
*
|
|
2382
|
-
* Accepts an optional `filter` (function or shorthand) followed by an
|
|
2383
|
-
* optional `configure` callback that receives a `MetaBuilder<'read'>`
|
|
2384
|
-
* for attaching typed annotations. To attach annotations without
|
|
2385
|
-
* narrowing further, pass `undefined` as the filter (or chain
|
|
2386
|
-
* `.where(...)` first):
|
|
2387
2629
|
*
|
|
2388
2630
|
* ```typescript
|
|
2389
|
-
* await db.User.
|
|
2390
|
-
* await db.User.first(undefined, (meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
|
|
2631
|
+
* await db.orm.User.all((meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
|
|
2391
2632
|
* ```
|
|
2392
2633
|
*/
|
|
2634
|
+
all(configure) {
|
|
2635
|
+
return this.#withAnnotationsFromMeta(configure, "all").#dispatch();
|
|
2636
|
+
}
|
|
2393
2637
|
async first(filter, configure) {
|
|
2394
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;
|
|
2395
2639
|
}
|
|
2396
2640
|
/**
|
|
2397
2641
|
* Read terminal: run an aggregate query (count, sum, avg, min, max)
|
|
2398
|
-
* 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
|
+
* ```
|
|
2399
2656
|
*
|
|
2400
2657
|
* Accepts an optional `configure` callback that receives a
|
|
2401
2658
|
* `MetaBuilder<'read'>` for attaching typed annotations.
|
|
@@ -2410,20 +2667,6 @@ var Collection = class Collection {
|
|
|
2410
2667
|
const compiled = mergeAnnotations(compileAggregate(this.contract, this.tableName, this.state.filters, aggregateSpec), annotationsMap);
|
|
2411
2668
|
return normalizeAggregateResult(aggregateSpec, (await executeQueryPlan(this.ctx.runtime, compiled).toArray())[0] ?? {});
|
|
2412
2669
|
}
|
|
2413
|
-
/**
|
|
2414
|
-
* Write terminal: insert one row and return it.
|
|
2415
|
-
*
|
|
2416
|
-
* Accepts an optional `configure` callback that receives a
|
|
2417
|
-
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
2418
|
-
* Annotations are merged into the compiled mutation plan's
|
|
2419
|
-
* `meta.annotations`.
|
|
2420
|
-
*
|
|
2421
|
-
* Note: when the input contains nested-mutation callbacks, the
|
|
2422
|
-
* operation is executed as a graph of internal queries via
|
|
2423
|
-
* `withMutationScope`. In that path, annotations apply to the
|
|
2424
|
-
* logical `create()` call but do not currently flow into each
|
|
2425
|
-
* constituent SQL statement issued for the related rows.
|
|
2426
|
-
*/
|
|
2427
2670
|
async create(data, configure) {
|
|
2428
2671
|
assertReturningCapability(this.contract, "create()");
|
|
2429
2672
|
const annotationsMap = this.#collectAnnotationsFromMeta(configure, "write", "create");
|
|
@@ -2443,6 +2686,33 @@ var Collection = class Collection {
|
|
|
2443
2686
|
if (created) return created;
|
|
2444
2687
|
throw new Error(`create() for model "${this.modelName}" did not return a row`);
|
|
2445
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
|
+
*/
|
|
2446
2716
|
createAll(data, configure) {
|
|
2447
2717
|
return this.#createAllWithAnnotations(data, this.#collectAnnotationsFromMeta(configure, "write", "createAll"));
|
|
2448
2718
|
}
|
|
@@ -2574,6 +2844,24 @@ var Collection = class Collection {
|
|
|
2574
2844
|
return mapped;
|
|
2575
2845
|
});
|
|
2576
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
|
+
*/
|
|
2577
2865
|
async createCount(data, configure) {
|
|
2578
2866
|
if (data.length === 0) return 0;
|
|
2579
2867
|
this.#assertNotMtiVariant("createCount()");
|
|
@@ -2591,9 +2879,36 @@ var Collection = class Collection {
|
|
|
2591
2879
|
return data.length;
|
|
2592
2880
|
}
|
|
2593
2881
|
/**
|
|
2594
|
-
*
|
|
2595
|
-
*
|
|
2596
|
-
*
|
|
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.
|
|
2597
2912
|
*/
|
|
2598
2913
|
async upsert(input, configure) {
|
|
2599
2914
|
assertReturningCapability(this.contract, "upsert()");
|
|
@@ -2629,7 +2944,30 @@ var Collection = class Collection {
|
|
|
2629
2944
|
}
|
|
2630
2945
|
/**
|
|
2631
2946
|
* Write terminal: update matching rows and return the first one (or
|
|
2632
|
-
* 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
|
+
* ```
|
|
2633
2971
|
*
|
|
2634
2972
|
* Accepts an optional `configure` callback that receives a
|
|
2635
2973
|
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
@@ -2662,6 +3000,31 @@ var Collection = class Collection {
|
|
|
2662
3000
|
return (await scoped.#clone({ filters: [identityWhere] }).#updateAllWithAnnotations(data, annotationsMap))[0] ?? null;
|
|
2663
3001
|
});
|
|
2664
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
|
+
*/
|
|
2665
3028
|
updateAll(data, configure) {
|
|
2666
3029
|
return this.#updateAllWithAnnotations(data, this.#collectAnnotationsFromMeta(configure, "write", "updateAll"));
|
|
2667
3030
|
}
|
|
@@ -2686,6 +3049,20 @@ var Collection = class Collection {
|
|
|
2686
3049
|
mapRow: (mapped) => mapped
|
|
2687
3050
|
});
|
|
2688
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
|
+
*/
|
|
2689
3066
|
async updateCount(data, configure) {
|
|
2690
3067
|
const mappedData = mapModelDataToStorageRow(this.contract, this.modelName, data);
|
|
2691
3068
|
if (Object.keys(mappedData).length === 0) return 0;
|
|
@@ -2704,8 +3081,14 @@ var Collection = class Collection {
|
|
|
2704
3081
|
return matchingRows.length;
|
|
2705
3082
|
}
|
|
2706
3083
|
/**
|
|
2707
|
-
* Write terminal: delete matching rows and return the first
|
|
2708
|
-
* 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
|
+
* ```
|
|
2709
3092
|
*
|
|
2710
3093
|
* Accepts an optional `configure` callback that receives a
|
|
2711
3094
|
* `MetaBuilder<'write'>` for attaching typed annotations.
|
|
@@ -2720,6 +3103,29 @@ var Collection = class Collection {
|
|
|
2720
3103
|
return (await scoped.#clone({ filters: [identityWhere] }).#executeDeleteReturning(annotationsMap).toArray())[0] ?? null;
|
|
2721
3104
|
});
|
|
2722
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
|
+
*/
|
|
2723
3129
|
deleteAll(configure) {
|
|
2724
3130
|
return this.#deleteAllWithAnnotations(this.#collectAnnotationsFromMeta(configure, "write", "deleteAll"));
|
|
2725
3131
|
}
|
|
@@ -2741,6 +3147,18 @@ var Collection = class Collection {
|
|
|
2741
3147
|
mapRow: (mapped) => mapped
|
|
2742
3148
|
});
|
|
2743
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
|
+
*/
|
|
2744
3162
|
async deleteCount(configure) {
|
|
2745
3163
|
const annotationsMap = this.#collectAnnotationsFromMeta(configure, "write", "deleteCount");
|
|
2746
3164
|
const primaryKeyColumn = resolvePrimaryKeyColumn(this.contract, this.tableName);
|