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.
Files changed (2) hide show
  1. package/mongoplus.js +46 -80
  2. 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 = [] // Define currentIndex to keep track of the current URI
16
+ static readonlymodels = []
17
+
16
18
  Schema(schema) {
17
19
  return mongoose.Schema(schema)
18
20
  }
21
+
19
22
  addIndex(schema, indextype) {
20
- return schema.index(indextype)
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
- const con = mongoose.createConnection(uri, {
33
- useNewUrlParser: true,
34
- useUnifiedTopology: true,
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
- //console.groupCollapsed("====>",Mongoplus.readonlydbs);
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, // Process 1000 items per batch by default
198
- concurrentBatches = true // Run batches concurrently or sequentially
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; // Normalize to _id
232
+ itemCopy._id = itemCopy.id;
244
233
  } else {
245
- // For new documents without ID, generate one
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, update) {
387
+
388
+ async findByIdAndDelete(dbIndex, id) {
415
389
  var currentModel = this.model[dbIndex]
416
- return currentModel.findByIdAndRemove(id, update, { new: true });
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; // Rethrow the error if needed
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.7",
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": "^6.13.8",
49
- "mongoose-sequence": "^5.3.1"
48
+ "mongoose": "^9.1.2",
49
+ "mongoose-sequence": "^6.0.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^24.10.1",