@mikro-orm/knex 6.0.0-dev.104 → 6.0.0-dev.106

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-dev.104",
3
+ "version": "6.0.0-dev.106",
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
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -66,6 +66,6 @@
66
66
  "@mikro-orm/core": "^5.8.3"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "~6.0.0-dev.104"
69
+ "@mikro-orm/core": "~6.0.0-dev.106"
70
70
  }
71
71
  }
@@ -1,10 +1,10 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'util';
3
3
  import type { Knex } from 'knex';
4
- import { LockMode, PopulateHint, QueryFlag, type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, type MetadataStorage, type ObjectQuery, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, type QueryResult, type RequiredEntityData, type LoggingOptions } from '@mikro-orm/core';
4
+ import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryResult, type RequiredEntityData } from '@mikro-orm/core';
5
5
  import { QueryType } from './enums';
6
6
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
7
- import { QueryBuilderHelper, type Alias } from './QueryBuilderHelper';
7
+ import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper';
8
8
  import type { SqlEntityManager } from '../SqlEntityManager';
9
9
  import type { Field } from '../typings';
10
10
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
@@ -92,11 +92,12 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
92
92
  delete(cond?: QBFilterQuery): DeleteQueryBuilder<T>;
93
93
  truncate(): TruncateQueryBuilder<T>;
94
94
  count(field?: string | string[], distinct?: boolean): CountQueryBuilder<T>;
95
- join(field: string, alias: string, cond?: QBFilterQuery, type?: 'leftJoin' | 'innerJoin' | 'pivotJoin', path?: string, schema?: string): this;
96
- leftJoin(field: string, alias: string, cond?: QBFilterQuery, schema?: string): this;
97
- joinAndSelect(field: string, alias: string, cond?: QBFilterQuery, type?: 'leftJoin' | 'innerJoin' | 'pivotJoin', path?: string, fields?: string[], schema?: string): SelectQueryBuilder<T>;
98
- leftJoinAndSelect(field: string, alias: string, cond?: QBFilterQuery, fields?: string[], schema?: string): SelectQueryBuilder<T>;
99
- innerJoinAndSelect(field: string, alias: string, cond?: QBFilterQuery, fields?: string[], schema?: string): SelectQueryBuilder<T>;
95
+ join(field: string | Knex.QueryBuilder | QueryBuilder<any>, alias: string, cond?: QBFilterQuery, type?: 'leftJoin' | 'innerJoin' | 'pivotJoin', path?: string, schema?: string): this;
96
+ innerJoin(field: string | Knex.QueryBuilder | QueryBuilder<any>, alias: string, cond?: QBFilterQuery, schema?: string): this;
97
+ leftJoin(field: string | Knex.QueryBuilder | QueryBuilder<any>, alias: string, cond?: QBFilterQuery, schema?: string): this;
98
+ joinAndSelect(field: string | [field: string, Knex.QueryBuilder | QueryBuilder<any>], alias: string, cond?: QBFilterQuery, type?: 'leftJoin' | 'innerJoin' | 'pivotJoin', path?: string, fields?: string[], schema?: string): SelectQueryBuilder<T>;
99
+ leftJoinAndSelect(field: string | [field: string, Knex.QueryBuilder | QueryBuilder<any>], alias: string, cond?: QBFilterQuery, fields?: string[], schema?: string): SelectQueryBuilder<T>;
100
+ innerJoinAndSelect(field: string | [field: string, Knex.QueryBuilder | QueryBuilder<any>], alias: string, cond?: QBFilterQuery, fields?: string[], schema?: string): SelectQueryBuilder<T>;
100
101
  protected getFieldsForJoinedLoad(prop: EntityProperty<T>, alias: string, explicitFields?: string[]): Field<T>[];
101
102
  withSubQuery(subQuery: Knex.QueryBuilder, alias: string): this;
102
103
  where(cond: QBFilterQuery<T>, operator?: keyof typeof GroupOperator): this;
@@ -158,6 +158,10 @@ class QueryBuilder {
158
158
  this.joinReference(field, alias, cond, type, path, schema);
159
159
  return this;
160
160
  }
161
+ innerJoin(field, alias, cond = {}, schema) {
162
+ this.join(field, alias, cond, 'innerJoin', undefined, schema);
163
+ return this;
164
+ }
161
165
  leftJoin(field, alias, cond = {}, schema) {
162
166
  return this.join(field, alias, cond, 'leftJoin', undefined, schema);
163
167
  }
@@ -165,9 +169,17 @@ class QueryBuilder {
165
169
  if (!this.type) {
166
170
  this.select('*');
167
171
  }
172
+ let subquery;
173
+ if (Array.isArray(field)) {
174
+ subquery = field[1] instanceof QueryBuilder ? field[1].getFormattedQuery() : field[1].toString();
175
+ field = field[0];
176
+ }
168
177
  const prop = this.joinReference(field, alias, cond, type, path, schema);
169
- this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
170
178
  const [fromAlias] = this.helper.splitField(field);
179
+ if (subquery) {
180
+ this._joins[`${fromAlias}.${prop.name}#${alias}`].subquery = subquery;
181
+ }
182
+ this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
171
183
  const populate = this._joinedProps.get(fromAlias);
172
184
  const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
173
185
  if (populate) {
@@ -662,6 +674,27 @@ class QueryBuilder {
662
674
  }
663
675
  joinReference(field, alias, cond, type, path, schema) {
664
676
  this.ensureNotFinalized();
677
+ if (typeof field === 'object') {
678
+ const prop = {
679
+ name: '__subquery__',
680
+ kind: core_1.ReferenceKind.MANY_TO_ONE,
681
+ };
682
+ if (field instanceof QueryBuilder) {
683
+ prop.type = field.mainAlias.entityName;
684
+ prop.targetMeta = field.mainAlias.metadata;
685
+ field = field.getKnexQuery();
686
+ }
687
+ this._joins[`${this.alias}.${prop.name}#${alias}`] = {
688
+ prop,
689
+ alias,
690
+ type,
691
+ cond,
692
+ schema,
693
+ subquery: field.toString(),
694
+ ownerAlias: this.alias,
695
+ };
696
+ return prop;
697
+ }
665
698
  const [fromAlias, fromField] = this.helper.splitField(field);
666
699
  const q = (str) => `'${str}'`;
667
700
  if (!this._aliases[fromAlias]) {
@@ -161,28 +161,43 @@ class QueryBuilderHelper {
161
161
  }
162
162
  processJoins(qb, joins, schema) {
163
163
  Object.values(joins).forEach(join => {
164
- let table = `${join.table} as ${join.alias}`;
164
+ let table = join.table;
165
165
  const method = join.type === 'innerJoin' ? 'inner join' : 'left join';
166
+ const conditions = [];
167
+ const params = [];
166
168
  schema = join.schema && join.schema !== '*' ? join.schema : schema;
167
169
  if (schema) {
168
170
  table = `${schema}.${table}`;
169
171
  }
170
- const conditions = [];
171
- const params = [];
172
- join.primaryKeys.forEach((primaryKey, idx) => {
173
- const left = `${join.ownerAlias}.${primaryKey}`;
174
- const right = `${join.alias}.${join.joinColumns[idx]}`;
175
- conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
176
- });
177
- if (join.prop.targetMeta.discriminatorValue && !join.path?.endsWith('[pivot]')) {
172
+ if (!join.subquery) {
173
+ join.primaryKeys.forEach((primaryKey, idx) => {
174
+ const left = `${join.ownerAlias}.${primaryKey}`;
175
+ const right = `${join.alias}.${join.joinColumns[idx]}`;
176
+ conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
177
+ });
178
+ }
179
+ if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
178
180
  const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
179
181
  const alias = join.inverseAlias ?? join.alias;
180
182
  join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
181
183
  }
182
184
  Object.keys(join.cond).forEach(key => {
183
- conditions.push(this.processJoinClause(key, join.cond[key], params));
185
+ const needsPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
186
+ const newKey = needsPrefix ? key : `${join.alias}.${key}`;
187
+ conditions.push(this.processJoinClause(newKey, join.cond[key], params));
184
188
  });
185
- return qb.joinRaw(`${method} ${this.knex.ref(table)} on ${conditions.join(' and ')}`, params);
189
+ let sql = method + ' ';
190
+ if (join.subquery) {
191
+ sql += `(${join.subquery})`;
192
+ }
193
+ else {
194
+ sql += this.knex.ref(table);
195
+ }
196
+ sql += ` as ${this.knex.ref(join.alias)}`;
197
+ if (conditions.length > 0) {
198
+ sql += ` on ${conditions.join(' and ')}`;
199
+ }
200
+ return qb.joinRaw(sql, params);
186
201
  });
187
202
  }
188
203
  processJoinClause(key, value, params, operator = '$eq') {
package/typings.d.ts CHANGED
@@ -26,6 +26,7 @@ export interface JoinOptions {
26
26
  prop: EntityProperty;
27
27
  cond: Dictionary;
28
28
  cond_?: Dictionary;
29
+ subquery?: string;
29
30
  }
30
31
  export interface Column {
31
32
  name: string;