@rudderjs/orm 1.6.0 → 1.7.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/README.md +225 -0
- package/dist/aggregate.d.ts +95 -0
- package/dist/aggregate.d.ts.map +1 -0
- package/dist/aggregate.js +390 -0
- package/dist/aggregate.js.map +1 -0
- package/dist/commands/prune.d.ts +18 -0
- package/dist/commands/prune.d.ts.map +1 -0
- package/dist/commands/prune.js +43 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/index.d.ts +220 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +620 -5
- package/dist/index.js.map +1 -1
- package/dist/prune.d.ts +27 -0
- package/dist/prune.d.ts.map +1 -0
- package/dist/prune.js +65 -0
- package/dist/prune.js.map +1 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -197,6 +197,105 @@ The discriminator stored in `{morphName}Type` defaults to the parent's class nam
|
|
|
197
197
|
|
|
198
198
|
`belongsToMany` and polymorphic v1 limitations: pivot columns are not surfaced on read results (write side only), no `withTimestamps`, no fluent eager-load (`User.with('comments.commentable')`) — drop to the adapter (Prisma `include`) for that. Mutations on the deferred read query (`create`/`update`/`delete`/`insertMany`/`deleteAll`) throw — write through the related model directly. `morphToMany` / `morphedByMany` are supported with the same `attach` / `detach` / `sync` accessor as `belongsToMany`, plus discriminator-scoped pivot reads/writes — see the [polymorphic many-to-many guide](https://rudderjs.com/docs/database/models#polymorphic-many-to-many-morphtomany-morphedbymany).
|
|
199
199
|
|
|
200
|
+
### Filtering by relation predicate — `whereHas` / `whereDoesntHave` / `withWhereHas` / `whereBelongsTo`
|
|
201
|
+
|
|
202
|
+
Filter a query by whether a relation has at least one matching row. The optional callback narrows the relation predicate further — chain plain `where()` calls inside it.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// Users with at least one post
|
|
206
|
+
await User.whereHas('posts').get()
|
|
207
|
+
|
|
208
|
+
// Users with at least one published post
|
|
209
|
+
await User.whereHas('posts', q => q.where('published', true)).get()
|
|
210
|
+
|
|
211
|
+
// Inverse — users with zero published posts
|
|
212
|
+
await User.whereDoesntHave('posts', q => q.where('published', true)).get()
|
|
213
|
+
|
|
214
|
+
// Filter AND eager-load under the same constraint (constrained eager-load
|
|
215
|
+
// via the adapter's `withConstrained` when supported, falls back to plain
|
|
216
|
+
// `with(relation)` otherwise — Drizzle today)
|
|
217
|
+
await User.withWhereHas('posts', q => q.where('published', true)).get()
|
|
218
|
+
|
|
219
|
+
// Sugar over `where(fk, parent.id)` — looks up the FK column from the
|
|
220
|
+
// belongsTo declaration. Pass the relation name when the calling class
|
|
221
|
+
// has multiple belongsTo to the same parent.
|
|
222
|
+
await Post.whereBelongsTo(user).get()
|
|
223
|
+
await Comment.whereBelongsTo(post, 'post').get()
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Supported relation types: `hasMany`, `hasOne`, `belongsTo`, `belongsToMany`, `morphMany`, `morphOne`, `morphToMany`, `morphedByMany`. **`morphTo` is intentionally not supported** — the related table is dynamic, so a single subquery can't represent it. Filter on the `{morphName}Id` / `{morphName}Type` columns directly when you need that semantic.
|
|
227
|
+
|
|
228
|
+
**Adapter notes:**
|
|
229
|
+
|
|
230
|
+
- **Prisma** uses native `some` / `none` filters for direct relations (`hasMany`/`hasOne`/`belongsTo`) — those relations must be declared in `schema.prisma` with the same name. Polymorphic and pivot relations route through a 2-step lookup (related → pivot → IN list) so they work without a Prisma-declared relation.
|
|
231
|
+
- **Drizzle** uses correlated `EXISTS (...)` / `NOT EXISTS (...)` subqueries. Every related table referenced from a `whereHas` call must be registered via `tables: { ... }` on `drizzle()` config or `DrizzleTableRegistry.register(name, table)`.
|
|
232
|
+
- **`withWhereHas`** uses `withConstrained` when the adapter implements it (Prisma → nested `include: { rel: { where } }`). The Drizzle adapter doesn't yet — `withWhereHas` falls back to plain `with(relation)` there.
|
|
233
|
+
- **Nested `whereHas` inside the constrain callback throws** — recursive predicates are deferred to v2. Filter on flat columns inside the callback for now.
|
|
234
|
+
- **Soft deletes inside the relation predicate** — apply `q.where('deletedAt', null)` explicitly inside the constrain callback when needed.
|
|
235
|
+
|
|
236
|
+
### Aggregate eager loading — `withCount` / `withSum` / `withMin` / `withMax` / `withAvg` / `withExists`
|
|
237
|
+
|
|
238
|
+
Eager-load aggregates of related rows alongside the parent in a single query. The result is stamped onto each parent under a deterministic alias (`<relation><Verb><Column>`) so admin tables, dashboards, and any list page can render counts / sums next to each row without N+1.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
// Counts: stamps user.postsCount on each row
|
|
242
|
+
await User.query().withCount('posts').get()
|
|
243
|
+
|
|
244
|
+
// Sum / min / max / avg of a related column — stamps postsSumViews etc.
|
|
245
|
+
await User.query().withSum('posts', 'views').get()
|
|
246
|
+
await Login.query().withMax('sessions', 'createdAt').get()
|
|
247
|
+
|
|
248
|
+
// Boolean — stamps subscriptionExists (true/false)
|
|
249
|
+
await User.query().withExists('subscription').get()
|
|
250
|
+
|
|
251
|
+
// Multiple at once
|
|
252
|
+
await User.query()
|
|
253
|
+
.withCount('posts')
|
|
254
|
+
.withSum('orders', 'total')
|
|
255
|
+
.paginate(1)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Constraint callbacks (map form)** — narrow what counts as a "matching" row, optionally aliasing the result key:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
await User.query()
|
|
262
|
+
.withCount({ posts: q => q.where('published', true).as('publishedPosts') })
|
|
263
|
+
.get()
|
|
264
|
+
// → user.publishedPostsCount
|
|
265
|
+
|
|
266
|
+
await User.query()
|
|
267
|
+
.withSum({
|
|
268
|
+
orders: { column: 'total', constraint: q => q.where('status', 'paid') },
|
|
269
|
+
})
|
|
270
|
+
.get()
|
|
271
|
+
// → user.ordersSumTotal
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Per-instance variants** — `loadCount` / `loadExists` / `loadSum` / `loadMin` / `loadMax` / `loadAvg` mutate a single instance in place. Use these when you've already fetched one parent and need the aggregate on demand. For batched loads on a list, prefer `Model.query().withCount(...)` on the parent query.
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
const user = await User.find(1)
|
|
278
|
+
await user!.loadCount('posts')
|
|
279
|
+
console.log(user!.postsCount)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**`loadMissing(...names)`** — eager-load each named relation onto the instance only when the property is currently `null` / `undefined`. Skips relations that are already populated.
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
const user = await User.query().with('profile').first()
|
|
286
|
+
// profile is already populated; only `posts` issues a query
|
|
287
|
+
await user!.loadMissing('profile', 'posts')
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Notes:**
|
|
291
|
+
|
|
292
|
+
- Aggregate columns are enumerable own-properties — they appear in `JSON.stringify(row)`, `Object.entries(row)`, and `{ ...row }` spreads. They're tagged via a Symbol so `model.save()` strips them out before writing back to the DB.
|
|
293
|
+
- **`withCount` on `belongsTo` throws** (every parent matches exactly one row, so the count is always 0 or 1). Use `withExists('relation')` to test presence, or query the inverse `hasMany` side.
|
|
294
|
+
- **`withCount` on `morphTo` throws** — the related table is dynamic. Aggregate per-target by querying each target class separately.
|
|
295
|
+
- Results are typed `unknown` at the property-access site — cast at the call site (`(user as { postsCount: number }).postsCount`) since the QB type doesn't track the injected aliases. The instance load path doesn't need a cast at the access site.
|
|
296
|
+
- **Soft deletes** on the related model are applied automatically — the adapter ANDs `deleted_at IS NULL` into the aggregate subquery.
|
|
297
|
+
- **Adapter behavior**: Prisma uses `_count.select` for direct count/exists (round-trip-saving) and a second-batch `groupBy` for polymorphic / pivot / numeric aggregates. Drizzle emits one correlated subselect per aggregate in the SELECT list, joining through the pivot table when present.
|
|
298
|
+
|
|
200
299
|
---
|
|
201
300
|
|
|
202
301
|
## Route model binding
|
|
@@ -599,6 +698,58 @@ await Article.query().scope('byAuthor', userId).get()
|
|
|
599
698
|
|
|
600
699
|
---
|
|
601
700
|
|
|
701
|
+
## Dirty Tracking
|
|
702
|
+
|
|
703
|
+
Every Model instance keeps a snapshot of its attributes as of the last
|
|
704
|
+
`hydrate()` / `save()` / `refresh()`. Use it to inspect what changed before
|
|
705
|
+
or after persistence.
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
const user = await User.find(1) // hydrated → not dirty
|
|
709
|
+
user.email = 'new@x.com'
|
|
710
|
+
user.isDirty() // → true
|
|
711
|
+
user.isDirty('email') // → true
|
|
712
|
+
user.isClean('name') // → true
|
|
713
|
+
user.getDirty() // → { email: 'new@x.com' }
|
|
714
|
+
user.getOriginal('email') // → 'old@x.com'
|
|
715
|
+
|
|
716
|
+
await user.save()
|
|
717
|
+
user.isDirty() // → false (baseline reset)
|
|
718
|
+
user.wasChanged() // → true
|
|
719
|
+
user.wasChanged('email') // → true
|
|
720
|
+
user.getChanges() // → { email: 'new@x.com', updatedAt: ... }
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
| Method | Returns |
|
|
724
|
+
|---|---|
|
|
725
|
+
| `isDirty(key?)` | true when any (or the named) attribute has changed since the last save / load / refresh. |
|
|
726
|
+
| `isClean(key?)` | inverse of `isDirty`. |
|
|
727
|
+
| `wasChanged(key?)` | true when the most recent `save()` actually persisted a change to that attribute. Stays true until the next save / refresh. |
|
|
728
|
+
| `getOriginal(key?)` | snapshot value(s) as of the last save / load / refresh. With a key, that single value; without, a full copy of the snapshot. |
|
|
729
|
+
| `getChanges()` | diff of attributes that changed during the most recent `save()`. |
|
|
730
|
+
| `getDirty()` | diff of attributes currently dirty (unsaved). |
|
|
731
|
+
|
|
732
|
+
**Equality semantics.** Primitives use `===`. Dates compare by `getTime()`.
|
|
733
|
+
Plain objects and arrays (typically `json` / `array` cast columns) compare
|
|
734
|
+
by `JSON.stringify` — key-order sensitive, so `{ a: 1, b: 2 }` and
|
|
735
|
+
`{ b: 2, a: 1 }` are considered different. This matches Eloquent's posture.
|
|
736
|
+
|
|
737
|
+
**`refresh()` discards pending writes.** A `refresh()` re-reads the row,
|
|
738
|
+
re-baselines `getOriginal()`, and clears `getChanges()`. Eloquent retains
|
|
739
|
+
`wasChanged` past a refresh; we don't — refresh is "throw away pending
|
|
740
|
+
state, re-read from DB."
|
|
741
|
+
|
|
742
|
+
**`increment()` / `decrement()` re-baseline.** After an instance counter
|
|
743
|
+
update, `isDirty('viewCount')` is `false` — the new value becomes the
|
|
744
|
+
baseline. Counter updates are pure data-plane and intentionally don't
|
|
745
|
+
fire observers (see `static increment` notes); dirty tracking matches.
|
|
746
|
+
|
|
747
|
+
**`replicate()` clones are unsaved.** A replicated instance has values
|
|
748
|
+
on it but an empty `getOriginal()`, so `isDirty()` is `true` until the
|
|
749
|
+
clone is saved.
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
602
753
|
## Soft Deletes
|
|
603
754
|
|
|
604
755
|
```ts
|
|
@@ -617,6 +768,52 @@ Post.query().onlyTrashed().get() // only soft-deleted
|
|
|
617
768
|
|
|
618
769
|
---
|
|
619
770
|
|
|
771
|
+
## Pruning
|
|
772
|
+
|
|
773
|
+
Models can opt into `pnpm rudder model:prune` by declaring `static prunable()`. The runner walks the registered models and deletes everything the query returns, in chunks. Two modes:
|
|
774
|
+
|
|
775
|
+
```ts
|
|
776
|
+
import { Model } from '@rudderjs/orm'
|
|
777
|
+
|
|
778
|
+
// Per-instance — observers fire, soft-deletes honored
|
|
779
|
+
class Session extends Model {
|
|
780
|
+
static override table = 'sessions'
|
|
781
|
+
static prunable() { return this.where('expiresAt', '<', new Date()) }
|
|
782
|
+
static pruning(s: Session) { /* optional pre-delete hook */ }
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Bulk — single deleteAll() per chunk; no observers, no pruning() hook,
|
|
786
|
+
// soft-deletes bypassed (mirrors the deleteAll() primitive)
|
|
787
|
+
class FailedJob extends Model {
|
|
788
|
+
static override table = 'failed_jobs'
|
|
789
|
+
static override pruneMode = 'mass' as const
|
|
790
|
+
static prunable() { return this.where('failedAt', '<', new Date(Date.now() - 7 * 86_400_000)) }
|
|
791
|
+
}
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Run from the CLI:
|
|
795
|
+
|
|
796
|
+
```bash
|
|
797
|
+
pnpm rudder model:prune # prune everything
|
|
798
|
+
pnpm rudder model:prune --pretend # dry-run; runs count() only
|
|
799
|
+
pnpm rudder model:prune --model=Session,FailedJob
|
|
800
|
+
pnpm rudder model:prune --except=AuditLog
|
|
801
|
+
pnpm rudder model:prune --chunk=500
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
Or schedule it from `routes/console.ts`:
|
|
805
|
+
|
|
806
|
+
```ts
|
|
807
|
+
scheduler.command('model:prune').daily()
|
|
808
|
+
scheduler.command('model:prune --pretend').weeklyOn(0, '09:00')
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
`Prunable` (default) calls `instance.delete()` per row — observers fire, soft-deletes apply. `MassPrunable` (`pruneMode = 'mass'`) is faster but bypasses both. Index the columns your `prunable()` filter touches; the runner re-queries per chunk because deletions shift the offset. `pruning()` exceptions are logged and the run continues — one bad row doesn't abort the sweep.
|
|
812
|
+
|
|
813
|
+
For programmatic use, `pruneModels({ models, except, chunk, pretend })` returns one `{ model, mode, count }` report per pruned model.
|
|
814
|
+
|
|
815
|
+
---
|
|
816
|
+
|
|
620
817
|
## Observers
|
|
621
818
|
|
|
622
819
|
Register lifecycle hooks on a model to transform data, log events, or cancel operations.
|
|
@@ -660,6 +857,34 @@ Article.on('deleting', (id) => { if (id === protectedId) return false })
|
|
|
660
857
|
> Use `Model.create()`/`Model.update()`/`Model.delete()` to trigger events.
|
|
661
858
|
> `Model.query().create()` does NOT fire events.
|
|
662
859
|
|
|
860
|
+
### Quiet Events
|
|
861
|
+
|
|
862
|
+
Persist, delete, or restore an instance without firing observers or
|
|
863
|
+
listeners — useful inside seeders, observer cascades, or any path that
|
|
864
|
+
shouldn't trigger lifecycle work twice.
|
|
865
|
+
|
|
866
|
+
```ts
|
|
867
|
+
const user = await User.find(1)
|
|
868
|
+
user.email = 'new@x.com'
|
|
869
|
+
await user.saveQuietly() // persists, observers silent
|
|
870
|
+
|
|
871
|
+
await user.deleteQuietly() // removes / soft-deletes silently
|
|
872
|
+
|
|
873
|
+
const trashed = await User.withTrashed().find(2)
|
|
874
|
+
await trashed.restoreQuietly() // clears deletedAt silently
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
Sugar over `Model.withoutEvents()` — `await ctor.withoutEvents(() => instance.save())`.
|
|
878
|
+
|
|
879
|
+
**Per-class isolation.** Quiet ops mute only the *current* class.
|
|
880
|
+
A `User.saveQuietly()` whose observer cascades into `Comment.delete()`
|
|
881
|
+
still fires `Comment` observers — same posture as Eloquent's
|
|
882
|
+
`saveQuietly`. Wrap the cascade in a broader `withoutEvents` block if
|
|
883
|
+
you need full silence.
|
|
884
|
+
|
|
885
|
+
`instance.restore()` (the non-quiet form) is also available — symmetric
|
|
886
|
+
to `instance.delete()` — and fires `restoring` / `restored` normally.
|
|
887
|
+
|
|
663
888
|
---
|
|
664
889
|
|
|
665
890
|
## toJSON()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { AggregateFn, AggregateRequest, AggregateJoinShape, WhereClause } from '@rudderjs/contracts';
|
|
2
|
+
import type { Model, RelationDefinition } from './index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Constraint callback for the map form of `withCount` / `withExists`. Receives
|
|
5
|
+
* an {@link AggregateConstraintBuilder} for narrow `where`/`orWhere`/`as`
|
|
6
|
+
* recording. Larger surface (`orderBy`, `limit`, terminals) is intentionally
|
|
7
|
+
* out of scope — the only ambiguous semantics in an aggregate context.
|
|
8
|
+
*/
|
|
9
|
+
export type AggregateConstraint = (q: AggregateConstraintBuilder) => AggregateConstraintBuilder;
|
|
10
|
+
/**
|
|
11
|
+
* `where`/`orWhere`/`as` recorder passed to {@link AggregateConstraint}
|
|
12
|
+
* callbacks. Captures clauses for the adapter to AND into the aggregate
|
|
13
|
+
* subquery, plus an optional alias prefix override (`.as('publishedPosts')`).
|
|
14
|
+
*/
|
|
15
|
+
export declare class AggregateConstraintBuilder {
|
|
16
|
+
/** @internal */
|
|
17
|
+
readonly _wheres: WhereClause[];
|
|
18
|
+
/** @internal — alias prefix override; falls back to the relation name. */
|
|
19
|
+
_aliasPrefix?: string;
|
|
20
|
+
where(column: string, valueOrOperator: unknown, maybeValue?: unknown): this;
|
|
21
|
+
orWhere(column: string, valueOrOperator: unknown, maybeValue?: unknown): this;
|
|
22
|
+
/**
|
|
23
|
+
* Override the alias prefix used to stamp the aggregate column onto the
|
|
24
|
+
* result row. Default = relation name. The verb suffix (`Count`/`SumX`/etc.)
|
|
25
|
+
* is preserved, so `.as('publishedPosts')` on a `withCount` call yields
|
|
26
|
+
* `publishedPostsCount`.
|
|
27
|
+
*
|
|
28
|
+
* Required when calling the same `withCount` / `withSum` twice on different
|
|
29
|
+
* constraints — distinct aliases prevent the second from clobbering the first.
|
|
30
|
+
*/
|
|
31
|
+
as(alias: string): this;
|
|
32
|
+
}
|
|
33
|
+
/** Map-form spec for `withSum` / `withMin` / `withMax` / `withAvg`. */
|
|
34
|
+
export interface AggregateSumSpec {
|
|
35
|
+
column: string;
|
|
36
|
+
constraint?: AggregateConstraint;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Per-instance set of attribute keys that came from an aggregate eager-load,
|
|
40
|
+
* not from the underlying schema. Read by `Model._toData()` to skip these
|
|
41
|
+
* keys on writes and by callers that need to distinguish injected columns.
|
|
42
|
+
*
|
|
43
|
+
* Cross-realm-safe via `Symbol.for(...)` — distinct copies of `@rudderjs/orm`
|
|
44
|
+
* loaded by different module graphs share the same tag.
|
|
45
|
+
*/
|
|
46
|
+
export declare const AGGREGATES_SYMBOL: unique symbol;
|
|
47
|
+
/** @internal — read or initialise the aggregate-key set on a Model instance. */
|
|
48
|
+
export declare function aggregateKeysOf(instance: object): Set<string>;
|
|
49
|
+
/**
|
|
50
|
+
* The verb suffix that distinguishes which aggregate is stamped on the parent.
|
|
51
|
+
* Combined with the relation name (or `.as(...)` override) to produce the
|
|
52
|
+
* final alias key — e.g. `posts` + `count` → `postsCount`,
|
|
53
|
+
* `posts` + sum of `views` → `postsSumViews`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function aggregateSuffix(fn: AggregateFn, column?: string): string;
|
|
56
|
+
export declare function aggregateAlias(fn: AggregateFn, baseAlias: string, column?: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Build the {@link AggregateJoinShape} for a relation declared on `Parent`.
|
|
59
|
+
* Mirrors `_buildRelationPredicate` but emits the join-shape subset rather
|
|
60
|
+
* than the full {@link RelationExistencePredicate}. `morphTo` and `belongsTo`
|
|
61
|
+
* are rejected by the caller before reaching this function; everything else
|
|
62
|
+
* routes through.
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildAggregateJoinShape(Parent: typeof Model, relation: string, def: Exclude<RelationDefinition, {
|
|
65
|
+
type: 'morphTo' | 'belongsTo';
|
|
66
|
+
}>): AggregateJoinShape;
|
|
67
|
+
type RelationsMapEntry = AggregateConstraint;
|
|
68
|
+
type RelationsMap = Record<string, RelationsMapEntry>;
|
|
69
|
+
/** Public entrypoint: normalize all `withCount(...)` overloads. */
|
|
70
|
+
export declare function normalizeWithCount(Parent: typeof Model, arg: string | readonly string[] | RelationsMap): AggregateRequest[];
|
|
71
|
+
/** Public entrypoint: normalize all `withExists(...)` overloads. */
|
|
72
|
+
export declare function normalizeWithExists(Parent: typeof Model, arg: string | readonly string[]): AggregateRequest[];
|
|
73
|
+
/** Public entrypoint: normalize all `withSum/withMin/withMax/withAvg(...)` overloads. */
|
|
74
|
+
export declare function normalizeWithNumericAggregate(Parent: typeof Model, fn: 'sum' | 'min' | 'max' | 'avg', arg1: string | Record<string, AggregateSumSpec>, arg2?: string): AggregateRequest[];
|
|
75
|
+
/**
|
|
76
|
+
* Implementation of `Model#loadCount` / `loadExists`. Mutates the instance
|
|
77
|
+
* in place; returns it for chaining at the call site.
|
|
78
|
+
*/
|
|
79
|
+
export declare function loadCountOrExists(instance: Model, fn: 'count' | 'exists', arg: string | readonly string[] | RelationsMap): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Implementation of `Model#loadSum` / `loadMin` / `loadMax` / `loadAvg`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function loadNumericAggregate(instance: Model, fn: 'sum' | 'min' | 'max' | 'avg', relation: string | Record<string, AggregateSumSpec>, column?: string): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Implementation of `Model#loadMissing`. Loads each named relation onto the
|
|
86
|
+
* instance only when the property is currently `null` / `undefined`.
|
|
87
|
+
*
|
|
88
|
+
* Always uses `instance.related(name).get()` (the chainable QB form) — for
|
|
89
|
+
* `hasOne` / `belongsTo` / `morphOne` semantics the caller can `[0]` the
|
|
90
|
+
* resulting array if they know the relation is single-valued, or call
|
|
91
|
+
* `loadMissing` only on the *array* relations they care about.
|
|
92
|
+
*/
|
|
93
|
+
export declare function loadMissingRelations(instance: Model, names: readonly string[]): Promise<void>;
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=aggregate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"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"}
|