mongodb-ops 0.11.0 → 0.11.2

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,381 +1,406 @@
1
- 'use strict';
2
-
3
- const MongoClient = require('mongodb').MongoClient;
4
- const ObjectID = require('mongodb').ObjectId;
5
-
6
- class MongoDBOps {
7
- /**
8
- * @class
9
- * @classdesc MongoDB operations - Note: writeConcern is not supported in this class
10
- *
11
- * @param {string} connString Database connection string
12
- */
13
- constructor(connString) {
14
- if (!connString) { throw new Error("missing-connection-string"); }
15
- this.connString = connString;
16
- }
17
-
18
- /**
19
- * Static method - Get ObjectId instance from string
20
- *
21
- * @param {string} id Object ID string
22
- * @returns {object} ObjectId instance
23
- */
24
- static getObjectId(id) { return new ObjectID(id); }
25
-
26
- /**
27
- * Instance method - Get ObjectId instance from string
28
- *
29
- * @param {string} id Object ID string
30
- * @returns {object} ObjectId instance
31
- */
32
- getObjectId(id) { return new ObjectID(id); }
33
-
34
- /**
35
- * Static method - Get DB client - It supports multiple db client with different connection string. Active db client will be reused for better performance
36
- *
37
- * @param {string} connString Database connection string
38
- * @returns {promise} Promise with db client
39
- */
40
- static async getDbClient(connString) {
41
- if (!connString) { throw new Error("missing-connection-string"); }
42
-
43
- if (!Array.isArray(MongoDBOps.dbClientList)) { MongoDBOps.dbClientList = []; }
44
-
45
- for (let item of MongoDBOps.dbClientList) {
46
- if (item.s.url === connString) {
47
- if (item.topology.s.state !== "connected") {
48
- item = await MongoClient.connect(connString);
49
- }
50
-
51
- return Promise.resolve(item);
52
- }
53
- }
54
-
55
- const newDbClient = await MongoClient.connect(connString);
56
- MongoDBOps.dbClientList.push(newDbClient);
57
-
58
- return Promise.resolve(newDbClient);
59
- }
60
-
61
- /**
62
- * Static method - Close database connection
63
- * @returns {promise}
64
- */
65
- static async closeDBConn() {
66
- if (Array.isArray(MongoDBOps.dbClientList)) {
67
- for (const item of MongoDBOps.dbClientList) {
68
- await item.close();
69
- }
70
- }
71
- return Promise.resolve();
72
- }
73
-
74
- /**
75
- * Instance method - Close database connection
76
- * @returns {promise}
77
- */
78
- async closeDBConn() {
79
- return Promise.resolve(await MongoDBOps.closeDBConn());
80
- }
81
-
82
- /**
83
- * Static method - Get estimated document count of a collection
84
- *
85
- * @param {string} collectionName Collection Name
86
- * @param {string} connString Database connection string
87
- */
88
- static async getCollectionCount(collectionName, connString) {
89
- const db = (await MongoDBOps.getDbClient(connString)).db();
90
- return Promise.resolve(await db.collection(collectionName).estimatedDocumentCount());
91
- }
92
-
93
- /**
94
- * Instance method - Get estimated document count of a collection
95
- *
96
- * @param {string} collectionName Collection Name
97
- * @param {string} connString Database connection string
98
- */
99
- async getCollectionCount(collectionName) {
100
- return Promise.resolve(await MongoDBOps.getData(collectionName, this.connString));
101
- }
102
-
103
- /**
104
- * Static method - Get documents from MongoDB
105
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/}
106
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/}
107
- *
108
- * @param {string} collectionName Collection name
109
- * @param {object} [queryExp] Query Specifies selection filter using query operators - {@link https://docs.mongodb.com/manual/reference/operator/}
110
- * @param {boolean} [isAggregate=false] Set true to use the aggregation
111
- * @param {object} [projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
112
- * @param {object} [sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
113
- * @param {object} [pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
114
- * @param {boolean} [isGetCount=false] Set true to get the number of doc count based on the queryExp
115
- * @param {string} connString Database connection string
116
- * @param {object} [collation] Collation {@link https://www.mongodb.com/docs/manual/reference/collation/#std-label-collation-document-fields}
117
- * @param {object} [options] Aggregate options {@link https://www.mongodb.com/docs/manual/reference/method/db.collection.aggregate/}
118
- * @returns {promise} Promise with object array
119
- */
120
- static async getData(collectionName, queryExp, isAggregate = false, projection, sort, pagination, isGetCount = false, connString, collation, options) {
121
- const db = (await MongoDBOps.getDbClient(connString)).db();
122
-
123
- if (isAggregate) { return Promise.resolve(await db.collection(collectionName).aggregate(queryExp, options).toArray()); }
124
-
125
- queryExp = queryExp || {};
126
- if (isGetCount) { return Promise.resolve(await db.collection(collectionName).countDocuments(queryExp)); }
127
-
128
- const { skip, limit } = parsePagination(pagination);
129
- if (limit < 1) { return Promise.resolve([]); }
130
-
131
- projection = { projection: projection || {} };
132
-
133
- let data = db.collection(collectionName).find(queryExp, projection);
134
- if (sort) { data = data.sort(sort); }
135
- if (pagination) { data = data.skip(skip).limit(limit); }
136
- if (collation) { data = data.collation(collation); }
137
-
138
- return Promise.resolve(await data.toArray());
139
- }
140
-
141
- /**
142
- * Instance method - Get documents from MongoDB
143
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/}
144
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/}
145
- *
146
- * @param {string} collectionName Collection name
147
- * @param {object} [queryExp] Query Specifies selection filter using query operators - {@link https://docs.mongodb.com/manual/reference/operator/}
148
- * @param {boolean} [isAggregate=false] Set true to use the aggregation
149
- * @param {object} [projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
150
- * @param {object} [sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
151
- * @param {object} [pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
152
- * @param {boolean} [isGetCount=false] Set true to get the number of doc count based on the queryExp
153
- * @param {object} [collation] Collation {@link https://www.mongodb.com/docs/manual/reference/collation/#std-label-collation-document-fields}
154
- * @param {object} [options] Aggregate options {@link https://www.mongodb.com/docs/manual/reference/method/db.collection.aggregate/}
155
- * @returns {promise} Promise with object array
156
- */
157
- async getData(collectionName, queryExp, isAggregate, projection, sort, pagination, isGetCount, collation, options) {
158
- return Promise.resolve(await MongoDBOps.getData(collectionName, queryExp, isAggregate, projection, sort, pagination, isGetCount, this.connString, collation, options));
159
- }
160
-
161
- /**
162
- * Static method - Use altas search at MongoDB
163
- * {@link https://www.mongodb.com/docs/atlas/atlas-search/}
164
- *
165
- * @param {string} connString Database connection string
166
- * @param {string} collectionName Collection name
167
- * @param {object} search $search object - {@link https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/}
168
- * @param {object} [obj]
169
- * @param {object} [obj.projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
170
- * @param {object} [obj.sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
171
- * @param {object} [obj.pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
172
- * @param {boolean} [isGetCount=true] Set true to get the number of total matching docs and current page number
173
- * @returns {promise} Promise with object or object array
174
- */
175
- static async search(connString, collectionName, search, { projection, sort, pagination }={}, isGetCount = true) {
176
- if ([connString, collectionName, search].includes(undefined)) { return Promise.reject("Connection string, collection name and search cannot be undefined"); }
177
-
178
- const { skip, limit } = parsePagination(pagination);
179
- if (limit < 1) { return Promise.resolve([]); }
180
-
181
- const payload = [
182
- { $search: search },
183
- { $addFields: { score: { $meta: "searchScore" }}},
184
- ];
185
-
186
- if (projection) { payload.push({ $project: projection }); }
187
- if (sort) { payload.push({ $sort: sort }); }
188
-
189
- const skipLimit = [];
190
- if (skip) { skipLimit.push({ $skip: skip }); }
191
- if (limit) { skipLimit.push({ $limit: limit }); }
192
-
193
- if (isGetCount) {
194
- payload.push({
195
- $facet: {
196
- metadata: [{ $count: "total" }, { $addFields: { page: Math.ceil((skip + 1) / limit) }}],
197
- data: skipLimit,
198
- }
199
- });
200
- }
201
- else { payload.push(...skipLimit); }
202
-
203
- const result = await MongoDBOps.getData(collectionName, payload, true, undefined, undefined, undefined, undefined, connString);
204
-
205
- return Promise.resolve(isGetCount ? result[0] : result);
206
- }
207
-
208
- /**
209
- * Instance method - Use altas search at MongoDB
210
- * {@link https://www.mongodb.com/docs/atlas/atlas-search/}
211
- *
212
- * @param {string} collectionName Collection name
213
- * @param {object} search $search object - {@link https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/}
214
- * @param {object} [obj]
215
- * @param {object} [obj.projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
216
- * @param {object} [obj.sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
217
- * @param {object} [obj.pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
218
- * @param {boolean} [isGetCount=true] Set true to get the number of total matching docs and current page number
219
- * @returns {promise} Promise with object or object array
220
- */
221
- async search(collectionName, search, { projection, sort, pagination }={}, isGetCount = true) {
222
- return Promise.resolve(await MongoDBOps.search(this.connString, collectionName, search, { projection, sort, pagination }, isGetCount));
223
- }
224
-
225
- /**
226
- * Static method - Write document to MongoDB
227
- *
228
- * References:
229
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.insertOne/}
230
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/}
231
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/}
232
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/}
233
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteOne/}
234
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/}
235
- *
236
- * @param {string} type Write type - insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany
237
- * @param {string} collectionName Collection name
238
- * @param {object} doc Data document
239
- * @param {object} filter Query filter {@link https://docs.mongodb.com/manual/core/document/#document-query-filter}
240
- * @param {string} connString Database connection string
241
- * @returns {promise}
242
- */
243
- static async writeData(type, collectionName, doc, filter, connString) {
244
- try {
245
- const db = (await MongoDBOps.getDbClient(connString)).db();
246
-
247
- let result;
248
- switch(type) {
249
- case "insertOne": result = await db.collection(collectionName).insertOne(doc); break;
250
- case "replaceOne": result = await db.collection(collectionName).replaceOne(filter, doc); break;
251
- case "updateOne": result = await db.collection(collectionName).updateOne(filter, doc); break;
252
- case "updateMany": result = await db.collection(collectionName).updateMany(filter, doc); break;
253
- case "deleteOne": result = await db.collection(collectionName).deleteOne(filter); break;
254
- case "deleteMany": result = await db.collection(collectionName).deleteMany(filter); break;
255
- default: throw new Error("invalid-writeData-type");
256
- }
257
- return Promise.resolve(result);
258
- }
259
- catch (err) { return Promise.reject(err.errmsg || err.message || err); }
260
- }
261
-
262
- /**
263
- * Instance method - Write document to MongoDB
264
- *
265
- * References:
266
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.insertOne/}
267
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/}
268
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/}
269
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/}
270
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteOne/}
271
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/}
272
- *
273
- * @param {string} type Write type - insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany
274
- * @param {string} collectionName Collection name
275
- * @param {object} doc Data document
276
- * @param {object} filter Query filter {@link https://docs.mongodb.com/manual/core/document/#document-query-filter}
277
- * @returns {promise}
278
- */
279
- async writeData(type, collectionName, doc, filter) {
280
- return Promise.resolve(await MongoDBOps.writeData(type, collectionName, doc, filter, this.connString));
281
- }
282
-
283
- /**
284
- * Static method - Write document to MongoDB via BulkOps / BulkWrite
285
- *
286
- * References:
287
- * {@link https://mongodb.github.io/node-mongodb-native/3.6/api/BulkOperationBase.html}
288
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/}
289
- *
290
- * @param {string} type Write type - insertBulk, replaceBulk, updateBulk, allBulk
291
- * @param {string} collectionName Collection name
292
- * @param {Array} docs Data documents array
293
- * @param {boolean} [ordered=false] Set true to use ordered bulkWrite
294
- * @param {string} connString Database connection string
295
- * @returns {promise}
296
- */
297
- static async writeBulkData(type, collectionName, docs, ordered = false, connString) {
298
- try {
299
- const db = (await MongoDBOps.getDbClient(connString)).db();
300
-
301
- let result;
302
- switch(type) {
303
- case "insertBulk":
304
- for (let i = 0; i < docs.length; ++i) { docs[i] = { insertOne: { "document": docs[i] }}; }
305
- break;
306
- case "replaceBulk":
307
- // doc = {
308
- // "filter": <document>,
309
- // "replacement": <document>,
310
- // "upsert": <boolean>,
311
- // "collation": <document>,
312
- // "hint": <document|string>
313
- // }
314
- for (let i = 0; i < docs.length; ++i) { docs[i] = { replaceOne: docs[i] }; }
315
- break;
316
- case "updateBulk":
317
- // doc = {
318
- // "filter": <document>,
319
- // "update": <document or pipeline>,
320
- // "upsert": <boolean>,
321
- // "collation": <document>,
322
- // "arrayFilters": [ <filterdocument1>, ... ],
323
- // "hint": <document|string>
324
- // }
325
- for (let i = 0; i < docs.length; ++i) { docs[i] = { updateOne: docs[i] }; }
326
- break;
327
- case "deleteBulk":
328
- // doc = {
329
- // "filter": <document>,
330
- // "collation": <document>
331
- // }
332
- for (let i = 0; i < docs.length; ++i) { docs[i] = { deleteOne: docs[i] }; }
333
- break;
334
- case "allBulk":
335
- // allowed bulkWrite operations include insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany
336
- break;
337
- default: throw new Error("invalid-writeBulkData-type");
338
- }
339
- result = await db.collection(collectionName).bulkWrite(docs, { ordered: ordered });
340
- return Promise.resolve(result);
341
- }
342
- catch (err) { return Promise.reject(err.result || err.errmsg || err.message); }
343
- }
344
-
345
- /**
346
- * Instance method - Write document to MongoDB via BulkOps / BulkWrite
347
- *
348
- * References:
349
- * {@link https://mongodb.github.io/node-mongodb-native/3.6/api/BulkOperationBase.html}
350
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/}
351
- *
352
- * @param {string} type Write type - insertBulk, replaceBulk, updateBulk, allBulk
353
- * @param {string} collectionName Collection name
354
- * @param {Array} docs Data documents array
355
- * @param {boolean} [ordered=false] Set true to use ordered bulkWrite
356
- * @returns {promise}
357
- */
358
- async writeBulkData(type, collectionName, docs, ordered = false) {
359
- return Promise.resolve(await MongoDBOps.writeBulkData(type, collectionName, docs, ordered, this.connString));
360
- }
361
- }
362
-
363
- module.exports = MongoDBOps;
364
-
365
- /**
366
- * Parse the pagaintion and return skip and limit values
367
- *
368
- * @param {object} obj
369
- * @param {number} [obj.startIndex] Start index
370
- * @param {number} [obj.endIndex] Start index
371
- * @returns {object} Object with skip and limit values
372
- */
373
- const parsePagination = ({ startIndex, endIndex }={})=> {
374
- startIndex = +startIndex; endIndex = +endIndex;
375
- startIndex = startIndex < 1 ? 1 : startIndex;
376
-
377
- return {
378
- skip: Number.isInteger(startIndex) ? startIndex - 1 : undefined,
379
- limit: Number.isInteger(startIndex) && Number.isInteger(endIndex) ? ((endIndex - startIndex + 1) < 0 ? 0 : endIndex - startIndex + 1) : undefined
380
- };
381
- }
1
+ 'use strict';
2
+
3
+ const MongoClient = require('mongodb').MongoClient;
4
+ const ObjectID = require('mongodb').ObjectId;
5
+
6
+ class MongoDBOps {
7
+ /**
8
+ * @class
9
+ * @classdesc MongoDB operations - Note: writeConcern is not supported in this class
10
+ *
11
+ * @param {string} connString Database connection string
12
+ */
13
+ constructor(connString) {
14
+ if (!connString) { throw new Error("missing-connection-string"); }
15
+ this.connString = connString;
16
+ }
17
+
18
+ /**
19
+ * Static method - Get ObjectId instance from string
20
+ *
21
+ * @param {string} id Object ID string
22
+ * @returns {object} ObjectId instance
23
+ */
24
+ static getObjectId(id) { return new ObjectID(id); }
25
+
26
+ /**
27
+ * Instance method - Get ObjectId instance from string
28
+ *
29
+ * @param {string} id Object ID string
30
+ * @returns {object} ObjectId instance
31
+ */
32
+ getObjectId(id) { return new ObjectID(id); }
33
+
34
+ /**
35
+ * Static method - Get DB client - It supports multiple db client with different connection string. Active db client will be reused for better performance
36
+ *
37
+ * @param {string} connString Database connection string
38
+ * @returns {promise} Promise with db client
39
+ */
40
+ static async getDbClient(connString) {
41
+ if (!connString) { throw new Error("missing-connection-string"); }
42
+
43
+ if (!(MongoDBOps.dbClients instanceof Map)) { MongoDBOps.dbClients = new Map(); }
44
+
45
+ let clientPromise = MongoDBOps.dbClients.get(connString);
46
+ if (!clientPromise) {
47
+ const options = Object.assign(
48
+ { serverSelectionTimeoutMS: 8000, retryWrites: true, retryReads: true },
49
+ MongoDBOps.clientOptions || {}
50
+ );
51
+
52
+ clientPromise = MongoClient.connect(connString, options).catch((err) => {
53
+ if (MongoDBOps.dbClients.get(connString) === clientPromise) {
54
+ MongoDBOps.dbClients.delete(connString);
55
+ }
56
+ throw err;
57
+ });
58
+ MongoDBOps.dbClients.set(connString, clientPromise);
59
+ }
60
+
61
+ return clientPromise;
62
+ }
63
+
64
+ /**
65
+ * Static method - Close database connection
66
+ * @returns {promise}
67
+ */
68
+ static async closeDBConn() {
69
+ if (MongoDBOps.dbClients instanceof Map) {
70
+ const clientPromises = Array.from(MongoDBOps.dbClients.values());
71
+ MongoDBOps.dbClients.clear();
72
+
73
+ for (const clientPromise of clientPromises) {
74
+ try {
75
+ const client = await clientPromise;
76
+ await client.close();
77
+ } catch {
78
+ // Failed or already-evicted connection attempts do not need separate cleanup.
79
+ }
80
+ }
81
+ }
82
+
83
+ if (Array.isArray(MongoDBOps.dbClientList)) {
84
+ const legacyClients = MongoDBOps.dbClientList;
85
+ MongoDBOps.dbClientList = [];
86
+
87
+ for (const item of legacyClients) {
88
+ try {
89
+ await item.close();
90
+ } catch {
91
+ // Ignore stale legacy clients from older mixed-version processes.
92
+ }
93
+ }
94
+ }
95
+
96
+ return Promise.resolve();
97
+ }
98
+
99
+ /**
100
+ * Instance method - Close database connection
101
+ * @returns {promise}
102
+ */
103
+ async closeDBConn() {
104
+ return Promise.resolve(await MongoDBOps.closeDBConn());
105
+ }
106
+
107
+ /**
108
+ * Static method - Get estimated document count of a collection
109
+ *
110
+ * @param {string} collectionName Collection Name
111
+ * @param {string} connString Database connection string
112
+ */
113
+ static async getCollectionCount(collectionName, connString) {
114
+ const db = (await MongoDBOps.getDbClient(connString)).db();
115
+ return Promise.resolve(await db.collection(collectionName).estimatedDocumentCount());
116
+ }
117
+
118
+ /**
119
+ * Instance method - Get estimated document count of a collection
120
+ *
121
+ * @param {string} collectionName Collection Name
122
+ * @param {string} connString Database connection string
123
+ */
124
+ async getCollectionCount(collectionName) {
125
+ return Promise.resolve(await MongoDBOps.getData(collectionName, this.connString));
126
+ }
127
+
128
+ /**
129
+ * Static method - Get documents from MongoDB
130
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/}
131
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/}
132
+ *
133
+ * @param {string} collectionName Collection name
134
+ * @param {object} [queryExp] Query Specifies selection filter using query operators - {@link https://docs.mongodb.com/manual/reference/operator/}
135
+ * @param {boolean} [isAggregate=false] Set true to use the aggregation
136
+ * @param {object} [projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
137
+ * @param {object} [sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
138
+ * @param {object} [pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
139
+ * @param {boolean} [isGetCount=false] Set true to get the number of doc count based on the queryExp
140
+ * @param {string} connString Database connection string
141
+ * @param {object} [collation] Collation {@link https://www.mongodb.com/docs/manual/reference/collation/#std-label-collation-document-fields}
142
+ * @param {object} [options] Aggregate options {@link https://www.mongodb.com/docs/manual/reference/method/db.collection.aggregate/}
143
+ * @returns {promise} Promise with object array
144
+ */
145
+ static async getData(collectionName, queryExp, isAggregate = false, projection, sort, pagination, isGetCount = false, connString, collation, options) {
146
+ const db = (await MongoDBOps.getDbClient(connString)).db();
147
+
148
+ if (isAggregate) { return Promise.resolve(await db.collection(collectionName).aggregate(queryExp, options).toArray()); }
149
+
150
+ queryExp = queryExp || {};
151
+ if (isGetCount) { return Promise.resolve(await db.collection(collectionName).countDocuments(queryExp)); }
152
+
153
+ const { skip, limit } = parsePagination(pagination);
154
+ if (limit < 1) { return Promise.resolve([]); }
155
+
156
+ projection = { projection: projection || {} };
157
+
158
+ let data = db.collection(collectionName).find(queryExp, projection);
159
+ if (sort) { data = data.sort(sort); }
160
+ if (pagination) { data = data.skip(skip).limit(limit); }
161
+ if (collation) { data = data.collation(collation); }
162
+
163
+ return Promise.resolve(await data.toArray());
164
+ }
165
+
166
+ /**
167
+ * Instance method - Get documents from MongoDB
168
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/}
169
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/}
170
+ *
171
+ * @param {string} collectionName Collection name
172
+ * @param {object} [queryExp] Query Specifies selection filter using query operators - {@link https://docs.mongodb.com/manual/reference/operator/}
173
+ * @param {boolean} [isAggregate=false] Set true to use the aggregation
174
+ * @param {object} [projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
175
+ * @param {object} [sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
176
+ * @param {object} [pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
177
+ * @param {boolean} [isGetCount=false] Set true to get the number of doc count based on the queryExp
178
+ * @param {object} [collation] Collation {@link https://www.mongodb.com/docs/manual/reference/collation/#std-label-collation-document-fields}
179
+ * @param {object} [options] Aggregate options {@link https://www.mongodb.com/docs/manual/reference/method/db.collection.aggregate/}
180
+ * @returns {promise} Promise with object array
181
+ */
182
+ async getData(collectionName, queryExp, isAggregate, projection, sort, pagination, isGetCount, collation, options) {
183
+ return Promise.resolve(await MongoDBOps.getData(collectionName, queryExp, isAggregate, projection, sort, pagination, isGetCount, this.connString, collation, options));
184
+ }
185
+
186
+ /**
187
+ * Static method - Use altas search at MongoDB
188
+ * {@link https://www.mongodb.com/docs/atlas/atlas-search/}
189
+ *
190
+ * @param {string} connString Database connection string
191
+ * @param {string} collectionName Collection name
192
+ * @param {object} search $search object - {@link https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/}
193
+ * @param {object} [obj]
194
+ * @param {object} [obj.projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
195
+ * @param {object} [obj.sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
196
+ * @param {object} [obj.pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
197
+ * @param {boolean} [isGetCount=true] Set true to get the number of total matching docs and current page number
198
+ * @returns {promise} Promise with object or object array
199
+ */
200
+ static async search(connString, collectionName, search, { projection, sort, pagination }={}, isGetCount = true) {
201
+ if ([connString, collectionName, search].includes(undefined)) { return Promise.reject("Connection string, collection name and search cannot be undefined"); }
202
+
203
+ const { skip, limit } = parsePagination(pagination);
204
+ if (limit < 1) { return Promise.resolve([]); }
205
+
206
+ const payload = [
207
+ { $search: search },
208
+ { $addFields: { score: { $meta: "searchScore" }}},
209
+ ];
210
+
211
+ if (projection) { payload.push({ $project: projection }); }
212
+ if (sort) { payload.push({ $sort: sort }); }
213
+
214
+ const skipLimit = [];
215
+ if (skip) { skipLimit.push({ $skip: skip }); }
216
+ if (limit) { skipLimit.push({ $limit: limit }); }
217
+
218
+ if (isGetCount) {
219
+ payload.push({
220
+ $facet: {
221
+ metadata: [{ $count: "total" }, { $addFields: { page: Math.ceil((skip + 1) / limit) }}],
222
+ data: skipLimit,
223
+ }
224
+ });
225
+ }
226
+ else { payload.push(...skipLimit); }
227
+
228
+ const result = await MongoDBOps.getData(collectionName, payload, true, undefined, undefined, undefined, undefined, connString);
229
+
230
+ return Promise.resolve(isGetCount ? result[0] : result);
231
+ }
232
+
233
+ /**
234
+ * Instance method - Use altas search at MongoDB
235
+ * {@link https://www.mongodb.com/docs/atlas/atlas-search/}
236
+ *
237
+ * @param {string} collectionName Collection name
238
+ * @param {object} search $search object - {@link https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/}
239
+ * @param {object} [obj]
240
+ * @param {object} [obj.projection] Projection {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection}
241
+ * @param {object} [obj.sort] Sort {@link https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort}
242
+ * @param {object} [obj.pagination] Pagination `E.g., { startIndex: 11, endIndex: 20 }`
243
+ * @param {boolean} [isGetCount=true] Set true to get the number of total matching docs and current page number
244
+ * @returns {promise} Promise with object or object array
245
+ */
246
+ async search(collectionName, search, { projection, sort, pagination }={}, isGetCount = true) {
247
+ return Promise.resolve(await MongoDBOps.search(this.connString, collectionName, search, { projection, sort, pagination }, isGetCount));
248
+ }
249
+
250
+ /**
251
+ * Static method - Write document to MongoDB
252
+ *
253
+ * References:
254
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.insertOne/}
255
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/}
256
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/}
257
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/}
258
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteOne/}
259
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/}
260
+ *
261
+ * @param {string} type Write type - insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany
262
+ * @param {string} collectionName Collection name
263
+ * @param {object} doc Data document
264
+ * @param {object} filter Query filter {@link https://docs.mongodb.com/manual/core/document/#document-query-filter}
265
+ * @param {string} connString Database connection string
266
+ * @returns {promise}
267
+ */
268
+ static async writeData(type, collectionName, doc, filter, connString) {
269
+ try {
270
+ const db = (await MongoDBOps.getDbClient(connString)).db();
271
+
272
+ let result;
273
+ switch(type) {
274
+ case "insertOne": result = await db.collection(collectionName).insertOne(doc); break;
275
+ case "replaceOne": result = await db.collection(collectionName).replaceOne(filter, doc); break;
276
+ case "updateOne": result = await db.collection(collectionName).updateOne(filter, doc); break;
277
+ case "updateMany": result = await db.collection(collectionName).updateMany(filter, doc); break;
278
+ case "deleteOne": result = await db.collection(collectionName).deleteOne(filter); break;
279
+ case "deleteMany": result = await db.collection(collectionName).deleteMany(filter); break;
280
+ default: throw new Error("invalid-writeData-type");
281
+ }
282
+ return Promise.resolve(result);
283
+ }
284
+ catch (err) { return Promise.reject(err); }
285
+ }
286
+
287
+ /**
288
+ * Instance method - Write document to MongoDB
289
+ *
290
+ * References:
291
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.insertOne/}
292
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/}
293
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/}
294
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/}
295
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteOne/}
296
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/}
297
+ *
298
+ * @param {string} type Write type - insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany
299
+ * @param {string} collectionName Collection name
300
+ * @param {object} doc Data document
301
+ * @param {object} filter Query filter {@link https://docs.mongodb.com/manual/core/document/#document-query-filter}
302
+ * @returns {promise}
303
+ */
304
+ async writeData(type, collectionName, doc, filter) {
305
+ return Promise.resolve(await MongoDBOps.writeData(type, collectionName, doc, filter, this.connString));
306
+ }
307
+
308
+ /**
309
+ * Static method - Write document to MongoDB via BulkOps / BulkWrite
310
+ *
311
+ * References:
312
+ * {@link https://mongodb.github.io/node-mongodb-native/3.6/api/BulkOperationBase.html}
313
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/}
314
+ *
315
+ * @param {string} type Write type - insertBulk, replaceBulk, updateBulk, allBulk
316
+ * @param {string} collectionName Collection name
317
+ * @param {Array} docs Data documents array
318
+ * @param {boolean} [ordered=false] Set true to use ordered bulkWrite
319
+ * @param {string} connString Database connection string
320
+ * @returns {promise}
321
+ */
322
+ static async writeBulkData(type, collectionName, docs, ordered = false, connString) {
323
+ try {
324
+ const db = (await MongoDBOps.getDbClient(connString)).db();
325
+
326
+ let result;
327
+ switch(type) {
328
+ case "insertBulk":
329
+ for (let i = 0; i < docs.length; ++i) { docs[i] = { insertOne: { "document": docs[i] }}; }
330
+ break;
331
+ case "replaceBulk":
332
+ // doc = {
333
+ // "filter": <document>,
334
+ // "replacement": <document>,
335
+ // "upsert": <boolean>,
336
+ // "collation": <document>,
337
+ // "hint": <document|string>
338
+ // }
339
+ for (let i = 0; i < docs.length; ++i) { docs[i] = { replaceOne: docs[i] }; }
340
+ break;
341
+ case "updateBulk":
342
+ // doc = {
343
+ // "filter": <document>,
344
+ // "update": <document or pipeline>,
345
+ // "upsert": <boolean>,
346
+ // "collation": <document>,
347
+ // "arrayFilters": [ <filterdocument1>, ... ],
348
+ // "hint": <document|string>
349
+ // }
350
+ for (let i = 0; i < docs.length; ++i) { docs[i] = { updateOne: docs[i] }; }
351
+ break;
352
+ case "deleteBulk":
353
+ // doc = {
354
+ // "filter": <document>,
355
+ // "collation": <document>
356
+ // }
357
+ for (let i = 0; i < docs.length; ++i) { docs[i] = { deleteOne: docs[i] }; }
358
+ break;
359
+ case "allBulk":
360
+ // allowed bulkWrite operations include insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany
361
+ break;
362
+ default: throw new Error("invalid-writeBulkData-type");
363
+ }
364
+ result = await db.collection(collectionName).bulkWrite(docs, { ordered: ordered });
365
+ return Promise.resolve(result);
366
+ }
367
+ catch (err) { return Promise.reject(err); }
368
+ }
369
+
370
+ /**
371
+ * Instance method - Write document to MongoDB via BulkOps / BulkWrite
372
+ *
373
+ * References:
374
+ * {@link https://mongodb.github.io/node-mongodb-native/3.6/api/BulkOperationBase.html}
375
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/}
376
+ *
377
+ * @param {string} type Write type - insertBulk, replaceBulk, updateBulk, allBulk
378
+ * @param {string} collectionName Collection name
379
+ * @param {Array} docs Data documents array
380
+ * @param {boolean} [ordered=false] Set true to use ordered bulkWrite
381
+ * @returns {promise}
382
+ */
383
+ async writeBulkData(type, collectionName, docs, ordered = false) {
384
+ return Promise.resolve(await MongoDBOps.writeBulkData(type, collectionName, docs, ordered, this.connString));
385
+ }
386
+ }
387
+
388
+ module.exports = MongoDBOps;
389
+
390
+ /**
391
+ * Parse the pagaintion and return skip and limit values
392
+ *
393
+ * @param {object} obj
394
+ * @param {number} [obj.startIndex] Start index
395
+ * @param {number} [obj.endIndex] Start index
396
+ * @returns {object} Object with skip and limit values
397
+ */
398
+ const parsePagination = ({ startIndex, endIndex }={})=> {
399
+ startIndex = +startIndex; endIndex = +endIndex;
400
+ startIndex = startIndex < 1 ? 1 : startIndex;
401
+
402
+ return {
403
+ skip: Number.isInteger(startIndex) ? startIndex - 1 : undefined,
404
+ limit: Number.isInteger(startIndex) && Number.isInteger(endIndex) ? ((endIndex - startIndex + 1) < 0 ? 0 : endIndex - startIndex + 1) : undefined
405
+ };
406
+ }