@mikro-orm/sql 7.0.0-rc.0 → 7.0.0-rc.1
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/AbstractSqlDriver.d.ts +7 -0
- package/AbstractSqlDriver.js +58 -25
- package/AbstractSqlPlatform.d.ts +8 -1
- package/AbstractSqlPlatform.js +18 -1
- package/dialects/mysql/BaseMySqlPlatform.d.ts +1 -1
- package/dialects/mysql/BaseMySqlPlatform.js +4 -3
- package/package.json +2 -2
- package/query/ObjectCriteriaNode.js +1 -1
- package/query/QueryBuilder.d.ts +37 -16
- package/query/QueryBuilder.js +80 -15
- package/query/QueryBuilderHelper.d.ts +2 -2
- package/query/QueryBuilderHelper.js +9 -9
- package/schema/SchemaComparator.js +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +4 -2
package/AbstractSqlDriver.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
19
19
|
private getTableProps;
|
|
20
20
|
/** Creates a FormulaTable object for use in formula callbacks. */
|
|
21
21
|
private createFormulaTable;
|
|
22
|
+
private validateSqlOptions;
|
|
22
23
|
createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
|
|
23
24
|
private createQueryBuilderFromOptions;
|
|
24
25
|
find<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: EntityName<T>, where: ObjectQuery<T>, options?: FindOptions<T, P, F, E>): Promise<EntityData<T>[]>;
|
|
@@ -30,6 +31,11 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
30
31
|
protected streamFromVirtual<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: StreamOptions<T, any>): AsyncIterableIterator<EntityData<T>>;
|
|
31
32
|
protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType): Promise<T[] | number>;
|
|
32
33
|
protected wrapVirtualExpressionInSubqueryStream<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any, any, any>, type: QueryType.SELECT): AsyncIterableIterator<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Virtual entities have no PKs, so to-many populate joins can't be deduplicated.
|
|
36
|
+
* Force balanced strategy to load to-many relations via separate queries.
|
|
37
|
+
*/
|
|
38
|
+
private forceBalancedStrategy;
|
|
33
39
|
mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?: QueryBuilder<T, any, any, any>, map?: Dictionary): EntityData<T> | null;
|
|
34
40
|
/**
|
|
35
41
|
* Maps aliased columns from TPT parent tables back to their original field names.
|
|
@@ -134,6 +140,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
134
140
|
protected buildOrderBy<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>): QueryOrderMap<T>[];
|
|
135
141
|
protected buildPopulateOrderBy<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, populateOrderBy: QueryOrderMap<T>[], parentPath: string, explicit: boolean, parentAlias?: string): QueryOrderMap<T>[];
|
|
136
142
|
protected buildJoinedPropsOrderBy<T extends object>(qb: QueryBuilder<T, any, any, any>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options?: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>, parentPath?: string): QueryOrderMap<T>[];
|
|
143
|
+
private buildToManyOrderBy;
|
|
137
144
|
protected normalizeFields<T extends object>(fields: InternalField<T>[], prefix?: string): string[];
|
|
138
145
|
protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: InternalField<T>[]): void;
|
|
139
146
|
protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T, any, any, any>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>, schema?: string): InternalField<T>[];
|
package/AbstractSqlDriver.js
CHANGED
|
@@ -32,6 +32,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
32
32
|
const qualifiedName = effectiveSchema ? `${effectiveSchema}.${meta.tableName}` : meta.tableName;
|
|
33
33
|
return { alias, name: meta.tableName, schema: effectiveSchema, qualifiedName, toString: () => alias };
|
|
34
34
|
}
|
|
35
|
+
validateSqlOptions(options) {
|
|
36
|
+
if (options.collation != null && typeof options.collation !== 'string') {
|
|
37
|
+
throw new Error('Collation option for SQL drivers must be a string (collation name). Use a CollationOptions object only with MongoDB.');
|
|
38
|
+
}
|
|
39
|
+
if (options.indexHint != null && typeof options.indexHint !== 'string') {
|
|
40
|
+
throw new Error('indexHint for SQL drivers must be a string (e.g. \'force index(my_index)\'). Use an object only with MongoDB.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
35
43
|
createEntityManager(useContext) {
|
|
36
44
|
const EntityManagerClass = this.config.get('entityManager', SqlEntityManager);
|
|
37
45
|
return new EntityManagerClass(this.config, this, this.metadata, useContext);
|
|
@@ -49,6 +57,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
49
57
|
if (Utils.isPrimaryKey(where, meta.compositePK)) {
|
|
50
58
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
51
59
|
}
|
|
60
|
+
this.validateSqlOptions(options);
|
|
52
61
|
const { first, last, before, after } = options;
|
|
53
62
|
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
54
63
|
qb.__populateWhere = options._populateWhere;
|
|
@@ -59,6 +68,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
59
68
|
.groupBy(options.groupBy)
|
|
60
69
|
.having(options.having)
|
|
61
70
|
.indexHint(options.indexHint)
|
|
71
|
+
.collation(options.collation)
|
|
62
72
|
.comment(options.comments)
|
|
63
73
|
.hintComment(options.hintComments);
|
|
64
74
|
if (isCursorPagination) {
|
|
@@ -180,7 +190,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
180
190
|
yield* res;
|
|
181
191
|
}
|
|
182
192
|
async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
|
|
183
|
-
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
193
|
+
const qb = await this.createQueryBuilderFromOptions(meta, where, this.forceBalancedStrategy(options));
|
|
184
194
|
qb.setFlag(QueryFlag.DISABLE_PAGINATE);
|
|
185
195
|
const isCursorPagination = [options.first, options.last, options.before, options.after].some(v => v != null);
|
|
186
196
|
const native = qb.getNativeQuery(false);
|
|
@@ -199,7 +209,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
199
209
|
return res.map(row => this.mapResult(row, meta));
|
|
200
210
|
}
|
|
201
211
|
async *wrapVirtualExpressionInSubqueryStream(meta, expression, where, options, type) {
|
|
202
|
-
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
212
|
+
const qb = await this.createQueryBuilderFromOptions(meta, where, this.forceBalancedStrategy(options));
|
|
203
213
|
qb.unsetFlag(QueryFlag.DISABLE_PAGINATE);
|
|
204
214
|
const native = qb.getNativeQuery(false);
|
|
205
215
|
native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
|
|
@@ -210,6 +220,24 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
210
220
|
yield this.mapResult(row, meta);
|
|
211
221
|
}
|
|
212
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Virtual entities have no PKs, so to-many populate joins can't be deduplicated.
|
|
225
|
+
* Force balanced strategy to load to-many relations via separate queries.
|
|
226
|
+
*/
|
|
227
|
+
forceBalancedStrategy(options) {
|
|
228
|
+
const clearStrategy = (hints) => {
|
|
229
|
+
return hints.map(hint => ({
|
|
230
|
+
...hint,
|
|
231
|
+
strategy: undefined,
|
|
232
|
+
children: hint.children ? clearStrategy(hint.children) : undefined,
|
|
233
|
+
}));
|
|
234
|
+
};
|
|
235
|
+
const opts = { ...options, strategy: 'balanced' };
|
|
236
|
+
if (Array.isArray(opts.populate)) {
|
|
237
|
+
opts.populate = clearStrategy(opts.populate);
|
|
238
|
+
}
|
|
239
|
+
return opts;
|
|
240
|
+
}
|
|
213
241
|
mapResult(result, meta, populate = [], qb, map = {}) {
|
|
214
242
|
// For TPT inheritance, map aliased parent table columns back to their field names
|
|
215
243
|
if (qb && meta.inheritanceType === 'tpt' && meta.tptParent) {
|
|
@@ -474,8 +502,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
474
502
|
if (meta && !Utils.isEmpty(populate)) {
|
|
475
503
|
this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
|
|
476
504
|
}
|
|
505
|
+
this.validateSqlOptions(options);
|
|
477
506
|
qb.__populateWhere = options._populateWhere;
|
|
478
507
|
qb.indexHint(options.indexHint)
|
|
508
|
+
.collation(options.collation)
|
|
479
509
|
.comment(options.comments)
|
|
480
510
|
.hintComment(options.hintComments)
|
|
481
511
|
.groupBy(options.groupBy)
|
|
@@ -1486,7 +1516,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1486
1516
|
return [raw(`${this.evaluateFormula(prop.formula, columns, table)} as ${aliased}`)];
|
|
1487
1517
|
}
|
|
1488
1518
|
return prop.fieldNames.map(fieldName => {
|
|
1489
|
-
return `${tableAlias}.${fieldName}
|
|
1519
|
+
return raw('?? as ??', [`${tableAlias}.${fieldName}`, `${tableAlias}__${fieldName}`]);
|
|
1490
1520
|
});
|
|
1491
1521
|
}
|
|
1492
1522
|
/** @internal */
|
|
@@ -1639,38 +1669,41 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1639
1669
|
for (const hint of joinedProps) {
|
|
1640
1670
|
const [propName, ref] = hint.field.split(':', 2);
|
|
1641
1671
|
const prop = meta.properties[propName];
|
|
1642
|
-
const propOrderBy = prop.orderBy;
|
|
1643
1672
|
let path = `${parentPath}.${propName}`;
|
|
1644
1673
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && ref) {
|
|
1645
1674
|
path += '[pivot]';
|
|
1646
1675
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.fixedOrder && join) {
|
|
1650
|
-
const alias = ref ? propAlias : join.ownerAlias;
|
|
1651
|
-
orderBy.push({ [`${alias}.${prop.fixedOrderColumn}`]: QueryOrder.ASC });
|
|
1652
|
-
}
|
|
1653
|
-
const effectiveOrderBy = QueryHelper.mergeOrderBy(propOrderBy, prop.targetMeta?.orderBy);
|
|
1654
|
-
for (const item of effectiveOrderBy) {
|
|
1655
|
-
for (const field of Utils.getObjectQueryKeys(item)) {
|
|
1656
|
-
const order = item[field];
|
|
1657
|
-
if (RawQueryFragment.isKnownFragmentSymbol(field)) {
|
|
1658
|
-
const { sql, params } = RawQueryFragment.getKnownFragment(field);
|
|
1659
|
-
const sql2 = propAlias ? sql.replace(new RegExp(ALIAS_REPLACEMENT_RE, 'g'), propAlias) : sql;
|
|
1660
|
-
const key = raw(sql2, params);
|
|
1661
|
-
orderBy.push({ [key]: order });
|
|
1662
|
-
continue;
|
|
1663
|
-
}
|
|
1664
|
-
orderBy.push({ [`${propAlias}.${field}`]: order });
|
|
1665
|
-
}
|
|
1676
|
+
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind)) {
|
|
1677
|
+
this.buildToManyOrderBy(qb, prop, path, ref, orderBy);
|
|
1666
1678
|
}
|
|
1667
1679
|
if (hint.children) {
|
|
1668
|
-
|
|
1669
|
-
orderBy.push(...buildJoinedPropsOrderBy);
|
|
1680
|
+
orderBy.push(...this.buildJoinedPropsOrderBy(qb, prop.targetMeta, hint.children, options, path));
|
|
1670
1681
|
}
|
|
1671
1682
|
}
|
|
1672
1683
|
return orderBy;
|
|
1673
1684
|
}
|
|
1685
|
+
buildToManyOrderBy(qb, prop, path, ref, orderBy) {
|
|
1686
|
+
const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
|
|
1687
|
+
const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true });
|
|
1688
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.fixedOrder && join) {
|
|
1689
|
+
const alias = ref ? propAlias : join.ownerAlias;
|
|
1690
|
+
orderBy.push({ [`${alias}.${prop.fixedOrderColumn}`]: QueryOrder.ASC });
|
|
1691
|
+
}
|
|
1692
|
+
const effectiveOrderBy = QueryHelper.mergeOrderBy(prop.orderBy, prop.targetMeta?.orderBy);
|
|
1693
|
+
for (const item of effectiveOrderBy) {
|
|
1694
|
+
for (const field of Utils.getObjectQueryKeys(item)) {
|
|
1695
|
+
const order = item[field];
|
|
1696
|
+
if (RawQueryFragment.isKnownFragmentSymbol(field)) {
|
|
1697
|
+
const { sql, params } = RawQueryFragment.getKnownFragment(field);
|
|
1698
|
+
const sql2 = propAlias ? sql.replace(new RegExp(ALIAS_REPLACEMENT_RE, 'g'), propAlias) : sql;
|
|
1699
|
+
const key = raw(sql2, params);
|
|
1700
|
+
orderBy.push({ [key]: order });
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
orderBy.push({ [`${propAlias}.${field}`]: order });
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1674
1707
|
normalizeFields(fields, prefix = '') {
|
|
1675
1708
|
const ret = [];
|
|
1676
1709
|
for (const field of fields) {
|
package/AbstractSqlPlatform.d.ts
CHANGED
|
@@ -33,5 +33,12 @@ export declare abstract class AbstractSqlPlatform extends Platform {
|
|
|
33
33
|
/**
|
|
34
34
|
* @internal
|
|
35
35
|
*/
|
|
36
|
-
getOrderByExpression(column: string, direction: string): string[];
|
|
36
|
+
getOrderByExpression(column: string, direction: string, collation?: string): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Quotes a collation name for use in COLLATE clauses.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
quoteCollation(collation: string): string;
|
|
42
|
+
/** @internal */
|
|
43
|
+
protected validateCollationName(collation: string): void;
|
|
37
44
|
}
|
package/AbstractSqlPlatform.js
CHANGED
|
@@ -94,7 +94,24 @@ export class AbstractSqlPlatform extends Platform {
|
|
|
94
94
|
/**
|
|
95
95
|
* @internal
|
|
96
96
|
*/
|
|
97
|
-
getOrderByExpression(column, direction) {
|
|
97
|
+
getOrderByExpression(column, direction, collation) {
|
|
98
|
+
if (collation) {
|
|
99
|
+
return [`${column} collate ${this.quoteCollation(collation)} ${direction.toLowerCase()}`];
|
|
100
|
+
}
|
|
98
101
|
return [`${column} ${direction.toLowerCase()}`];
|
|
99
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Quotes a collation name for use in COLLATE clauses.
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
quoteCollation(collation) {
|
|
108
|
+
this.validateCollationName(collation);
|
|
109
|
+
return this.quoteIdentifier(collation);
|
|
110
|
+
}
|
|
111
|
+
/** @internal */
|
|
112
|
+
validateCollationName(collation) {
|
|
113
|
+
if (!/^[\w]+$/.test(collation)) {
|
|
114
|
+
throw new Error(`Invalid collation name: '${collation}'. Collation names must contain only word characters.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
100
117
|
}
|
|
@@ -41,6 +41,6 @@ export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
|
41
41
|
supportsCreatingFullTextIndex(): boolean;
|
|
42
42
|
getFullTextWhereClause(): string;
|
|
43
43
|
getFullTextIndexExpression(indexName: string, schemaName: string | undefined, tableName: string, columns: SimpleColumnMeta[]): string;
|
|
44
|
-
getOrderByExpression(column: string, direction: string): string[];
|
|
44
|
+
getOrderByExpression(column: string, direction: string, collation?: string): string[];
|
|
45
45
|
getDefaultClientUrl(): string;
|
|
46
46
|
}
|
|
@@ -105,13 +105,14 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
|
105
105
|
const quotedIndexName = this.quoteIdentifier(indexName);
|
|
106
106
|
return `alter table ${quotedTableName} add fulltext index ${quotedIndexName}(${quotedColumnNames.join(',')})`;
|
|
107
107
|
}
|
|
108
|
-
getOrderByExpression(column, direction) {
|
|
108
|
+
getOrderByExpression(column, direction, collation) {
|
|
109
109
|
const ret = [];
|
|
110
110
|
const dir = direction.toLowerCase();
|
|
111
|
+
const col = collation ? `${column} collate ${this.quoteCollation(collation)}` : column;
|
|
111
112
|
if (dir in this.ORDER_BY_NULLS_TRANSLATE) {
|
|
112
|
-
ret.push(`${
|
|
113
|
+
ret.push(`${col} ${this.ORDER_BY_NULLS_TRANSLATE[dir]}`);
|
|
113
114
|
}
|
|
114
|
-
ret.push(`${
|
|
115
|
+
ret.push(`${col} ${dir.replace(/(\s|nulls|first|last)*/gi, '')}`);
|
|
115
116
|
return ret;
|
|
116
117
|
}
|
|
117
118
|
getDefaultClientUrl() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.0.0-rc.
|
|
3
|
+
"version": "7.0.0-rc.1",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"@mikro-orm/core": "^6.6.4"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@mikro-orm/core": "7.0.0-rc.
|
|
59
|
+
"@mikro-orm/core": "7.0.0-rc.1"
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -90,7 +90,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
90
90
|
const operator = Utils.isOperator(field);
|
|
91
91
|
const isRawField = RawQueryFragment.isKnownFragmentSymbol(field);
|
|
92
92
|
// we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
|
|
93
|
-
const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
|
|
93
|
+
const virtual = childNode.prop?.persist === false && !childNode.prop?.formula && !!options?.type;
|
|
94
94
|
// if key is missing, we are inside group operator and we need to prefix with alias
|
|
95
95
|
const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
|
|
96
96
|
const isToOne = childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -52,14 +52,16 @@ type AddToContext<Type extends object, Context, Field extends string, Alias exte
|
|
|
52
52
|
[K in Alias]: [GetPath<Context, Field>, K, ExpandProperty<Type[GetPropName<Field> & keyof Type]>, Select];
|
|
53
53
|
};
|
|
54
54
|
type GetPath<Context, Field extends string> = GetAlias<Field> extends infer Alias ? IsNever<Alias> extends true ? GetPropName<Field> : Alias extends keyof Context ? Context[Alias] extends [infer Path, ...any[]] ? AppendToHint<Path & string, GetPropName<Field>> : GetPropName<Field> : GetPropName<Field> : GetPropName<Field>;
|
|
55
|
-
type GetType<Type extends object, Context, Field extends string> = GetAlias<Field> extends infer Alias ? IsNever<Alias> extends true ? Type : Alias extends keyof Context ? Context[Alias] extends [string, string, infer PropType, any] ? PropType & object : Type : Type : Type;
|
|
55
|
+
type GetType<Type extends object, Context, Field extends string> = GetAlias<Field> extends infer Alias ? IsNever<Alias> extends true ? Type : [Context] extends [never] ? Type : Alias extends keyof Context ? Context[Alias] extends [string, string, infer PropType, any] ? PropType & object : Type : Type : Type;
|
|
56
56
|
type AddToHint<RootAlias, Context, Field extends string, Select extends boolean = false> = Select extends true ? GetAlias<Field> extends infer Alias ? IsNever<Alias> extends true ? GetPropName<Field> : Alias extends RootAlias ? GetPropName<Field> : Alias extends keyof Context ? Context[Alias] extends [infer Path, ...any[]] ? AppendToHint<Path & string, GetPropName<Field>> : GetPropName<Field> : GetPropName<Field> : GetPropName<Field> : never;
|
|
57
57
|
export type ModifyHint<RootAlias, Context, Hint extends string, Field extends string, Select extends boolean = false> = Hint | AddToHint<RootAlias, Context, Field, Select>;
|
|
58
58
|
export type ModifyContext<Entity extends object, Context, Field extends string, Alias extends string, Select extends boolean = false> = IsNever<Context> extends true ? AddToContext<GetType<Entity, object, Field>, object, Field, Alias, Select> : Context & AddToContext<GetType<Entity, Context, Field>, Context, Field, Alias, Select>;
|
|
59
59
|
type StripRootAlias<F extends string, RootAlias extends string, Context = never> = F extends `${RootAlias}.${infer Field}` ? Field : F extends `${infer Alias}.${string}` ? Alias extends AliasNames<Context> ? never : F : F;
|
|
60
|
-
type
|
|
60
|
+
type StripFieldAlias<F extends string> = F extends `${infer Path} as ${string}` ? Path : F;
|
|
61
|
+
type ExtractRootFields<Fields, RootAlias extends string, Context = never> = [Fields] extends ['*'] ? '*' : Fields extends `${RootAlias}.*` ? '*' : Fields extends string ? StripRootAlias<StripFieldAlias<Fields>, RootAlias, Context> : never;
|
|
61
62
|
type PrefixWithPath<Path extends string, Field extends string> = `${Path}.${Field}`;
|
|
62
63
|
type StripJoinAlias<F extends string, Alias extends string> = F extends `${Alias}.${infer Field}` ? Field : F;
|
|
64
|
+
export type JoinSelectField<JoinedEntity, Alias extends string> = (keyof JoinedEntity & string) | `${Alias}.${keyof JoinedEntity & string}`;
|
|
63
65
|
type AddJoinFields<RootAlias, Context, Field extends string, Alias extends string, JoinFields extends readonly string[]> = JoinFields extends readonly (infer F)[] ? F extends string ? PrefixWithPath<AddToHint<RootAlias, Context, Field, true> & string, StripJoinAlias<F, Alias>> : never : never;
|
|
64
66
|
export type ModifyFields<CurrentFields extends string, RootAlias, Context, Field extends string, Alias extends string, JoinFields extends readonly string[] | undefined> = JoinFields extends readonly string[] ? CurrentFields | AddJoinFields<RootAlias, Context, Field, Alias, JoinFields> : CurrentFields;
|
|
65
67
|
type EntityRelations<T> = EntityKey<T, true>;
|
|
@@ -68,25 +70,33 @@ type AliasNames<Context> = Context[keyof Context] extends infer Join ? Join exte
|
|
|
68
70
|
type ContextRelationKeys<Context> = Context[keyof Context] extends infer Join ? Join extends any ? Join extends [string, infer Alias, infer Type, any] ? `${Alias & string}.${EntityRelations<Type & object>}` : never : never : never;
|
|
69
71
|
export type QBField<Entity, RootAlias extends string, Context> = EntityRelations<Entity> | `${RootAlias}.${EntityRelations<Entity>}` | ([Context] extends [never] ? never : ContextRelationKeys<Context>);
|
|
70
72
|
type ContextFieldKeys<Context> = Context[keyof Context] extends infer Join ? Join extends any ? Join extends [string, infer Alias, infer Type, any] ? `${Alias & string}.${keyof Type & string}` : never : never : never;
|
|
71
|
-
|
|
73
|
+
type WithAlias<T extends string> = T | `${T} as ${string}`;
|
|
74
|
+
export type Field<Entity, RootAlias extends string = never, Context = never> = WithAlias<EntityKey<Entity>> | (IsNever<RootAlias> extends true ? never : WithAlias<`${RootAlias}.${EntityKey<Entity>}`> | `${RootAlias}.*`) | ([Context] extends [never] ? never : WithAlias<ContextFieldKeys<Context>> | `${AliasNames<Context>}.*`) | '*' | QueryBuilder<any> | NativeQueryBuilder | RawQueryFragment<any> | (RawQueryFragment & symbol);
|
|
72
75
|
type RootAliasOrderKeys<RootAlias extends string, Entity> = {
|
|
73
76
|
[K in `${RootAlias}.${EntityKey<Entity>}`]?: QueryOrderKeysFlat;
|
|
74
77
|
};
|
|
75
78
|
type ContextOrderKeys<Context> = {
|
|
76
79
|
[K in ContextFieldKeys<Context>]?: QueryOrderKeysFlat;
|
|
77
80
|
};
|
|
78
|
-
|
|
81
|
+
type RawOrderKeys<RawAliases extends string> = {
|
|
82
|
+
[K in RawAliases]?: QueryOrderKeysFlat;
|
|
83
|
+
};
|
|
84
|
+
export type ContextOrderByMap<Entity, RootAlias extends string = never, Context = never, RawAliases extends string = never> = QueryOrderMap<Entity> | ((IsNever<RootAlias> extends true ? {} : RootAliasOrderKeys<RootAlias, Entity>) & ([Context] extends [never] ? {} : ContextOrderKeys<Context>) & (IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawOrderKeys<RawAliases>));
|
|
79
85
|
type AliasedPath<Alias extends string, Type, P extends string> = P extends `${Alias}.*` ? P : P extends `${Alias}.${infer Rest}` ? `${Alias}.${AutoPath<Type & object, Rest, `${PopulatePath.ALL}`>}` : never;
|
|
80
86
|
type ContextAliasedPath<Context, P extends string> = Context[keyof Context] extends infer Join ? Join extends any ? Join extends [string, infer Alias, infer Type, any] ? AliasedPath<Alias & string, Type, P> : never : never : never;
|
|
81
|
-
type NestedAutoPath<Entity, RootAlias extends string, Context, P extends string> = P extends `${string}:ref` ? never : AliasedPath<RootAlias, Entity, P> | ContextAliasedPath<Context, P> | AutoPath<Entity, P, `${PopulatePath.ALL}`>;
|
|
87
|
+
type NestedAutoPath<Entity, RootAlias extends string, Context, P extends string> = P extends `${string}:ref` ? never : P extends `${infer Path} as ${string}` ? (AliasedPath<RootAlias, Entity, Path> | ContextAliasedPath<Context, Path> | AutoPath<Entity, Path, `${PopulatePath.ALL}`>) extends never ? never : P : AliasedPath<RootAlias, Entity, P> | ContextAliasedPath<Context, P> | AutoPath<Entity, P, `${PopulatePath.ALL}`>;
|
|
82
88
|
type AliasedObjectQuery<Entity extends object, Alias extends string> = {
|
|
83
89
|
[K in EntityKey<Entity> as `${Alias}.${K}`]?: ObjectQuery<Entity>[K];
|
|
84
90
|
};
|
|
85
|
-
type JoinCondition<JoinedEntity extends object, Alias extends string> = ObjectQuery<JoinedEntity> | AliasedObjectQuery<JoinedEntity, Alias
|
|
91
|
+
type JoinCondition<JoinedEntity extends object, Alias extends string> = (ObjectQuery<JoinedEntity> | AliasedObjectQuery<JoinedEntity, Alias>) & {
|
|
92
|
+
$not?: JoinCondition<JoinedEntity, Alias>;
|
|
93
|
+
$or?: JoinCondition<JoinedEntity, Alias>[];
|
|
94
|
+
$and?: JoinCondition<JoinedEntity, Alias>[];
|
|
95
|
+
};
|
|
86
96
|
type RawJoinCondition = {
|
|
87
97
|
[key: string]: FilterValue<Scalar> | RawQueryFragment;
|
|
88
98
|
};
|
|
89
|
-
type ExtractRawAliasFromField<F> = F extends RawQueryFragment<infer A> ? (A extends string ? A : never) : never;
|
|
99
|
+
type ExtractRawAliasFromField<F> = F extends RawQueryFragment<infer A> ? (A extends string ? A : never) : F extends `${string} as ${infer A}` ? A : never;
|
|
90
100
|
type ExtractRawAliasesFromTuple<T extends readonly unknown[]> = T extends readonly [infer Head, ...infer Tail] ? ExtractRawAliasFromField<Head> | ExtractRawAliasesFromTuple<Tail> : never;
|
|
91
101
|
type ExtractRawAliases<Fields> = Fields extends readonly unknown[] ? ExtractRawAliasesFromTuple<Fields> : ExtractRawAliasFromField<Fields>;
|
|
92
102
|
type FlatOperatorMap = {
|
|
@@ -194,6 +204,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
194
204
|
protected _joinedProps: Map<string, PopulateOptions<any>>;
|
|
195
205
|
protected _cache?: boolean | number | [string, number];
|
|
196
206
|
protected _indexHint?: string;
|
|
207
|
+
protected _collation?: string;
|
|
197
208
|
protected _comments: string[];
|
|
198
209
|
protected _hintComments: string[];
|
|
199
210
|
protected flushMode?: FlushMode;
|
|
@@ -228,6 +239,10 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
228
239
|
* // Select with raw expressions
|
|
229
240
|
* qb.select([raw('count(*) as total')]);
|
|
230
241
|
*
|
|
242
|
+
* // Select with aliases (works for regular and formula properties)
|
|
243
|
+
* qb.select(['id', 'fullName as displayName']);
|
|
244
|
+
* qb.select(['id', sql.ref('fullName').as('displayName')]);
|
|
245
|
+
*
|
|
231
246
|
* // Select with distinct
|
|
232
247
|
* qb.select('*', true);
|
|
233
248
|
* ```
|
|
@@ -396,11 +411,11 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
396
411
|
* .where({ 'a.name': 'John' });
|
|
397
412
|
* ```
|
|
398
413
|
*/
|
|
399
|
-
joinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly string[] | undefined = undefined>(field: Field | [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, type?: JoinType, path?: string, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
400
|
-
leftJoinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly string[] | undefined = undefined>(field: Field | [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
401
|
-
leftJoinLateralAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly string[] | undefined = undefined>(field: [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
402
|
-
innerJoinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly string[] | undefined = undefined>(field: Field | [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
403
|
-
innerJoinLateralAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly string[] | undefined = undefined>(field: [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
414
|
+
joinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly [JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>, ...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[]] | undefined = undefined>(field: Field | [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, type?: JoinType, path?: string, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
415
|
+
leftJoinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly [JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>, ...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[]] | undefined = undefined>(field: Field | [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
416
|
+
leftJoinLateralAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly [JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>, ...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[]] | undefined = undefined>(field: [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
417
|
+
innerJoinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly [JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>, ...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[]] | undefined = undefined>(field: Field | [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
418
|
+
innerJoinLateralAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string, const JoinFields extends readonly [JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>, ...JoinSelectField<JoinedEntityType<Entity, Context, Field & string>, Alias>[]] | undefined = undefined>(field: [Field, RawQueryFragment | QueryBuilder<any>], alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, fields?: JoinFields, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>, RawAliases, ModifyFields<Fields, RootAlias, Context, Field, Alias, JoinFields>>;
|
|
404
419
|
protected getFieldsForJoinedLoad(prop: EntityProperty<Entity>, alias: string, explicitFields?: readonly string[]): InternalField<Entity>[];
|
|
405
420
|
/**
|
|
406
421
|
* Apply filters to the QB where condition.
|
|
@@ -503,7 +518,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
503
518
|
* qb.orderBy({ 'profile.bio': 'asc' }); // nested via dot notation
|
|
504
519
|
* ```
|
|
505
520
|
*/
|
|
506
|
-
orderBy(orderBy: ContextOrderByMap<Entity, RootAlias, Context> | ContextOrderByMap<Entity, RootAlias, Context>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields>;
|
|
521
|
+
orderBy(orderBy: ContextOrderByMap<Entity, RootAlias, Context, RawAliases> | ContextOrderByMap<Entity, RootAlias, Context, RawAliases>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields>;
|
|
507
522
|
/**
|
|
508
523
|
* Adds an ORDER BY clause to the query, replacing any existing order.
|
|
509
524
|
*
|
|
@@ -516,19 +531,21 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
516
531
|
* ```
|
|
517
532
|
*/
|
|
518
533
|
orderBy<const T extends Record<string, QueryOrderKeysFlat>>(orderBy: T & {
|
|
519
|
-
[K in keyof T]: K extends NestedAutoPath<Entity, RootAlias, Context, K & string> ? T[K] : never;
|
|
534
|
+
[K in keyof T]: K extends NestedAutoPath<Entity, RootAlias, Context, K & string> ? T[K] : (K extends RawAliases ? T[K] : never);
|
|
520
535
|
}): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields>;
|
|
521
536
|
/**
|
|
522
537
|
* Adds additional ORDER BY clause without replacing existing order.
|
|
523
538
|
*/
|
|
524
|
-
andOrderBy(orderBy: ContextOrderByMap<Entity, RootAlias, Context> | ContextOrderByMap<Entity, RootAlias, Context>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields>;
|
|
539
|
+
andOrderBy(orderBy: ContextOrderByMap<Entity, RootAlias, Context, RawAliases> | ContextOrderByMap<Entity, RootAlias, Context, RawAliases>[]): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields>;
|
|
525
540
|
/**
|
|
526
541
|
* Adds additional ORDER BY clause without replacing existing order.
|
|
527
542
|
*/
|
|
528
543
|
andOrderBy<const T extends Record<string, QueryOrderKeysFlat>>(orderBy: T & {
|
|
529
|
-
[K in keyof T]: K extends NestedAutoPath<Entity, RootAlias, Context, K & string> ? T[K] : never;
|
|
544
|
+
[K in keyof T]: K extends NestedAutoPath<Entity, RootAlias, Context, K & string> ? T[K] : (K extends RawAliases ? T[K] : never);
|
|
530
545
|
}): SelectQueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields>;
|
|
531
546
|
private processOrderBy;
|
|
547
|
+
/** Collect custom aliases from select fields (stored as 'resolved as alias' strings by select()). */
|
|
548
|
+
private getSelectAliases;
|
|
532
549
|
/**
|
|
533
550
|
* Adds a GROUP BY clause to the query.
|
|
534
551
|
*
|
|
@@ -611,6 +628,10 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
611
628
|
* Adds index hint to the FROM clause.
|
|
612
629
|
*/
|
|
613
630
|
indexHint(sql: string | undefined): this;
|
|
631
|
+
/**
|
|
632
|
+
* Adds COLLATE clause to ORDER BY expressions.
|
|
633
|
+
*/
|
|
634
|
+
collation(collation: string | undefined): this;
|
|
614
635
|
/**
|
|
615
636
|
* Prepend comment to the sql query using the syntax `/* ... *‍/`. Some characters are forbidden such as `/*, *‍/` and `?`.
|
|
616
637
|
*/
|