mongoplusplus 1.0.7 → 1.0.9
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/mongoplus.js +46 -80
- package/package.json +3 -3
package/mongoplus.js
CHANGED
|
@@ -1,42 +1,48 @@
|
|
|
1
1
|
const mongoose = require('mongoose');
|
|
2
|
+
|
|
2
3
|
const { performance } = require('perf_hooks');
|
|
4
|
+
|
|
3
5
|
class Mongoplus {
|
|
4
6
|
constructor(mongoURI) {
|
|
5
|
-
|
|
6
7
|
this.mongoURI = mongoURI;
|
|
7
8
|
this.allConnections = [];
|
|
8
9
|
this.currentIndex = 0;
|
|
9
10
|
if (this.mongoURI.filter((uri) => uri.startsWith("readonly")).length == this.mongoURI.length) {
|
|
10
11
|
throw new Error('Some of your URIs must be writable. If it is a mistake remove the `readonly:` flag from your urls')
|
|
11
12
|
}
|
|
12
|
-
|
|
13
13
|
}
|
|
14
|
+
|
|
14
15
|
static readonlydbs = []
|
|
15
|
-
static readonlymodels = []
|
|
16
|
+
static readonlymodels = []
|
|
17
|
+
|
|
16
18
|
Schema(schema) {
|
|
17
19
|
return mongoose.Schema(schema)
|
|
18
20
|
}
|
|
21
|
+
|
|
19
22
|
addIndex(schema, indextype) {
|
|
20
|
-
|
|
23
|
+
// MongoDB 4.2+ no longer supports 'background' option, removing it if present
|
|
24
|
+
const cleanIndexType = { ...indextype };
|
|
25
|
+
delete cleanIndexType.background;
|
|
26
|
+
return schema.index(cleanIndexType)
|
|
21
27
|
}
|
|
28
|
+
|
|
22
29
|
getNextMongoURI() {
|
|
23
30
|
const uri = this.mongoURI[this.currentIndex];
|
|
24
31
|
this.currentIndex = (this.currentIndex + 1) % this.mongoURI.length;
|
|
25
32
|
return uri;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
connectToAll() {
|
|
35
|
+
connectToAll(connectionOptions = {}) {
|
|
29
36
|
for (let i = 0; i < this.mongoURI.length; i++) {
|
|
30
|
-
|
|
31
37
|
const uri = this.mongoURI[i].replaceAll("readonly:", '');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
const con = mongoose.createConnection(uri, connectionOptions);
|
|
36
43
|
|
|
37
44
|
this.allConnections.push(con);
|
|
38
45
|
if (this.mongoURI[i].startsWith('readonly:')) {
|
|
39
|
-
|
|
40
46
|
Mongoplus.readonlydbs.push(con)
|
|
41
47
|
}
|
|
42
48
|
}
|
|
@@ -57,14 +63,13 @@ class Mongoplus {
|
|
|
57
63
|
}
|
|
58
64
|
const allConnections = this.allConnections;
|
|
59
65
|
const model = [];
|
|
60
|
-
|
|
66
|
+
|
|
61
67
|
for (let i = 0; i < allConnections.length; i++) {
|
|
62
68
|
const mongooseConnection = allConnections[i];
|
|
63
69
|
var currentm = mongooseConnection.model(name, schema)
|
|
64
70
|
model.push(currentm);
|
|
65
|
-
//console.count(Mongoplus.readonlydbs[i]);
|
|
66
|
-
if (Mongoplus.readonlydbs.includes(allConnections[i])) {
|
|
67
71
|
|
|
72
|
+
if (Mongoplus.readonlydbs.includes(allConnections[i])) {
|
|
68
73
|
Mongoplus.readonlymodels.push(currentm)
|
|
69
74
|
}
|
|
70
75
|
}
|
|
@@ -81,11 +86,9 @@ class MongoModel {
|
|
|
81
86
|
this.model = model;
|
|
82
87
|
this.readonlydbs = readonlydbs
|
|
83
88
|
this.s = s
|
|
84
|
-
|
|
85
|
-
|
|
86
89
|
}
|
|
90
|
+
|
|
87
91
|
static currentIndex = 0
|
|
88
|
-
//===================
|
|
89
92
|
|
|
90
93
|
async findInAllDatabase(filter, chain = {}) {
|
|
91
94
|
const dynamicComputationPromises = [];
|
|
@@ -94,6 +97,7 @@ class MongoModel {
|
|
|
94
97
|
});
|
|
95
98
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
96
99
|
}
|
|
100
|
+
|
|
97
101
|
async aggregateInAllDatabase(filter, chain = {}) {
|
|
98
102
|
const dynamicComputationPromises = [];
|
|
99
103
|
this.model.forEach((modelRef) => {
|
|
@@ -101,10 +105,9 @@ class MongoModel {
|
|
|
101
105
|
});
|
|
102
106
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
103
107
|
}
|
|
104
|
-
|
|
108
|
+
|
|
105
109
|
async writeInAllDatabase(data) {
|
|
106
110
|
data["dbIndex"] = -1
|
|
107
|
-
const dynamicComputationPromises = [];
|
|
108
111
|
const promises = [];
|
|
109
112
|
for (let i = 0; i < this.model.length; i++) {
|
|
110
113
|
if (Mongoplus.readonlymodels.includes(this.model[i])) continue;
|
|
@@ -114,50 +117,41 @@ class MongoModel {
|
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
return Promise.all(promises);
|
|
117
|
-
|
|
118
120
|
}
|
|
119
|
-
//==================
|
|
120
|
-
async UpdateOneInAllDatabase(filter, update) {
|
|
121
121
|
|
|
122
|
+
async UpdateOneInAllDatabase(filter, update) {
|
|
122
123
|
const dynamicComputationPromises = [];
|
|
123
124
|
this.model.forEach((modelRef) => {
|
|
124
|
-
|
|
125
125
|
dynamicComputationPromises.push({ fn: modelRef.findOneAndUpdate.bind(modelRef), params: [filter, update, { new: true }], chain: {} });
|
|
126
126
|
});
|
|
127
127
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
128
|
-
|
|
129
128
|
}
|
|
130
|
-
//==================
|
|
131
|
-
async UpdateByIdInAllDatabase(id, update) {
|
|
132
129
|
|
|
130
|
+
async UpdateByIdInAllDatabase(id, update) {
|
|
133
131
|
const dynamicComputationPromises = [];
|
|
134
132
|
this.model.forEach((modelRef) => {
|
|
135
|
-
|
|
136
133
|
dynamicComputationPromises.push({ fn: modelRef.findByIdAndUpdate.bind(modelRef), params: [id, update, { new: true }], chain: {} });
|
|
137
134
|
});
|
|
138
135
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
139
|
-
|
|
140
136
|
}
|
|
141
|
-
async findByIdInAllDatabaseAndDelete(id) {
|
|
142
137
|
|
|
138
|
+
async findByIdInAllDatabaseAndDelete(id) {
|
|
143
139
|
const dynamicComputationPromises = [];
|
|
144
140
|
this.model.forEach((modelRef) => {
|
|
145
|
-
|
|
141
|
+
// Changed from findByIdAndRemove to findByIdAndDelete (Mongoose 8 removed findByIdAndRemove)
|
|
146
142
|
dynamicComputationPromises.push({ fn: modelRef.findByIdAndDelete.bind(modelRef), params: [id], chain: {} });
|
|
147
143
|
});
|
|
148
144
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
149
|
-
|
|
150
145
|
}
|
|
151
|
-
async findOneInAllDatabaseAndDelete(filter) {
|
|
152
146
|
|
|
147
|
+
async findOneInAllDatabaseAndDelete(filter) {
|
|
153
148
|
const dynamicComputationPromises = [];
|
|
154
149
|
this.model.forEach((modelRef) => {
|
|
155
|
-
|
|
156
150
|
dynamicComputationPromises.push({ fn: modelRef.findOneAndDelete.bind(modelRef), params: [filter], chain: {} });
|
|
157
151
|
});
|
|
158
152
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
159
|
-
|
|
160
153
|
}
|
|
154
|
+
|
|
161
155
|
/**
|
|
162
156
|
* Delete many documents matching `filter` in all databases and return aggregated results.
|
|
163
157
|
*/
|
|
@@ -168,34 +162,29 @@ class MongoModel {
|
|
|
168
162
|
});
|
|
169
163
|
return await this.runLargeComputations(dynamicComputationPromises);
|
|
170
164
|
}
|
|
171
|
-
//=======================
|
|
172
|
-
async write(data) {
|
|
173
|
-
|
|
174
165
|
|
|
166
|
+
async write(data) {
|
|
175
167
|
const currentModel = this.model[MongoModel.currentIndex];
|
|
176
168
|
data["dbIndex"] = MongoModel.currentIndex;
|
|
177
169
|
MongoModel.currentIndex = (MongoModel.currentIndex + 1) % this.model.length;
|
|
170
|
+
|
|
178
171
|
if (Mongoplus.readonlymodels.includes(currentModel)) {
|
|
179
172
|
return await this.write(data);
|
|
180
173
|
}
|
|
181
174
|
|
|
182
|
-
|
|
183
175
|
try {
|
|
184
|
-
|
|
185
176
|
let dataToWrite = new currentModel(data)
|
|
186
177
|
return await dataToWrite.save()
|
|
187
178
|
} catch (error) {
|
|
188
179
|
throw error
|
|
189
180
|
}
|
|
190
|
-
|
|
191
181
|
}
|
|
192
|
-
//==================
|
|
193
182
|
|
|
194
183
|
async bulkWrite(data, options = {}) {
|
|
195
184
|
// Default options
|
|
196
185
|
const {
|
|
197
|
-
batchSize = 1000,
|
|
198
|
-
concurrentBatches = true
|
|
186
|
+
batchSize = 1000,
|
|
187
|
+
concurrentBatches = true
|
|
199
188
|
} = options;
|
|
200
189
|
|
|
201
190
|
if (!data || data.length === 0) return [];
|
|
@@ -240,10 +229,9 @@ class MongoModel {
|
|
|
240
229
|
filter = { _id: itemCopy._id };
|
|
241
230
|
} else if (itemCopy.id) {
|
|
242
231
|
filter = { _id: itemCopy.id };
|
|
243
|
-
itemCopy._id = itemCopy.id;
|
|
232
|
+
itemCopy._id = itemCopy.id;
|
|
244
233
|
} else {
|
|
245
|
-
//
|
|
246
|
-
const mongoose = require('mongoose');
|
|
234
|
+
// Mongoose 9: use 'new' keyword with ObjectId (required since Mongoose 7)
|
|
247
235
|
itemCopy._id = new mongoose.Types.ObjectId();
|
|
248
236
|
filter = { _id: itemCopy._id };
|
|
249
237
|
}
|
|
@@ -264,9 +252,8 @@ class MongoModel {
|
|
|
264
252
|
session = await model.db.startSession();
|
|
265
253
|
} catch (sessionError) {
|
|
266
254
|
throw new Error(`[Mongoplus] Database ${modelIndex} is a standalone instance. Transactions (required for bulkWriteZipper) only work on Replica Sets. \nError: ${JSON.stringify(sessionError)}`);
|
|
267
|
-
|
|
268
|
-
|
|
269
255
|
}
|
|
256
|
+
|
|
270
257
|
const attemptWrite = async () => {
|
|
271
258
|
let result;
|
|
272
259
|
await session.withTransaction(async () => {
|
|
@@ -315,14 +302,12 @@ class MongoModel {
|
|
|
315
302
|
// Process all batches
|
|
316
303
|
try {
|
|
317
304
|
if (concurrentBatches && batches.length > 1) {
|
|
318
|
-
// Run all batches concurrently (faster but more resource intensive)
|
|
319
305
|
console.log(`[Mongoplus] Running ${batches.length} batches concurrently`);
|
|
320
306
|
const batchResults = await Promise.all(
|
|
321
307
|
batches.map((batch, idx) => processBatch(batch, idx + 1))
|
|
322
308
|
);
|
|
323
309
|
allResults.push(...batchResults.flat());
|
|
324
310
|
} else {
|
|
325
|
-
// Run batches sequentially (slower but safer for large datasets)
|
|
326
311
|
console.log(`[Mongoplus] Running ${batches.length} batches sequentially`);
|
|
327
312
|
for (let i = 0; i < batches.length; i++) {
|
|
328
313
|
const result = await processBatch(batches[i], i + 1);
|
|
@@ -345,7 +330,6 @@ class MongoModel {
|
|
|
345
330
|
throw exception;
|
|
346
331
|
}
|
|
347
332
|
}
|
|
348
|
-
//===================
|
|
349
333
|
|
|
350
334
|
async findOne(dbIndex, filter, chain = {}) {
|
|
351
335
|
var currentModel = this.model[dbIndex]
|
|
@@ -358,18 +342,13 @@ class MongoModel {
|
|
|
358
342
|
else if (chain.skip) {
|
|
359
343
|
return currentModel.findOne(filter).skip(chain.skip)
|
|
360
344
|
}
|
|
361
|
-
|
|
362
345
|
else if (chain.limit) {
|
|
363
346
|
return currentModel.findOne(filter).limit(chain.limit)
|
|
364
347
|
} else {
|
|
365
348
|
return currentModel.findOne(filter);
|
|
366
349
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
350
|
}
|
|
370
351
|
|
|
371
|
-
//===============
|
|
372
|
-
|
|
373
352
|
async find(dbIndex, filter, chain = {}) {
|
|
374
353
|
var currentModel = this.model[dbIndex]
|
|
375
354
|
// Start with the base query
|
|
@@ -383,11 +362,8 @@ class MongoModel {
|
|
|
383
362
|
}
|
|
384
363
|
|
|
385
364
|
return query;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
365
|
}
|
|
390
|
-
|
|
366
|
+
|
|
391
367
|
async findById(dbIndex, filter, chain = {}) {
|
|
392
368
|
const currentModel = this.model[dbIndex];
|
|
393
369
|
|
|
@@ -404,36 +380,30 @@ class MongoModel {
|
|
|
404
380
|
return query;
|
|
405
381
|
}
|
|
406
382
|
|
|
407
|
-
|
|
408
|
-
//====================
|
|
409
383
|
async findByIdAndUpdate(dbIndex, id, update) {
|
|
410
384
|
var currentModel = this.model[dbIndex]
|
|
411
385
|
return currentModel.findByIdAndUpdate(id, update, { new: true });
|
|
412
386
|
}
|
|
413
|
-
|
|
414
|
-
async findByIdAndDelete(dbIndex, id
|
|
387
|
+
|
|
388
|
+
async findByIdAndDelete(dbIndex, id) {
|
|
415
389
|
var currentModel = this.model[dbIndex]
|
|
416
|
-
|
|
390
|
+
// Changed from findByIdAndRemove to findByIdAndDelete (Mongoose 8 removed findByIdAndRemove)
|
|
391
|
+
return currentModel.findByIdAndDelete(id, { new: true });
|
|
417
392
|
}
|
|
418
|
-
|
|
393
|
+
|
|
419
394
|
async findOneAndUpdate(dbIndex, filter, update) {
|
|
420
395
|
var currentModel = this.model[dbIndex]
|
|
421
396
|
return currentModel.findOneAndUpdate(filter, update, { new: true });
|
|
422
397
|
}
|
|
423
|
-
|
|
398
|
+
|
|
424
399
|
async aggregate(dbIndex, filter, update) {
|
|
425
400
|
var currentModel = this.model[dbIndex]
|
|
426
401
|
return currentModel.aggregate(filter);
|
|
427
402
|
}
|
|
428
|
-
|
|
403
|
+
|
|
429
404
|
async watch(dbIndex) {
|
|
430
405
|
return this.model[dbIndex].watch()
|
|
431
406
|
}
|
|
432
|
-
//================
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
407
|
|
|
438
408
|
getNextModel() {
|
|
439
409
|
const currentModel = this.model[this.currentIndex];
|
|
@@ -441,6 +411,7 @@ class MongoModel {
|
|
|
441
411
|
this.currentIndex = (this.currentIndex + 1) % this.model.length;
|
|
442
412
|
return [currentModel, writen];
|
|
443
413
|
}
|
|
414
|
+
|
|
444
415
|
async runLargeComputations(computationPairs) {
|
|
445
416
|
try {
|
|
446
417
|
const startTime = performance.now();
|
|
@@ -450,7 +421,6 @@ class MongoModel {
|
|
|
450
421
|
computationPairs.map(async pair => {
|
|
451
422
|
var chain = pair.chain;
|
|
452
423
|
var query = pair.fn(...pair.params);
|
|
453
|
-
// Start with the base query
|
|
454
424
|
|
|
455
425
|
// Dynamically apply chain options if they exist
|
|
456
426
|
for (const [key, value] of Object.entries(chain)) {
|
|
@@ -460,20 +430,16 @@ class MongoModel {
|
|
|
460
430
|
}
|
|
461
431
|
|
|
462
432
|
return query;
|
|
463
|
-
|
|
464
433
|
})
|
|
465
434
|
);
|
|
466
435
|
|
|
467
436
|
const endTime = performance.now();
|
|
468
437
|
const totalTime = endTime - startTime;
|
|
469
438
|
|
|
470
|
-
// Process the results as needed
|
|
471
|
-
// const sum = results.reduce((acc, result) => acc + result, 0);
|
|
472
|
-
|
|
473
439
|
return { results: [].concat(...results), totalTime };
|
|
474
440
|
} catch (error) {
|
|
475
441
|
console.error('Error:', error);
|
|
476
|
-
throw error;
|
|
442
|
+
throw error;
|
|
477
443
|
}
|
|
478
444
|
}
|
|
479
445
|
}
|
|
@@ -483,4 +449,4 @@ Mongoplus.MongoModel = MongoModel;
|
|
|
483
449
|
module.exports = Mongoplus;
|
|
484
450
|
module.exports.default = Mongoplus;
|
|
485
451
|
module.exports.MongoModel = MongoModel;
|
|
486
|
-
exports.MongoModel = MongoModel;
|
|
452
|
+
exports.MongoModel = MongoModel;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongoplusplus",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "load balancing of read and write operations across multiple MongoDB servers ",
|
|
5
5
|
"main": "mongoplus.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
],
|
|
46
46
|
"license": "ISC",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"mongoose": "^
|
|
49
|
-
"mongoose-sequence": "^
|
|
48
|
+
"mongoose": "^9.1.2",
|
|
49
|
+
"mongoose-sequence": "^6.0.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^24.10.1",
|