uql-orm 0.56.0 → 0.58.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 +7 -9
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +4 -4
- package/dist/cockroachdb/cockroachDialect.d.ts +5 -2
- package/dist/cockroachdb/cockroachDialect.js +2 -10
- package/dist/d1/d1SqliteDialect.d.ts +1 -0
- package/dist/d1/d1SqliteDialect.js +2 -0
- package/dist/dialect/abstractSqlDialect.d.ts +196 -33
- package/dist/dialect/abstractSqlDialect.js +410 -203
- package/dist/dialect/aliases.d.ts +10 -7
- package/dist/dialect/aliases.js +12 -7
- package/dist/dialect/hydrateColumn.d.ts +8 -2
- package/dist/dialect/hydrateColumn.js +33 -1
- package/dist/dialect/jsonSql.d.ts +13 -5
- package/dist/dialect/jsonSql.js +24 -7
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +31 -3
- package/dist/dialect/mysqlLikeSqlDialect.js +57 -5
- package/dist/dialect/pgLikeSqlDialect.d.ts +20 -20
- package/dist/dialect/pgLikeSqlDialect.js +23 -48
- package/dist/dialect/pgVectorMetrics.d.ts +13 -0
- package/dist/dialect/pgVectorMetrics.js +17 -0
- package/dist/dialect/queryContext.d.ts +3 -7
- package/dist/dialect/queryContext.js +13 -8
- package/dist/dialect/queryJoins.d.ts +8 -4
- package/dist/dialect/queryJoins.js +26 -11
- package/dist/dialect/vectorSqlDialect.d.ts +2 -2
- package/dist/dialect/vectorSqlDialect.js +2 -3
- package/dist/entity/decorator/bag.d.ts +2 -2
- package/dist/entity/decorator/entity.d.ts +8 -9
- package/dist/entity/decorator/entity.js +6 -7
- package/dist/entity/decorator/members.d.ts +7 -6
- package/dist/entity/decorator/members.js +2 -1
- package/dist/entity/metadata/definition.d.ts +16 -11
- package/dist/entity/metadata/definition.js +54 -42
- package/dist/http/handler.d.ts +2 -2
- package/dist/http/handler.js +0 -1
- package/dist/maria/mariaDialect.d.ts +13 -6
- package/dist/maria/mariaDialect.js +29 -9
- package/dist/migrate/cli.d.ts +2 -3
- package/dist/migrate/cli.js +2 -2
- package/dist/migrate/codegen/entityCodeGenerator.js +6 -4
- package/dist/migrate/codegen/entityTypes.d.ts +1 -1
- package/dist/migrate/codegen/entityTypes.js +4 -3
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +5 -4
- package/dist/migrate/codegen/indexDecoratorSource.js +17 -13
- package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
- package/dist/migrate/codegen/sourceLiteral.js +4 -0
- package/dist/migrate/ddl/index.d.ts +1 -5
- package/dist/migrate/ddl/index.js +14 -25
- package/dist/migrate/ddl/indexDdl.d.ts +11 -2
- package/dist/migrate/ddl/indexDdl.js +17 -1
- package/dist/migrate/ddl/mssqlIndexDdl.d.ts +10 -0
- package/dist/migrate/ddl/mssqlIndexDdl.js +10 -0
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +10 -17
- package/dist/migrate/ddl/mysqlIndexDdl.js +16 -27
- package/dist/migrate/ddl/pgIndexDdl.d.ts +18 -8
- package/dist/migrate/ddl/pgIndexDdl.js +29 -12
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +3 -3
- package/dist/migrate/migrator.d.ts +4 -4
- package/dist/migrate/schemaGenerator.d.ts +8 -8
- package/dist/migrate/schemaGenerator.js +5 -7
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -3
- package/dist/mongo/mongoDialect.d.ts +31 -18
- package/dist/mongo/mongoDialect.js +146 -104
- package/dist/mongo/mongodbQuerier.d.ts +10 -17
- package/dist/mongo/mongodbQuerier.js +31 -106
- package/dist/mssql/mssqlDialect.d.ts +16 -0
- package/dist/mssql/mssqlDialect.js +26 -4
- package/dist/mysql/mysqlDialect.d.ts +2 -0
- package/dist/mysql/mysqlDialect.js +4 -0
- package/dist/querier/abstractQuerier.d.ts +20 -36
- package/dist/querier/abstractQuerier.js +35 -129
- package/dist/querier/abstractQuerierPool.d.ts +2 -2
- package/dist/querier/abstractSqlQuerier.d.ts +4 -17
- package/dist/querier/abstractSqlQuerier.js +40 -50
- package/dist/schema/canonicalType.js +4 -4
- package/dist/schema/indexDifferences.js +4 -4
- package/dist/schema/schemaASTBuilder.d.ts +3 -3
- package/dist/schema/schemaASTBuilder.js +32 -3
- package/dist/schema/schemaASTDiffer.js +5 -5
- package/dist/sqlite/sqliteDialect.d.ts +20 -1
- package/dist/sqlite/sqliteDialect.js +38 -7
- package/dist/turso/tursoDialect.d.ts +2 -0
- package/dist/turso/tursoDialect.js +2 -0
- package/dist/type/config.d.ts +3 -3
- package/dist/type/dialect.d.ts +4 -5
- package/dist/type/entity.d.ts +110 -69
- package/dist/type/migration.d.ts +7 -7
- package/dist/type/migratorDialect.d.ts +4 -0
- package/dist/type/querier.d.ts +6 -6
- package/dist/type/querierPool.d.ts +2 -2
- package/dist/type/query.d.ts +41 -72
- package/dist/type/query.js +10 -5
- package/dist/type/queryAggregate.d.ts +43 -34
- package/dist/type/queryAggregate.js +1 -1
- package/dist/type/queryWhere.d.ts +12 -9
- package/dist/type/universalQuerier.d.ts +4 -4
- package/dist/util/dialect.util.d.ts +4 -4
- package/dist/util/dialect.util.js +24 -15
- package/dist/util/field.util.d.ts +5 -0
- package/dist/util/field.util.js +19 -0
- package/dist/util/object.util.d.ts +2 -0
- package/dist/util/object.util.js +4 -0
- package/dist/util/relationQuery.util.d.ts +12 -65
- package/dist/util/relationQuery.util.js +27 -81
- package/dist/util/rowKey.util.d.ts +1 -11
- package/dist/util/rowKey.util.js +1 -13
- package/package.json +1 -1
- package/dist/querier/relationCount.d.ts +0 -16
- package/dist/querier/relationCount.js +0 -121
package/dist/type/query.js
CHANGED
|
@@ -9,8 +9,8 @@ export const COUNT_RESULT_KEY = '_count';
|
|
|
9
9
|
* Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build
|
|
10
10
|
* rather than the runtime if a clause is ever renamed.
|
|
11
11
|
*
|
|
12
|
-
* `$lock`
|
|
13
|
-
*
|
|
12
|
+
* `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query
|
|
13
|
+
* accepts it.
|
|
14
14
|
*/
|
|
15
15
|
export const QUERY_OBJECT_CLAUSES = [
|
|
16
16
|
'$select',
|
|
@@ -20,9 +20,8 @@ export const QUERY_OBJECT_CLAUSES = [
|
|
|
20
20
|
'$sort',
|
|
21
21
|
];
|
|
22
22
|
/**
|
|
23
|
-
* Object clauses only the statement itself takes
|
|
24
|
-
*
|
|
25
|
-
* populated relation's rows are assembled after that, so there is nothing for a nested one to count.
|
|
23
|
+
* Object clauses only the statement itself takes: a populated relation's rows keep their declared type,
|
|
24
|
+
* so a `$count` inside one would have no `_count` to land in.
|
|
26
25
|
*/
|
|
27
26
|
export const QUERY_ROOT_OBJECT_CLAUSES = ['$count'];
|
|
28
27
|
export const QUERY_NUMBER_CLAUSES = ['$skip', '$limit'];
|
|
@@ -33,3 +32,9 @@ export const QUERY_NUMBER_CLAUSES = ['$skip', '$limit'];
|
|
|
33
32
|
*/
|
|
34
33
|
export const QUERY_ROOT_NUMBER_CLAUSES = ['$candidates'];
|
|
35
34
|
export const QUERY_BOOLEAN_CLAUSES = ['$distinct'];
|
|
35
|
+
/** The clauses that describe the statement, which a populated relation's own query refuses by name. */
|
|
36
|
+
export const QUERY_STATEMENT_CLAUSES = [
|
|
37
|
+
'$lock',
|
|
38
|
+
...QUERY_ROOT_OBJECT_CLAUSES,
|
|
39
|
+
...QUERY_ROOT_NUMBER_CLAUSES,
|
|
40
|
+
];
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { FieldKey } from './entity.js';
|
|
2
|
-
import type { QueryPager, QuerySortDirection } from './query.js';
|
|
2
|
+
import type { QueryPager, QuerySelect, QuerySortDirection } from './query.js';
|
|
3
3
|
import type { QueryWhere, QueryWhereFieldValue } from './queryWhere.js';
|
|
4
4
|
/**
|
|
5
5
|
* Maps the offending keys to `never`, turning an excess key into a compile error; resolves to
|
|
6
|
-
* `unknown` (an inert intersection member) when there are none. Needed because `$group`/`$
|
|
6
|
+
* `unknown` (an inert intersection member) when there are none. Needed because `$group`/`$select` are
|
|
7
7
|
* captured as whole maps, and TypeScript skips excess-property checking on a naked type parameter.
|
|
8
8
|
* A find captures key sets instead, where an unknown key fails the capture's own constraint.
|
|
9
9
|
* @internal
|
|
@@ -21,7 +21,7 @@ type GroupedKeys<G> = {
|
|
|
21
21
|
}[keyof G];
|
|
22
22
|
/**
|
|
23
23
|
* The keys `T` declares by name, or `never` when `T` is only an index signature - which is what an
|
|
24
|
-
* uninferred `$
|
|
24
|
+
* uninferred `$select` is, and what would otherwise make every key look like a declared alias.
|
|
25
25
|
* @internal
|
|
26
26
|
*/
|
|
27
27
|
type NamedKeys<T> = string extends keyof T ? never : keyof T;
|
|
@@ -37,7 +37,7 @@ export type QueryAggregateOp = (typeof QUERY_AGGREGATE_OPS)[number];
|
|
|
37
37
|
export declare function isQueryAggregateOp(op: string): op is QueryAggregateOp;
|
|
38
38
|
/**
|
|
39
39
|
* DISTINCT-qualified aggregate ops, each mapped to the base op it applies to a field's distinct
|
|
40
|
-
* values: `$countDistinct`
|
|
40
|
+
* values: `$countDistinct` -> `COUNT(DISTINCT col)`, and likewise `$sumDistinct`/`$avgDistinct`. Flat
|
|
41
41
|
* (not a nested `{ $distinct }` argument) so the op is self-documenting and greppable. `$min`/`$max`
|
|
42
42
|
* are omitted: DISTINCT is a no-op for them.
|
|
43
43
|
*/
|
|
@@ -57,8 +57,20 @@ export declare function resolveAggregateOp(key: string): {
|
|
|
57
57
|
op: QueryAggregateOp;
|
|
58
58
|
distinct: boolean;
|
|
59
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* Exactly one key of `T` with its value; every other key is forbidden (`never`). `Pick`, not `Record`,
|
|
62
|
+
* so the chosen key stays linked to `T`'s own property and renames follow it through.
|
|
63
|
+
*/
|
|
64
|
+
type ExactlyOne<T> = {
|
|
65
|
+
[K in keyof T]: Readonly<Pick<T, K>> & Partial<Readonly<Record<Exclude<keyof T, K>, never>>>;
|
|
66
|
+
}[keyof T];
|
|
67
|
+
/**
|
|
68
|
+
* One field named as a key - `{ amount: true }` - the way a statement names every field, so an editor
|
|
69
|
+
* rename reaches it where a string never would. `F` narrows which fields qualify.
|
|
70
|
+
*/
|
|
71
|
+
export type QueryFieldRef<E, F extends keyof E = FieldKey<E>> = ExactlyOne<Required<QuerySelect<E, F, true>>>;
|
|
60
72
|
/** The argument of an aggregate function: a field, or `'*'` (only meaningful for `COUNT(*)`). */
|
|
61
|
-
export type QueryAggregateArg<E> =
|
|
73
|
+
export type QueryAggregateArg<E> = QueryFieldRef<E> | '*';
|
|
62
74
|
/**
|
|
63
75
|
* Fields `SUM`/`AVG` can total. Restricted to numeric columns because the result is declared
|
|
64
76
|
* `number`: totalling a text or date column is either an engine error or a coercion, and neither
|
|
@@ -81,21 +93,17 @@ type TotallingOp = OpsOf<'$sum' | '$avg' | '$sumDistinct' | '$avgDistinct'>;
|
|
|
81
93
|
* Every aggregate op mapped to the argument it accepts: `$count` a field or `'*'` (`COUNT(*)`),
|
|
82
94
|
* the totalling ops a numeric field, `$min`/`$max`/`$countDistinct` any field.
|
|
83
95
|
*/
|
|
84
|
-
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, NumericFieldKey<E
|
|
85
|
-
/** Exactly one key of `T`: the chosen op with its value; every other op key is forbidden (`never`). */
|
|
86
|
-
type ExactlyOne<T> = {
|
|
87
|
-
[K in keyof T]: Readonly<Record<K, T[K]>> & Partial<Readonly<Record<Exclude<keyof T, K>, never>>>;
|
|
88
|
-
}[keyof T];
|
|
96
|
+
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, QueryFieldRef<E, NumericFieldKey<E>>> & Record<Exclude<AggregateOp, '$count' | TotallingOp>, QueryFieldRef<E>>;
|
|
89
97
|
/**
|
|
90
98
|
* An aggregate function applied to a field. Exactly one operation per entry (a second op is a
|
|
91
99
|
* compile error). Only `$count` accepts `'*'` (i.e. `COUNT(*)`); every other op requires a field.
|
|
92
100
|
* DISTINCT variants are flat ops (`$countDistinct`/`$sumDistinct`/`$avgDistinct`) taking a field.
|
|
93
101
|
*
|
|
94
|
-
* @example { $count: '*' }
|
|
95
|
-
* @example { $countDistinct:
|
|
96
|
-
* @example { $sum:
|
|
97
|
-
* @example { $sumDistinct:
|
|
98
|
-
* @example { $avg:
|
|
102
|
+
* @example { $count: '*' } -> COUNT(*)
|
|
103
|
+
* @example { $countDistinct: { id: true } } -> COUNT(DISTINCT "id")
|
|
104
|
+
* @example { $sum: { amount: true } } -> SUM("amount")
|
|
105
|
+
* @example { $sumDistinct: { amount: true } } -> SUM(DISTINCT "amount")
|
|
106
|
+
* @example { $avg: { age: true } } -> AVG("age")
|
|
99
107
|
*/
|
|
100
108
|
export type QueryAggregateFn<E> = ExactlyOne<QueryAggregateArgMap<E>>;
|
|
101
109
|
/** A single-key `{ [op]: unknown }` shape for each op in `Ops`, matched to infer that op's result. */
|
|
@@ -109,16 +117,14 @@ type CountingOp = OpsOf<'$count' | '$countDistinct'>;
|
|
|
109
117
|
/**
|
|
110
118
|
* Group-by columns: an object mapping entity field keys to `true`, exactly like {@link QuerySelect}.
|
|
111
119
|
* Typed against the entity, so a typo'd column is a compile error. Compute aggregate columns with
|
|
112
|
-
* {@link QueryAggMap} (the `$
|
|
120
|
+
* {@link QueryAggMap} (the `$select` key), not here.
|
|
113
121
|
*
|
|
114
122
|
* @example
|
|
115
123
|
* ```ts
|
|
116
|
-
* { status: true } //
|
|
124
|
+
* { status: true } // -> GROUP BY "status"
|
|
117
125
|
* ```
|
|
118
126
|
*/
|
|
119
|
-
export type QueryGroupMap<E> =
|
|
120
|
-
readonly [K in FieldKey<E>]?: true;
|
|
121
|
-
};
|
|
127
|
+
export type QueryGroupMap<E> = Readonly<QuerySelect<E, FieldKey<E>, true>>;
|
|
122
128
|
/**
|
|
123
129
|
* Computed aggregate columns: an object mapping your chosen output alias to an aggregate function.
|
|
124
130
|
* Alias names are free (you are naming new columns); the aggregated field reference inside each
|
|
@@ -126,8 +132,8 @@ export type QueryGroupMap<E> = {
|
|
|
126
132
|
*
|
|
127
133
|
* @example
|
|
128
134
|
* ```ts
|
|
129
|
-
* { count: { $count: '*' }, avgAge: { $avg:
|
|
130
|
-
* //
|
|
135
|
+
* { count: { $count: '*' }, avgAge: { $avg: { age: true } } }
|
|
136
|
+
* // -> COUNT(*) AS "count", AVG("age") AS "avgAge"
|
|
131
137
|
* ```
|
|
132
138
|
*/
|
|
133
139
|
export type QueryAggMap<E> = {
|
|
@@ -151,7 +157,7 @@ type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number :
|
|
|
151
157
|
readonly $min: infer F;
|
|
152
158
|
} | {
|
|
153
159
|
readonly $max: infer F;
|
|
154
|
-
} ? FieldValueType<E, F> | null : unknown;
|
|
160
|
+
} ? FieldValueType<E, keyof F> | null : unknown;
|
|
155
161
|
/**
|
|
156
162
|
* Flattens an intersection into a single object literal for readable editor hovers.
|
|
157
163
|
* @internal
|
|
@@ -166,17 +172,15 @@ type Simplify<T> = {
|
|
|
166
172
|
* Grouped columns come from {@link GroupedKeys}, not `keyof G`, so a `$group` the compiler could
|
|
167
173
|
* not read contributes none rather than all of them.
|
|
168
174
|
*/
|
|
169
|
-
export type QueryAggregateResult<E, G, A> = Simplify<{
|
|
170
|
-
-readonly [K in GroupedKeys<G> & FieldKey<E>]: E[K];
|
|
171
|
-
} & {
|
|
175
|
+
export type QueryAggregateResult<E, G, A> = Simplify<Pick<E, GroupedKeys<G> & FieldKey<E>> & {
|
|
172
176
|
-readonly [K in keyof A]: QueryAggregateFnResult<E, A[K]>;
|
|
173
177
|
}>;
|
|
174
178
|
/**
|
|
175
|
-
* Erased runtime shape of a HAVING clause (alias
|
|
179
|
+
* Erased runtime shape of a HAVING clause (alias -> comparison), consumed by the dialect builders.
|
|
176
180
|
* Values are `unknown` because the SQL is built generically; the typed, per-column value checking
|
|
177
181
|
* lives in {@link QueryAggregate.$having}.
|
|
178
182
|
*
|
|
179
|
-
* @example { count: { $gt: 5 } }
|
|
183
|
+
* @example { count: { $gt: 5 } } -> HAVING COUNT(*) > 5
|
|
180
184
|
*/
|
|
181
185
|
export type QueryHavingMap = {
|
|
182
186
|
readonly [alias: string]: QueryWhereFieldValue<unknown> | undefined;
|
|
@@ -190,7 +194,7 @@ export type QueryHavingMap = {
|
|
|
190
194
|
* querier.aggregate(User, {
|
|
191
195
|
* $where: { deletedAt: { $isNull: true } },
|
|
192
196
|
* $group: { status: true },
|
|
193
|
-
* $
|
|
197
|
+
* $select: { count: { $count: '*' }, avgAge: { $avg: { age: true } } },
|
|
194
198
|
* $having: { count: { $gt: 5 } },
|
|
195
199
|
* $sort: { count: -1 },
|
|
196
200
|
* });
|
|
@@ -203,17 +207,22 @@ export type QueryAggregate<E, G extends QueryGroupMap<E> = QueryGroupMap<E>, A e
|
|
|
203
207
|
readonly $where?: QueryWhere<E>;
|
|
204
208
|
/**
|
|
205
209
|
* Columns to group by - `{ status: true }`, typed against the entity like `$select`. A computed
|
|
206
|
-
* aggregate wrongly placed here (it belongs in `$
|
|
207
|
-
* `$group` is captured as a generic and a bare generic skips excess-property checking.
|
|
210
|
+
* aggregate wrongly placed here (it belongs in `$select`) is rejected via {@link Reject}, since
|
|
211
|
+
* `$group` is captured as a generic and a bare generic skips excess-property checking. The captured
|
|
212
|
+
* map meets its schema, {@link QueryGroupMap}, so each key keeps its link to the entity property.
|
|
208
213
|
*/
|
|
209
|
-
readonly $group?: G & Reject<Exclude<keyof G, FieldKey<E>>>;
|
|
214
|
+
readonly $group?: G & QueryGroupMap<E> & Reject<Exclude<keyof G, FieldKey<E>>>;
|
|
210
215
|
/**
|
|
211
|
-
* Computed aggregate columns - `{ count: { $count: '*' }, avgAge: { $avg:
|
|
216
|
+
* Computed aggregate columns - `{ count: { $count: '*' }, avgAge: { $avg: { age: true } } }`. The
|
|
217
|
+
* captured map meets its schema over the same aliases, as `$group` does, so field keys stay linked
|
|
218
|
+
* (a `Record<keyof A, ...>` spelling of the same type breaks the inference of `A`).
|
|
212
219
|
*
|
|
213
220
|
* An alias repeating a `$group` column is rejected: both would be emitted under that one name,
|
|
214
221
|
* leaving the driver to keep whichever it read last.
|
|
215
222
|
*/
|
|
216
|
-
readonly $
|
|
223
|
+
readonly $select?: A & {
|
|
224
|
+
readonly [K in keyof A]: QueryAggregateFn<E>;
|
|
225
|
+
} & Reject<NamedKeys<A> & GroupedKeys<G>>;
|
|
217
226
|
/**
|
|
218
227
|
* Post-aggregation filtering, applied after grouping (SQL `HAVING`, MongoDB post-group `$match`).
|
|
219
228
|
* Keyed by the result columns (grouped columns + computed aliases), and each value is typed to that
|
|
@@ -8,7 +8,7 @@ export function isQueryAggregateOp(op) {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* DISTINCT-qualified aggregate ops, each mapped to the base op it applies to a field's distinct
|
|
11
|
-
* values: `$countDistinct`
|
|
11
|
+
* values: `$countDistinct` -> `COUNT(DISTINCT col)`, and likewise `$sumDistinct`/`$avgDistinct`. Flat
|
|
12
12
|
* (not a nested `{ $distinct }` argument) so the op is self-documenting and greppable. `$min`/`$max`
|
|
13
13
|
* are omitted: DISTINCT is a no-op for them.
|
|
14
14
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FieldKey, JsonFieldPaths, JsonFieldPathValue, RelationKey, RelationTarget } from './entity.js';
|
|
2
|
+
import type { QuerySelect } from './query.js';
|
|
2
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
3
4
|
import type { ExpandScalar, IsMany, QueryComparableScalar, Scalar } from './utility.js';
|
|
4
5
|
import type { QueryVectorQuery } from './vector.js';
|
|
@@ -11,9 +12,9 @@ export type QueryTextSearchOptions<E> = {
|
|
|
11
12
|
*/
|
|
12
13
|
$value: string;
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
+
* the fields to search, `{ title: true, body: true }`, in the order a MySQL `FULLTEXT` index lists them.
|
|
15
16
|
*/
|
|
16
|
-
$fields?:
|
|
17
|
+
$fields?: QuerySelect<E>;
|
|
17
18
|
/**
|
|
18
19
|
* Postgres text-search configuration (e.g. `'english'`), applied to both the document and the
|
|
19
20
|
* query. Defaults to the server's `default_text_search_config`. Ignored by other dialects.
|
|
@@ -27,17 +28,19 @@ export type QueryTextSearchOptions<E> = {
|
|
|
27
28
|
* filtered via nested typed objects; dotted relation paths are not supported (the dialects throw
|
|
28
29
|
* for non-JSON dotted keys).
|
|
29
30
|
*
|
|
30
|
-
*
|
|
31
|
-
* {@link
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Fields and relations share one mapped type over `K extends keyof E`, which keeps each key linked to
|
|
32
|
+
* its property (see {@link QuerySelect}). JSON paths are not keys of `E`, so they are a second member,
|
|
33
|
+
* and only where the entity has one: an empty member would switch off the weak-type check that
|
|
34
|
+
* rejects `$where: 1`.
|
|
34
35
|
*
|
|
35
36
|
* An object and nothing else: in a union with ids or lists, TypeScript reports a wrong value against
|
|
36
37
|
* the whole `$where` instead of the key holding it. Ids are `{ id: 1 }`, or the by-id methods.
|
|
37
38
|
*/
|
|
38
|
-
export type QueryWhere<E> = QueryWhereRootOperator<E> & {
|
|
39
|
-
[
|
|
40
|
-
}
|
|
39
|
+
export type QueryWhere<E, K extends keyof E = FieldKey<E> | RelationKey<E>> = QueryWhereRootOperator<E> & {
|
|
40
|
+
[P in K]?: P extends FieldKey<E> ? QueryWhereFieldValue<E[P]> : QueryWhere<RelationTarget<E[P]>> | QueryRelationSizeFilter;
|
|
41
|
+
} & ([JsonFieldPaths<E>] extends [never] ? unknown : {
|
|
42
|
+
[P in JsonFieldPaths<E>]?: QueryWhereFieldValue<JsonFieldPathValue<E, P>>;
|
|
43
|
+
});
|
|
41
44
|
/**
|
|
42
45
|
* Filter a to-many relation by its row count.
|
|
43
46
|
* @example { users: { $size: 2 } }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityData, EntityId, FieldKey, RelationKey, UpdatePayload, WrittenId } from './entity.js';
|
|
2
|
-
import type { QueryConflictPaths, QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch,
|
|
2
|
+
import type { QueryConflictPaths, QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, QueryUpsertOneResult, QueryUpsertManyResult } from './query.js';
|
|
3
3
|
import type { QueryAggMap, QueryAggregate, QueryAggregateResult, QueryGroupMap } from './queryAggregate.js';
|
|
4
4
|
import type { Type } from './utility.js';
|
|
5
5
|
import type { QuerierCountedResult, QuerierResult, QuerierTransport } from './wire.js';
|
|
@@ -97,14 +97,14 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
|
|
|
97
97
|
*/
|
|
98
98
|
export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions> {
|
|
99
99
|
/**
|
|
100
|
-
* streams the records matching the given search parameters as an async iterable
|
|
101
|
-
*
|
|
100
|
+
* streams the records matching the given search parameters as an async iterable, each with the
|
|
101
|
+
* relations and counts `findMany` reads. Fires no lifecycle hooks, and holds one row at a time, for
|
|
102
102
|
* bulk reads (ETL, exports, migrations).
|
|
103
103
|
* @param entity the target entity
|
|
104
104
|
* @param q the criteria options
|
|
105
105
|
* @return an async iterable of records
|
|
106
106
|
*/
|
|
107
|
-
findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never>(entity: Type<E>, q:
|
|
107
|
+
findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
|
|
108
108
|
/**
|
|
109
109
|
* Insert a single record and return its ID (provided, `onInsert`-generated, or
|
|
110
110
|
* database-generated - see {@link UniversalQuerier.insertMany} for the exact semantics).
|
|
@@ -128,7 +128,7 @@ export type ParsedGroupEntry = {
|
|
|
128
128
|
readonly alias: string;
|
|
129
129
|
readonly op: QueryAggregateOp;
|
|
130
130
|
readonly fieldRef: string;
|
|
131
|
-
/** `true` for a flat distinct op (`$countDistinct`, ...)
|
|
131
|
+
/** `true` for a flat distinct op (`$countDistinct`, ...) -> `COUNT(DISTINCT field)`. */
|
|
132
132
|
readonly distinct: boolean;
|
|
133
133
|
};
|
|
134
134
|
/**
|
|
@@ -145,10 +145,10 @@ export declare function parseRelationSize(val: unknown): number | QuerySizeCompa
|
|
|
145
145
|
*/
|
|
146
146
|
export declare function parseSortByCount(val: unknown): unknown;
|
|
147
147
|
/**
|
|
148
|
-
* Parse the `$group` (grouped columns) and `$
|
|
148
|
+
* Parse the `$group` (grouped columns) and `$select` (computed aggregates) maps into structured
|
|
149
149
|
* entries consumable by any dialect. Grouped columns come first, then computed columns.
|
|
150
150
|
*/
|
|
151
|
-
export declare function parseGroupMap<E>(group?: QueryGroupMap<E>,
|
|
151
|
+
export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: QueryAggMap<E>): ParsedGroupEntry[];
|
|
152
152
|
/**
|
|
153
153
|
* Whether `value` is a map of comparison operators rather than a value to compare against. Only a
|
|
154
154
|
* plain object qualifies: `Date`, `QueryRaw`, `Uint8Array` and arrays are all `typeof 'object'`, and
|
|
@@ -165,7 +165,7 @@ export declare function isOperatorMap(value: unknown): value is Record<string, u
|
|
|
165
165
|
export declare function assertNonNegativeInteger(value: number, clause: string): number;
|
|
166
166
|
/**
|
|
167
167
|
* Rejects a `$having`/`$sort` key naming something an aggregate does not emit. Its rows are its
|
|
168
|
-
* `$group` columns and its `$
|
|
168
|
+
* `$group` columns and its `$select` aliases; anything else is a value that is not there. Shared so
|
|
169
169
|
* SQL and MongoDB refuse the same query with the same words.
|
|
170
170
|
*/
|
|
171
171
|
export declare function throwUnknownAggregateColumn(key: string, clause: string): never;
|
|
@@ -3,7 +3,7 @@ import { soleIdOf } from '../entity/metadata/definition.js';
|
|
|
3
3
|
import { QueryRaw, resolveAggregateOp, SOFT_DELETE_FILTER, } from '../type/index.js';
|
|
4
4
|
import { VECTOR_INDEX_TYPES } from '../type/vector.js';
|
|
5
5
|
import { isDatabaseWritten } from './field.util.js';
|
|
6
|
-
import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, isWhereMap, someKey } from './object.util.js';
|
|
6
|
+
import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, isRecord, isWhereMap, someKey, } from './object.util.js';
|
|
7
7
|
export function filterFieldKeys(meta, payload, callbackKey) {
|
|
8
8
|
return getKeys(payload).filter((key) => {
|
|
9
9
|
const fieldOpts = meta.fields[key];
|
|
@@ -355,10 +355,10 @@ export function parseSortByCount(val) {
|
|
|
355
355
|
return val.$count;
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
358
|
-
* Parse the `$group` (grouped columns) and `$
|
|
358
|
+
* Parse the `$group` (grouped columns) and `$select` (computed aggregates) maps into structured
|
|
359
359
|
* entries consumable by any dialect. Grouped columns come first, then computed columns.
|
|
360
360
|
*/
|
|
361
|
-
export function parseGroupMap(group,
|
|
361
|
+
export function parseGroupMap(group, select) {
|
|
362
362
|
const entries = [];
|
|
363
363
|
const groupMap = group ?? {};
|
|
364
364
|
for (const alias of getKeys(groupMap)) {
|
|
@@ -366,22 +366,30 @@ export function parseGroupMap(group, agg) {
|
|
|
366
366
|
entries.push({ kind: 'key', alias });
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
|
-
if (!
|
|
369
|
+
if (!select) {
|
|
370
370
|
return entries;
|
|
371
371
|
}
|
|
372
|
-
for (const alias of getKeys(
|
|
373
|
-
const fnEntry =
|
|
372
|
+
for (const alias of getKeys(select)) {
|
|
373
|
+
const fnEntry = select[alias];
|
|
374
374
|
const key = getKeys(fnEntry)[0];
|
|
375
375
|
// Flat DISTINCT ops (`$countDistinct`, ...) normalize to their base op + a `distinct` flag.
|
|
376
376
|
const { op, distinct } = resolveAggregateOp(key);
|
|
377
|
-
|
|
378
|
-
if (fieldRef === undefined) {
|
|
379
|
-
throw TypeError(`empty aggregate function for: ${alias}`);
|
|
380
|
-
}
|
|
381
|
-
entries.push({ kind: 'fn', alias, op, fieldRef, distinct });
|
|
377
|
+
entries.push({ kind: 'fn', alias, op, fieldRef: aggregateFieldRef(alias, fnEntry[key]), distinct });
|
|
382
378
|
}
|
|
383
379
|
return entries;
|
|
384
380
|
}
|
|
381
|
+
/** The column an aggregate reads: `'*'`, or the one field its `{ field: true }` names. */
|
|
382
|
+
function aggregateFieldRef(alias, arg) {
|
|
383
|
+
const [field, ...rest] = arg === '*' ? [arg] : namedKeys(arg);
|
|
384
|
+
if (field === undefined || rest.length) {
|
|
385
|
+
throw new TypeError(`aggregate '${alias}' takes one field as { field: true }, or '*': got ${JSON.stringify(arg)}`);
|
|
386
|
+
}
|
|
387
|
+
return field;
|
|
388
|
+
}
|
|
389
|
+
/** The keys a `{ key: true }` map switches on; anything that is not such a map switches on none. */
|
|
390
|
+
function namedKeys(map) {
|
|
391
|
+
return isRecord(map) ? getKeys(map).filter((key) => map[key]) : [];
|
|
392
|
+
}
|
|
385
393
|
/**
|
|
386
394
|
* Whether `value` is a map of comparison operators rather than a value to compare against. Only a
|
|
387
395
|
* plain object qualifies: `Date`, `QueryRaw`, `Uint8Array` and arrays are all `typeof 'object'`, and
|
|
@@ -410,11 +418,11 @@ export function assertNonNegativeInteger(value, clause) {
|
|
|
410
418
|
}
|
|
411
419
|
/**
|
|
412
420
|
* Rejects a `$having`/`$sort` key naming something an aggregate does not emit. Its rows are its
|
|
413
|
-
* `$group` columns and its `$
|
|
421
|
+
* `$group` columns and its `$select` aliases; anything else is a value that is not there. Shared so
|
|
414
422
|
* SQL and MongoDB refuse the same query with the same words.
|
|
415
423
|
*/
|
|
416
424
|
export function throwUnknownAggregateColumn(key, clause) {
|
|
417
|
-
throw new TypeError(`cannot ${clause} by '${key}': it is neither a $group column nor
|
|
425
|
+
throw new TypeError(`cannot ${clause} by '${key}': it is neither a $group column nor a $select alias`);
|
|
418
426
|
}
|
|
419
427
|
/** {@link throwUnknownAggregateColumn} over every key of a clause, for backends that check up front. */
|
|
420
428
|
export function assertAggregateColumns(clauseMap, emitted, clause) {
|
|
@@ -430,8 +438,9 @@ export function assertAggregateColumns(clauseMap, emitted, clause) {
|
|
|
430
438
|
* where neither says, rather than guessed: every engine answers a guess with an error of its own.
|
|
431
439
|
*/
|
|
432
440
|
export function textSearchFields(meta, search) {
|
|
433
|
-
|
|
434
|
-
|
|
441
|
+
const named = namedKeys(search.$fields);
|
|
442
|
+
if (named.length) {
|
|
443
|
+
return named;
|
|
435
444
|
}
|
|
436
445
|
const fulltext = (meta.indexes ?? []).filter((index) => index.type === 'fulltext');
|
|
437
446
|
if (fulltext.length === 1) {
|
|
@@ -22,6 +22,11 @@ export declare const COLUMN_TYPES_BY_FAMILY: {
|
|
|
22
22
|
};
|
|
23
23
|
/** The family of a logical field type, or `undefined` where it names none. */
|
|
24
24
|
export declare function columnFamily(type: unknown): ColumnFamily | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Whether a field's column holds whole numbers: a declared integer type, or a `Number` or `BigInt`
|
|
27
|
+
* with no scale, which every engine here stores as BIGINT.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isIntegerColumn(field: Pick<FieldOptions, 'type' | 'columnType' | 'precision' | 'scale'>): boolean;
|
|
25
30
|
/**
|
|
26
31
|
* Whether the field's expression is spliced into each statement that reads it, rather than stored.
|
|
27
32
|
* Every read site asks this - the DDL skip, the projection, the `$where` and `ORDER BY` operands -
|
package/dist/util/field.util.js
CHANGED
|
@@ -46,6 +46,25 @@ for (const family of getKeys(COLUMN_TYPES_BY_FAMILY)) {
|
|
|
46
46
|
export function columnFamily(type) {
|
|
47
47
|
return FAMILY_OF.get(typeof type === 'string' ? type.toLowerCase() : type);
|
|
48
48
|
}
|
|
49
|
+
/** The numeric column types that hold whole numbers. */
|
|
50
|
+
const INTEGER_COLUMN_TYPES = new Set([
|
|
51
|
+
'int',
|
|
52
|
+
'integer',
|
|
53
|
+
'tinyint',
|
|
54
|
+
'smallint',
|
|
55
|
+
'bigint',
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Whether a field's column holds whole numbers: a declared integer type, or a `Number` or `BigInt`
|
|
59
|
+
* with no scale, which every engine here stores as BIGINT.
|
|
60
|
+
*/
|
|
61
|
+
export function isIntegerColumn(field) {
|
|
62
|
+
const type = field.columnType ?? field.type;
|
|
63
|
+
if (typeof type === 'string') {
|
|
64
|
+
return INTEGER_COLUMN_TYPES.has(type.toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
return type === BigInt || (type === Number && !field.precision && !field.scale);
|
|
67
|
+
}
|
|
49
68
|
/**
|
|
50
69
|
* Whether the field's expression is spliced into each statement that reads it, rather than stored.
|
|
51
70
|
* Every read site asks this - the DDL skip, the projection, the `$where` and `ORDER BY` operands -
|
|
@@ -19,6 +19,8 @@ export declare function someValue(obj: object, pred: (value: unknown) => boolean
|
|
|
19
19
|
export declare function isOperatorObject(value: unknown): value is Record<string, unknown>;
|
|
20
20
|
/** Whether every key of the non-empty object `value` is an operator (no plain field names mixed in). */
|
|
21
21
|
export declare function isOperatorOnlyObject(value: unknown): value is Record<string, unknown>;
|
|
22
|
+
/** Whether `value` is an object that is not an array, whose keys can be read. */
|
|
23
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
22
24
|
export declare function getKeys<T extends object>(obj: T): (keyof T & string)[];
|
|
23
25
|
/** The entries of `record` holding a value: a key declared but left `undefined` is no entry at all. */
|
|
24
26
|
export declare function definedEntries<K extends string, V>(record: Partial<Record<K, V>>): [K, V][];
|
package/dist/util/object.util.js
CHANGED
|
@@ -49,6 +49,10 @@ export function isOperatorObject(value) {
|
|
|
49
49
|
export function isOperatorOnlyObject(value) {
|
|
50
50
|
return hasKeys(value) && !Array.isArray(value) && !someKey(value, (key) => !isOperatorKey(key));
|
|
51
51
|
}
|
|
52
|
+
/** Whether `value` is an object that is not an array, whose keys can be read. */
|
|
53
|
+
export function isRecord(value) {
|
|
54
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
55
|
+
}
|
|
52
56
|
export function getKeys(obj) {
|
|
53
57
|
return obj ? Object.keys(obj) : [];
|
|
54
58
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityMeta,
|
|
1
|
+
import type { EntityMeta, QueryCount, QueryPopulate, RelationKey, RelationMeta, RelationQuery, QueryWhere } from '../type/index.js';
|
|
2
2
|
export type RelationRequestSummary<E> = {
|
|
3
3
|
readonly requestedKeys: readonly RelationKey<E>[];
|
|
4
4
|
readonly joinableKeys: readonly RelationKey<E>[];
|
|
@@ -32,67 +32,10 @@ export declare function parentJoins(relOpts: Pick<RelationMeta, 'references' | '
|
|
|
32
32
|
* which is the mistake this module exists to prevent.
|
|
33
33
|
*/
|
|
34
34
|
export declare function targetKeyColumns(relOpts: Pick<RelationMeta, 'references'>, parentKeyCount: number): string[];
|
|
35
|
-
/** `{ joined column: true }`: the projection or grouping that keeps a parent's key on the rows read. */
|
|
36
|
-
export declare function joinedColumns(joins: readonly ParentJoin[]): Record<string, true>;
|
|
37
|
-
/**
|
|
38
|
-
* One side's columns: `'parent'` for the columns a parent is keyed by, `'joined'` for the ones a
|
|
39
|
-
* child or tally row carries that key in. Matching the two halves means agreeing on every column, so
|
|
40
|
-
* each side is read through `joins` rather than through the parent's own key list - the same set only
|
|
41
|
-
* for a to-many, and silently a different one otherwise. `keyof ParentJoin` is what keeps the two
|
|
42
|
-
* sides from being spelled apart.
|
|
43
|
-
*
|
|
44
|
-
* Lifted out of `joins` once per relation, not once per row: {@link rowKey} takes the list and reads
|
|
45
|
-
* each row itself, so a page of children costs one key each and nothing else.
|
|
46
|
-
*/
|
|
47
|
-
export declare function keyColumns(joins: readonly ParentJoin[], side: keyof ParentJoin): string[];
|
|
48
|
-
/**
|
|
49
|
-
* `{ joined column: every parent's value for it }`, the filter that fetches a whole page of parents'
|
|
50
|
-
* children in one statement.
|
|
51
|
-
*
|
|
52
|
-
* A composite over-selects, because the lists are independent and a pairing no parent has can still
|
|
53
|
-
* match. Regrouping the rows keys on every column, so those rows find no parent and are dropped -
|
|
54
|
-
* cheaper than the row-value comparison no engine spells the same way.
|
|
55
|
-
*/
|
|
56
|
-
export declare function parentsIn(joins: readonly ParentJoin[], parents: readonly unknown[]): Record<string, unknown[]>;
|
|
57
|
-
/**
|
|
58
|
-
* The parents a bounded to-many read fans out over: the rows themselves, the columns matching them to
|
|
59
|
-
* their children.
|
|
60
|
-
*/
|
|
61
|
-
export type ParentPartition = {
|
|
62
|
-
readonly joins: readonly ParentJoin[];
|
|
63
|
-
readonly parents: readonly unknown[];
|
|
64
|
-
/** The parent's own fields: a `LATERAL` row source has to spell its key column's type. */
|
|
65
|
-
readonly parentFields: Readonly<Record<string, FieldMeta | undefined>>;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* Whether a to-many's own query asks for a share *per parent* rather than a slice of the whole page.
|
|
69
|
-
* Only `$limit`/`$skip` do: without one, a single flat statement over an `IN (...)` list is both
|
|
70
|
-
* correct and cheaper.
|
|
71
|
-
*/
|
|
72
|
-
export declare function isBoundedPerParent(query: Pick<RelationQuery, '$limit' | '$skip'>): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* `query` narrowed to one parent's children: what a single branch of a bounded per-parent read asks
|
|
75
|
-
* for. Shared by the backends so how the parent's filter merges into the relation's own is decided
|
|
76
|
-
* once - both spelled it out, and a rule that ever needs more than a spread would have to change twice.
|
|
77
|
-
*/
|
|
78
|
-
export declare function queryChildrenOf<E>(query: Query<E>, joins: readonly ParentJoin[], parent: unknown): Query<E>;
|
|
79
|
-
/**
|
|
80
|
-
* `query` narrowed to the children of a whole page of parents, which is the flat read a relation with
|
|
81
|
-
* no share of its own takes. Over-selects on a composite key exactly as {@link parentsIn} does.
|
|
82
|
-
*/
|
|
83
|
-
export declare function queryChildrenOfAll<E>(query: Query<E>, joins: readonly ParentJoin[], parents: readonly unknown[]): Query<E>;
|
|
84
|
-
/**
|
|
85
|
-
* `query` with `filter` merged into its own `$where`: the one rule for narrowing a relation's query to
|
|
86
|
-
* the parents it is being read for, whether the filter names their keys as values or, for a correlated
|
|
87
|
-
* shape, as a reference to a row source.
|
|
88
|
-
*/
|
|
89
|
-
export declare function queryNarrowedTo<E>(query: Query<E>, filter: Record<string, unknown>): Query<E>;
|
|
90
35
|
/**
|
|
91
36
|
* The `$where` naming exactly the children of the rows `parentIds` identifies: an `IN` over the one
|
|
92
|
-
* column a single key contributes, an OR of key maps for several
|
|
93
|
-
*
|
|
94
|
-
* Exact, unlike {@link parentsIn}: a read absorbs over-selection by regrouping its rows, and a write
|
|
95
|
-
* has nothing to regroup - a pairing no parent has would delete a child of a parent that survives.
|
|
37
|
+
* column a single key contributes, an OR of whole key maps for several - lists of each column apart
|
|
38
|
+
* would pair values no parent has, and a delete would take a child of a parent that survives.
|
|
96
39
|
*/
|
|
97
40
|
export declare function childrenOf(joins: readonly ParentJoin[], parentIds: readonly unknown[]): Record<string, unknown>;
|
|
98
41
|
/**
|
|
@@ -107,11 +50,15 @@ export type JoinedRelationRejectedKey = (typeof JOINED_RELATION_REJECTIONS)[numb
|
|
|
107
50
|
export declare function getRelationRequestSummary<E>(meta: EntityMeta<E>, populate?: QueryPopulate<E>): RelationRequestSummary<E>;
|
|
108
51
|
/** True when `$populate` includes at least one relation key. */
|
|
109
52
|
export declare function populatesRelations<E>(meta: EntityMeta<E>, populate?: QueryPopulate<E>): boolean;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Each relation a `$count` tallies, and the filter narrowing what it counts: the target's, whose type
|
|
55
|
+
* only the metadata knows this far down, as for a relation filter reaching the same subquery.
|
|
56
|
+
*/
|
|
57
|
+
export declare function countedRelations<E>(meta: EntityMeta<E>, counts: QueryCount<E> | undefined): {
|
|
58
|
+
readonly relKey: RelationKey<E>;
|
|
59
|
+
readonly relation: RelationMeta;
|
|
60
|
+
readonly where: QueryWhere<object>;
|
|
61
|
+
}[];
|
|
115
62
|
export type ParsedRelationQuery<E extends object = object> = {
|
|
116
63
|
query: RelationQuery<E>;
|
|
117
64
|
required: boolean;
|