@mikro-orm/mongodb 7.0.4-dev.9 → 7.0.4

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,224 +1,228 @@
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));
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 });
7
27
  }
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 => 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);
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 });
27
36
  }
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 });
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;
65
+ /* 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));
34
71
  }
35
- const promises = metadata
36
- .filter(meta => existing.includes(meta.collection))
37
- .map(meta => this.connection.dropCollection(meta.class));
38
- await Promise.all(promises);
72
+ }
39
73
  }
40
- async update(options = {}) {
41
- await this.create(options);
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 });
42
82
  }
43
- async ensureDatabase() {
44
- return false;
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
+ }
45
94
  }
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
- }
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 });
69
107
  }
70
- await Promise.all(promises);
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
+ });
71
119
  }
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
- });
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('.'))];
116
128
  }
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 [];
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';
205
150
  }
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
- ];
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 [];
223
209
  }
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
+ }
224
228
  }
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,4 +8,8 @@ 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, type MongoOptions as Options, defineMongoConfig as defineConfig, } from './MongoMikroORM.js';
11
+ export {
12
+ MongoMikroORM as MikroORM,
13
+ type MongoOptions as Options,
14
+ defineMongoConfig as defineConfig,
15
+ } 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-dev.9",
3
+ "version": "7.0.4",
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",
@@ -50,10 +50,10 @@
50
50
  "mongodb": "7.1.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@mikro-orm/core": "^7.0.3"
53
+ "@mikro-orm/core": "^7.0.4"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.0.4-dev.9"
56
+ "@mikro-orm/core": "7.0.4"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"