@mikro-orm/mongodb 7.0.4 → 7.0.5-dev.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.
@@ -1,228 +1,224 @@
1
- import { Utils, inspect } from '@mikro-orm/core';
1
+ import { Utils, inspect, } from '@mikro-orm/core';
2
2
  import { AbstractSchemaGenerator } from '@mikro-orm/core/schema';
3
3
  /** Schema generator for MongoDB that manages collections and indexes. */
4
4
  export class MongoSchemaGenerator extends AbstractSchemaGenerator {
5
- static register(orm) {
6
- orm.config.registerExtension('@mikro-orm/schema-generator', () => new MongoSchemaGenerator(orm.em));
7
- }
8
- async create(options = {}) {
9
- await this.connection.ensureConnection();
10
- options.ensureIndexes ??= true;
11
- const existing = await this.connection.listCollections();
12
- const metadata = this.getOrderedMetadata();
13
- /* v8 ignore next */
14
- const promises = metadata
15
- .filter(meta => !existing.includes(meta.collection))
16
- .map(meta =>
17
- this.connection.createCollection(meta.class).catch(err => {
18
- const existsErrorMessage = `Collection ${this.config.get('dbName')}.${meta.collection} already exists.`;
19
- // ignore errors about the collection already existing
20
- if (!(err.name === 'MongoServerError' && err.message.includes(existsErrorMessage))) {
21
- throw err;
22
- }
23
- }),
24
- );
25
- if (options.ensureIndexes) {
26
- await this.ensureIndexes({ ensureCollections: false });
5
+ static register(orm) {
6
+ orm.config.registerExtension('@mikro-orm/schema-generator', () => new MongoSchemaGenerator(orm.em));
27
7
  }
28
- await Promise.all(promises);
29
- }
30
- async drop(options = {}) {
31
- await this.connection.ensureConnection();
32
- const existing = await this.connection.listCollections();
33
- const metadata = this.getOrderedMetadata();
34
- if (options.dropMigrationsTable) {
35
- metadata.push({ collection: this.config.get('migrations').tableName });
36
- }
37
- const promises = metadata
38
- .filter(meta => existing.includes(meta.collection))
39
- .map(meta => this.connection.dropCollection(meta.class));
40
- await Promise.all(promises);
41
- }
42
- async update(options = {}) {
43
- await this.create(options);
44
- }
45
- async ensureDatabase() {
46
- return false;
47
- }
48
- async refresh(options = {}) {
49
- await this.ensureDatabase();
50
- await this.drop();
51
- await this.create(options);
52
- }
53
- async dropIndexes(options) {
54
- await this.connection.ensureConnection();
55
- const db = this.connection.getDb();
56
- const collections = await db.listCollections().toArray();
57
- const promises = [];
58
- for (const collection of collections) {
59
- if (options?.collectionsWithFailedIndexes && !options.collectionsWithFailedIndexes.includes(collection.name)) {
60
- continue;
61
- }
62
- const indexes = await db.collection(collection.name).listIndexes().toArray();
63
- for (const index of indexes) {
64
- const isIdIndex = index.key._id === 1 && Utils.getObjectKeysSize(index.key) === 1;
8
+ async create(options = {}) {
9
+ await this.connection.ensureConnection();
10
+ options.ensureIndexes ??= true;
11
+ const existing = await this.connection.listCollections();
12
+ const metadata = this.getOrderedMetadata();
65
13
  /* v8 ignore next */
66
- if (
67
- !isIdIndex &&
68
- !options?.skipIndexes?.find(idx => idx.collection === collection.name && idx.indexName === index.name)
69
- ) {
70
- promises.push(this.executeQuery(db.collection(collection.name), 'dropIndex', index.name));
14
+ const promises = metadata
15
+ .filter(meta => !existing.includes(meta.collection))
16
+ .map(meta => this.connection.createCollection(meta.class).catch(err => {
17
+ const existsErrorMessage = `Collection ${this.config.get('dbName')}.${meta.collection} already exists.`;
18
+ // ignore errors about the collection already existing
19
+ if (!(err.name === 'MongoServerError' && err.message.includes(existsErrorMessage))) {
20
+ throw err;
21
+ }
22
+ }));
23
+ if (options.ensureIndexes) {
24
+ await this.ensureIndexes({ ensureCollections: false });
25
+ }
26
+ await Promise.all(promises);
27
+ }
28
+ async drop(options = {}) {
29
+ await this.connection.ensureConnection();
30
+ const existing = await this.connection.listCollections();
31
+ const metadata = this.getOrderedMetadata();
32
+ if (options.dropMigrationsTable) {
33
+ metadata.push({ collection: this.config.get('migrations').tableName });
71
34
  }
72
- }
35
+ const promises = metadata
36
+ .filter(meta => existing.includes(meta.collection))
37
+ .map(meta => this.connection.dropCollection(meta.class));
38
+ await Promise.all(promises);
73
39
  }
74
- await Promise.all(promises);
75
- }
76
- async ensureIndexes(options = {}) {
77
- await this.connection.ensureConnection();
78
- options.ensureCollections ??= true;
79
- options.retryLimit ??= 3;
80
- if (options.ensureCollections) {
81
- await this.create({ ensureIndexes: false });
40
+ async update(options = {}) {
41
+ await this.create(options);
82
42
  }
83
- const promises = [];
84
- for (const meta of this.getOrderedMetadata()) {
85
- if (Array.isArray(options?.retry) && !options.retry.includes(meta.collection)) {
86
- continue;
87
- }
88
- promises.push(...this.createIndexes(meta));
89
- promises.push(...this.createUniqueIndexes(meta));
90
- for (const prop of meta.props) {
91
- promises.push(...this.createPropertyIndexes(meta, prop, 'index'));
92
- promises.push(...this.createPropertyIndexes(meta, prop, 'unique'));
93
- }
43
+ async ensureDatabase() {
44
+ return false;
94
45
  }
95
- const res = await Promise.allSettled(promises.map(p => p[1]));
96
- if (res.some(r => r.status === 'rejected') && options.retry !== false) {
97
- const skipIndexes = [];
98
- const collectionsWithFailedIndexes = [];
99
- const errors = [];
100
- for (let i = 0; i < res.length; i++) {
101
- const r = res[i];
102
- if (r.status === 'rejected') {
103
- collectionsWithFailedIndexes.push(promises[i][0]);
104
- errors.push(r.reason);
105
- } else {
106
- skipIndexes.push({ collection: promises[i][0], indexName: r.value });
46
+ async refresh(options = {}) {
47
+ await this.ensureDatabase();
48
+ await this.drop();
49
+ await this.create(options);
50
+ }
51
+ async dropIndexes(options) {
52
+ await this.connection.ensureConnection();
53
+ const db = this.connection.getDb();
54
+ const collections = await db.listCollections().toArray();
55
+ const promises = [];
56
+ for (const collection of collections) {
57
+ if (options?.collectionsWithFailedIndexes && !options.collectionsWithFailedIndexes.includes(collection.name)) {
58
+ continue;
59
+ }
60
+ const indexes = await db.collection(collection.name).listIndexes().toArray();
61
+ for (const index of indexes) {
62
+ const isIdIndex = index.key._id === 1 && Utils.getObjectKeysSize(index.key) === 1;
63
+ /* v8 ignore next */
64
+ if (!isIdIndex &&
65
+ !options?.skipIndexes?.find(idx => idx.collection === collection.name && idx.indexName === index.name)) {
66
+ promises.push(this.executeQuery(db.collection(collection.name), 'dropIndex', index.name));
67
+ }
68
+ }
107
69
  }
108
- }
109
- await this.dropIndexes({ skipIndexes, collectionsWithFailedIndexes });
110
- if (options.retryLimit === 0) {
111
- const details = errors.map(e => e.message).join('\n');
112
- const message = `Failed to create indexes on the following collections: ${collectionsWithFailedIndexes.join(', ')}\n${details}`;
113
- throw new Error(message, { cause: errors });
114
- }
115
- await this.ensureIndexes({
116
- retry: collectionsWithFailedIndexes,
117
- retryLimit: options.retryLimit - 1,
118
- });
70
+ await Promise.all(promises);
119
71
  }
120
- }
121
- mapIndexProperties(index, meta) {
122
- return Utils.flatten(
123
- Utils.asArray(index.properties).map(propName => {
124
- const rootPropName = propName.split('.')[0];
125
- const prop = meta.properties[rootPropName];
126
- if (propName.includes('.')) {
127
- return [prop.fieldNames[0] + propName.substring(propName.indexOf('.'))];
72
+ async ensureIndexes(options = {}) {
73
+ await this.connection.ensureConnection();
74
+ options.ensureCollections ??= true;
75
+ options.retryLimit ??= 3;
76
+ if (options.ensureCollections) {
77
+ await this.create({ ensureIndexes: false });
78
+ }
79
+ const promises = [];
80
+ for (const meta of this.getOrderedMetadata()) {
81
+ if (Array.isArray(options?.retry) && !options.retry.includes(meta.collection)) {
82
+ continue;
83
+ }
84
+ promises.push(...this.createIndexes(meta));
85
+ promises.push(...this.createUniqueIndexes(meta));
86
+ for (const prop of meta.props) {
87
+ promises.push(...this.createPropertyIndexes(meta, prop, 'index'));
88
+ promises.push(...this.createPropertyIndexes(meta, prop, 'unique'));
89
+ }
90
+ }
91
+ const res = await Promise.allSettled(promises.map(p => p[1]));
92
+ if (res.some(r => r.status === 'rejected') && options.retry !== false) {
93
+ const skipIndexes = [];
94
+ const collectionsWithFailedIndexes = [];
95
+ const errors = [];
96
+ for (let i = 0; i < res.length; i++) {
97
+ const r = res[i];
98
+ if (r.status === 'rejected') {
99
+ collectionsWithFailedIndexes.push(promises[i][0]);
100
+ errors.push(r.reason);
101
+ }
102
+ else {
103
+ skipIndexes.push({ collection: promises[i][0], indexName: r.value });
104
+ }
105
+ }
106
+ await this.dropIndexes({ skipIndexes, collectionsWithFailedIndexes });
107
+ if (options.retryLimit === 0) {
108
+ const details = errors.map(e => e.message).join('\n');
109
+ const message = `Failed to create indexes on the following collections: ${collectionsWithFailedIndexes.join(', ')}\n${details}`;
110
+ throw new Error(message, { cause: errors });
111
+ }
112
+ await this.ensureIndexes({
113
+ retry: collectionsWithFailedIndexes,
114
+ retryLimit: options.retryLimit - 1,
115
+ });
128
116
  }
129
- return prop?.fieldNames ?? propName;
130
- }),
131
- );
132
- }
133
- createIndexes(meta) {
134
- const res = [];
135
- meta.indexes.forEach(index => {
136
- let fieldOrSpec;
137
- const properties = this.mapIndexProperties(index, meta);
138
- const collection = this.connection.getCollection(meta.class);
139
- if (Array.isArray(index.options) && index.options.length === 2 && properties.length === 0) {
140
- res.push([collection.collectionName, collection.createIndex(index.options[0], index.options[1])]);
141
- return;
142
- }
143
- if (index.options && properties.length === 0) {
144
- res.push([collection.collectionName, collection.createIndex(index.options)]);
145
- return;
146
- }
147
- if (index.type) {
148
- if (index.type === 'fulltext') {
149
- index.type = 'text';
117
+ }
118
+ mapIndexProperties(index, meta) {
119
+ return Utils.flatten(Utils.asArray(index.properties).map(propName => {
120
+ const rootPropName = propName.split('.')[0];
121
+ const prop = meta.properties[rootPropName];
122
+ if (propName.includes('.')) {
123
+ return [prop.fieldNames[0] + propName.substring(propName.indexOf('.'))];
124
+ }
125
+ return prop?.fieldNames ?? propName;
126
+ }));
127
+ }
128
+ createIndexes(meta) {
129
+ const res = [];
130
+ meta.indexes.forEach(index => {
131
+ let fieldOrSpec;
132
+ const properties = this.mapIndexProperties(index, meta);
133
+ const collection = this.connection.getCollection(meta.class);
134
+ if (Array.isArray(index.options) && index.options.length === 2 && properties.length === 0) {
135
+ res.push([collection.collectionName, collection.createIndex(index.options[0], index.options[1])]);
136
+ return;
137
+ }
138
+ if (index.options && properties.length === 0) {
139
+ res.push([collection.collectionName, collection.createIndex(index.options)]);
140
+ return;
141
+ }
142
+ if (index.type) {
143
+ if (index.type === 'fulltext') {
144
+ index.type = 'text';
145
+ }
146
+ const spec = {};
147
+ properties.forEach(prop => (spec[prop] = index.type));
148
+ fieldOrSpec = spec;
149
+ }
150
+ else {
151
+ fieldOrSpec = properties.reduce((o, i) => {
152
+ o[i] = 1;
153
+ return o;
154
+ }, {});
155
+ }
156
+ // MongoDB uses 'hidden' for invisible indexes
157
+ const indexOptions = {
158
+ name: index.name,
159
+ unique: false,
160
+ ...index.options,
161
+ };
162
+ if (index.invisible) {
163
+ indexOptions.hidden = true;
164
+ }
165
+ res.push([collection.collectionName, this.executeQuery(collection, 'createIndex', fieldOrSpec, indexOptions)]);
166
+ });
167
+ return res;
168
+ }
169
+ async executeQuery(collection, method, ...args) {
170
+ const now = Date.now();
171
+ return collection[method](...args).then((res) => {
172
+ Utils.dropUndefinedProperties(args);
173
+ const query = `db.getCollection('${collection.collectionName}').${method}(${args.map(arg => inspect(arg)).join(', ')});`;
174
+ this.config.getLogger().logQuery({
175
+ level: 'info',
176
+ query,
177
+ took: Date.now() - now,
178
+ });
179
+ return res;
180
+ });
181
+ }
182
+ createUniqueIndexes(meta) {
183
+ const res = [];
184
+ meta.uniques.forEach(index => {
185
+ const properties = this.mapIndexProperties(index, meta);
186
+ const fieldOrSpec = properties.reduce((o, i) => {
187
+ o[i] = 1;
188
+ return o;
189
+ }, {});
190
+ const collection = this.connection.getCollection(meta.class);
191
+ res.push([
192
+ collection.collectionName,
193
+ this.executeQuery(collection, 'createIndex', fieldOrSpec, {
194
+ name: index.name,
195
+ unique: true,
196
+ ...index.options,
197
+ }),
198
+ ]);
199
+ });
200
+ return res;
201
+ }
202
+ createPropertyIndexes(meta, prop, type) {
203
+ if (!prop[type] || !meta.collection) {
204
+ return [];
150
205
  }
151
- const spec = {};
152
- properties.forEach(prop => (spec[prop] = index.type));
153
- fieldOrSpec = spec;
154
- } else {
155
- fieldOrSpec = properties.reduce((o, i) => {
156
- o[i] = 1;
157
- return o;
158
- }, {});
159
- }
160
- // MongoDB uses 'hidden' for invisible indexes
161
- const indexOptions = {
162
- name: index.name,
163
- unique: false,
164
- ...index.options,
165
- };
166
- if (index.invisible) {
167
- indexOptions.hidden = true;
168
- }
169
- res.push([collection.collectionName, this.executeQuery(collection, 'createIndex', fieldOrSpec, indexOptions)]);
170
- });
171
- return res;
172
- }
173
- async executeQuery(collection, method, ...args) {
174
- const now = Date.now();
175
- return collection[method](...args).then(res => {
176
- Utils.dropUndefinedProperties(args);
177
- const query = `db.getCollection('${collection.collectionName}').${method}(${args.map(arg => inspect(arg)).join(', ')});`;
178
- this.config.getLogger().logQuery({
179
- level: 'info',
180
- query,
181
- took: Date.now() - now,
182
- });
183
- return res;
184
- });
185
- }
186
- createUniqueIndexes(meta) {
187
- const res = [];
188
- meta.uniques.forEach(index => {
189
- const properties = this.mapIndexProperties(index, meta);
190
- const fieldOrSpec = properties.reduce((o, i) => {
191
- o[i] = 1;
192
- return o;
193
- }, {});
194
- const collection = this.connection.getCollection(meta.class);
195
- res.push([
196
- collection.collectionName,
197
- this.executeQuery(collection, 'createIndex', fieldOrSpec, {
198
- name: index.name,
199
- unique: true,
200
- ...index.options,
201
- }),
202
- ]);
203
- });
204
- return res;
205
- }
206
- createPropertyIndexes(meta, prop, type) {
207
- if (!prop[type] || !meta.collection) {
208
- return [];
206
+ const collection = this.connection.getCollection(meta.class);
207
+ const fieldOrSpec = prop.embeddedPath
208
+ ? prop.embeddedPath.join('.')
209
+ : prop.fieldNames.reduce((o, i) => {
210
+ o[i] = 1;
211
+ return o;
212
+ }, {});
213
+ return [
214
+ [
215
+ collection.collectionName,
216
+ this.executeQuery(collection, 'createIndex', fieldOrSpec, {
217
+ name: typeof prop[type] === 'string' ? prop[type] : undefined,
218
+ unique: type === 'unique',
219
+ sparse: prop.nullable === true,
220
+ }),
221
+ ],
222
+ ];
209
223
  }
210
- const collection = this.connection.getCollection(meta.class);
211
- const fieldOrSpec = prop.embeddedPath
212
- ? prop.embeddedPath.join('.')
213
- : prop.fieldNames.reduce((o, i) => {
214
- o[i] = 1;
215
- return o;
216
- }, {});
217
- return [
218
- [
219
- collection.collectionName,
220
- this.executeQuery(collection, 'createIndex', fieldOrSpec, {
221
- name: typeof prop[type] === 'string' ? prop[type] : undefined,
222
- unique: type === 'unique',
223
- sparse: prop.nullable === true,
224
- }),
225
- ],
226
- ];
227
- }
228
224
  }
package/README.md CHANGED
@@ -133,7 +133,7 @@ const author = await em.findOneOrFail(Author, 1, {
133
133
  populate: ['books'],
134
134
  });
135
135
  author.name = 'Jon Snow II';
136
- author.books.getItems().forEach(book => (book.title += ' (2nd ed.)'));
136
+ author.books.getItems().forEach(book => book.title += ' (2nd ed.)');
137
137
  author.books.add(orm.em.create(Book, { title: 'New Book', author }));
138
138
 
139
139
  // Flush computes change sets and executes them in a single transaction
package/index.d.ts CHANGED
@@ -8,8 +8,4 @@ export * from './MongoEntityRepository.js';
8
8
  export * from './MongoSchemaGenerator.js';
9
9
  export { MongoEntityManager as EntityManager } from './MongoEntityManager.js';
10
10
  export { MongoEntityRepository as EntityRepository } from './MongoEntityRepository.js';
11
- export {
12
- MongoMikroORM as MikroORM,
13
- type MongoOptions as Options,
14
- defineMongoConfig as defineConfig,
15
- } from './MongoMikroORM.js';
11
+ export { MongoMikroORM as MikroORM, type MongoOptions as Options, defineMongoConfig as defineConfig, } from './MongoMikroORM.js';
package/index.js CHANGED
@@ -8,4 +8,4 @@ export * from './MongoEntityRepository.js';
8
8
  export * from './MongoSchemaGenerator.js';
9
9
  export { MongoEntityManager as EntityManager } from './MongoEntityManager.js';
10
10
  export { MongoEntityRepository as EntityRepository } from './MongoEntityRepository.js';
11
- export { MongoMikroORM as MikroORM, defineMongoConfig as defineConfig } from './MongoMikroORM.js';
11
+ export { MongoMikroORM as MikroORM, defineMongoConfig as defineConfig, } from './MongoMikroORM.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/mongodb",
3
- "version": "7.0.4",
3
+ "version": "7.0.5-dev.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
  "keywords": [
6
6
  "data-mapper",
@@ -53,7 +53,7 @@
53
53
  "@mikro-orm/core": "^7.0.4"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.0.4"
56
+ "@mikro-orm/core": "7.0.5-dev.1"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"