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