@mikro-orm/knex 6.0.0-dev.116 → 6.0.0-dev.117

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.
@@ -1,5 +1,5 @@
1
1
  import type { Knex } from 'knex';
2
- import { type AnyEntity, type Collection, type Configuration, type ConnectionType, type Constructor, type CountOptions, DatabaseDriver, type DeleteOptions, type Dictionary, type DriverMethodOptions, type EntityData, type EntityDictionary, type EntityField, EntityManagerType, type EntityMetadata, type EntityName, type EntityProperty, type FilterQuery, type FindOneOptions, type FindOptions, type IDatabaseDriver, type LockOptions, type LoggingOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type PopulateOptions, type Primary, type QueryOrderMap, type QueryResult, type Transaction, type UpsertManyOptions, type UpsertOptions, type OrderDefinition } from '@mikro-orm/core';
2
+ import { type AnyEntity, type Collection, type Configuration, type ConnectionType, type Constructor, type CountOptions, DatabaseDriver, type DeleteOptions, type Dictionary, type DriverMethodOptions, type EntityData, type EntityDictionary, type EntityField, EntityManagerType, type EntityMetadata, type EntityName, type EntityProperty, type FilterQuery, type FindOneOptions, type FindOptions, type IDatabaseDriver, type LockOptions, type LoggingOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type OrderDefinition, type PopulateOptions, type Primary, type QueryOrderMap, type QueryResult, type Transaction, type UpsertManyOptions, type UpsertOptions } from '@mikro-orm/core';
3
3
  import type { AbstractSqlConnection } from './AbstractSqlConnection';
4
4
  import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
5
5
  import { QueryBuilder, QueryType } from './query';
@@ -28,7 +28,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
28
28
  nativeUpdate<T extends object>(entityName: string, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T> & UpsertOptions<T>): Promise<QueryResult<T>>;
29
29
  nativeUpdateMany<T extends object>(entityName: string, where: FilterQuery<T>[], data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T> & UpsertManyOptions<T>): Promise<QueryResult<T>>;
30
30
  nativeDelete<T extends object>(entityName: string, where: FilterQuery<T> | string | any, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
31
- syncCollection<T extends object, O extends object>(coll: Collection<T, O>, options?: DriverMethodOptions): Promise<void>;
31
+ syncCollections<T extends object, O extends object>(collections: Iterable<Collection<T, O>>, options?: DriverMethodOptions): Promise<void>;
32
32
  loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: OrderDefinition<T>, ctx?: Transaction, options?: FindOptions<T, any, any>, pivotJoin?: boolean): Promise<Dictionary<T[]>>;
33
33
  private getPivotOrderBy;
34
34
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(queryOrKnex: string | Knex.QueryBuilder | Knex.Raw, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<T>;
@@ -54,9 +54,6 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
54
54
  }): ConnectionType;
55
55
  protected extractManyToMany<T>(entityName: string, data: EntityDictionary<T>): EntityData<T>;
56
56
  protected processManyToMany<T extends object>(meta: EntityMetadata<T> | undefined, pks: Primary<T>[], collections: EntityData<T>, clear: boolean, options?: DriverMethodOptions): Promise<void>;
57
- protected updateCollectionDiff<T extends object, O extends object>(meta: EntityMetadata<O>, prop: EntityProperty<O>, pks: Primary<O>[], deleteDiff: Primary<T>[][] | boolean, insertDiff: Primary<T>[][], options?: DriverMethodOptions & {
58
- ownerSchema?: string;
59
- }): Promise<void>;
60
57
  lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
61
58
  protected buildJoinedPropsOrderBy<T extends object>(entityName: string, qb: QueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], parentPath?: string): QueryOrderMap<T>[];
62
59
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
@@ -4,6 +4,7 @@ exports.AbstractSqlDriver = void 0;
4
4
  const core_1 = require("@mikro-orm/core");
5
5
  const query_1 = require("./query");
6
6
  const SqlEntityManager_1 = require("./SqlEntityManager");
7
+ const PivotCollectionPersister_1 = require("./PivotCollectionPersister");
7
8
  class AbstractSqlDriver extends core_1.DatabaseDriver {
8
9
  [core_1.EntityManagerType];
9
10
  connection;
@@ -525,55 +526,64 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
525
526
  const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
526
527
  return this.rethrow(qb.execute('run', false));
527
528
  }
528
- async syncCollection(coll, options) {
529
- const wrapped = (0, core_1.helper)(coll.owner);
530
- const meta = wrapped.__meta;
531
- const pks = wrapped.getPrimaryKeys(true);
532
- const snap = coll.getSnapshot();
533
- const includes = (arr, item) => !!arr.find(i => core_1.Utils.equals(i, item));
534
- const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
535
- const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
536
- const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
537
- const insertDiff = current.filter(item => !includes(snapshot, item));
538
- const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
539
- const equals = core_1.Utils.equals(current, target);
540
- const ctx = options?.ctx;
541
- // wrong order if we just delete and insert to the end (only owning sides can have fixed order)
542
- if (coll.property.owner && coll.property.fixedOrder && !equals && Array.isArray(deleteDiff)) {
543
- deleteDiff.length = insertDiff.length = 0;
544
- deleteDiff.push(...snapshot);
545
- insertDiff.push(...current);
546
- }
547
- if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
548
- const cols = coll.property.referencedColumnNames;
549
- const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
550
- .withSchema(this.getSchemaName(meta, options));
551
- if (coll.getSnapshot() === undefined) {
552
- if (coll.property.orphanRemoval) {
553
- const kqb = qb.delete({ [coll.property.mappedBy]: pks })
529
+ async syncCollections(collections, options) {
530
+ const groups = {};
531
+ for (const coll of collections) {
532
+ const wrapped = (0, core_1.helper)(coll.owner);
533
+ const meta = wrapped.__meta;
534
+ const pks = wrapped.getPrimaryKeys(true);
535
+ const snap = coll.getSnapshot();
536
+ const includes = (arr, item) => !!arr.find(i => core_1.Utils.equals(i, item));
537
+ const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
538
+ const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
539
+ const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
540
+ const insertDiff = current.filter(item => !includes(snapshot, item));
541
+ const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
542
+ const equals = core_1.Utils.equals(current, target);
543
+ // wrong order if we just delete and insert to the end (only owning sides can have fixed order)
544
+ if (coll.property.owner && coll.property.fixedOrder && !equals && Array.isArray(deleteDiff)) {
545
+ deleteDiff.length = insertDiff.length = 0;
546
+ deleteDiff.push(...snapshot);
547
+ insertDiff.push(...current);
548
+ }
549
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
550
+ const cols = coll.property.referencedColumnNames;
551
+ const qb = this.createQueryBuilder(coll.property.type, options?.ctx, 'write')
552
+ .withSchema(this.getSchemaName(meta, options));
553
+ if (coll.getSnapshot() === undefined) {
554
+ if (coll.property.orphanRemoval) {
555
+ const kqb = qb.delete({ [coll.property.mappedBy]: pks })
556
+ .getKnexQuery()
557
+ .whereNotIn(cols, insertDiff);
558
+ return this.rethrow(this.execute(kqb));
559
+ }
560
+ const kqb = qb.update({ [coll.property.mappedBy]: null })
554
561
  .getKnexQuery()
555
562
  .whereNotIn(cols, insertDiff);
556
563
  return this.rethrow(this.execute(kqb));
557
564
  }
558
- const kqb = qb.update({ [coll.property.mappedBy]: null })
565
+ const kqb = qb.update({ [coll.property.mappedBy]: pks })
559
566
  .getKnexQuery()
560
- .whereNotIn(cols, insertDiff);
567
+ .whereIn(cols, insertDiff);
561
568
  return this.rethrow(this.execute(kqb));
562
569
  }
563
- const kqb = qb.update({ [coll.property.mappedBy]: pks })
564
- .getKnexQuery()
565
- .whereIn(cols, insertDiff);
566
- return this.rethrow(this.execute(kqb));
567
- }
568
- /* istanbul ignore next */
569
- const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
570
- const pivotMeta = this.metadata.find(coll.property.pivotEntity);
571
- if (pivotMeta.schema === '*') {
572
570
  /* istanbul ignore next */
573
- options ??= {};
574
- options.schema = ownerSchema;
571
+ const pivotMeta = this.metadata.find(coll.property.pivotEntity);
572
+ let schema = pivotMeta.schema;
573
+ if (schema === '*') {
574
+ const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
575
+ schema = coll.property.owner ? ownerSchema : this.config.get('schema');
576
+ }
577
+ else if (schema == null) {
578
+ schema = this.config.get('schema');
579
+ }
580
+ const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
581
+ const persister = groups[tableName] ??= new PivotCollectionPersister_1.PivotCollectionPersister(pivotMeta, this, options?.ctx, schema);
582
+ persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks);
583
+ }
584
+ for (const persister of core_1.Utils.values(groups)) {
585
+ await this.rethrow(persister.execute());
575
586
  }
576
- return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
577
587
  }
578
588
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
579
589
  const pivotMeta = this.metadata.find(prop.pivotEntity);
@@ -818,48 +828,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
818
828
  }
819
829
  for (const prop of meta.relations) {
820
830
  if (collections[prop.name]) {
821
- await this.rethrow(this.updateCollectionDiff(meta, prop, pks, clear, collections[prop.name], options));
822
- }
823
- }
824
- }
825
- async updateCollectionDiff(meta, prop, pks, deleteDiff, insertDiff, options) {
826
- if (!deleteDiff) {
827
- deleteDiff = [];
828
- }
829
- const pivotMeta = this.metadata.find(prop.pivotEntity);
830
- if (deleteDiff === true || deleteDiff.length > 0) {
831
- const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(pivotMeta, options));
832
- const knex = qb1.getKnex();
833
- if (Array.isArray(deleteDiff)) {
834
- knex.whereIn(prop.inverseJoinColumns, deleteDiff);
831
+ const pivotMeta = this.metadata.find(prop.pivotEntity);
832
+ const persister = new PivotCollectionPersister_1.PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema);
833
+ persister.enqueueUpdate(prop, collections[prop.name], clear, pks);
834
+ await this.rethrow(persister.execute());
835
835
  }
836
- prop.joinColumns.forEach((joinColumn, idx) => knex.andWhere(joinColumn, pks[idx]));
837
- await this.execute(knex.delete());
838
- }
839
- if (insertDiff.length === 0) {
840
- return;
841
- }
842
- const items = insertDiff.map(item => {
843
- const cond = {};
844
- prop.joinColumns.forEach((joinColumn, idx) => cond[joinColumn] = pks[idx]);
845
- prop.inverseJoinColumns.forEach((inverseJoinColumn, idx) => cond[inverseJoinColumn] = item[idx]);
846
- return cond;
847
- });
848
- /* istanbul ignore else */
849
- if (this.platform.allowsMultiInsert()) {
850
- await this.nativeInsertMany(prop.pivotEntity, items, {
851
- ...options,
852
- convertCustomTypes: false,
853
- processCollections: false,
854
- });
855
- }
856
- else {
857
- await core_1.Utils.runSerial(items, item => {
858
- return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
859
- .withSchema(this.getSchemaName(pivotMeta, options))
860
- .insert(item)
861
- .execute('run', false);
862
- });
863
836
  }
864
837
  }
865
838
  async lockPessimistic(entity, options) {
@@ -0,0 +1,17 @@
1
+ import { type EntityMetadata, type EntityProperty, type Primary } from '@mikro-orm/core';
2
+ import { type AbstractSqlDriver } from './AbstractSqlDriver';
3
+ export declare class PivotCollectionPersister<Entity extends object> {
4
+ private readonly meta;
5
+ private readonly driver;
6
+ private readonly ctx?;
7
+ private readonly schema?;
8
+ private readonly platform;
9
+ private readonly inserts;
10
+ private readonly deletes;
11
+ private order;
12
+ constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: any, schema?: string | undefined);
13
+ enqueueUpdate(prop: EntityProperty<Entity>, insertDiff: Primary<Entity>[][], deleteDiff: Primary<Entity>[][] | boolean, pks: Primary<Entity>[]): void;
14
+ private enqueueInsert;
15
+ private enqueueDelete;
16
+ execute(): Promise<void>;
17
+ }
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PivotCollectionPersister = void 0;
4
+ const core_1 = require("@mikro-orm/core");
5
+ class InsertStatement {
6
+ keys;
7
+ data;
8
+ order;
9
+ constructor(keys, data, order) {
10
+ this.keys = keys;
11
+ this.data = data;
12
+ this.order = order;
13
+ }
14
+ getHash() {
15
+ return JSON.stringify(this.data);
16
+ }
17
+ getData() {
18
+ const data = {};
19
+ this.keys.forEach((key, idx) => data[key] = this.data[idx]);
20
+ return data;
21
+ }
22
+ }
23
+ class DeleteStatement {
24
+ keys;
25
+ cond;
26
+ constructor(keys, cond) {
27
+ this.keys = keys;
28
+ this.cond = cond;
29
+ }
30
+ getHash() {
31
+ return JSON.stringify(this.cond);
32
+ }
33
+ getCondition() {
34
+ const cond = {};
35
+ this.keys.forEach((key, idx) => cond[key] = this.cond[idx]);
36
+ return cond;
37
+ }
38
+ }
39
+ class PivotCollectionPersister {
40
+ meta;
41
+ driver;
42
+ ctx;
43
+ schema;
44
+ platform;
45
+ inserts = new Map();
46
+ deletes = new Map();
47
+ order = 0;
48
+ constructor(meta, driver, ctx, schema) {
49
+ this.meta = meta;
50
+ this.driver = driver;
51
+ this.ctx = ctx;
52
+ this.schema = schema;
53
+ this.platform = this.driver.getPlatform();
54
+ }
55
+ enqueueUpdate(prop, insertDiff, deleteDiff, pks) {
56
+ if (insertDiff.length) {
57
+ this.enqueueInsert(prop, insertDiff, pks);
58
+ }
59
+ if (deleteDiff === true || (Array.isArray(deleteDiff) && deleteDiff.length)) {
60
+ this.enqueueDelete(prop, deleteDiff, pks);
61
+ }
62
+ }
63
+ enqueueInsert(prop, insertDiff, pks) {
64
+ for (const fks of insertDiff) {
65
+ const data = prop.owner ? [...fks, ...pks] : [...pks, ...fks];
66
+ const keys = prop.owner
67
+ ? [...prop.inverseJoinColumns, ...prop.joinColumns]
68
+ : [...prop.joinColumns, ...prop.inverseJoinColumns];
69
+ const statement = new InsertStatement(keys, data, this.order++);
70
+ const hash = statement.getHash();
71
+ if (prop.owner || !this.inserts.has(hash)) {
72
+ this.inserts.set(hash, statement);
73
+ }
74
+ }
75
+ }
76
+ enqueueDelete(prop, deleteDiff, pks) {
77
+ if (deleteDiff === true) {
78
+ const statement = new DeleteStatement(prop.joinColumns, pks);
79
+ this.deletes.set(statement.getHash(), statement);
80
+ return;
81
+ }
82
+ for (const fks of deleteDiff) {
83
+ const data = prop.owner ? [...fks, ...pks] : [...pks, ...fks];
84
+ const keys = prop.owner
85
+ ? [...prop.inverseJoinColumns, ...prop.joinColumns]
86
+ : [...prop.joinColumns, ...prop.inverseJoinColumns];
87
+ const statement = new DeleteStatement(keys, data);
88
+ this.deletes.set(statement.getHash(), statement);
89
+ }
90
+ }
91
+ async execute() {
92
+ if (this.deletes.size > 0) {
93
+ const knex = this.driver.createQueryBuilder(this.meta.className, this.ctx, 'write')
94
+ .withSchema(this.schema)
95
+ .getKnex();
96
+ for (const item of this.deletes.values()) {
97
+ knex.orWhere(item.getCondition());
98
+ }
99
+ await this.driver.execute(knex.delete());
100
+ }
101
+ if (this.inserts.size === 0) {
102
+ return;
103
+ }
104
+ let items = [];
105
+ for (const insert of this.inserts.values()) {
106
+ items[insert.order] = insert.getData();
107
+ }
108
+ items = items.filter(i => i);
109
+ /* istanbul ignore else */
110
+ if (this.platform.allowsMultiInsert()) {
111
+ await this.driver.nativeInsertMany(this.meta.className, items, {
112
+ ctx: this.ctx,
113
+ schema: this.schema,
114
+ convertCustomTypes: false,
115
+ processCollections: false,
116
+ });
117
+ }
118
+ else {
119
+ await core_1.Utils.runSerial(items, item => {
120
+ return this.driver.createQueryBuilder(this.meta.className, this.ctx, 'write')
121
+ .withSchema(this.schema)
122
+ .insert(item)
123
+ .execute('run', false);
124
+ });
125
+ }
126
+ }
127
+ }
128
+ exports.PivotCollectionPersister = PivotCollectionPersister;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-dev.116",
3
+ "version": "6.0.0-dev.117",
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.6"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "6.0.0-dev.116"
69
+ "@mikro-orm/core": "6.0.0-dev.117"
70
70
  }
71
71
  }