@drawbridge/mongodb 0.0.1

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.
@@ -0,0 +1,500 @@
1
+ const { ObjectId } = require( 'mongodb' );
2
+
3
+ const ids = ({
4
+ _id
5
+ }) => {
6
+
7
+ const id = _id || new ObjectId();
8
+
9
+ return {
10
+ _id : id,
11
+ id : id.toString()
12
+ };
13
+
14
+ };
15
+
16
+ const formats = {
17
+ documents : {
18
+ insert : ( data, authenticated ) => {
19
+
20
+ const date = new Date();
21
+
22
+ return {
23
+ createdAt : date,
24
+ updatedAt : date,
25
+ ...ids( data ),
26
+ ...( authenticated && {
27
+ createdBy : authenticated?.id + '.' + date.getTime()
28
+ }),
29
+ ...data
30
+ }
31
+
32
+ },
33
+ update : (
34
+ {
35
+ $set,
36
+ $setOnInsert,
37
+ ...rest
38
+ },
39
+ authenticated
40
+ ) => {
41
+
42
+ const date = new Date();
43
+
44
+ return {
45
+ $set : {
46
+ updatedAt : date,
47
+ ...( authenticated && {
48
+ updatedBy : authenticated?.id + '.' + date.getTime()
49
+ }),
50
+ ...$set
51
+ },
52
+ $setOnInsert : {
53
+ ...ids( rest ),
54
+ createdAt : date,
55
+ ...( authenticated && {
56
+ createdBy : authenticated?.id + '.' + date.getTime()
57
+ }),
58
+ ...$setOnInsert
59
+ },
60
+ ...rest
61
+ };
62
+
63
+ }
64
+ },
65
+ timeseries : {
66
+ insert : ( data ) => {
67
+
68
+ const { _id, id } = ids( data );
69
+ const date = new Date();
70
+
71
+ return {
72
+ _id,
73
+ meta : {
74
+ ...data,
75
+ id
76
+ },
77
+ time : date
78
+ };
79
+
80
+ },
81
+ update : ({
82
+ $set
83
+ }) => {
84
+
85
+ return {
86
+ $set
87
+ };
88
+
89
+ }
90
+ }
91
+ };
92
+
93
+ module.exports = ({
94
+ client,
95
+ database
96
+ }) => ({
97
+
98
+ aggregate : async ({
99
+ collection,
100
+ options = {},
101
+ pipeline = []
102
+ }) => {
103
+
104
+ return await database.collection( collection ).aggregate( pipeline, options ).toArray();
105
+
106
+ },
107
+
108
+ bulk : async ({
109
+ collection,
110
+ options = {},
111
+ pipeline = []
112
+ }) => {
113
+
114
+ return await database.collection( collection ).bulkWrite( pipeline, options );
115
+
116
+ },
117
+
118
+ count : async ({
119
+ collection,
120
+ options = {},
121
+ query
122
+ }) => {
123
+
124
+ return await database.collection( collection ).countDocuments( query, options );
125
+
126
+ },
127
+
128
+ create : async ({
129
+ authenticated,
130
+ collection,
131
+ data,
132
+ multiple = false,
133
+ options,
134
+ pre,
135
+ timeseries = false
136
+ }) => {
137
+
138
+ const endpoint = multiple ? 'insertMany' : 'insertOne';
139
+ const format = timeseries ? 'timeseries' : 'documents';
140
+
141
+ let result;
142
+
143
+ try {
144
+
145
+ if( multiple ){
146
+
147
+ result = data.map( ( item ) => formats[ format ].insert( item, authenticated ) );
148
+
149
+ } else {
150
+
151
+ result = formats[ format ].insert( data, authenticated );
152
+
153
+ }
154
+
155
+ if( pre instanceof Function ){
156
+
157
+ result = await pre( result );
158
+
159
+ }
160
+
161
+ await database.collection( collection )[ endpoint ](
162
+ result,
163
+ timeseries ? {} : {
164
+ returnDocument : 'after',
165
+ returnNewDocument : true,
166
+ ...options
167
+ }
168
+ );
169
+
170
+ return result;
171
+
172
+ } catch ( error ) {
173
+
174
+ throw error;
175
+
176
+ }
177
+
178
+ },
179
+
180
+ delete : async ({
181
+ collection,
182
+ query,
183
+ multiple,
184
+ options = {}
185
+ }) => {
186
+
187
+ let result;
188
+
189
+ try {
190
+
191
+ if( multiple ){
192
+
193
+ result = await database.collection( collection ).find(
194
+ query,
195
+ {},
196
+ options
197
+ ).toArray();
198
+
199
+ await database.collection( collection ).deleteMany(
200
+ query,
201
+ options
202
+ );
203
+
204
+ } else {
205
+
206
+ result = await database.collection( collection ).findOne(
207
+ query
208
+ );
209
+
210
+ await database.collection( collection ).deleteOne(
211
+ query,
212
+ options
213
+ );
214
+
215
+ }
216
+
217
+ return result;
218
+
219
+ } catch ( error ) {
220
+
221
+ throw error;
222
+
223
+ }
224
+
225
+ },
226
+
227
+ formats,
228
+
229
+ get : async ({
230
+ collection,
231
+ extend = [],
232
+ options = {},
233
+ refine = {},
234
+ query
235
+ }) => {
236
+
237
+ try {
238
+
239
+ const pipeline = [
240
+ {
241
+ $match : query
242
+ },
243
+ ...extend
244
+ ];
245
+
246
+ if( refine ){
247
+
248
+ pipeline.push({
249
+ $match : refine
250
+ });
251
+
252
+ }
253
+
254
+ const result = await database.collection( collection ).aggregate(
255
+ pipeline,
256
+ options
257
+ ).toArray();
258
+
259
+ return result?.[ 0 ];
260
+
261
+ } catch ( error ) {
262
+
263
+ throw error;
264
+
265
+ }
266
+
267
+ },
268
+
269
+ paged : async ({
270
+ collection,
271
+ limit,
272
+ filters = {},
273
+ refine,
274
+ search = null,
275
+ skip,
276
+ sort,
277
+ ...rest
278
+ }) => {
279
+
280
+ try {
281
+
282
+ const pipelines = {
283
+ query : []
284
+ };
285
+
286
+ if( filters && Object.keys( filters ).length > 0 ){
287
+
288
+ pipelines.query.push(
289
+ {
290
+ $match : filters
291
+ }
292
+ );
293
+
294
+ }
295
+
296
+ pipelines.query.push({
297
+ $sort : Object.keys( sort ).reduce(
298
+ ( accumulator, key ) => {
299
+
300
+ accumulator[ key ] = Number( sort[ key ] );
301
+
302
+ return accumulator;
303
+
304
+ },
305
+ {}
306
+ )
307
+ });
308
+
309
+ // must be after match
310
+ if( ( rest?.extend || [] ).length > 0 ){
311
+
312
+ pipelines.query = [ ...pipelines.query, ...rest.extend ];
313
+
314
+ }
315
+
316
+ if( refine && Object.keys( refine ).length > 0 ){
317
+
318
+ pipelines.query.push(
319
+ {
320
+ $match : refine
321
+ }
322
+ );
323
+
324
+ }
325
+
326
+ if( search?.value && search?.keys?.length > 0 ){
327
+
328
+ const value = search.value.trim().replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
329
+
330
+ search = {
331
+ $match : {
332
+ $or : ( search?.keys || [] ).reduce(
333
+ ( accumulator, key ) => {
334
+
335
+ accumulator.push({
336
+ [ key ] : {
337
+ $regex : value,
338
+ $options : 'i'
339
+ }
340
+ });
341
+
342
+ return accumulator;
343
+
344
+ },
345
+ []
346
+ )
347
+ }
348
+ };
349
+
350
+ pipelines.query.push( search );
351
+
352
+ }
353
+
354
+ const dataStages = [];
355
+
356
+ if( skip && limit ){
357
+
358
+ dataStages.push({
359
+ $skip : Number( Number( Number( skip ) * Number( limit ) ) )
360
+ });
361
+
362
+ }
363
+
364
+ if( limit ){
365
+
366
+ dataStages.push({
367
+ $limit : Number( limit )
368
+ });
369
+
370
+ }
371
+
372
+ if( rest?.project ){
373
+
374
+ dataStages.push({
375
+ $project : rest?.project
376
+ });
377
+
378
+ }
379
+
380
+ const facetPipeline = [
381
+ ...pipelines.query,
382
+ {
383
+ $facet : {
384
+ items : dataStages,
385
+ total : [
386
+ {
387
+ $group : {
388
+ _id : null,
389
+ total : {
390
+ $sum : 1
391
+ }
392
+ }
393
+ }
394
+ ]
395
+ }
396
+ }
397
+ ];
398
+
399
+ const [ facetResult ] = await database.collection( collection ).aggregate( facetPipeline ).toArray();
400
+ const items = facetResult?.items || [];
401
+ const total = ( facetResult?.total?.[ 0 ]?.total || 0 );
402
+ const offset = Number( Number( skip ) + 1 ) * Number( limit );
403
+ const hasPreviousPage = Boolean( Number( Number( skip ) ) * Number( limit ) > 0 );
404
+ const hasNextPage = Boolean( offset < Number( total ) );
405
+
406
+ return {
407
+ items,
408
+ pageInfo : {
409
+ hasNextPage,
410
+ hasPreviousPage,
411
+ offset : hasNextPage ? offset : total,
412
+ total
413
+ }
414
+ };
415
+
416
+ } catch ( error ) {
417
+
418
+ throw error;
419
+
420
+ }
421
+
422
+ },
423
+
424
+ transaction : async ( callback ) => {
425
+
426
+ try {
427
+
428
+ const result = await new Promise(
429
+ ( resolve, reject ) => client.withSession(
430
+ async ( session ) => session.withTransaction(
431
+ async ( session ) => resolve( await callback( session ) ),
432
+ null
433
+ ).catch( reject )
434
+ ).catch( reject )
435
+ );
436
+
437
+ return result;
438
+
439
+ } catch ( error ) {
440
+
441
+ throw error
442
+
443
+ }
444
+
445
+ },
446
+
447
+ update : async ({
448
+ authenticated,
449
+ collection,
450
+ data,
451
+ query,
452
+ multiple = false,
453
+ options,
454
+ timeseries = false
455
+ }) => {
456
+
457
+ let result = {};
458
+
459
+ try {
460
+
461
+ if( multiple || timeseries ){
462
+
463
+ const format = timeseries ? 'timeseries' : 'documents';
464
+
465
+ result.value = await database.collection( collection ).find(
466
+ query,
467
+ {},
468
+ options
469
+ ).toArray();
470
+
471
+ await database.collection( collection ).updateMany(
472
+ query,
473
+ formats[ format].update( data, authenticated ),
474
+ options
475
+ );
476
+
477
+ } else {
478
+
479
+ result = await database.collection( collection ).findOneAndUpdate(
480
+ query,
481
+ formats.documents.update( data, authenticated ),
482
+ {
483
+ returnDocument : 'after',
484
+ returnNewDocument : true,
485
+ ...options
486
+ }
487
+ );
488
+
489
+ }
490
+
491
+ return result;
492
+
493
+ } catch ( error ) {
494
+
495
+ throw error;
496
+
497
+ }
498
+
499
+ }
500
+ });