@mikro-orm/knex 6.3.3-dev.1 → 6.3.3-dev.10

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.3.3-dev.1",
3
+ "version": "6.3.3-dev.10",
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,7 +66,7 @@
66
66
  "@mikro-orm/core": "^6.3.2"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.3.3-dev.1",
69
+ "@mikro-orm/core": "6.3.3-dev.10",
70
70
  "better-sqlite3": "*",
71
71
  "libsql": "*",
72
72
  "mariadb": "*"
@@ -28,7 +28,7 @@ class CriteriaNode {
28
28
  return;
29
29
  }
30
30
  for (const k of pks) {
31
- this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k));
31
+ this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k && prop.persist !== false));
32
32
  const isProp = this.prop || meta.props.find(prop => (prop.fieldNames || []).includes(k));
33
33
  // do not validate if the key is prefixed or type casted (e.g. `k::text`)
34
34
  if (validate && !isProp && !k.includes('.') && !k.includes('::') && !core_1.Utils.isOperator(k) && !core_1.RawQueryFragment.isKnownFragment(k)) {
@@ -1,6 +1,6 @@
1
1
  import { inspect } from 'node:util';
2
2
  import type { Knex } from 'knex';
3
- import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, RawQueryFragment, type RequiredEntityData, type ExpandProperty } from '@mikro-orm/core';
3
+ import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type ExpandProperty, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, RawQueryFragment, type RequiredEntityData } from '@mikro-orm/core';
4
4
  import { JoinType, QueryType } from './enums';
5
5
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
6
6
  import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper';
@@ -140,6 +140,10 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
140
140
  innerJoinAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(field: Field | [field: Field, qb: Knex.QueryBuilder | QueryBuilder<any>], alias: Alias, cond?: QBFilterQuery, fields?: string[], schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>>;
141
141
  innerJoinLateralAndSelect<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(field: [field: Field, qb: Knex.QueryBuilder | QueryBuilder<any>], alias: Alias, cond?: QBFilterQuery, fields?: string[], schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field, true> & {}, ModifyContext<Entity, Context, Field, Alias, true>>;
142
142
  protected getFieldsForJoinedLoad(prop: EntityProperty<Entity>, alias: string, explicitFields?: string[]): Field<Entity>[];
143
+ /**
144
+ * Apply filters to the QB where condition.
145
+ */
146
+ applyFilters(filterOptions?: Dictionary<boolean | Dictionary> | string[] | boolean): Promise<this>;
143
147
  withSubQuery(subQuery: Knex.QueryBuilder, alias: string): this;
144
148
  where(cond: QBFilterQuery<Entity>, operator?: keyof typeof GroupOperator): this;
145
149
  where(cond: string, params?: any[], operator?: keyof typeof GroupOperator): this;
@@ -253,6 +253,18 @@ class QueryBuilder {
253
253
  .forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
254
254
  return fields;
255
255
  }
256
+ /**
257
+ * Apply filters to the QB where condition.
258
+ */
259
+ async applyFilters(filterOptions = {}) {
260
+ /* istanbul ignore next */
261
+ if (!this.em) {
262
+ throw new Error('Cannot apply filters, this QueryBuilder is not attached to an EntityManager');
263
+ }
264
+ const cond = await this.em.applyFilters(this.mainAlias.entityName, {}, filterOptions, 'read');
265
+ this.andWhere(cond);
266
+ return this;
267
+ }
256
268
  withSubQuery(subQuery, alias) {
257
269
  this.ensureNotFinalized();
258
270
  this.subQueries[alias] = subQuery.toString();
@@ -1155,7 +1167,7 @@ class QueryBuilder {
1155
1167
  if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
1156
1168
  return;
1157
1169
  }
1158
- const joins = Object.values(this._joins);
1170
+ let joins = Object.values(this._joins);
1159
1171
  joins.forEach(join => {
1160
1172
  join.cond_ = join.cond;
1161
1173
  join.cond = {};
@@ -1172,6 +1184,15 @@ class QueryBuilder {
1172
1184
  const [alias] = this.helper.splitField(k);
1173
1185
  const join = joins.find(j => j.alias === alias);
1174
1186
  if (join) {
1187
+ const parentJoin = joins.find(j => j.alias === join.ownerAlias);
1188
+ // https://stackoverflow.com/a/56815807/3665878
1189
+ if (parentJoin) {
1190
+ const nested = (parentJoin.nested ??= new Set());
1191
+ join.type = join.type === enums_1.JoinType.innerJoin || [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(parentJoin.prop.kind)
1192
+ ? enums_1.JoinType.nestedInnerJoin
1193
+ : enums_1.JoinType.nestedLeftJoin;
1194
+ nested.add(join);
1195
+ }
1175
1196
  if (join.cond[k]) {
1176
1197
  join.cond = { [op ?? '$and']: [join.cond, { [k]: cond[k] }] };
1177
1198
  }
@@ -1189,6 +1210,7 @@ class QueryBuilder {
1189
1210
  const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
1190
1211
  .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
1191
1212
  .process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true });
1213
+ joins = Object.values(this._joins); // there might be new joins created by processing the `populateWhere` object
1192
1214
  replaceOnConditions(cond);
1193
1215
  }
1194
1216
  }
@@ -23,6 +23,10 @@ export declare class QueryBuilderHelper {
23
23
  joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: JoinType, cond?: Dictionary, schema?: string): JoinOptions;
24
24
  joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: JoinType, cond: Dictionary, path: string, schema?: string): Dictionary<JoinOptions>;
25
25
  processJoins(qb: Knex.QueryBuilder, joins: Dictionary<JoinOptions>, schema?: string): void;
26
+ createJoinExpression(join: JoinOptions, joins: Dictionary<JoinOptions>, schema?: string): {
27
+ sql: string;
28
+ params: Knex.Value[];
29
+ };
26
30
  private processJoinClause;
27
31
  private wrapQueryGroup;
28
32
  mapJoinColumns(type: QueryType, join: JoinOptions): (string | Knex.Raw)[];
@@ -183,56 +183,75 @@ class QueryBuilderHelper {
183
183
  }
184
184
  processJoins(qb, joins, schema) {
185
185
  Object.values(joins).forEach(join => {
186
- let table = join.table;
187
- const method = join.type === enums_1.JoinType.pivotJoin ? 'left join' : join.type;
188
- const conditions = [];
189
- const params = [];
190
- schema = join.schema && join.schema !== '*' ? join.schema : schema;
191
- if (schema) {
192
- table = `${schema}.${table}`;
186
+ if ([enums_1.JoinType.nestedInnerJoin, enums_1.JoinType.nestedLeftJoin].includes(join.type)) {
187
+ return;
193
188
  }
194
- if (join.prop.name !== '__subquery__') {
195
- join.primaryKeys.forEach((primaryKey, idx) => {
196
- const right = `${join.alias}.${join.joinColumns[idx]}`;
197
- if (join.prop.formula) {
198
- const alias = this.platform.quoteIdentifier(join.ownerAlias);
199
- const left = join.prop.formula(alias);
200
- conditions.push(`${left} = ${this.knex.ref(right)}`);
201
- return;
202
- }
203
- const left = join.prop.object && join.prop.fieldNameRaw
204
- ? join.prop.fieldNameRaw.replaceAll(core_1.ALIAS_REPLACEMENT, join.ownerAlias)
205
- : this.knex.ref(`${join.ownerAlias}.${primaryKey}`);
189
+ const { sql, params } = this.createJoinExpression(join, joins, schema);
190
+ qb.joinRaw(sql, params);
191
+ });
192
+ }
193
+ createJoinExpression(join, joins, schema) {
194
+ let table = join.table;
195
+ const method = {
196
+ [enums_1.JoinType.nestedInnerJoin]: 'inner join',
197
+ [enums_1.JoinType.nestedLeftJoin]: 'left join',
198
+ [enums_1.JoinType.pivotJoin]: 'left join',
199
+ }[join.type] ?? join.type;
200
+ const conditions = [];
201
+ const params = [];
202
+ schema = join.schema && join.schema !== '*' ? join.schema : schema;
203
+ if (schema) {
204
+ table = `${schema}.${table}`;
205
+ }
206
+ if (join.prop.name !== '__subquery__') {
207
+ join.primaryKeys.forEach((primaryKey, idx) => {
208
+ const right = `${join.alias}.${join.joinColumns[idx]}`;
209
+ if (join.prop.formula) {
210
+ const alias = this.platform.quoteIdentifier(join.ownerAlias);
211
+ const left = join.prop.formula(alias);
206
212
  conditions.push(`${left} = ${this.knex.ref(right)}`);
207
- });
208
- }
209
- if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
210
- const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
211
- const alias = join.inverseAlias ?? join.alias;
212
- join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
213
- }
214
- for (const key of Object.keys(join.cond)) {
215
- const hasPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
216
- const newKey = hasPrefix ? key : `${join.alias}.${key}`;
217
- const clause = this.processJoinClause(newKey, join.cond[key], join.alias, params);
218
- /* istanbul ignore else */
219
- if (clause !== '()') {
220
- conditions.push(clause);
213
+ return;
221
214
  }
215
+ const left = join.prop.object && join.prop.fieldNameRaw
216
+ ? join.prop.fieldNameRaw.replaceAll(core_1.ALIAS_REPLACEMENT, join.ownerAlias)
217
+ : this.knex.ref(`${join.ownerAlias}.${primaryKey}`);
218
+ conditions.push(`${left} = ${this.knex.ref(right)}`);
219
+ });
220
+ }
221
+ if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
222
+ const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
223
+ const alias = join.inverseAlias ?? join.alias;
224
+ join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
225
+ }
226
+ for (const key of Object.keys(join.cond)) {
227
+ const hasPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
228
+ const newKey = hasPrefix ? key : `${join.alias}.${key}`;
229
+ const clause = this.processJoinClause(newKey, join.cond[key], join.alias, params);
230
+ /* istanbul ignore else */
231
+ if (clause !== '()') {
232
+ conditions.push(clause);
222
233
  }
223
- let sql = method + ' ';
224
- if (join.subquery) {
225
- sql += `(${join.subquery})`;
226
- }
227
- else {
228
- sql += this.knex.ref(table);
229
- }
230
- sql += ` as ${this.knex.ref(join.alias)}`;
231
- if (conditions.length > 0) {
232
- sql += ` on ${conditions.join(' and ')}`;
234
+ }
235
+ let sql = method + ' ';
236
+ if (join.nested) {
237
+ sql += `(${this.knex.ref(table)} as ${this.knex.ref(join.alias)}`;
238
+ for (const nested of join.nested) {
239
+ const { sql: nestedSql, params: nestedParams } = this.createJoinExpression(nested, joins, schema);
240
+ sql += ' ' + nestedSql;
241
+ params.unshift(...nestedParams);
233
242
  }
234
- return qb.joinRaw(sql, params);
235
- });
243
+ sql += `)`;
244
+ }
245
+ else if (join.subquery) {
246
+ sql += `(${join.subquery}) as ${this.knex.ref(join.alias)}`;
247
+ }
248
+ else {
249
+ sql += `${this.knex.ref(table)} as ${this.knex.ref(join.alias)}`;
250
+ }
251
+ if (conditions.length > 0) {
252
+ sql += ` on ${conditions.join(' and ')}`;
253
+ }
254
+ return { sql, params };
236
255
  }
237
256
  processJoinClause(key, value, alias, params, operator = '$eq') {
238
257
  if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
package/query/enums.d.ts CHANGED
@@ -9,6 +9,8 @@ export declare enum QueryType {
9
9
  export declare enum JoinType {
10
10
  leftJoin = "left join",
11
11
  innerJoin = "inner join",
12
+ nestedLeftJoin = "nested left join",
13
+ nestedInnerJoin = "nested inner join",
12
14
  pivotJoin = "pivot join",
13
15
  innerJoinLateral = "inner join lateral",
14
16
  leftJoinLateral = "left join lateral"
package/query/enums.js CHANGED
@@ -14,6 +14,8 @@ var JoinType;
14
14
  (function (JoinType) {
15
15
  JoinType["leftJoin"] = "left join";
16
16
  JoinType["innerJoin"] = "inner join";
17
+ JoinType["nestedLeftJoin"] = "nested left join";
18
+ JoinType["nestedInnerJoin"] = "nested inner join";
17
19
  JoinType["pivotJoin"] = "pivot join";
18
20
  JoinType["innerJoinLateral"] = "inner join lateral";
19
21
  JoinType["leftJoinLateral"] = "left join lateral";
package/typings.d.ts CHANGED
@@ -27,6 +27,7 @@ export interface JoinOptions {
27
27
  cond: Dictionary;
28
28
  cond_?: Dictionary;
29
29
  subquery?: string;
30
+ nested?: Set<JoinOptions>;
30
31
  }
31
32
  export interface Column {
32
33
  name: string;