@cocreate/mongodb 1.4.0 → 1.4.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.
package/src/index.js CHANGED
@@ -1,805 +1,805 @@
1
- const { MongoClient, ObjectId } = require('mongodb');
2
- const { dotNotationToObject, queryData, searchData, sortData } = require('@cocreate/utils')
3
- const clients = new Map()
4
-
5
-
6
- async function dbClient(data) {
7
- if (data.dbUrl) {
8
- let client = clients.get(data.dbUrl)
9
- if (!client) {
10
- try {
11
- client = MongoClient.connect(data.dbUrl, { useNewUrlParser: true, useUnifiedTopology: true });
12
- clients.set(data.dbUrl, client)
13
- } catch (error) {
14
- console.error(error)
15
- return { status: false }
16
- }
17
- }
18
- return client
19
- }
20
- return
21
- }
22
-
23
- async function databaseStats(data) {
24
- const client = await dbClient(data)
25
- if (!client) return
26
- let db = client.db(data.organization_id)
27
- let stats = db.stats()
28
- return stats
29
- }
30
-
31
- function createDatabase(data) {
32
- return database('createDatabase', data)
33
- }
34
-
35
- function readDatabase(data) {
36
- return database('readDatabase', data)
37
- }
38
-
39
- function updateDatabase(data) {
40
- return database('updateDatabase', data)
41
- }
42
-
43
- function deleteDatabase(data) {
44
- return database('deleteDatabase', data)
45
- }
46
-
47
- function database(action, data) {
48
- return new Promise(async (resolve, reject) => {
49
- let type = 'database'
50
- let databaseArray = []
51
-
52
- try {
53
- const client = await dbClient(data)
54
- if (!client) return
55
- if (action == 'readDatabase') {
56
- const db = client.db().admin();
57
-
58
- // List all the available databases
59
- db.listDatabases(function (err, dbs) {
60
-
61
- for (let database of dbs.databases) {
62
- if (data.filter && data.filter.query) {
63
- let isFilter = queryData(database, data.filter.query)
64
- if (isFilter)
65
- databaseArray.push({ database, db: 'mongodb' })
66
- } else
67
- databaseArray.push({ database, db: 'mongodb' })
68
- }
69
-
70
- resolve(createData(data, databaseArray, type))
71
- })
72
- }
73
- if (action == 'deleteDatabase') {
74
- const db = client.db(data.database);
75
- db.dropDatabase().then(response => {
76
- resolve(response)
77
- })
78
- }
79
- } catch (error) {
80
- errorHandler(data, error)
81
- console.log(action, 'error', error);
82
- resolve(data);
83
- }
84
-
85
- }, (error) => {
86
- errorHandler(data, error)
87
- });
88
- }
89
-
90
-
91
- function createCollection(data) {
92
- return collection('createCollection', data)
93
- }
94
-
95
- function readCollection(data) {
96
- return collection('readCollection', data)
97
- }
98
-
99
- function updateCollection(data) {
100
- return collection('updateCollection', data)
101
- }
102
-
103
- function deleteCollection(data) {
104
- return collection('deleteCollection', data)
105
- }
106
-
107
- function collection(action, data) {
108
- return new Promise(async (resolve, reject) => {
109
- let type = 'collection'
110
- let collectionArray = [];
111
-
112
- try {
113
- const client = await dbClient(data)
114
- if (!client) return
115
-
116
- if (data.request)
117
- data.collection = data.request
118
-
119
- let databases = data.database;
120
- if (!Array.isArray(databases))
121
- databases = [databases]
122
-
123
- let databasesLength = databases.length
124
- for (let database of databases) {
125
- const db = client.db(database);
126
-
127
- if (action == 'readCollection') {
128
-
129
- let { query, sort } = getFilters(data);
130
-
131
- db.listCollections().toArray(function (error, result) {
132
- if (error)
133
- errorHandler(data, error, database)
134
-
135
- if (result) {
136
- for (let res of result) {
137
- if (data.filter && data.filter.query) {
138
- let isFilter = queryData(res, data.filter.query)
139
- if (isFilter)
140
- collectionArray.push({ name: res.name, database, db: 'mongodb' })
141
- } else
142
- collectionArray.push({ name: res.name, database, db: 'mongodb' })
143
- }
144
- }
145
-
146
- databasesLength -= 1
147
- if (!databasesLength) {
148
- data = createData(data, collectionArray, type)
149
- resolve(data)
150
- }
151
- })
152
- } else {
153
- let collections
154
- let value
155
- if (action == 'updateCollection')
156
- collections = Object.entries(data.collection)
157
- else
158
- collections = data.collection;
159
-
160
- if (!Array.isArray(collections))
161
- collections = [collections]
162
-
163
- let collectionsLength = collections.length
164
- for (let collection of collections) {
165
-
166
- if (action == 'createCollection') {
167
- db.createCollection(collection, function (error, result) {
168
- if (error)
169
- errorHandler(data, error, database, collection)
170
-
171
- if (result)
172
- collectionArray.push({ name: collection, database, db: 'mongodb' })
173
-
174
- collectionsLength -= 1
175
- if (!collectionsLength)
176
- databasesLength -= 1
177
-
178
- if (!databasesLength && !collectionsLength) {
179
- data = createData(data, collectionArray, type)
180
- resolve(data)
181
- }
182
- })
183
- } else {
184
- if (action == 'updateCollection') {
185
- [collection, value] = collection
186
- }
187
-
188
- const collectionObj = db.collection(collection);
189
-
190
- if (action == 'updateCollection') {
191
- collectionObj.rename(value, function (error, result) {
192
- if (error)
193
- errorHandler(data, error, database, collection)
194
-
195
- if (result)
196
- collectionArray.push({ name: value, oldName: collection, database, db: 'mongodb' })
197
-
198
- collectionsLength -= 1
199
- if (!collectionsLength)
200
- databasesLength -= 1
201
-
202
- if (!databasesLength && !collectionsLength) {
203
- data = createData(data, collectionArray, type)
204
- resolve(data)
205
- }
206
-
207
- })
208
- }
209
-
210
- if (action == 'deleteCollection') {
211
- collectionObj.drop(function (error, result) {
212
- if (error)
213
- errorHandler(data, error, database, collection)
214
-
215
- if (result)
216
- collectionArray.push({ name: collection, database, db: 'mongodb' })
217
-
218
- collectionsLength -= 1
219
- if (!collectionsLength)
220
- databasesLength -= 1
221
-
222
- if (!databasesLength && !collectionsLength) {
223
- data = createData(data, collectionArray, type)
224
- resolve(data)
225
- }
226
-
227
- })
228
-
229
- }
230
- }
231
-
232
- }
233
- }
234
- }
235
-
236
- } catch (error) {
237
- errorHandler(data, error)
238
- console.log(action, 'error', error);
239
- resolve(data);
240
- }
241
- }, (error) => {
242
- errorHandler(data, error)
243
- });
244
- }
245
-
246
-
247
- function createDocument(data) {
248
- return document('createDocument', data)
249
- }
250
-
251
- function readDocument(data) {
252
- return document('readDocument', data)
253
- }
254
-
255
- function updateDocument(data) {
256
- return document('updateDocument', data)
257
- }
258
-
259
- function deleteDocument(data) {
260
- return document('deleteDocument', data)
261
- }
262
-
263
- function document(action, data) {
264
- return new Promise(async (resolve, reject) => {
265
- try {
266
- const client = await dbClient(data)
267
- if (!client) return
268
-
269
- let type = 'document'
270
- let documents = [];
271
-
272
- if (data.request)
273
- data[type] = data.request
274
-
275
-
276
- if (!data['timeStamp'])
277
- data['timeStamp'] = new Date().toISOString()
278
-
279
-
280
- let isFilter
281
- if (data.filter && data.filter.query)
282
- isFilter = true
283
-
284
- let databases = data.database;
285
- if (!Array.isArray(databases))
286
- databases = [databases]
287
-
288
- let databasesLength = databases.length
289
- for (let database of databases) {
290
- let collections = data.collection;
291
- if (!Array.isArray(collections))
292
- collections = [collections]
293
-
294
- let collectionsLength = collections.length
295
- for (let collection of collections) {
296
- const db = client.db(database);
297
- const collectionObj = db.collection(collection);
298
-
299
- let { query, sort } = getFilters(data);
300
- if (data['organization_id']) {
301
- query['organization_id'] = { $eq: data['organization_id'] }
302
- }
303
-
304
- let _ids = []
305
- let update_ids = []
306
- let updateData = {}
307
-
308
- if (data[type]) {
309
- if (!Array.isArray(data[type]))
310
- data[type] = [data[type]]
311
- for (let i = 0; i < data[type].length; i++) {
312
- data[type][i] = replaceArray(data[type][i])
313
- data[type][i]['organization_id'] = data['organization_id'];
314
-
315
-
316
- if (action == 'createDocument') {
317
- data[type][i] = dotNotationToObject(data[type][i])
318
-
319
- if (!data[type][i]._id)
320
- data[type][i]._id = ObjectId()
321
- else
322
- data[type][i]._id = ObjectId(data[type][i]._id)
323
- data[type][i]['created'] = { on: data.timeStamp, by: data.user_id || data.clientId }
324
- }
325
- if (action == 'readDocument' && data[type][i]._id) {
326
- _ids.push(ObjectId(data[type][i]._id))
327
- }
328
- if (action == 'updateDocument') {
329
- if (data[type][i]._id)
330
- update_ids.push({ _id: data[type][i]._id, updateDoc: data[type][i], updateType: '_id' })
331
-
332
- if (!data[type][i]._id)
333
- updateData = createUpdate({ document: [data[type][i]] }, type)
334
-
335
- data[type][i]['modified'] = { on: data.timeStamp, by: data.user_id || data.clientId }
336
-
337
- }
338
- if (action == 'deleteDocument') {
339
- if (data[type][i]._id) {
340
- _ids.push(ObjectId(data[type][i]._id))
341
- documents.push({ _id: data[type][i]._id, db: 'mongodb', database, collection })
342
- }
343
- }
344
- }
345
- if (_ids.length == 1)
346
- query['_id'] = ObjectId(_ids[0])
347
- else if (_ids.length > 0)
348
- query['_id'] = { $in: _ids }
349
- }
350
-
351
-
352
- if (action == 'createDocument') {
353
- collectionObj.insertMany(data[type], function (error, result) {
354
- if (error)
355
- errorHandler(data, error, database, collection)
356
-
357
- for (let i = 0; i < data[type].length; i++)
358
- documents.push({ db: 'mongodb', database, collection, ...data[type][i] })
359
-
360
- collectionsLength -= 1
361
- if (!collectionsLength)
362
- databasesLength -= 1
363
-
364
- if (!databasesLength && !collectionsLength) {
365
- data = createData(data, documents, type)
366
- resolve(data)
367
- }
368
- });
369
- }
370
-
371
- if (action == 'readDocument') {
372
- let index = 0, limit = 0
373
- if (data.filter) {
374
- const count = await collectionObj.estimatedDocumentCount()
375
- data.filter.count = count
376
-
377
- if (data.filter.startIndex)
378
- index = data.filter.startIndex
379
- if (data.filter.limit)
380
- limit = data.filter.limit
381
- if (limit)
382
- limit = index + limit;
383
- }
384
-
385
- collectionObj.find(query).limit(limit).sort(sort).toArray(function (error, result) {
386
- if (error)
387
- errorHandler(data, error, database, collection)
388
-
389
- if (result) {
390
- // TODO: forEach at cursor
391
- for (let doc of result) {
392
- let isMatch = true
393
- if (data.filter && data.filter['search'])
394
- isMatch = searchData(doc, data.filter['search'])
395
- if (isMatch) {
396
- doc.db = 'mongodb'
397
- doc.database = database
398
- doc.collection = collection
399
- doc._id = doc._id.toString()
400
-
401
- if (data.returnDocument == false) {
402
- let tempDoc = {};
403
- let docs = new Map(data[type].map((obj) => [obj._id, obj]));
404
- let doc1 = docs.get(doc._id)
405
- if (doc1) {
406
- tempDoc._id = tempDoc
407
- for (let key of Object.keys(doc1)) {
408
- tempDoc[key] = doc[key]
409
- }
410
- doc = tempDoc
411
- }
412
- }
413
-
414
- documents.push(doc)
415
- }
416
- }
417
-
418
- if (index && limit) {
419
- documents = documents.slice(index, limit)
420
- }
421
- }
422
-
423
- collectionsLength -= 1
424
- if (!collectionsLength)
425
- databasesLength -= 1
426
-
427
- if (!databasesLength && !collectionsLength) {
428
- data = createData(data, documents, type)
429
- resolve(data)
430
- }
431
- });
432
- }
433
-
434
- if (action == 'updateDocument' || action == 'deleteDocument') {
435
- const queryDocs = () => {
436
- return new Promise(async (resolve, reject) => {
437
-
438
- collectionObj.find(query).sort(sort).toArray(function (error, result) {
439
- if (error)
440
- errorHandler(data, error, database, collection)
441
-
442
- if (data.filter && data.filter.search) {
443
- let searchResult = []
444
-
445
- for (let doc of result) {
446
- let isMatch = searchData(doc, data.filter.search)
447
- if (isMatch)
448
- searchResult.push(doc)
449
- }
450
- result = searchResult
451
- }
452
- resolve(result)
453
- })
454
- }, (err) => {
455
- console.log(err);
456
- });
457
- }
458
-
459
- let Result, $update, update, projection;
460
-
461
- if (isFilter && data.returnDocument != false)
462
- if (action == 'deleteDocument' || action == 'updateDocument' && updateData.update)
463
- Result = await queryDocs()
464
-
465
- if (Result) {
466
- for (let doc of Result) {
467
- if (action == 'deleteDocument')
468
- documents.push({ _id: doc._id, db: 'mongodb', database, collection })
469
- else
470
- doc['modified'] = { on: data.timeStamp, by: data.user_id || data.clientId }
471
-
472
- _ids.push(doc._id)
473
- }
474
- update_ids.push({ updateType: 'filter' })
475
- }
476
-
477
- if (action == 'updateDocument') {
478
- let docsLength = update_ids.length
479
- for (let { updateDoc, updateType } of update_ids) {
480
-
481
- if (updateType == '_id') {
482
- let update_id = updateDoc._id
483
- query['_id'] = ObjectId(update_id)
484
- $update = createUpdate({ document: [updateDoc] }, type)
485
- update = $update.update
486
- projection = $update.projection
487
- documents.push({ _id: update_id, db: 'mongodb', database, collection, ...update['$set'] })
488
- }
489
-
490
- if (updateType == 'filter') {
491
- query['_id'] = { $in: _ids }
492
- $update = updateData
493
- update = $update.update
494
- projection = $update.projection
495
- for (let _id of _ids)
496
- documents.push({ _id, db: 'mongodb', database, collection, ...update['$set'] })
497
-
498
- }
499
-
500
- update['$set']['organization_id'] = data.organization_id
501
-
502
- collectionObj.updateMany(query, update, {
503
- upsert: data.upsert,
504
- projection
505
- }).then((result) => {
506
-
507
- }).catch((error) => {
508
- errorHandler(data, error, database, collection)
509
- console.log(action, 'error', error);
510
- }).finally((error) => {
511
- docsLength -= 1
512
- if (!docsLength)
513
- collectionsLength -= 1
514
-
515
- if (!collectionsLength)
516
- databasesLength -= 1
517
-
518
- if (!databasesLength && !collectionsLength) {
519
- data = createData(data, documents, type)
520
- resolve(data)
521
- }
522
- })
523
- }
524
-
525
- if (!update_ids.length) {
526
- docsLength -= 1
527
- if (!docsLength)
528
- collectionsLength -= 1
529
-
530
- if (!collectionsLength)
531
- databasesLength -= 1
532
-
533
- if (!databasesLength && !collectionsLength) {
534
- data = createData(data, documents, type)
535
- resolve(data)
536
- }
537
- }
538
-
539
- }
540
-
541
- if (action == 'deleteDocument') {
542
- if (_ids.length == 1)
543
- query['_id'] = ObjectId(_ids[0])
544
- else if (_ids.length > 0)
545
- query['_id'] = { $in: _ids }
546
- collectionObj.deleteMany(query, function (error, result) {
547
- collectionsLength -= 1
548
- if (!collectionsLength)
549
- databasesLength -= 1
550
-
551
- if (!databasesLength && !collectionsLength) {
552
- data = createData(data, documents, type)
553
- resolve(data)
554
- }
555
-
556
- })
557
- }
558
-
559
- }
560
-
561
- }
562
- }
563
-
564
- } catch (error) {
565
- errorHandler(data, error)
566
- console.log(action, 'error', error);
567
- resolve(data);
568
- }
569
- }, (error) => {
570
- errorHandler(data, error)
571
- });
572
-
573
- }
574
-
575
- function createUpdate(data, type) {
576
- let update = {}, projection = {};
577
- if (data[type][0]) {
578
- update['$set'] = data[type][0]
579
- // update['$set']['organization_id'] = data['organization_id'];
580
- if (update['$set']['_id'])
581
- delete update['$set']['_id']
582
- Object.keys(update['$set']).forEach(key => {
583
-
584
- if (key.includes('[u]')) {
585
- update['$addToSet'] = { [key.replace('[u]', '')]: update['$set'][key] }
586
- delete update['$set'][key]
587
- } else if (key.includes('[]')) {
588
- if (!Array.isArray(update['$set'][key]))
589
- update['$set'][key] = [update['$set'][key]]
590
-
591
- update['$push'] = {
592
- [key.replace('[]', '')]: { $each: update['$set'][key] }
593
- }
594
- delete update['$set'][key]
595
- }
596
- // { $push: { "skills": { $each: ["Sports", "Acting"] } } })
597
- // { $addToSet: { "skills": "GST" } }) // adds "GST"to all arrays if not exist
598
- projection[key] = 1
599
- })
600
- }
601
-
602
- if (data['deleteName']) {
603
- update['$unset'] = replaceArray(data['deleteName']);
604
- }
605
-
606
- if (data['updateName']) {
607
- update['$rename'] = replaceArray(data['updateName'])
608
- for (const [key, value] of Object.entries(update['$rename'])) {
609
- if (/\.([0-9]*)/g.test(key) || /\[([0-9]*)\]/g.test(value)) {
610
- console.log('key is array', /\[([0-9]*)\]/g.test(value), /\.([0-9]*)/g.test(key))
611
- } else {
612
- let newValue = replaceArray({ [value]: value })
613
- let oldkey = key;
614
- for (const [key] of Object.entries(newValue)) {
615
- update['$rename'][oldkey] = key
616
- }
617
- }
618
- }
619
- }
620
-
621
- return { update, projection }
622
-
623
- }
624
-
625
- function createData(data, array, type) {
626
- if (!data.request)
627
- data.request = data[type] || {}
628
-
629
- if (data.filter && data.filter.sort)
630
- data[type] = sortData(array, data.filter.sort)
631
- else
632
- data[type] = array
633
-
634
- if (data.returnLog) {
635
- if (!data.log)
636
- data.log = []
637
- data.log.push(...data[type])
638
- }
639
-
640
- return data
641
- }
642
-
643
- function getFilters(data) {
644
- let query = {}, sort = {}
645
- let filter = {
646
- query: [],
647
- sort: [],
648
- search: {
649
- value: [],
650
- type: "or"
651
- },
652
- startIndex: 0,
653
- ...data.filter
654
- };
655
-
656
- query = createQuery(filter.query);
657
-
658
-
659
- if (filter.sort) {
660
- for (let i = 0; i < filter.sort.length; i++) {
661
- let direction = filter.sort[i].direction
662
- if (direction == 'desc' || direction == -1)
663
- direction = -1;
664
- else
665
- direction = 1;
666
-
667
- sort[filter.sort[i].name] = filter.sort[i].direction
668
- }
669
- }
670
- return { query, sort }
671
- }
672
-
673
- // TODO: create impved mongodb query to cover many cases
674
- function createQuery(filters) {
675
- let query = new Object();
676
-
677
-
678
- for (let item of filters) {
679
-
680
- if (!item.name)
681
- continue
682
-
683
- if (item.name == "_id") {
684
- if (item.value)
685
- item.value = ObjectId(item.value)
686
- else
687
- continue
688
- }
689
-
690
- let key = item.name;
691
- if (!query[key]) {
692
- query[key] = {};
693
- }
694
-
695
- switch (item.operator) {
696
- case '$includes':
697
- case 'includes':
698
- query[key]['$regex'] = item.value;
699
- break;
700
-
701
- case '$range':
702
- if (item.value[0] !== null && item.value[1] !== null) {
703
- query[key] = { $gte: item.value[0], $lte: item.value[1] };
704
- } else if (item.value[0] !== null) {
705
- query[key] = { $gte: item.value[0] };
706
- } else if (item.value[1] !== null) {
707
- query[key] = { $lte: item.value[1] };
708
- }
709
- break;
710
-
711
- case 'equals':
712
- query[$eq][item.operator] = item.value;
713
- case '$eq':
714
- case '$ne':
715
- case '$lt':
716
- case '$lte':
717
- case '$gt':
718
- case '$gte':
719
- case '$regex':
720
- query[key][item.operator] = item.value;
721
- break;
722
- case '$in':
723
- case '$nin':
724
- if (!Array.isArray(item.value))
725
- query[key] = [item.value]
726
- else
727
- query[key] = { $in: item.value }
728
- break;
729
- case '$geoWithin':
730
- try {
731
- let value = JSON.parse(item.value);
732
- if (item.type) {
733
- query[key]['$geoWithin'] = {
734
- [item.type]: value
735
- }
736
- }
737
- } catch (e) {
738
- console.log('geowithin error');
739
- }
740
- break;
741
- }
742
- }
743
-
744
- //. global search
745
- //. we have to set indexes in text fields ex: db.chart.createIndex({ "$**": "text" })
746
- // if (data['searchKey']) {
747
- // query["$text"] = {$search: "\"Ni\""};
748
- // }
749
-
750
- return query;
751
- }
752
-
753
- function errorHandler(data, error, database, collection) {
754
- if (typeof error == 'object')
755
- error['db'] = 'mongodb'
756
- else
757
- error = { db: 'mongodb', message: error }
758
-
759
- if (database)
760
- error['database'] = database
761
- if (collection)
762
- error['collection'] = collection
763
- if (data.error)
764
- data.error.push(error)
765
- else
766
- data.error = [error]
767
- }
768
-
769
- function replaceArray(data) {
770
- let keys = Object.keys(data);
771
- let objectData = {};
772
-
773
- keys.forEach((k) => {
774
- let nk = k
775
- if (/\[([0-9]*)\]/g.test(k)) {
776
- nk = nk.replace(/\[/g, '.');
777
- if (nk.endsWith(']'))
778
- nk = nk.slice(0, -1)
779
- nk = nk.replace(/\]./g, '.');
780
- nk = nk.replace(/\]/g, '.');
781
- }
782
- objectData[nk] = data[k];
783
- });
784
-
785
- return objectData;
786
- }
787
-
788
-
789
- module.exports = {
790
- databaseStats,
791
- createDatabase,
792
- readDatabase,
793
- updateDatabase,
794
- deleteDatabase,
795
-
796
- createCollection,
797
- readCollection,
798
- updateCollection,
799
- deleteCollection,
800
-
801
- createDocument,
802
- readDocument,
803
- updateDocument,
804
- deleteDocument,
805
- }
1
+ const { MongoClient, ObjectId } = require('mongodb');
2
+ const { dotNotationToObject, queryData, searchData, sortData } = require('@cocreate/utils')
3
+ const clients = new Map()
4
+
5
+
6
+ async function dbClient(data) {
7
+ if (data.dbUrl) {
8
+ let client = clients.get(data.dbUrl)
9
+ if (!client) {
10
+ try {
11
+ client = MongoClient.connect(data.dbUrl, { useNewUrlParser: true, useUnifiedTopology: true });
12
+ clients.set(data.dbUrl, client)
13
+ } catch (error) {
14
+ console.error(error)
15
+ return { status: false }
16
+ }
17
+ }
18
+ return client
19
+ }
20
+ return
21
+ }
22
+
23
+ async function databaseStats(data) {
24
+ const client = await dbClient(data)
25
+ if (!client) return
26
+ let db = client.db(data.organization_id)
27
+ let stats = db.stats()
28
+ return stats
29
+ }
30
+
31
+ function createDatabase(data) {
32
+ return database('createDatabase', data)
33
+ }
34
+
35
+ function readDatabase(data) {
36
+ return database('readDatabase', data)
37
+ }
38
+
39
+ function updateDatabase(data) {
40
+ return database('updateDatabase', data)
41
+ }
42
+
43
+ function deleteDatabase(data) {
44
+ return database('deleteDatabase', data)
45
+ }
46
+
47
+ function database(action, data) {
48
+ return new Promise(async (resolve, reject) => {
49
+ let type = 'database'
50
+ let databaseArray = []
51
+
52
+ try {
53
+ const client = await dbClient(data)
54
+ if (!client) return
55
+ if (action == 'readDatabase') {
56
+ const db = client.db().admin();
57
+
58
+ // List all the available databases
59
+ db.listDatabases(function (err, dbs) {
60
+
61
+ for (let database of dbs.databases) {
62
+ if (data.filter && data.filter.query) {
63
+ let isFilter = queryData(database, data.filter.query)
64
+ if (isFilter)
65
+ databaseArray.push({ database, db: 'mongodb' })
66
+ } else
67
+ databaseArray.push({ database, db: 'mongodb' })
68
+ }
69
+
70
+ resolve(createData(data, databaseArray, type))
71
+ })
72
+ }
73
+ if (action == 'deleteDatabase') {
74
+ const db = client.db(data.database);
75
+ db.dropDatabase().then(response => {
76
+ resolve(response)
77
+ })
78
+ }
79
+ } catch (error) {
80
+ errorHandler(data, error)
81
+ console.log(action, 'error', error);
82
+ resolve(data);
83
+ }
84
+
85
+ }, (error) => {
86
+ errorHandler(data, error)
87
+ });
88
+ }
89
+
90
+
91
+ function createCollection(data) {
92
+ return collection('createCollection', data)
93
+ }
94
+
95
+ function readCollection(data) {
96
+ return collection('readCollection', data)
97
+ }
98
+
99
+ function updateCollection(data) {
100
+ return collection('updateCollection', data)
101
+ }
102
+
103
+ function deleteCollection(data) {
104
+ return collection('deleteCollection', data)
105
+ }
106
+
107
+ function collection(action, data) {
108
+ return new Promise(async (resolve, reject) => {
109
+ let type = 'collection'
110
+ let collectionArray = [];
111
+
112
+ try {
113
+ const client = await dbClient(data)
114
+ if (!client) return
115
+
116
+ if (data.request)
117
+ data.collection = data.request
118
+
119
+ let databases = data.database;
120
+ if (!Array.isArray(databases))
121
+ databases = [databases]
122
+
123
+ let databasesLength = databases.length
124
+ for (let database of databases) {
125
+ const db = client.db(database);
126
+
127
+ if (action == 'readCollection') {
128
+
129
+ let { query, sort } = getFilters(data);
130
+
131
+ db.listCollections().toArray(function (error, result) {
132
+ if (error)
133
+ errorHandler(data, error, database)
134
+
135
+ if (result) {
136
+ for (let res of result) {
137
+ if (data.filter && data.filter.query) {
138
+ let isFilter = queryData(res, data.filter.query)
139
+ if (isFilter)
140
+ collectionArray.push({ name: res.name, database, db: 'mongodb' })
141
+ } else
142
+ collectionArray.push({ name: res.name, database, db: 'mongodb' })
143
+ }
144
+ }
145
+
146
+ databasesLength -= 1
147
+ if (!databasesLength) {
148
+ data = createData(data, collectionArray, type)
149
+ resolve(data)
150
+ }
151
+ })
152
+ } else {
153
+ let collections
154
+ let value
155
+ if (action == 'updateCollection')
156
+ collections = Object.entries(data.collection)
157
+ else
158
+ collections = data.collection;
159
+
160
+ if (!Array.isArray(collections))
161
+ collections = [collections]
162
+
163
+ let collectionsLength = collections.length
164
+ for (let collection of collections) {
165
+
166
+ if (action == 'createCollection') {
167
+ db.createCollection(collection, function (error, result) {
168
+ if (error)
169
+ errorHandler(data, error, database, collection)
170
+
171
+ if (result)
172
+ collectionArray.push({ name: collection, database, db: 'mongodb' })
173
+
174
+ collectionsLength -= 1
175
+ if (!collectionsLength)
176
+ databasesLength -= 1
177
+
178
+ if (!databasesLength && !collectionsLength) {
179
+ data = createData(data, collectionArray, type)
180
+ resolve(data)
181
+ }
182
+ })
183
+ } else {
184
+ if (action == 'updateCollection') {
185
+ [collection, value] = collection
186
+ }
187
+
188
+ const collectionObj = db.collection(collection);
189
+
190
+ if (action == 'updateCollection') {
191
+ collectionObj.rename(value, function (error, result) {
192
+ if (error)
193
+ errorHandler(data, error, database, collection)
194
+
195
+ if (result)
196
+ collectionArray.push({ name: value, oldName: collection, database, db: 'mongodb' })
197
+
198
+ collectionsLength -= 1
199
+ if (!collectionsLength)
200
+ databasesLength -= 1
201
+
202
+ if (!databasesLength && !collectionsLength) {
203
+ data = createData(data, collectionArray, type)
204
+ resolve(data)
205
+ }
206
+
207
+ })
208
+ }
209
+
210
+ if (action == 'deleteCollection') {
211
+ collectionObj.drop(function (error, result) {
212
+ if (error)
213
+ errorHandler(data, error, database, collection)
214
+
215
+ if (result)
216
+ collectionArray.push({ name: collection, database, db: 'mongodb' })
217
+
218
+ collectionsLength -= 1
219
+ if (!collectionsLength)
220
+ databasesLength -= 1
221
+
222
+ if (!databasesLength && !collectionsLength) {
223
+ data = createData(data, collectionArray, type)
224
+ resolve(data)
225
+ }
226
+
227
+ })
228
+
229
+ }
230
+ }
231
+
232
+ }
233
+ }
234
+ }
235
+
236
+ } catch (error) {
237
+ errorHandler(data, error)
238
+ console.log(action, 'error', error);
239
+ resolve(data);
240
+ }
241
+ }, (error) => {
242
+ errorHandler(data, error)
243
+ });
244
+ }
245
+
246
+
247
+ function createDocument(data) {
248
+ return document('createDocument', data)
249
+ }
250
+
251
+ function readDocument(data) {
252
+ return document('readDocument', data)
253
+ }
254
+
255
+ function updateDocument(data) {
256
+ return document('updateDocument', data)
257
+ }
258
+
259
+ function deleteDocument(data) {
260
+ return document('deleteDocument', data)
261
+ }
262
+
263
+ function document(action, data) {
264
+ return new Promise(async (resolve, reject) => {
265
+ try {
266
+ const client = await dbClient(data)
267
+ if (!client) return
268
+
269
+ let type = 'document'
270
+ let documents = [];
271
+
272
+ if (data.request)
273
+ data[type] = data.request
274
+
275
+
276
+ if (!data['timeStamp'])
277
+ data['timeStamp'] = new Date().toISOString()
278
+
279
+
280
+ let isFilter
281
+ if (data.filter && data.filter.query)
282
+ isFilter = true
283
+
284
+ let databases = data.database;
285
+ if (!Array.isArray(databases))
286
+ databases = [databases]
287
+
288
+ let databasesLength = databases.length
289
+ for (let database of databases) {
290
+ let collections = data.collection;
291
+ if (!Array.isArray(collections))
292
+ collections = [collections]
293
+
294
+ let collectionsLength = collections.length
295
+ for (let collection of collections) {
296
+ const db = client.db(database);
297
+ const collectionObj = db.collection(collection);
298
+
299
+ let { query, sort } = getFilters(data);
300
+ if (data['organization_id']) {
301
+ query['organization_id'] = { $eq: data['organization_id'] }
302
+ }
303
+
304
+ let _ids = []
305
+ let update_ids = []
306
+ let updateData = {}
307
+
308
+ if (data[type]) {
309
+ if (!Array.isArray(data[type]))
310
+ data[type] = [data[type]]
311
+ for (let i = 0; i < data[type].length; i++) {
312
+ data[type][i] = replaceArray(data[type][i])
313
+ data[type][i]['organization_id'] = data['organization_id'];
314
+
315
+
316
+ if (action == 'createDocument') {
317
+ data[type][i] = dotNotationToObject(data[type][i])
318
+
319
+ if (!data[type][i]._id)
320
+ data[type][i]._id = ObjectId()
321
+ else
322
+ data[type][i]._id = ObjectId(data[type][i]._id)
323
+ data[type][i]['created'] = { on: data.timeStamp, by: data.user_id || data.clientId }
324
+ }
325
+ if (action == 'readDocument' && data[type][i]._id) {
326
+ _ids.push(ObjectId(data[type][i]._id))
327
+ }
328
+ if (action == 'updateDocument') {
329
+ if (data[type][i]._id)
330
+ update_ids.push({ _id: data[type][i]._id, updateDoc: data[type][i], updateType: '_id' })
331
+
332
+ if (!data[type][i]._id)
333
+ updateData = createUpdate({ document: [data[type][i]] }, type)
334
+
335
+ data[type][i]['modified'] = { on: data.timeStamp, by: data.user_id || data.clientId }
336
+
337
+ }
338
+ if (action == 'deleteDocument') {
339
+ if (data[type][i]._id) {
340
+ _ids.push(ObjectId(data[type][i]._id))
341
+ documents.push({ _id: data[type][i]._id, db: 'mongodb', database, collection })
342
+ }
343
+ }
344
+ }
345
+ if (_ids.length == 1)
346
+ query['_id'] = ObjectId(_ids[0])
347
+ else if (_ids.length > 0)
348
+ query['_id'] = { $in: _ids }
349
+ }
350
+
351
+
352
+ if (action == 'createDocument') {
353
+ collectionObj.insertMany(data[type], function (error, result) {
354
+ if (error)
355
+ errorHandler(data, error, database, collection)
356
+
357
+ for (let i = 0; i < data[type].length; i++)
358
+ documents.push({ db: 'mongodb', database, collection, ...data[type][i] })
359
+
360
+ collectionsLength -= 1
361
+ if (!collectionsLength)
362
+ databasesLength -= 1
363
+
364
+ if (!databasesLength && !collectionsLength) {
365
+ data = createData(data, documents, type)
366
+ resolve(data)
367
+ }
368
+ });
369
+ }
370
+
371
+ if (action == 'readDocument') {
372
+ let index = 0, limit = 0
373
+ if (data.filter) {
374
+ const count = await collectionObj.estimatedDocumentCount()
375
+ data.filter.count = count
376
+
377
+ if (data.filter.startIndex)
378
+ index = data.filter.startIndex
379
+ if (data.filter.limit)
380
+ limit = data.filter.limit
381
+ if (limit)
382
+ limit = index + limit;
383
+ }
384
+
385
+ collectionObj.find(query).limit(limit).sort(sort).toArray(function (error, result) {
386
+ if (error)
387
+ errorHandler(data, error, database, collection)
388
+
389
+ if (result) {
390
+ // TODO: forEach at cursor
391
+ for (let doc of result) {
392
+ let isMatch = true
393
+ if (data.filter && data.filter['search'])
394
+ isMatch = searchData(doc, data.filter['search'])
395
+ if (isMatch) {
396
+ doc.db = 'mongodb'
397
+ doc.database = database
398
+ doc.collection = collection
399
+ doc._id = doc._id.toString()
400
+
401
+ if (data.returnDocument == false) {
402
+ let tempDoc = {};
403
+ let docs = new Map(data[type].map((obj) => [obj._id, obj]));
404
+ let doc1 = docs.get(doc._id)
405
+ if (doc1) {
406
+ tempDoc._id = tempDoc
407
+ for (let key of Object.keys(doc1)) {
408
+ tempDoc[key] = doc[key]
409
+ }
410
+ doc = tempDoc
411
+ }
412
+ }
413
+
414
+ documents.push(doc)
415
+ }
416
+ }
417
+
418
+ if (index && limit) {
419
+ documents = documents.slice(index, limit)
420
+ }
421
+ }
422
+
423
+ collectionsLength -= 1
424
+ if (!collectionsLength)
425
+ databasesLength -= 1
426
+
427
+ if (!databasesLength && !collectionsLength) {
428
+ data = createData(data, documents, type)
429
+ resolve(data)
430
+ }
431
+ });
432
+ }
433
+
434
+ if (action == 'updateDocument' || action == 'deleteDocument') {
435
+ const queryDocs = () => {
436
+ return new Promise(async (resolve, reject) => {
437
+
438
+ collectionObj.find(query).sort(sort).toArray(function (error, result) {
439
+ if (error)
440
+ errorHandler(data, error, database, collection)
441
+
442
+ if (data.filter && data.filter.search) {
443
+ let searchResult = []
444
+
445
+ for (let doc of result) {
446
+ let isMatch = searchData(doc, data.filter.search)
447
+ if (isMatch)
448
+ searchResult.push(doc)
449
+ }
450
+ result = searchResult
451
+ }
452
+ resolve(result)
453
+ })
454
+ }, (err) => {
455
+ console.log(err);
456
+ });
457
+ }
458
+
459
+ let Result, $update, update, projection;
460
+
461
+ if (isFilter && data.returnDocument != false)
462
+ if (action == 'deleteDocument' || action == 'updateDocument' && updateData.update)
463
+ Result = await queryDocs()
464
+
465
+ if (Result) {
466
+ for (let doc of Result) {
467
+ if (action == 'deleteDocument')
468
+ documents.push({ _id: doc._id, db: 'mongodb', database, collection })
469
+ else
470
+ doc['modified'] = { on: data.timeStamp, by: data.user_id || data.clientId }
471
+
472
+ _ids.push(doc._id)
473
+ }
474
+ update_ids.push({ updateType: 'filter' })
475
+ }
476
+
477
+ if (action == 'updateDocument') {
478
+ let docsLength = update_ids.length
479
+ for (let { updateDoc, updateType } of update_ids) {
480
+
481
+ if (updateType == '_id') {
482
+ let update_id = updateDoc._id
483
+ query['_id'] = ObjectId(update_id)
484
+ $update = createUpdate({ document: [updateDoc] }, type)
485
+ update = $update.update
486
+ projection = $update.projection
487
+ documents.push({ _id: update_id, db: 'mongodb', database, collection, ...update['$set'] })
488
+ }
489
+
490
+ if (updateType == 'filter') {
491
+ query['_id'] = { $in: _ids }
492
+ $update = updateData
493
+ update = $update.update
494
+ projection = $update.projection
495
+ for (let _id of _ids)
496
+ documents.push({ _id, db: 'mongodb', database, collection, ...update['$set'] })
497
+
498
+ }
499
+
500
+ update['$set']['organization_id'] = data.organization_id
501
+
502
+ collectionObj.updateMany(query, update, {
503
+ upsert: data.upsert,
504
+ projection
505
+ }).then((result) => {
506
+
507
+ }).catch((error) => {
508
+ errorHandler(data, error, database, collection)
509
+ console.log(action, 'error', error);
510
+ }).finally((error) => {
511
+ docsLength -= 1
512
+ if (!docsLength)
513
+ collectionsLength -= 1
514
+
515
+ if (!collectionsLength)
516
+ databasesLength -= 1
517
+
518
+ if (!databasesLength && !collectionsLength) {
519
+ data = createData(data, documents, type)
520
+ resolve(data)
521
+ }
522
+ })
523
+ }
524
+
525
+ if (!update_ids.length) {
526
+ docsLength -= 1
527
+ if (!docsLength)
528
+ collectionsLength -= 1
529
+
530
+ if (!collectionsLength)
531
+ databasesLength -= 1
532
+
533
+ if (!databasesLength && !collectionsLength) {
534
+ data = createData(data, documents, type)
535
+ resolve(data)
536
+ }
537
+ }
538
+
539
+ }
540
+
541
+ if (action == 'deleteDocument') {
542
+ if (_ids.length == 1)
543
+ query['_id'] = ObjectId(_ids[0])
544
+ else if (_ids.length > 0)
545
+ query['_id'] = { $in: _ids }
546
+ collectionObj.deleteMany(query, function (error, result) {
547
+ collectionsLength -= 1
548
+ if (!collectionsLength)
549
+ databasesLength -= 1
550
+
551
+ if (!databasesLength && !collectionsLength) {
552
+ data = createData(data, documents, type)
553
+ resolve(data)
554
+ }
555
+
556
+ })
557
+ }
558
+
559
+ }
560
+
561
+ }
562
+ }
563
+
564
+ } catch (error) {
565
+ errorHandler(data, error)
566
+ console.log(action, 'error', error);
567
+ resolve(data);
568
+ }
569
+ }, (error) => {
570
+ errorHandler(data, error)
571
+ });
572
+
573
+ }
574
+
575
+ function createUpdate(data, type) {
576
+ let update = {}, projection = {};
577
+ if (data[type][0]) {
578
+ update['$set'] = data[type][0]
579
+ // update['$set']['organization_id'] = data['organization_id'];
580
+ if (update['$set']['_id'])
581
+ delete update['$set']['_id']
582
+ Object.keys(update['$set']).forEach(key => {
583
+
584
+ if (key.includes('[u]')) {
585
+ update['$addToSet'] = { [key.replace('[u]', '')]: update['$set'][key] }
586
+ delete update['$set'][key]
587
+ } else if (key.includes('[]')) {
588
+ if (!Array.isArray(update['$set'][key]))
589
+ update['$set'][key] = [update['$set'][key]]
590
+
591
+ update['$push'] = {
592
+ [key.replace('[]', '')]: { $each: update['$set'][key] }
593
+ }
594
+ delete update['$set'][key]
595
+ }
596
+ // { $push: { "skills": { $each: ["Sports", "Acting"] } } })
597
+ // { $addToSet: { "skills": "GST" } }) // adds "GST"to all arrays if not exist
598
+ projection[key] = 1
599
+ })
600
+ }
601
+
602
+ if (data['deleteName']) {
603
+ update['$unset'] = replaceArray(data['deleteName']);
604
+ }
605
+
606
+ if (data['updateName']) {
607
+ update['$rename'] = replaceArray(data['updateName'])
608
+ for (const [key, value] of Object.entries(update['$rename'])) {
609
+ if (/\.([0-9]*)/g.test(key) || /\[([0-9]*)\]/g.test(value)) {
610
+ console.log('key is array', /\[([0-9]*)\]/g.test(value), /\.([0-9]*)/g.test(key))
611
+ } else {
612
+ let newValue = replaceArray({ [value]: value })
613
+ let oldkey = key;
614
+ for (const [key] of Object.entries(newValue)) {
615
+ update['$rename'][oldkey] = key
616
+ }
617
+ }
618
+ }
619
+ }
620
+
621
+ return { update, projection }
622
+
623
+ }
624
+
625
+ function createData(data, array, type) {
626
+ if (!data.request)
627
+ data.request = data[type] || {}
628
+
629
+ if (data.filter && data.filter.sort)
630
+ data[type] = sortData(array, data.filter.sort)
631
+ else
632
+ data[type] = array
633
+
634
+ if (data.returnLog) {
635
+ if (!data.log)
636
+ data.log = []
637
+ data.log.push(...data[type])
638
+ }
639
+
640
+ return data
641
+ }
642
+
643
+ function getFilters(data) {
644
+ let query = {}, sort = {}
645
+ let filter = {
646
+ query: [],
647
+ sort: [],
648
+ search: {
649
+ value: [],
650
+ type: "or"
651
+ },
652
+ startIndex: 0,
653
+ ...data.filter
654
+ };
655
+
656
+ query = createQuery(filter.query);
657
+
658
+
659
+ if (filter.sort) {
660
+ for (let i = 0; i < filter.sort.length; i++) {
661
+ let direction = filter.sort[i].direction
662
+ if (direction == 'desc' || direction == -1)
663
+ direction = -1;
664
+ else
665
+ direction = 1;
666
+
667
+ sort[filter.sort[i].name] = filter.sort[i].direction
668
+ }
669
+ }
670
+ return { query, sort }
671
+ }
672
+
673
+ // TODO: create impved mongodb query to cover many cases
674
+ function createQuery(filters) {
675
+ let query = new Object();
676
+
677
+
678
+ for (let item of filters) {
679
+
680
+ if (!item.name)
681
+ continue
682
+
683
+ if (item.name == "_id") {
684
+ if (item.value)
685
+ item.value = ObjectId(item.value)
686
+ else
687
+ continue
688
+ }
689
+
690
+ let key = item.name;
691
+ if (!query[key]) {
692
+ query[key] = {};
693
+ }
694
+
695
+ switch (item.operator) {
696
+ case '$includes':
697
+ case 'includes':
698
+ query[key]['$regex'] = item.value;
699
+ break;
700
+
701
+ case '$range':
702
+ if (item.value[0] !== null && item.value[1] !== null) {
703
+ query[key] = { $gte: item.value[0], $lte: item.value[1] };
704
+ } else if (item.value[0] !== null) {
705
+ query[key] = { $gte: item.value[0] };
706
+ } else if (item.value[1] !== null) {
707
+ query[key] = { $lte: item.value[1] };
708
+ }
709
+ break;
710
+
711
+ case 'equals':
712
+ query[$eq][item.operator] = item.value;
713
+ case '$eq':
714
+ case '$ne':
715
+ case '$lt':
716
+ case '$lte':
717
+ case '$gt':
718
+ case '$gte':
719
+ case '$regex':
720
+ query[key][item.operator] = item.value;
721
+ break;
722
+ case '$in':
723
+ case '$nin':
724
+ if (!Array.isArray(item.value))
725
+ query[key] = [item.value]
726
+ else
727
+ query[key] = { $in: item.value }
728
+ break;
729
+ case '$geoWithin':
730
+ try {
731
+ let value = JSON.parse(item.value);
732
+ if (item.type) {
733
+ query[key]['$geoWithin'] = {
734
+ [item.type]: value
735
+ }
736
+ }
737
+ } catch (e) {
738
+ console.log('geowithin error');
739
+ }
740
+ break;
741
+ }
742
+ }
743
+
744
+ //. global search
745
+ //. we have to set indexes in text fields ex: db.chart.createIndex({ "$**": "text" })
746
+ // if (data['searchKey']) {
747
+ // query["$text"] = {$search: "\"Ni\""};
748
+ // }
749
+
750
+ return query;
751
+ }
752
+
753
+ function errorHandler(data, error, database, collection) {
754
+ if (typeof error == 'object')
755
+ error['db'] = 'mongodb'
756
+ else
757
+ error = { db: 'mongodb', message: error }
758
+
759
+ if (database)
760
+ error['database'] = database
761
+ if (collection)
762
+ error['collection'] = collection
763
+ if (data.error)
764
+ data.error.push(error)
765
+ else
766
+ data.error = [error]
767
+ }
768
+
769
+ function replaceArray(data) {
770
+ let keys = Object.keys(data);
771
+ let objectData = {};
772
+
773
+ keys.forEach((k) => {
774
+ let nk = k
775
+ if (/\[([0-9]*)\]/g.test(k)) {
776
+ nk = nk.replace(/\[/g, '.');
777
+ if (nk.endsWith(']'))
778
+ nk = nk.slice(0, -1)
779
+ nk = nk.replace(/\]./g, '.');
780
+ nk = nk.replace(/\]/g, '.');
781
+ }
782
+ objectData[nk] = data[k];
783
+ });
784
+
785
+ return objectData;
786
+ }
787
+
788
+
789
+ module.exports = {
790
+ databaseStats,
791
+ createDatabase,
792
+ readDatabase,
793
+ updateDatabase,
794
+ deleteDatabase,
795
+
796
+ createCollection,
797
+ readCollection,
798
+ updateCollection,
799
+ deleteCollection,
800
+
801
+ createDocument,
802
+ readDocument,
803
+ updateDocument,
804
+ deleteDocument,
805
+ }