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.
Files changed (2) hide show
  1. package/mongoplus.js +46 -77
  2. 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 = [] // 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;
@@ -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
- const con = mongoose.createConnection(uri, {
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
- //console.groupCollapsed("====>",Mongoplus.readonlydbs);
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, // Process 1000 items per batch by default
198
- concurrentBatches = true // Run batches concurrently or sequentially
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; // Normalize to _id
235
+ itemCopy._id = itemCopy.id;
244
236
  } else {
245
- // For new documents without ID, generate one
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, update) {
390
+
391
+ async findByIdAndDelete(dbIndex, id) {
415
392
  var currentModel = this.model[dbIndex]
416
- return currentModel.findByIdAndRemove(id, update, { new: true });
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; // Rethrow the error if needed
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.7",
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": "^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",
@@ -60,4 +60,4 @@
60
60
  },
61
61
  "./package.json": "./package.json"
62
62
  }
63
- }
63
+ }