uql-orm 0.65.1 → 0.67.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/dist/browser/querier/httpQuerier.js +1 -8
- package/dist/browser/uql-browser.min.js.map +5 -5
- package/dist/bunSql/bunSql.util.d.ts +2 -6
- package/dist/bunSql/bunSql.util.js +2 -6
- package/dist/bunSql/bunSqlQuerier.d.ts +2 -5
- package/dist/bunSql/bunSqlQuerier.js +2 -5
- package/dist/cockroachdb/cockroachDialect.d.ts +4 -13
- package/dist/cockroachdb/cockroachDialect.js +4 -13
- package/dist/context/context.browser.js +2 -10
- package/dist/context/context.d.ts +4 -17
- package/dist/context/context.js +4 -17
- package/dist/dialect/abstractDialect.d.ts +4 -19
- package/dist/dialect/abstractDialect.js +2 -20
- package/dist/dialect/abstractSqlDialect.d.ts +47 -212
- package/dist/dialect/abstractSqlDialect.js +68 -222
- package/dist/dialect/aliases.d.ts +2 -12
- package/dist/dialect/aliases.js +4 -12
- package/dist/dialect/hydrateColumn.d.ts +2 -6
- package/dist/dialect/hydrateColumn.js +3 -13
- package/dist/dialect/jsonArrayElemMatchUtils.d.ts +1 -7
- package/dist/dialect/jsonArrayElemMatchUtils.js +1 -7
- package/dist/dialect/jsonSql.d.ts +6 -27
- package/dist/dialect/jsonSql.js +6 -27
- package/dist/dialect/mergeSqlDialect.d.ts +4 -22
- package/dist/dialect/mergeSqlDialect.js +4 -22
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -37
- package/dist/dialect/mysqlLikeSqlDialect.js +35 -51
- package/dist/dialect/pgLikeSqlDialect.d.ts +8 -22
- package/dist/dialect/pgLikeSqlDialect.js +36 -39
- package/dist/dialect/queryContext.d.ts +4 -22
- package/dist/dialect/queryContext.js +4 -22
- package/dist/dialect/queryJoins.d.ts +3 -12
- package/dist/dialect/queryJoins.js +3 -12
- package/dist/dialect/vectorCast.d.ts +2 -12
- package/dist/dialect/vectorCast.js +3 -19
- package/dist/dialect/vectorSqlDialect.d.ts +8 -38
- package/dist/dialect/vectorSqlDialect.js +7 -38
- package/dist/entity/decorator/bag.d.ts +6 -19
- package/dist/entity/decorator/bag.js +6 -22
- package/dist/entity/decorator/entity.d.ts +5 -10
- package/dist/entity/decorator/entity.js +2 -7
- package/dist/entity/decorator/members.d.ts +10 -31
- package/dist/entity/decorator/members.js +3 -12
- package/dist/entity/metadata/definition.d.ts +5 -21
- package/dist/entity/metadata/definition.js +69 -91
- package/dist/http/handler.d.ts +2 -14
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/libsql/libsqlDialect.d.ts +1 -8
- package/dist/libsql/libsqlDialect.js +1 -8
- package/dist/maria/mariaDialect.d.ts +3 -5
- package/dist/maria/mariaDialect.js +5 -5
- package/dist/maria/mariadbQuerier.js +2 -2
- package/dist/maria/mariadbQuerierPool.js +1 -6
- package/dist/migrate/builder/migrationBuilder.js +3 -19
- package/dist/migrate/builder/splitSqlStatements.d.ts +1 -14
- package/dist/migrate/builder/splitSqlStatements.js +2 -22
- package/dist/migrate/builder/types.d.ts +2 -15
- package/dist/migrate/cli-config.js +2 -11
- package/dist/migrate/cli.js +2 -7
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -15
- package/dist/migrate/codegen/entityCodeGenerator.js +15 -44
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -8
- package/dist/migrate/codegen/fieldOptionsSource.js +3 -22
- package/dist/migrate/ddl/indexDdl.d.ts +2 -5
- package/dist/migrate/ddl/indexDdl.js +2 -5
- package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -13
- package/dist/migrate/ddl/pgIndexDdl.js +3 -13
- package/dist/migrate/generator/definitionToNode.d.ts +2 -9
- package/dist/migrate/generator/definitionToNode.js +3 -17
- package/dist/migrate/generator/indexNodeToSchema.d.ts +2 -3
- package/dist/migrate/generator/indexNodeToSchema.js +2 -3
- package/dist/migrate/generator/mongoCommand.d.ts +1 -8
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -8
- package/dist/migrate/generator/mongoSchemaGenerator.js +1 -8
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +6 -26
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +9 -41
- package/dist/migrate/introspection/baseSqlIntrospector.js +0 -1
- package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
- package/dist/migrate/introspection/mongoIntrospector.js +48 -46
- package/dist/migrate/introspection/mssqlIntrospector.d.ts +4 -4
- package/dist/migrate/introspection/mssqlIntrospector.js +18 -27
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +7 -2
- package/dist/migrate/introspection/mysqlIntrospector.js +16 -14
- package/dist/migrate/introspection/postgresIntrospector.d.ts +24 -9
- package/dist/migrate/introspection/postgresIntrospector.js +68 -59
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -1
- package/dist/migrate/introspection/sqliteIntrospector.js +8 -10
- package/dist/migrate/migrator.d.ts +9 -53
- package/dist/migrate/migrator.js +32 -65
- package/dist/migrate/schemaGenerator.d.ts +20 -66
- package/dist/migrate/schemaGenerator.js +32 -93
- package/dist/mongo/mongoDialect.d.ts +21 -53
- package/dist/mongo/mongoDialect.js +25 -70
- package/dist/mongo/mongodbQuerier.d.ts +5 -8
- package/dist/mongo/mongodbQuerier.js +31 -65
- package/dist/mssql/mssqlDialect.d.ts +8 -34
- package/dist/mssql/mssqlDialect.js +37 -51
- package/dist/mssql/mssqlQuerier.d.ts +37 -4
- package/dist/mssql/mssqlQuerier.js +2 -2
- package/dist/mssql/mssqlWireTypes.d.ts +2 -14
- package/dist/mssql/mssqlWireTypes.js +2 -14
- package/dist/nestjs/uqlModule.js +2 -7
- package/dist/pglite/pgliteQuerier.d.ts +1 -9
- package/dist/pglite/pgliteQuerierPool.d.ts +4 -26
- package/dist/pglite/pgliteQuerierPool.js +3 -18
- package/dist/postgres/abstractPgQuerierPool.d.ts +1 -8
- package/dist/postgres/abstractPgQuerierPool.js +1 -8
- package/dist/postgres/pgNumericTypes.d.ts +3 -26
- package/dist/postgres/pgNumericTypes.js +3 -26
- package/dist/postgres/postgresDialect.d.ts +4 -10
- package/dist/postgres/postgresDialect.js +4 -10
- package/dist/querier/abstractQuerier.d.ts +35 -103
- package/dist/querier/abstractQuerier.js +105 -201
- package/dist/querier/abstractSharedHandleQuerierPool.d.ts +3 -17
- package/dist/querier/abstractSharedHandleQuerierPool.js +3 -17
- package/dist/querier/abstractSqlQuerier.d.ts +15 -36
- package/dist/querier/abstractSqlQuerier.js +49 -131
- package/dist/schema/canonicalType.d.ts +3 -21
- package/dist/schema/canonicalType.js +22 -67
- package/dist/schema/dependencyGraph.d.ts +2 -8
- package/dist/schema/dependencyGraph.js +2 -32
- package/dist/schema/index.d.ts +1 -25
- package/dist/schema/index.js +0 -26
- package/dist/schema/indexColumns.d.ts +1 -8
- package/dist/schema/indexColumns.js +1 -8
- package/dist/schema/indexDifferences.d.ts +7 -40
- package/dist/schema/indexDifferences.js +6 -31
- package/dist/schema/schemaAST.d.ts +8 -175
- package/dist/schema/schemaAST.js +13 -365
- package/dist/schema/schemaASTBuilder.d.ts +2 -24
- package/dist/schema/schemaASTBuilder.js +6 -41
- package/dist/schema/schemaASTDiffer.d.ts +6 -46
- package/dist/schema/schemaASTDiffer.js +8 -56
- package/dist/schema/types.d.ts +5 -61
- package/dist/schema/types.js +3 -6
- package/dist/sqlite/abstractSqliteQuerier.d.ts +1 -8
- package/dist/sqlite/localSqliteQuerierPool.d.ts +1 -7
- package/dist/sqlite/localSqliteQuerierPool.js +1 -7
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -7
- package/dist/sqlite/nodeSqliteQuerierPool.js +2 -7
- package/dist/sqlite/sqliteDialect.d.ts +5 -20
- package/dist/sqlite/sqliteDialect.js +29 -35
- package/dist/turso/tursoDialect.d.ts +4 -6
- package/dist/turso/tursoDialect.js +4 -6
- package/dist/turso/tursoLocalQuerierPool.d.ts +1 -7
- package/dist/turso/tursoLocalQuerierPool.js +1 -7
- package/dist/turso/tursoQuerierPool.d.ts +2 -6
- package/dist/turso/tursoQuerierPool.js +2 -6
- package/dist/turso/tursoSessionQuerier.d.ts +1 -7
- package/dist/turso/tursoSessionQuerier.js +1 -7
- package/dist/type/dialect.d.ts +42 -94
- package/dist/type/dialect.js +3 -13
- package/dist/type/entity.d.ts +189 -551
- package/dist/type/entity.js +26 -9
- package/dist/type/logger.d.ts +2 -14
- package/dist/type/migration.d.ts +9 -38
- package/dist/type/querier.d.ts +9 -28
- package/dist/type/querierPool.d.ts +4 -26
- package/dist/type/query.d.ts +28 -78
- package/dist/type/query.js +2 -7
- package/dist/type/queryAggregate.d.ts +18 -98
- package/dist/type/queryRaw.d.ts +1 -8
- package/dist/type/queryRaw.js +1 -8
- package/dist/type/queryWhere.d.ts +13 -61
- package/dist/type/universalQuerier.d.ts +18 -105
- package/dist/type/utility.d.ts +12 -24
- package/dist/type/vector.d.ts +8 -38
- package/dist/type/vector.js +1 -1
- package/dist/type/wire.d.ts +2 -5
- package/dist/util/dialect.util.d.ts +9 -27
- package/dist/util/dialect.util.js +10 -27
- package/dist/util/field.util.d.ts +5 -37
- package/dist/util/field.util.js +7 -50
- package/dist/util/fieldOption.util.d.ts +7 -15
- package/dist/util/fieldOption.util.js +1 -1
- package/dist/util/filters.util.d.ts +2 -5
- package/dist/util/filters.util.js +2 -5
- package/dist/util/logger.d.ts +2 -6
- package/dist/util/logger.js +2 -6
- package/dist/util/object.util.d.ts +2 -6
- package/dist/util/object.util.js +1 -5
- package/dist/util/raw.d.ts +3 -23
- package/dist/util/relationQuery.util.d.ts +3 -14
- package/dist/util/relationQuery.util.js +3 -14
- package/dist/util/rowKey.util.d.ts +2 -10
- package/dist/util/rowKey.util.js +2 -10
- package/dist/util/sql.util.d.ts +6 -37
- package/dist/util/sql.util.js +13 -73
- package/dist/util/sqlLiteral.d.ts +2 -13
- package/dist/util/sqlLiteral.js +8 -13
- package/dist/util/string.util.js +0 -2
- package/package.json +4 -4
package/dist/type/entity.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Allow to customize the name of the property that identifies an entity
|
|
3
|
-
*/
|
|
1
|
+
/** Brands the property an entity is identified by, where it is not `id`, `_id` or `uuid`. */
|
|
4
2
|
export const idKey = Symbol('idKey');
|
|
5
|
-
/**
|
|
6
|
-
* The one filter name uql registers itself, from `@Field({ softDelete })`. Four ends have to agree on
|
|
7
|
-
* it and none would fail if they drifted: the field that registers it, the decorator that reserves
|
|
8
|
-
* the name against a user's own filter, the hard delete that switches it off, and the bypass check
|
|
9
|
-
* that lets it through on an entity which never declared one.
|
|
10
|
-
*/
|
|
3
|
+
/** The filter `@Field({ softDelete })` registers, a name reserved against an entity's own filters. */
|
|
11
4
|
export const SOFT_DELETE_FILTER = 'softDelete';
|
|
5
|
+
/** Every SQL column type a field may declare, by family: the unions below and `columnFamily` both read it. */
|
|
6
|
+
export const COLUMN_TYPES = {
|
|
7
|
+
numeric: [
|
|
8
|
+
'int',
|
|
9
|
+
'integer',
|
|
10
|
+
'tinyint',
|
|
11
|
+
'smallint',
|
|
12
|
+
'bigint',
|
|
13
|
+
'float',
|
|
14
|
+
'float4',
|
|
15
|
+
'float8',
|
|
16
|
+
'double',
|
|
17
|
+
'double precision',
|
|
18
|
+
'decimal',
|
|
19
|
+
'numeric',
|
|
20
|
+
'real',
|
|
21
|
+
],
|
|
22
|
+
string: ['char', 'varchar', 'text', 'uuid'],
|
|
23
|
+
date: ['date', 'time', 'datetime', 'timestamp', 'timestamptz'],
|
|
24
|
+
json: ['json', 'jsonb'],
|
|
25
|
+
blob: ['blob', 'bytea'],
|
|
26
|
+
boolean: ['bool', 'boolean'],
|
|
27
|
+
vector: ['vector', 'halfvec', 'sparsevec'],
|
|
28
|
+
};
|
package/dist/type/logger.d.ts
CHANGED
|
@@ -13,13 +13,7 @@ export interface Logger {
|
|
|
13
13
|
* @param duration - The time it took to execute the query in milliseconds.
|
|
14
14
|
*/
|
|
15
15
|
logQuery?(query: string, values?: unknown[], duration?: number): void;
|
|
16
|
-
/**
|
|
17
|
-
* Logs a slow query.
|
|
18
|
-
* @param query - The SQL query string.
|
|
19
|
-
* @param values - The parameters passed to the query (already redacted to `undefined`
|
|
20
|
-
* upstream, per `ExtraOptions.logValues`, when values shouldn't be logged).
|
|
21
|
-
* @param duration - The time it took to execute the query in milliseconds.
|
|
22
|
-
*/
|
|
16
|
+
/** Logs a query that took longer than the threshold, its values `undefined` unless `logValues` is on. */
|
|
23
17
|
logSlowQuery?(query: string, values?: unknown[], duration?: number): void;
|
|
24
18
|
/**
|
|
25
19
|
* Logs a warning.
|
|
@@ -50,11 +44,5 @@ export interface Logger {
|
|
|
50
44
|
* Function type for backward compatibility with simple loggers.
|
|
51
45
|
*/
|
|
52
46
|
export type LoggerFunction = (message: unknown, ...args: unknown[]) => void;
|
|
53
|
-
/**
|
|
54
|
-
* Options for configuring ORM logging.
|
|
55
|
-
* - boolean: true to enable all logs with DefaultLogger, false to disable.
|
|
56
|
-
* - LogLevel[]: enable specific log levels with DefaultLogger.
|
|
57
|
-
* - Logger: use a custom logger implementation.
|
|
58
|
-
* - LoggerFunction: use a custom function (backward compatibility).
|
|
59
|
-
*/
|
|
47
|
+
/** How logging is configured: on or off, the levels to log, or a logger of your own. */
|
|
60
48
|
export type LoggingOptions = boolean | LogLevel[] | Logger | LoggerFunction;
|
package/dist/type/migration.d.ts
CHANGED
|
@@ -94,21 +94,11 @@ export interface MigrationResult {
|
|
|
94
94
|
readonly success: boolean;
|
|
95
95
|
readonly error?: Error;
|
|
96
96
|
}
|
|
97
|
-
/**
|
|
98
|
-
* A column as a statement describes one: {@link ColumnNode} with the engine's type spelling in place
|
|
99
|
-
* of the canonical one, and without the graph links.
|
|
100
|
-
*
|
|
101
|
-
* Derived so a field the node gains reaches every path that renders a column. Listed field by field,
|
|
102
|
-
* this dropped `enum` and then `generatedAs`, and a column added to an existing table arrived without
|
|
103
|
-
* the constraint or the expression the entity declared.
|
|
104
|
-
*/
|
|
97
|
+
/** A column as a statement renders one: a {@link ColumnNode} with the engine's type spelling and no graph links. */
|
|
105
98
|
export interface ColumnSchema extends Omit<ColumnNode, 'type' | 'table' | 'referencedBy' | 'references'> {
|
|
106
99
|
/**
|
|
107
|
-
* The engine's own type spelling,
|
|
108
|
-
*
|
|
109
|
-
* and several canonical types share one storage type per engine - an entity `boolean` is `TINYINT(1)`
|
|
110
|
-
* on MySQL and `INTEGER` on SQLite. Comparing canonical categories instead reports an alteration on
|
|
111
|
-
* every sync for those columns. Use `sqlToCanonical` to interpret it.
|
|
100
|
+
* The engine's own type spelling, `TINYINT(1)`, compared as stored: canonical types would differ where
|
|
101
|
+
* the engine stores them alike. `sqlToCanonical` reads it.
|
|
112
102
|
*/
|
|
113
103
|
readonly type: string;
|
|
114
104
|
/** Bounds introspection reports beside the type, where the engine states them separately. */
|
|
@@ -193,11 +183,8 @@ export interface SchemaDiff {
|
|
|
193
183
|
readonly schema?: string;
|
|
194
184
|
readonly type: 'create' | 'alter' | 'drop';
|
|
195
185
|
/**
|
|
196
|
-
* The
|
|
197
|
-
*
|
|
198
|
-
* Compared by columns, never by name: the engine named the existing one, so requiring a derived
|
|
199
|
-
* name to match would rewrite the primary key of every table on the first migration after
|
|
200
|
-
* upgrading. `fromName` is what the database reported, and the only name a `DROP` can use.
|
|
186
|
+
* The table's key against the entity's, where their columns differ; `fromName` is the name the
|
|
187
|
+
* database reported, which is what a `DROP` needs.
|
|
201
188
|
*/
|
|
202
189
|
readonly primaryKey?: {
|
|
203
190
|
readonly from: string[];
|
|
@@ -259,13 +246,7 @@ export interface DropSchemaOptions {
|
|
|
259
246
|
* Interface for generating DDL statements from entity metadata
|
|
260
247
|
*/
|
|
261
248
|
export interface SchemaGenerator {
|
|
262
|
-
/**
|
|
263
|
-
* The whole schema for `entities`: every table, then the foreign keys between them.
|
|
264
|
-
*
|
|
265
|
-
* There is deliberately no per-entity counterpart. One entity means an AST holding one table, so every
|
|
266
|
-
* cross-entity foreign key has nothing to resolve against and is dropped: all three call sites that
|
|
267
|
-
* used to work that way emitted schemas with no referential integrity.
|
|
268
|
-
*/
|
|
249
|
+
/** The whole schema for `entities`, tables then the foreign keys between them, which need every entity at once. */
|
|
269
250
|
generateCreateSchema(entities: readonly Type<object>[], options?: CreateSchemaOptions): string[];
|
|
270
251
|
/**
|
|
271
252
|
* Every `DROP TABLE` for `entities`, dependents first. The inverse of {@link generateCreateSchema},
|
|
@@ -307,21 +288,11 @@ export interface SchemaGenerator {
|
|
|
307
288
|
*/
|
|
308
289
|
compileIndexPredicate(where: EntityWhereMeta<object>, entity: Type<object>, indexName: string): string;
|
|
309
290
|
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* `desiredAst` is the entity side, from {@link buildAST}, and must span every entity a foreign key
|
|
313
|
-
* on this table points at: a relation whose target is absent resolves to nothing, so the constraint
|
|
314
|
-
* reads as missing from both sides, which is a match and no statement. Defaults to this entity
|
|
315
|
-
* alone, which is right only where it has no relations.
|
|
291
|
+
* An entity's differences from its table. `desiredAst`, from {@link buildAST}, has to span every entity
|
|
292
|
+
* a foreign key here points at, or those keys read as matching.
|
|
316
293
|
*/
|
|
317
294
|
diffSchema(entity: Type<object>, currentTable: TableNode | undefined, desiredAst?: SchemaAST): SchemaDiff | undefined;
|
|
318
|
-
/**
|
|
319
|
-
* The entity side as an AST, to hand to every {@link diffSchema} of one run - building it per
|
|
320
|
-
* entity instead is quadratic in the number of entities.
|
|
321
|
-
*
|
|
322
|
-
* Optional because not every generator compares one: MongoDB has no foreign keys and diffs only
|
|
323
|
-
* indexes, so it neither implements this nor reads the argument.
|
|
324
|
-
*/
|
|
295
|
+
/** The entities as one AST, built once per run for every {@link diffSchema}. Absent on MongoDB, which diffs only indexes. */
|
|
325
296
|
buildAST?(entities: readonly Type<object>[]): SchemaAST;
|
|
326
297
|
/**
|
|
327
298
|
* The table's key: {@link resolveTableAlias} behind {@link resolveSchema}, which is how a
|
package/dist/type/querier.d.ts
CHANGED
|
@@ -16,11 +16,8 @@ export type IsolationLevel = 'read uncommitted' | 'read committed' | 'repeatable
|
|
|
16
16
|
*/
|
|
17
17
|
export type TransactionOptions = {
|
|
18
18
|
/**
|
|
19
|
-
* Applies to this transaction only.
|
|
20
|
-
*
|
|
21
|
-
* @remarks MySQL and MariaDB set it as a statement of its own ahead of `START TRANSACTION`, so a
|
|
22
|
-
* `START TRANSACTION` that then fails leaves the level applied to whatever the pooled connection
|
|
23
|
-
* runs next. Set it per transaction that needs it rather than relying on what a connection carries.
|
|
19
|
+
* Applies to this transaction only. The MySQL family sets it ahead of `START TRANSACTION`, where a
|
|
20
|
+
* failed start leaves it on the connection: set it per transaction that needs it.
|
|
24
21
|
*/
|
|
25
22
|
readonly isolationLevel?: IsolationLevel;
|
|
26
23
|
};
|
|
@@ -32,53 +29,37 @@ export type DialectName = SqlDialectName | 'mongodb';
|
|
|
32
29
|
* what makes a typo'd query key report as itself rather than as a missing `$entity`.
|
|
33
30
|
*/
|
|
34
31
|
export interface Querier extends UniversalQuerier {
|
|
35
|
-
/**
|
|
36
|
-
* Find one record. Supports both entity-as-argument and entity-as-field patterns.
|
|
37
|
-
*/
|
|
32
|
+
/** Find one record, the entity passed first or as the query's `$entity`. */
|
|
38
33
|
findOne<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>(q: QueryOneProjected<E, S, V, X, P, C> & {
|
|
39
34
|
$entity: Type<E>;
|
|
40
35
|
}, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C> | undefined>;
|
|
41
36
|
findOne<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: QueryOneProjected<E, S, V, X, P, C>, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C> | undefined>;
|
|
42
|
-
/**
|
|
43
|
-
* Find many records. Supports both entity-as-argument and entity-as-field patterns.
|
|
44
|
-
*/
|
|
37
|
+
/** Find many records, the entity passed first or as the query's `$entity`. */
|
|
45
38
|
findMany<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>(q: QueryProjected<E, S, V, X, P, C> & {
|
|
46
39
|
$entity: Type<E>;
|
|
47
40
|
}, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C>[]>;
|
|
48
41
|
findMany<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): Promise<QueryFindResult<E, S, V, X, P, C>[]>;
|
|
49
|
-
/**
|
|
50
|
-
* Stream records as an async iterable, in both patterns, each with the relations and counts
|
|
51
|
-
* `findMany` reads. Fires no lifecycle hooks.
|
|
52
|
-
*/
|
|
42
|
+
/** Stream records with the relations and counts `findMany` reads, the entity passed first or as `$entity`. No hooks fire. */
|
|
53
43
|
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>(q: QueryProjected<E, S, V, X, P, C> & {
|
|
54
44
|
$entity: Type<E>;
|
|
55
45
|
}, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
|
|
56
46
|
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>>;
|
|
57
|
-
/**
|
|
58
|
-
* Find many records and count. Supports both patterns.
|
|
59
|
-
*/
|
|
47
|
+
/** Find many records and count every match, the entity passed first or as the query's `$entity`. */
|
|
60
48
|
findManyAndCount<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>(q: QueryProjected<E, S, V, X, P, C> & {
|
|
61
49
|
$entity: Type<E>;
|
|
62
50
|
}, opts?: QueryOptions): Promise<[QueryFindResult<E, S, V, X, P, C>[], number]>;
|
|
63
51
|
findManyAndCount<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): Promise<[QueryFindResult<E, S, V, X, P, C>[], number]>;
|
|
64
|
-
/**
|
|
65
|
-
* Count records. Supports both patterns.
|
|
66
|
-
*/
|
|
52
|
+
/** Count records, the entity passed first or as the query's `$entity`. */
|
|
67
53
|
count<E extends object>(q: QueryPage<E> & {
|
|
68
54
|
$entity: Type<E>;
|
|
69
55
|
}, opts?: QueryOptions): Promise<number>;
|
|
70
56
|
count<E extends object>(entity: Type<E>, q?: QueryPage<E>, opts?: QueryOptions): Promise<number>;
|
|
71
|
-
/**
|
|
72
|
-
* Whether anything matches. Supports both patterns.
|
|
73
|
-
*/
|
|
57
|
+
/** Whether anything matches, the entity passed first or as the query's `$entity`. */
|
|
74
58
|
exists<E extends object>(q: QueryFilter<E> & {
|
|
75
59
|
$entity: Type<E>;
|
|
76
60
|
}, opts?: QueryOptions): Promise<boolean>;
|
|
77
61
|
exists<E extends object>(entity: Type<E>, q?: QueryFilter<E>, opts?: QueryOptions): Promise<boolean>;
|
|
78
|
-
/**
|
|
79
|
-
* Delete many records (soft-deletes when the entity has a soft-delete field, else removes them).
|
|
80
|
-
* Supports both entity-as-argument and entity-as-field patterns.
|
|
81
|
-
*/
|
|
62
|
+
/** Delete many records, the entity passed first or as `$entity`; soft-deletes where the entity has a soft-delete field. */
|
|
82
63
|
deleteMany<E extends object>(q: QuerySearch<E> & {
|
|
83
64
|
$entity: Type<E>;
|
|
84
65
|
}, opts?: QueryOptions): Promise<number>;
|
|
@@ -13,25 +13,9 @@ export interface PoolRunOptions {
|
|
|
13
13
|
readonly context?: UqlContext;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* caller passes its own querier or the pool. `pool.op(...)` is exactly
|
|
20
|
-
* `pool.withQuerier((querier) => querier.op(...))`, so two pool calls are two units of work; when they
|
|
21
|
-
* must commit together, that is `transaction`.
|
|
22
|
-
*
|
|
23
|
-
* Acquiring per call is also what makes `Promise.all([pool.findMany(A, {}), pool.count(B, {})])` run on
|
|
24
|
-
* separate connections, while the same calls inside one `withQuerier`/`transaction` share a pinned
|
|
25
|
-
* connection and serialize. Single-connection backends (better-sqlite3, Bun sqlite, D1) stay correct
|
|
26
|
-
* but always serialize.
|
|
27
|
-
*
|
|
28
|
-
* An enclosing `withContext` scopes pool calls (`security` filters apply), which is why they take no
|
|
29
|
-
* per-call `context` option (unlike `withQuerier`/`transaction`).
|
|
30
|
-
*
|
|
31
|
-
* Pool calls take the entity-as-argument form only; the `{ $entity }` form needs a querier.
|
|
32
|
-
*
|
|
33
|
-
* @typeParam Q - Querier implementation returned from the pool.
|
|
34
|
-
* @typeParam D - Concrete dialect class held by the pool.
|
|
16
|
+
* A pool of queriers, and a {@link UniversalQuerier} itself: each call runs on a querier of its own, so
|
|
17
|
+
* two calls are two units of work (use `transaction` to join them) and run in parallel where the backend
|
|
18
|
+
* has more than one connection. An enclosing `withContext` scopes its calls. The `{ $entity }` form needs a querier.
|
|
35
19
|
*/
|
|
36
20
|
export interface QuerierPool<Q extends Querier = Querier, D extends AbstractDialect = AbstractDialect> extends UniversalQuerier {
|
|
37
21
|
/**
|
|
@@ -66,13 +50,7 @@ export interface QuerierPool<Q extends Querier = Querier, D extends AbstractDial
|
|
|
66
50
|
*/
|
|
67
51
|
end(): Promise<void>;
|
|
68
52
|
}
|
|
69
|
-
/**
|
|
70
|
-
* SQL pool surface: adds the raw-SQL executors of {@link SqlQuerier} (`all`/`run`), with the same
|
|
71
|
-
* connection-per-call semantics as the {@link QuerierPool} read helpers (see that doc for the
|
|
72
|
-
* parallelism model and its single-connection caveat).
|
|
73
|
-
*
|
|
74
|
-
* Raw `all`/`run` bypass query generation, so they are **not** scoped by `security` filters/context.
|
|
75
|
-
*/
|
|
53
|
+
/** A SQL pool, adding raw `all`/`run`, which no `security` filter scopes. */
|
|
76
54
|
export interface SqlQuerierPool<Q extends SqlQuerier = SqlQuerier, D extends AbstractSqlDialect = AbstractSqlDialect> extends QuerierPool<Q, D>, Pick<SqlQuerier, 'all' | 'run'> {
|
|
77
55
|
}
|
|
78
56
|
/**
|
package/dist/type/query.d.ts
CHANGED
|
@@ -73,15 +73,8 @@ export type QueryPopulateRelationOptions<V> = IsMany<V> extends true ? RelationQ
|
|
|
73
73
|
$required?: boolean;
|
|
74
74
|
};
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* your keys once via declaration merging and get them typed wherever context is read:
|
|
79
|
-
*
|
|
80
|
-
* ```ts
|
|
81
|
-
* declare module 'uql-orm' {
|
|
82
|
-
* interface UqlContext { tenantId: number; userId: string }
|
|
83
|
-
* }
|
|
84
|
-
* ```
|
|
76
|
+
* The per-request context parameterized filters read, set with `withContext(ctx, cb)`. An interface,
|
|
77
|
+
* so its keys can be typed once: `declare module 'uql-orm' { interface UqlContext { tenantId: number } }`.
|
|
85
78
|
*/
|
|
86
79
|
export interface UqlContext {
|
|
87
80
|
[key: string]: unknown;
|
|
@@ -103,14 +96,18 @@ export type FilterOptions<E = unknown> = {
|
|
|
103
96
|
readonly where: FilterWhere<E>;
|
|
104
97
|
/** Applied to every query unless bypassed via `QueryOptions.filters`. Defaults to `true`. */
|
|
105
98
|
readonly default?: boolean;
|
|
99
|
+
} & ({
|
|
100
|
+
readonly security?: false;
|
|
101
|
+
/** What to do when {@link FilterOptions.where} returns `undefined`. Defaults to `skip`. */
|
|
102
|
+
readonly onMissing?: FilterOnMissing;
|
|
103
|
+
} | {
|
|
106
104
|
/**
|
|
107
105
|
* Row-level-security filter: always applied (ignores `QueryOptions.filters` bypass) and
|
|
108
|
-
* AND-merged so a client `$where` on the same field can't override it.
|
|
106
|
+
* AND-merged so a client `$where` on the same field can't override it. It fails closed.
|
|
109
107
|
*/
|
|
110
|
-
readonly security
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
};
|
|
108
|
+
readonly security: true;
|
|
109
|
+
readonly onMissing?: 'throw';
|
|
110
|
+
});
|
|
114
111
|
/**
|
|
115
112
|
* direction for the sort.
|
|
116
113
|
*/
|
|
@@ -136,16 +133,9 @@ export type QuerySortByCount = {
|
|
|
136
133
|
$count: QuerySortDirection;
|
|
137
134
|
};
|
|
138
135
|
/**
|
|
139
|
-
* sort by
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* the queried entity. A relation of it is joined in one row at a time, so there is nothing to rank
|
|
143
|
-
* there - the SQL dialects throw, and MongoDB would quietly drop it, so this is its only guard.
|
|
144
|
-
*
|
|
145
|
-
* One mapped type over the three key sets rather than three intersected. The sets are disjoint - a
|
|
146
|
-
* JSON path is dotted, and a field key cannot also be a relation key - and an assignability check
|
|
147
|
-
* against an intersection is repeated per constituent, which made this the single most expensive
|
|
148
|
-
* type in the package to check.
|
|
136
|
+
* A sort by fields, JSON paths, a to-one relation's fields, a to-many's `$count`, or a vector distance,
|
|
137
|
+
* which `Vector` confines to the queried entity. One mapped type over the key sets: an intersection is
|
|
138
|
+
* checked once per member, which made this the costliest type to check.
|
|
149
139
|
*/
|
|
150
140
|
export type QuerySortMap<E, Vector extends boolean = true, K extends keyof E = FieldKey<E> | RelationKey<E>> = {
|
|
151
141
|
[P in K]?: P extends RelationKey<E> ? IsMany<E[P]> extends true ? QuerySortByCount : QuerySortMap<RelationTarget<E[P]>, false> : Vector extends true ? NonNullable<E[P]> extends readonly number[] ? QuerySortValue : QuerySortDirection : QuerySortDirection;
|
|
@@ -225,25 +215,13 @@ export type Query<E> = {
|
|
|
225
215
|
*/
|
|
226
216
|
$distinct?: boolean;
|
|
227
217
|
/**
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* the rows, so it is rejected rather than emitted. Locks only the queried entity, never anything
|
|
231
|
-
* reached through `$populate`. SQL only; MongoDB and the SQLite family reject it.
|
|
232
|
-
*
|
|
233
|
-
* Declared here rather than on {@link QuerySearch}, which `update`/`delete` take: that placement
|
|
234
|
-
* is what keeps the clause off those statements at the type level.
|
|
218
|
+
* Lock the rows this query returns, `SELECT ... FOR UPDATE`, inside an open transaction: outside one
|
|
219
|
+
* it is refused, since the lock would drop before the rows are used. SQL only, and not the SQLite family.
|
|
235
220
|
*/
|
|
236
221
|
$lock?: QueryLock;
|
|
237
222
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* tuned for speed. Ignored where the search is exact (SQLite, libSQL and Turso scan every row) and
|
|
241
|
-
* where the field carries no ANN index, since there is nothing to widen.
|
|
242
|
-
*
|
|
243
|
-
* The units are the index's, not UQL's, so the number is not comparable across index types: it
|
|
244
|
-
* becomes `hnsw.ef_search` or `ivfflat.probes` on Postgres, `mhnsw_ef_search` on MariaDB, and
|
|
245
|
-
* `numCandidates` on MongoDB Atlas. On Postgres it needs an open transaction, since a `SET LOCAL`
|
|
246
|
-
* outside one applies to nothing.
|
|
223
|
+
* How many candidates an ANN index explores before ranking a vector search, in that index's own units
|
|
224
|
+
* (`hnsw.ef_search`, `numCandidates`...); ignored where the search is exact. Postgres needs a transaction.
|
|
247
225
|
*/
|
|
248
226
|
$candidates?: number;
|
|
249
227
|
/**
|
|
@@ -260,13 +238,8 @@ export type Query<E> = {
|
|
|
260
238
|
$limit?: number;
|
|
261
239
|
};
|
|
262
240
|
/**
|
|
263
|
-
* `Query`'s clauses grouped by the shape of their value
|
|
264
|
-
*
|
|
265
|
-
* Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build
|
|
266
|
-
* rather than the runtime if a clause is ever renamed.
|
|
267
|
-
*
|
|
268
|
-
* `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query
|
|
269
|
-
* accepts it.
|
|
241
|
+
* `Query`'s clauses grouped by the shape of their value, for the wire parser and the relation query
|
|
242
|
+
* check alike; `satisfies` keeps them in step with `Query`.
|
|
270
243
|
*/
|
|
271
244
|
export declare const QUERY_OBJECT_CLAUSES: readonly ["$select", "$populate", "$exclude", "$where", "$sort"];
|
|
272
245
|
/**
|
|
@@ -301,16 +274,8 @@ export type QueryOne<E> = Except<Query<E>, '$limit'>;
|
|
|
301
274
|
*/
|
|
302
275
|
export type QueryUnique<E> = Pick<QueryOne<E>, '$select' | '$exclude' | '$populate' | '$where'>;
|
|
303
276
|
/**
|
|
304
|
-
* The clauses that
|
|
305
|
-
*
|
|
306
|
-
* selecting, as it does at runtime, and a widened map is how a projection that is not statically
|
|
307
|
-
* known announces itself), and the relation names `$populate` lists.
|
|
308
|
-
*
|
|
309
|
-
* Each is captured as a *key set* rather than as the map itself, which is what keeps the checks
|
|
310
|
-
* intact: TypeScript skips excess-property checking on a naked type parameter, so a captured map
|
|
311
|
-
* would take a typo'd key without a word, while a captured key set makes that typo fail its own
|
|
312
|
-
* `FieldKey<E>` / `RelationKey<E>` constraint. Every other clause - `$where`, `$sort`, and each
|
|
313
|
-
* populated relation's own query - stays the concrete {@link Query} it is today.
|
|
277
|
+
* The clauses that shape a row, captured as key sets rather than maps: a naked type parameter skips
|
|
278
|
+
* excess-property checks, while a key set fails its own constraint on a typo.
|
|
314
279
|
* @internal
|
|
315
280
|
*/
|
|
316
281
|
type QueryProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = {
|
|
@@ -328,10 +293,8 @@ export type QueryProjected<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P
|
|
|
328
293
|
*/
|
|
329
294
|
export type QueryOneProjected<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E> = never> = QueryOne<E> & QueryProjection<E, S, V, X, P, C>;
|
|
330
295
|
/**
|
|
331
|
-
* The keys a query comes back with,
|
|
332
|
-
*
|
|
333
|
-
* subtracts, plus the relations `$populate` asked for. A positive `$select` wins outright, which is
|
|
334
|
-
* why `$exclude` is only read on the branch where there is none.
|
|
296
|
+
* The keys a query comes back with, as the runtime projects them: a positive `$select`'s, or every
|
|
297
|
+
* field minus what `$select` or `$exclude` subtracts, plus the populated relations.
|
|
335
298
|
* @internal
|
|
336
299
|
*/
|
|
337
300
|
type ProjectedKeys<E, S, V, X, P> = ([V] extends [false | 0] ? Exclude<FieldKey<E>, S> : [S] extends [never] ? Exclude<FieldKey<E>, X> : S) | P;
|
|
@@ -341,16 +304,9 @@ type ProjectedKeys<E, S, V, X, P> = ([V] extends [false | 0] ? Exclude<FieldKey<
|
|
|
341
304
|
*/
|
|
342
305
|
type IsUniform<V> = [V] extends [true | 1] ? true : [V] extends [false | 0] ? true : false;
|
|
343
306
|
/**
|
|
344
|
-
* A
|
|
345
|
-
*
|
|
346
|
-
* `
|
|
347
|
-
* with it where a helper has to take one: `QueryFindResult<User, 'id' | 'name'>`.
|
|
348
|
-
*
|
|
349
|
-
* The entity itself when the query projects nothing, when it uses a raw-projection array (columns,
|
|
350
|
-
* not fields), and when the projection is not uniform - a `Query<E>` built elsewhere, or a map
|
|
351
|
-
* mixing selected and subtracted entries, whose positive keys inference cannot recover. Relations
|
|
352
|
-
* keep their declared type: narrowing them means capturing their queries as maps, which costs those
|
|
353
|
-
* queries their own checks.
|
|
307
|
+
* A find's row: the entity narrowed to what the query projected and populated, so reading anything
|
|
308
|
+
* else does not compile. The entity itself where the projection is raw, absent or not uniform.
|
|
309
|
+
* @example `QueryFindResult<User, 'id' | 'name'>`
|
|
354
310
|
*/
|
|
355
311
|
export type QueryFindResult<E, S extends FieldKey<E> = never, V = true, X extends FieldKey<E> = never, P extends RelationKey<E> = never, C extends RelationKey<E> = never> = QueryProjectedRow<E, S, V, X, P, C> & CountedRelations<C>;
|
|
356
312
|
/**
|
|
@@ -375,13 +331,7 @@ type PopulatedToMany<E, P> = Extract<P, ToManyRelationKey<E>>;
|
|
|
375
331
|
export type QueryStringified = {
|
|
376
332
|
[K in keyof Query<unknown>]?: string;
|
|
377
333
|
};
|
|
378
|
-
/**
|
|
379
|
-
* What upserting one row reports, against the entity rather than the driver.
|
|
380
|
-
*
|
|
381
|
-
* `created` is here and not on {@link QueryUpsertManyResult} because it is only ever knowable for a
|
|
382
|
-
* single statement: a batch's `affectedRows` is a weighted sum on the dialects that report one at
|
|
383
|
-
* all, and a batch of mixed shapes is several statements.
|
|
384
|
-
*/
|
|
334
|
+
/** What upserting one row reports. `created` is only knowable for a single statement, so a batch has none. */
|
|
385
335
|
export type QueryUpsertOneResult<E> = {
|
|
386
336
|
readonly id?: WrittenId<E>;
|
|
387
337
|
readonly changes?: number;
|
package/dist/type/query.js
CHANGED
|
@@ -4,13 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const COUNT_RESULT_KEY = '_count';
|
|
6
6
|
/**
|
|
7
|
-
* `Query`'s clauses grouped by the shape of their value
|
|
8
|
-
*
|
|
9
|
-
* Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build
|
|
10
|
-
* rather than the runtime if a clause is ever renamed.
|
|
11
|
-
*
|
|
12
|
-
* `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query
|
|
13
|
-
* accepts it.
|
|
7
|
+
* `Query`'s clauses grouped by the shape of their value, for the wire parser and the relation query
|
|
8
|
+
* check alike; `satisfies` keeps them in step with `Query`.
|
|
14
9
|
*/
|
|
15
10
|
export const QUERY_OBJECT_CLAUSES = [
|
|
16
11
|
'$select',
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
import type { FieldKey } from './entity.js';
|
|
2
2
|
import type { QueryPager, QuerySelect, QuerySortDirection } from './query.js';
|
|
3
3
|
import type { QueryWhere, QueryWhereFieldValue } from './queryWhere.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* `unknown` (an inert intersection member) when there are none. Needed because `$group`/`$select` are
|
|
7
|
-
* captured as whole maps, and TypeScript skips excess-property checking on a naked type parameter.
|
|
8
|
-
* A find captures key sets instead, where an unknown key fails the capture's own constraint.
|
|
9
|
-
* @internal
|
|
10
|
-
*/
|
|
11
|
-
type Reject<K> = [K] extends [never] ? unknown : Record<K & string, never>;
|
|
12
|
-
/**
|
|
13
|
-
* The columns `$group` actually names: keys whose value is literally `true`, not `keyof G`.
|
|
14
|
-
* Wherever `G` cannot be inferred - `$group` omitted, hoisted, or annotated - it *is* its own
|
|
15
|
-
* constraint, whose every value is `true | undefined`, and keying off values yields `never` there
|
|
16
|
-
* rather than every field of the entity.
|
|
17
|
-
* @internal
|
|
18
|
-
*/
|
|
4
|
+
import type { RejectKeys } from './utility.js';
|
|
5
|
+
/** The columns `$group` names by a literal `true`, so an uninferred `$group`, its own constraint, names none. */
|
|
19
6
|
type GroupedKeys<G> = {
|
|
20
7
|
[K in keyof G]: G[K] extends true ? K : never;
|
|
21
8
|
}[keyof G];
|
|
@@ -95,15 +82,8 @@ type TotallingOp = OpsOf<'$sum' | '$avg' | '$sumDistinct' | '$avgDistinct'>;
|
|
|
95
82
|
*/
|
|
96
83
|
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, QueryFieldRef<E, NumericFieldKey<E>>> & Record<Exclude<AggregateOp, '$count' | TotallingOp>, QueryFieldRef<E>>;
|
|
97
84
|
/**
|
|
98
|
-
* An aggregate
|
|
99
|
-
*
|
|
100
|
-
* DISTINCT variants are flat ops (`$countDistinct`/`$sumDistinct`/`$avgDistinct`) taking a field.
|
|
101
|
-
*
|
|
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")
|
|
85
|
+
* An aggregate over one field, exactly one op per entry: `{ $sum: { amount: true } }` is `SUM("amount")`,
|
|
86
|
+
* `{ $countDistinct: { id: true } }` is `COUNT(DISTINCT "id")`, and only `$count` takes `'*'`.
|
|
107
87
|
*/
|
|
108
88
|
export type QueryAggregateFn<E> = ExactlyOne<QueryAggregateArgMap<E>>;
|
|
109
89
|
/** A single-key `{ [op]: unknown }` shape for each op in `Ops`, matched to infer that op's result. */
|
|
@@ -114,44 +94,17 @@ type FnWithOp<Ops extends string> = {
|
|
|
114
94
|
}[Ops];
|
|
115
95
|
/** Ops that count rows. Alone among the ops they answer `0`, never NULL, over an empty group. */
|
|
116
96
|
type CountingOp = OpsOf<'$count' | '$countDistinct'>;
|
|
117
|
-
/**
|
|
118
|
-
* Group-by columns: an object mapping entity field keys to `true`, exactly like {@link QuerySelect}.
|
|
119
|
-
* Typed against the entity, so a typo'd column is a compile error. Compute aggregate columns with
|
|
120
|
-
* {@link QueryAggMap} (the `$select` key), not here.
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```ts
|
|
124
|
-
* { status: true } // -> GROUP BY "status"
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
97
|
+
/** The columns to group by, `{ status: true }`, typed against the entity like `$select`. */
|
|
127
98
|
export type QueryGroupMap<E> = Readonly<QuerySelect<E, FieldKey<E>, true>>;
|
|
128
|
-
/**
|
|
129
|
-
* Computed aggregate columns: an object mapping your chosen output alias to an aggregate function.
|
|
130
|
-
* Alias names are free (you are naming new columns); the aggregated field reference inside each
|
|
131
|
-
* function is typed against the entity.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```ts
|
|
135
|
-
* { count: { $count: '*' }, avgAge: { $avg: { age: true } } }
|
|
136
|
-
* // -> COUNT(*) AS "count", AVG("age") AS "avgAge"
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
99
|
+
/** Computed columns by the alias each is read back under: `{ count: { $count: '*' }, avgAge: { $avg: { age: true } } }`. */
|
|
139
100
|
export type QueryAggMap<E> = {
|
|
140
101
|
readonly [alias: string]: QueryAggregateFn<E>;
|
|
141
102
|
};
|
|
142
103
|
/** The entity type of an aggregated field reference `F`, or `unknown` if it is not a known field. */
|
|
143
104
|
type FieldValueType<E, F> = F extends keyof E ? E[F] : unknown;
|
|
144
105
|
/**
|
|
145
|
-
*
|
|
146
|
-
* `number
|
|
147
|
-
* field's own type, likewise or `null`.
|
|
148
|
-
*
|
|
149
|
-
* Everything but `$count` is nullable: an aggregate over zero rows is NULL, and an ungrouped one
|
|
150
|
-
* still returns a row, so a `$where` matching nothing hands back a row of NULLs.
|
|
151
|
-
*
|
|
152
|
-
* `$sum`/`$avg` are exact to 2^53: Postgres widens a sum over BIGINT to NUMERIC, and decoding that
|
|
153
|
-
* text to satisfy this `number` drops the digits past that bound. Use `raw()` for a wider total.
|
|
154
|
-
* @internal
|
|
106
|
+
* A computed column's type: a count is a `number`; every other aggregate is `null` over no rows, a
|
|
107
|
+
* total a `number` (exact to 2^53, `raw` beyond) and `$min`/`$max` the field's own type.
|
|
155
108
|
*/
|
|
156
109
|
type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number : Fn extends FnWithOp<TotallingOp> ? number | null : Fn extends {
|
|
157
110
|
readonly $min: infer F;
|
|
@@ -165,40 +118,17 @@ type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number :
|
|
|
165
118
|
type Simplify<T> = {
|
|
166
119
|
[K in keyof T]: T[K];
|
|
167
120
|
} & {};
|
|
168
|
-
/**
|
|
169
|
-
* Infers the aggregated result row: grouped columns (`G`) keep their entity type; computed columns
|
|
170
|
-
* (`A`) resolve from their aggregate function via {@link QueryAggregateFnResult}.
|
|
171
|
-
*
|
|
172
|
-
* Grouped columns come from {@link GroupedKeys}, not `keyof G`, so a `$group` the compiler could
|
|
173
|
-
* not read contributes none rather than all of them.
|
|
174
|
-
*/
|
|
121
|
+
/** An aggregate's row: each grouped column with its entity type, each computed one with its aggregate's. */
|
|
175
122
|
export type QueryAggregateResult<E, G, A> = Simplify<Pick<E, GroupedKeys<G> & FieldKey<E>> & {
|
|
176
123
|
-readonly [K in keyof A]: QueryAggregateFnResult<E, A[K]>;
|
|
177
124
|
}>;
|
|
178
|
-
/**
|
|
179
|
-
* Erased runtime shape of a HAVING clause (alias -> comparison), consumed by the dialect builders.
|
|
180
|
-
* Values are `unknown` because the SQL is built generically; the typed, per-column value checking
|
|
181
|
-
* lives in {@link QueryAggregate.$having}.
|
|
182
|
-
*
|
|
183
|
-
* @example { count: { $gt: 5 } } -> HAVING COUNT(*) > 5
|
|
184
|
-
*/
|
|
125
|
+
/** A `HAVING` as the dialects read it, erased; {@link QueryAggregate.$having} is where it is typed. `{ count: { $gt: 5 } }` */
|
|
185
126
|
export type QueryHavingMap = {
|
|
186
127
|
readonly [alias: string]: QueryWhereFieldValue<unknown> | undefined;
|
|
187
128
|
};
|
|
188
129
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* @example
|
|
193
|
-
* ```ts
|
|
194
|
-
* querier.aggregate(User, {
|
|
195
|
-
* $where: { deletedAt: { $isNull: true } },
|
|
196
|
-
* $group: { status: true },
|
|
197
|
-
* $select: { count: { $count: '*' }, avgAge: { $avg: { age: true } } },
|
|
198
|
-
* $having: { count: { $gt: 5 } },
|
|
199
|
-
* $sort: { count: -1 },
|
|
200
|
-
* });
|
|
201
|
-
* ```
|
|
130
|
+
* An aggregate query, apart from `Query` so its row type stays honest:
|
|
131
|
+
* `aggregate(User, { $group: { status: true }, $select: { n: { $count: '*' } }, $having: { n: { $gt: 5 } } })`.
|
|
202
132
|
*/
|
|
203
133
|
export type QueryAggregate<E, G extends QueryGroupMap<E> = QueryGroupMap<E>, A extends QueryAggMap<E> = QueryAggMap<E>> = {
|
|
204
134
|
/**
|
|
@@ -207,29 +137,19 @@ export type QueryAggregate<E, G extends QueryGroupMap<E> = QueryGroupMap<E>, A e
|
|
|
207
137
|
readonly $where?: QueryWhere<E>;
|
|
208
138
|
/**
|
|
209
139
|
* Columns to group by - `{ status: true }`, typed against the entity like `$select`. A computed
|
|
210
|
-
* aggregate wrongly placed here (it belongs in `$select`) is rejected via {@link
|
|
140
|
+
* aggregate wrongly placed here (it belongs in `$select`) is rejected via {@link RejectKeys}, since
|
|
211
141
|
* `$group` is captured as a generic and a bare generic skips excess-property checking. The captured
|
|
212
142
|
* map meets its schema, {@link QueryGroupMap}, so each key keeps its link to the entity property.
|
|
213
143
|
*/
|
|
214
|
-
readonly $group?: G & QueryGroupMap<E> &
|
|
144
|
+
readonly $group?: G & QueryGroupMap<E> & RejectKeys<Exclude<keyof G, FieldKey<E>>>;
|
|
215
145
|
/**
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* (a `Record<keyof A, ...>` spelling of the same type breaks the inference of `A`).
|
|
219
|
-
*
|
|
220
|
-
* An alias repeating a `$group` column is rejected: both would be emitted under that one name,
|
|
221
|
-
* leaving the driver to keep whichever it read last.
|
|
146
|
+
* The computed columns by alias, the captured map meeting its schema so field keys stay linked. An alias
|
|
147
|
+
* repeating a `$group` column is refused, since both would come back under one name.
|
|
222
148
|
*/
|
|
223
149
|
readonly $select?: A & {
|
|
224
150
|
readonly [K in keyof A]: QueryAggregateFn<E>;
|
|
225
|
-
} &
|
|
226
|
-
/**
|
|
227
|
-
* Post-aggregation filtering, applied after grouping (SQL `HAVING`, MongoDB post-group `$match`).
|
|
228
|
-
* Keyed by the result columns (grouped columns + computed aliases), and each value is typed to that
|
|
229
|
-
* column's result type - a `$min`/`$max` over a `Date` field compares against a `Date`, a grouped
|
|
230
|
-
* column against its own type - reusing {@link QueryAggregateResult}. A name that is neither is a
|
|
231
|
-
* compile error.
|
|
232
|
-
*/
|
|
151
|
+
} & RejectKeys<NamedKeys<A> & GroupedKeys<G>>;
|
|
152
|
+
/** Filtering after grouping, by a result column, each value typed as that column is. */
|
|
233
153
|
readonly $having?: {
|
|
234
154
|
readonly [K in keyof QueryAggregateResult<E, G, A>]?: QueryWhereFieldValue<QueryAggregateResult<E, G, A>[K]>;
|
|
235
155
|
};
|