@mikro-orm/knex 7.0.0-dev.6 → 7.0.0-dev.8

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.
@@ -81,6 +81,7 @@ export class AbstractSqlConnection extends Connection {
81
81
  this.logQuery(this.platform.getSavepointSQL(savepointName));
82
82
  return trx;
83
83
  }
84
+ await this.ensureConnection();
84
85
  await options.eventBroadcaster?.dispatchEvent(EventType.beforeTransactionStart);
85
86
  let trxBuilder = this.client.startTransaction();
86
87
  if (options.isolationLevel) {
@@ -14,12 +14,12 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
14
14
  protected constructor(config: Configuration, platform: Platform, connection: Constructor<Connection>, connector: string[]);
15
15
  getPlatform(): Platform;
16
16
  createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
17
- find<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P, F, E>): Promise<EntityData<T>[]>;
18
- findOne<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P, F, E>): Promise<EntityData<T> | null>;
17
+ find<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: string, where: ObjectQuery<T>, options?: FindOptions<T, P, F, E>): Promise<EntityData<T>[]>;
18
+ findOne<T extends object, P extends string = never, F extends string = PopulatePath.ALL, E extends string = never>(entityName: string, where: ObjectQuery<T>, options?: FindOneOptions<T, P, F, E>): Promise<EntityData<T> | null>;
19
19
  protected hasToManyJoins<T extends object>(hint: PopulateOptions<T>, meta: EntityMetadata<T>): boolean;
20
- findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
21
- countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T, any>): Promise<number>;
22
- protected findFromVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any> | CountOptions<T, any>, type: QueryType): Promise<EntityData<T>[] | number>;
20
+ findVirtual<T extends object>(entityName: string, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
21
+ countVirtual<T extends object>(entityName: string, where: ObjectQuery<T>, options: CountOptions<T, any>): Promise<number>;
22
+ protected findFromVirtual<T extends object>(entityName: string, where: ObjectQuery<T>, options: FindOptions<T, any> | CountOptions<T, any>, type: QueryType): Promise<EntityData<T>[] | number>;
23
23
  protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType): Promise<T[] | number>;
24
24
  mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?: QueryBuilder<T, any, any, any>, map?: Dictionary): EntityData<T> | null;
25
25
  private mapJoinedProps;
@@ -63,6 +63,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
63
63
  if (options.lockMode) {
64
64
  qb.setLockMode(options.lockMode, options.lockTableAliases);
65
65
  }
66
+ if (options.em) {
67
+ await qb.applyJoinedFilters(options.em, options.filters);
68
+ }
66
69
  const result = await this.rethrow(qb.execute('all'));
67
70
  if (isCursorPagination && !first && !!last) {
68
71
  result.reverse();
@@ -437,7 +440,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
437
440
  else {
438
441
  const field = prop.fieldNames[0];
439
442
  if (!duplicates.includes(field) || !usedDups.includes(field)) {
440
- if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && !isRaw(row[prop.name])) {
443
+ if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && row[prop.name] != null && !isRaw(row[prop.name])) {
441
444
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
442
445
  }
443
446
  else {
@@ -588,7 +591,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
588
591
  if (key in data[idx]) {
589
592
  const pks = Utils.getOrderedPrimaryKeys(cond, meta);
590
593
  sql += ` when (${pkCond}) then `;
591
- if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && !isRaw(data[idx][key])) {
594
+ if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && data[idx][prop.name] != null && !isRaw(data[idx][key])) {
592
595
  sql += prop.customType.convertToDatabaseValueSQL('?', this.platform);
593
596
  }
594
597
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "7.0.0-dev.6",
3
+ "version": "7.0.0-dev.8",
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": {
@@ -54,9 +54,9 @@
54
54
  "sqlstring": "2.3.3"
55
55
  },
56
56
  "devDependencies": {
57
- "@mikro-orm/core": "^6.4.7"
57
+ "@mikro-orm/core": "^6.4.9"
58
58
  },
59
59
  "peerDependencies": {
60
- "@mikro-orm/core": "7.0.0-dev.6"
60
+ "@mikro-orm/core": "7.0.0-dev.8"
61
61
  }
62
62
  }
@@ -69,7 +69,7 @@ export class CriteriaNode {
69
69
  let joinAlias = qb.getAliasForJoinPath(this.getPath());
70
70
  if (!joinAlias && this.parent && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind) && this.prop.owner) {
71
71
  joinAlias = qb.getAliasForJoinPath(this.parent.getPath());
72
- return Utils.getPrimaryKeyHash(this.prop.ownColumns.map(col => `${joinAlias ?? qb.alias}.${col}`));
72
+ return Utils.getPrimaryKeyHash(this.prop.joinColumns.map(col => `${joinAlias ?? qb.alias}.${col}`));
73
73
  }
74
74
  const alias = joinAlias ?? qb.alias;
75
75
  if (this.prop.kind === ReferenceKind.MANY_TO_MANY) {
@@ -200,16 +200,18 @@ export class ObjectCriteriaNode extends CriteriaNode {
200
200
  const operator = Utils.isPlainObject(this.payload) && Object.keys(this.payload).every(k => Utils.isOperator(k, false));
201
201
  const field = `${alias}.${this.prop.name}`;
202
202
  const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
203
+ const path = this.getPath();
203
204
  if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
204
- qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, this.getPath());
205
+ qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
205
206
  }
206
207
  else {
207
208
  const prev = qb._fields?.slice();
208
- qb[method](field, nestedAlias, undefined, JoinType.leftJoin, this.getPath());
209
+ qb[method](field, nestedAlias, undefined, JoinType.leftJoin, path);
209
210
  if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
210
211
  qb._fields = prev;
211
212
  }
212
213
  }
214
+ qb.scheduleFilterCheck(path);
213
215
  return nestedAlias;
214
216
  }
215
217
  isPrefixed(field) {
@@ -1,5 +1,5 @@
1
1
  import { inspect } from 'node:util';
2
- 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, type Transaction } from '@mikro-orm/core';
2
+ import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityKey, type EntityManager, 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, type Transaction } from '@mikro-orm/core';
3
3
  import { JoinType, QueryType } from './enums.js';
4
4
  import type { AbstractSqlDriver } from '../AbstractSqlDriver.js';
5
5
  import { type Alias, type OnConflictClause, QueryBuilderHelper } from './QueryBuilderHelper.js';
@@ -141,6 +141,15 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
141
141
  * Apply filters to the QB where condition.
142
142
  */
143
143
  applyFilters(filterOptions?: Dictionary<boolean | Dictionary> | string[] | boolean): Promise<void>;
144
+ private readonly autoJoinedPaths;
145
+ /**
146
+ * @internal
147
+ */
148
+ scheduleFilterCheck(path: string): void;
149
+ /**
150
+ * @internal
151
+ */
152
+ applyJoinedFilters(em: EntityManager, filterOptions?: Dictionary<boolean | Dictionary> | string[] | boolean): Promise<void>;
144
153
  withSubQuery(subQuery: RawQueryFragment | NativeQueryBuilder, alias: string): this;
145
154
  where(cond: QBFilterQuery<Entity>, operator?: keyof typeof GroupOperator): this;
146
155
  where(cond: string, params?: any[], operator?: keyof typeof GroupOperator): this;
@@ -257,6 +257,34 @@ export class QueryBuilder {
257
257
  const cond = await this.em.applyFilters(this.mainAlias.entityName, {}, filterOptions, 'read');
258
258
  this.andWhere(cond);
259
259
  }
260
+ autoJoinedPaths = [];
261
+ /**
262
+ * @internal
263
+ */
264
+ scheduleFilterCheck(path) {
265
+ this.autoJoinedPaths.push(path);
266
+ }
267
+ /**
268
+ * @internal
269
+ */
270
+ async applyJoinedFilters(em, filterOptions = {}) {
271
+ for (const path of this.autoJoinedPaths) {
272
+ const join = this.getJoinForPath(path);
273
+ if (join.type === JoinType.pivotJoin) {
274
+ continue;
275
+ }
276
+ const cond = await em.applyFilters(join.prop.type, join.cond, filterOptions, 'read');
277
+ if (Utils.hasObjectKeys(cond)) {
278
+ if (Utils.hasObjectKeys(join.cond)) {
279
+ /* istanbul ignore next */
280
+ join.cond = { $and: [join.cond, cond] };
281
+ }
282
+ else {
283
+ join.cond = { ...cond };
284
+ }
285
+ }
286
+ }
287
+ }
260
288
  withSubQuery(subQuery, alias) {
261
289
  this.ensureNotFinalized();
262
290
  if (subQuery instanceof RawQueryFragment) {
@@ -1152,7 +1180,7 @@ export class QueryBuilder {
1152
1180
  let joins = Object.values(this._joins);
1153
1181
  for (const join of joins) {
1154
1182
  join.cond_ ??= join.cond;
1155
- join.cond = filter ? { ...join.cond } : {};
1183
+ join.cond = { ...join.cond };
1156
1184
  }
1157
1185
  if (typeof this[key] === 'object') {
1158
1186
  const cond = CriteriaNodeFactory
@@ -699,16 +699,28 @@ export class DatabaseTable {
699
699
  }
700
700
  addIndex(meta, index, type) {
701
701
  const properties = Utils.unique(Utils.flatten(Utils.asArray(index.properties).map(prop => {
702
- const root = prop.replace(/\..+$/, '');
702
+ const parts = prop.split('.');
703
+ const root = parts[0];
703
704
  if (meta.properties[prop]) {
704
705
  if (meta.properties[prop].embeddedPath) {
705
706
  return [meta.properties[prop].embeddedPath.join('.')];
706
707
  }
707
708
  return meta.properties[prop].fieldNames;
708
709
  }
710
+ const rootProp = meta.properties[root];
711
+ // inline embedded property index, we need to find the field name of the child property
712
+ if (rootProp?.embeddable && !rootProp.object && parts.length > 1) {
713
+ const expand = (p, i) => {
714
+ if (parts.length === i) {
715
+ return p.fieldNames[0];
716
+ }
717
+ return expand(p.embeddedProps[parts[i]], i + 1);
718
+ };
719
+ return [expand(rootProp, 1)];
720
+ }
709
721
  // json index, we need to rename the column only
710
- if (meta.properties[root]) {
711
- return [prop.replace(root, meta.properties[root].fieldNames[0])];
722
+ if (rootProp) {
723
+ return [prop.replace(root, rootProp.fieldNames[0])];
712
724
  }
713
725
  /* v8 ignore next */
714
726
  return [prop];
package/typings.d.ts CHANGED
@@ -163,6 +163,7 @@ export interface IQueryBuilder<T> {
163
163
  setFlag(flag: QueryFlag): this;
164
164
  unsetFlag(flag: QueryFlag): this;
165
165
  hasFlag(flag: QueryFlag): boolean;
166
+ scheduleFilterCheck(path: string): void;
166
167
  }
167
168
  export interface ICriteriaNodeProcessOptions {
168
169
  alias?: string;