liwi-mongo 8.3.1 → 9.1.0

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,672 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- const mongodb = require('mongodb');
6
- const liwiStore = require('liwi-store');
7
- const liwiSubscribeStore = require('liwi-subscribe-store');
8
- const mingo = require('mingo');
9
- const Logger = require('nightingale-logger');
10
-
11
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
12
-
13
- const mongodb__default = /*#__PURE__*/_interopDefaultLegacy(mongodb);
14
- const mingo__default = /*#__PURE__*/_interopDefaultLegacy(mingo);
15
- const Logger__default = /*#__PURE__*/_interopDefaultLegacy(Logger);
16
-
17
- class MongoCursor extends liwiStore.AbstractStoreCursor {
18
- // key in AbstractCursor
19
- constructor(store, cursor) {
20
- super(store);
21
- this.cursor = cursor;
22
- }
23
-
24
- advance(count) {
25
- this.cursor.skip(count);
26
- }
27
-
28
- next() {
29
- return this.cursor.next().then(value => {
30
- this._result = value;
31
- this.key = value === null || value === void 0 ? void 0 : value._id;
32
- return this.key;
33
- });
34
- }
35
-
36
- limit(newLimit) {
37
- this.cursor.limit(newLimit);
38
- return Promise.resolve(this);
39
- }
40
-
41
- count(applySkipLimit = false) {
42
- return this.cursor.count(applySkipLimit);
43
- }
44
-
45
- result() {
46
- if (!this._result) throw new Error('Cannot call result() before next()');
47
- return Promise.resolve(this._result);
48
- }
49
-
50
- close() {
51
- if (this.cursor) {
52
- this.cursor.close();
53
- }
54
-
55
- return Promise.resolve();
56
- }
57
-
58
- toArray() {
59
- return this.cursor.toArray();
60
- }
61
-
62
- }
63
-
64
- /* eslint-disable complexity, max-lines */
65
-
66
- const identityTransformer$1 = model => model;
67
-
68
- class MongoQueryCollection extends liwiSubscribeStore.AbstractSubscribableStoreQuery {
69
- constructor(store, options, transformer = identityTransformer$1) {
70
- super();
71
- this.store = store;
72
- this.options = options;
73
- this.transformer = transformer;
74
- }
75
-
76
- createTestCriteria() {
77
- if (!this.testCriteria) {
78
- if (!this.options.criteria) {
79
- return () => true;
80
- }
81
-
82
- const mingoQuery = new mingo__default.Query(this.options.criteria);
83
- this.testCriteria = mingoQuery.test.bind(mingoQuery);
84
- }
85
-
86
- return this.testCriteria;
87
- }
88
-
89
- async fetch(onFulfilled) {
90
- const cursor = await this.createMongoCursor();
91
- const [result, count] = await Promise.all([cursor.toArray(), cursor.count()]);
92
- return onFulfilled({
93
- result: result.map(this.transformer),
94
- meta: {
95
- total: count
96
- },
97
- info: {
98
- sort: this.options.sort,
99
- limit: this.options.limit,
100
- keyPath: this.store.keyPath
101
- }
102
- });
103
- }
104
-
105
- _subscribe(callback, _includeInitial) {
106
- const store = super.getSubscribeStore();
107
- const testCriteria = this.createTestCriteria();
108
- const promise = _includeInitial ? this.fetch(({
109
- result,
110
- meta,
111
- info
112
- }) => {
113
- callback(null, [{
114
- type: 'initial',
115
- initial: result,
116
- queryInfo: info,
117
- meta
118
- }]);
119
- }) : Promise.resolve();
120
- const unsubscribe = store.subscribe(action => {
121
- const changes = [];
122
-
123
- switch (action.type) {
124
- case 'inserted':
125
- {
126
- const filtered = action.next.filter(testCriteria);
127
-
128
- if (filtered.length > 0) {
129
- changes.push({
130
- type: 'inserted',
131
- result: filtered.map(this.transformer)
132
- });
133
- }
134
-
135
- break;
136
- }
137
-
138
- case 'deleted':
139
- {
140
- const filtered = action.prev.filter(testCriteria);
141
-
142
- if (filtered.length > 0) {
143
- changes.push({
144
- type: 'deleted',
145
- keys: filtered.map(object => object[this.store.keyPath])
146
- });
147
- }
148
-
149
- break;
150
- }
151
-
152
- case 'updated':
153
- {
154
- const {
155
- deleted,
156
- updated,
157
- inserted
158
- } = action.changes.reduce((acc, [prevObject, nextObject]) => {
159
- if (testCriteria(prevObject)) {
160
- if (!testCriteria(nextObject)) {
161
- acc.deleted.push(prevObject[this.store.keyPath]);
162
- } else {
163
- acc.updated.push(this.transformer(nextObject));
164
- }
165
- } else if (testCriteria(nextObject)) {
166
- acc.inserted.push(this.transformer(nextObject));
167
- }
168
-
169
- return acc;
170
- }, {
171
- deleted: [],
172
- updated: [],
173
- inserted: []
174
- });
175
-
176
- if (deleted.length > 0) {
177
- changes.push({
178
- type: 'deleted',
179
- keys: deleted
180
- });
181
- }
182
-
183
- if (updated.length > 0) {
184
- changes.push({
185
- type: 'updated',
186
- result: updated
187
- });
188
- }
189
-
190
- if (inserted.length > 0) {
191
- changes.push({
192
- type: 'inserted',
193
- result: inserted
194
- });
195
- }
196
-
197
- break;
198
- }
199
-
200
- default:
201
- throw new Error('Unsupported type');
202
- }
203
-
204
- if (changes.length === 0) return;
205
- callback(null, changes);
206
- }); // let _feed;
207
- // const promise = this.queryCallback(this.store.query(), this.store.r)
208
- // .changes({
209
- // includeInitial: _includeInitial,
210
- // includeStates: true,
211
- // includeTypes: true,
212
- // includeOffsets: true,
213
- // })
214
- // .then((feed) => {
215
- // if (args.length === 0) {
216
- // _feed = feed;
217
- // delete this._promise;
218
- // }
219
- //
220
- // feed.each(callback);
221
- // return feed;
222
- // });
223
- //
224
- // if (args.length === 0) this._promise = promise;
225
-
226
- return {
227
- stop: unsubscribe,
228
- cancel: unsubscribe,
229
- then: (onFulfilled, onRejected) => promise.then(onFulfilled, onRejected)
230
- };
231
- }
232
-
233
- async createMongoCursor() {
234
- const cursor = await this.store.cursor(this.options.criteria, this.options.sort);
235
-
236
- if (this.options.skip) {
237
- cursor.advance(this.options.skip);
238
- }
239
-
240
- if (this.options.limit) {
241
- await cursor.limit(this.options.limit);
242
- }
243
-
244
- return cursor;
245
- }
246
-
247
- }
248
-
249
- const identityTransformer = model => model;
250
-
251
- class MongoQuerySingleItem extends liwiSubscribeStore.AbstractSubscribableStoreQuery {
252
- constructor(store, options, transformer = identityTransformer) {
253
- super();
254
- this.store = store;
255
- this.options = options;
256
- this.transformer = transformer;
257
- }
258
-
259
- createMingoTestCriteria() {
260
- if (!this.testCriteria) {
261
- if (!this.options.criteria) {
262
- return () => true;
263
- }
264
-
265
- const mingoQuery = new mingo__default.Query(this.options.criteria);
266
- this.testCriteria = mingoQuery.test.bind(mingoQuery);
267
- }
268
-
269
- return this.testCriteria;
270
- }
271
-
272
- async fetch(onFulfilled) {
273
- const cursor = await this.createMongoCursor();
274
- await cursor.limit(1);
275
- return cursor.toArray().then(result => {
276
- const item = result.length === 0 ? null : this.transformer(result[0]);
277
- return onFulfilled({
278
- result: item,
279
- meta: {
280
- total: result === null ? 0 : 1
281
- },
282
- info: {
283
- limit: 1,
284
- keyPath: this.store.keyPath
285
- }
286
- });
287
- });
288
- }
289
-
290
- _subscribe(callback, _includeInitial) {
291
- const store = super.getSubscribeStore();
292
- const testCriteria = this.createMingoTestCriteria();
293
- const promise = _includeInitial ? this.fetch(({
294
- result,
295
- meta,
296
- info
297
- }) => {
298
- callback(null, [{
299
- type: 'initial',
300
- initial: result,
301
- queryInfo: info,
302
- meta
303
- }]);
304
- }) : Promise.resolve();
305
- const unsubscribe = store.subscribe(async action => {
306
- const changes = [];
307
-
308
- switch (action.type) {
309
- case 'inserted':
310
- {
311
- const filtered = action.next.filter(testCriteria);
312
-
313
- if (filtered.length > 0) {
314
- changes.push({
315
- type: 'updated',
316
- result: this.transformer(filtered[0])
317
- });
318
- }
319
-
320
- break;
321
- }
322
-
323
- case 'deleted':
324
- {
325
- const filtered = action.prev.filter(testCriteria);
326
-
327
- if (filtered.length > 0) {
328
- changes.push({
329
- type: 'deleted',
330
- keys: filtered.map(object => object[this.store.keyPath])
331
- });
332
- }
333
-
334
- break;
335
- }
336
-
337
- case 'updated':
338
- {
339
- const filtered = action.changes.filter(([prev, next]) => testCriteria(prev));
340
-
341
- if (filtered.length > 0) {
342
- if (this.options.sort) {
343
- const {
344
- result
345
- } = await this.fetch(res => res);
346
- changes.push({
347
- type: 'updated',
348
- result
349
- });
350
- } else if (filtered.length !== 1) {
351
- throw new Error('should not match more than 1, use sort if you can have multiple match');
352
- } else {
353
- const [, next] = filtered[0];
354
- changes.push({
355
- type: 'updated',
356
- result: testCriteria(next) ? this.transformer(next) : null
357
- });
358
- }
359
- } else if (filtered.length === 0) ;
360
-
361
- break;
362
- }
363
-
364
- default:
365
- throw new Error('Unsupported type');
366
- }
367
-
368
- if (changes.length === 0) return;
369
- callback(null, changes);
370
- });
371
- return {
372
- stop: unsubscribe,
373
- cancel: unsubscribe,
374
- then: (onFulfilled, onRejected) => promise.then(onFulfilled, onRejected)
375
- };
376
- }
377
-
378
- async createMongoCursor() {
379
- const cursor = await this.store.cursor(this.options.criteria, this.options.sort);
380
-
381
- if (this.options.limit) {
382
- await cursor.limit(this.options.limit);
383
- }
384
-
385
- return cursor;
386
- }
387
-
388
- }
389
-
390
- /* eslint-disable max-lines */
391
- class MongoStore {
392
- constructor(connection, collectionName) {
393
- this.keyPath = '_id';
394
- this.connection = connection;
395
-
396
- if (!collectionName) {
397
- throw new Error(`Invalid collectionName: "${collectionName}"`);
398
- }
399
-
400
- this._collection = connection.getConnection().then(client => {
401
- this._collection = client.db().collection(collectionName);
402
- return this._collection;
403
- }, err => {
404
- this._collection = Promise.reject(err);
405
- return this._collection;
406
- });
407
- }
408
-
409
- get collection() {
410
- if (this.connection.connectionFailed) {
411
- return Promise.reject(new Error('MongoDB connection failed'));
412
- }
413
-
414
- return Promise.resolve(this._collection);
415
- }
416
-
417
- createQuerySingleItem(options, transformer) {
418
- return new MongoQuerySingleItem(this, options, transformer);
419
- }
420
-
421
- createQueryCollection(options, transformer) {
422
- return new MongoQueryCollection(this, options, transformer);
423
- }
424
-
425
- async insertOne(object) {
426
- if (!object._id) {
427
- object._id = new mongodb__default.ObjectID().toString();
428
- }
429
-
430
- if (!object.created) object.created = new Date();
431
- if (!object.updated) object.updated = new Date();
432
- const collection = await this.collection;
433
- const {
434
- result
435
- } = await collection.insertOne(object);
436
-
437
- if (!result.ok || result.n !== 1) {
438
- throw new Error('Fail to insert');
439
- }
440
-
441
- return object;
442
- }
443
-
444
- async replaceOne(object) {
445
- if (!object.updated) object.updated = new Date();
446
- const collection = await this.collection;
447
- await collection.replaceOne({
448
- _id: object._id
449
- }, object);
450
- return object;
451
- }
452
-
453
- async upsertOne(object, setOnInsertPartialObject) {
454
- const result = await this.upsertOneWithInfo(object, setOnInsertPartialObject);
455
- return result.object;
456
- }
457
-
458
- async upsertOneWithInfo(object, setOnInsertPartialObject) {
459
- const $setOnInsert = {
460
- created: object.created || new Date(),
461
- ...setOnInsertPartialObject
462
- };
463
-
464
- if (!object.updated) {
465
- object.updated = new Date();
466
- }
467
-
468
- const $set = { ...object
469
- };
470
- delete $set.created;
471
- const collection = await this.collection;
472
- const {
473
- upsertedCount
474
- } = await collection.updateOne({
475
- _id: object._id
476
- }, {
477
- $set,
478
- $setOnInsert
479
- }, {
480
- upsert: true
481
- });
482
-
483
- if (upsertedCount) {
484
- Object.assign(object, $setOnInsert);
485
- }
486
-
487
- return {
488
- object: object,
489
- inserted: !!upsertedCount
490
- };
491
- }
492
-
493
- replaceSeveral(objects) {
494
- return Promise.all(objects.map(object => this.replaceOne(object)));
495
- }
496
-
497
- async partialUpdateByKey(key, partialUpdate, criteria) {
498
- const collection = await this.collection;
499
- const commandResult = await collection.updateOne({
500
- _id: key,
501
- ...criteria
502
- }, partialUpdate);
503
-
504
- if (!commandResult.result.ok) {
505
- console.error(commandResult);
506
- throw new Error('Update failed');
507
- }
508
-
509
- const object = await this.findByKey(key);
510
- return object;
511
- }
512
-
513
- partialUpdateOne(object, partialUpdate) {
514
- return this.partialUpdateByKey(object._id, partialUpdate);
515
- }
516
-
517
- partialUpdateMany(criteria, partialUpdate) {
518
- return this.collection.then(collection => collection.updateMany(criteria, partialUpdate)).then(() => undefined); // TODO return updated object
519
- }
520
-
521
- deleteByKey(key, criteria) {
522
- return this.collection.then(collection => collection.deleteOne({
523
- _id: key,
524
- ...criteria
525
- })).then(() => undefined);
526
- }
527
-
528
- deleteOne(object) {
529
- return this.deleteByKey(object._id);
530
- }
531
-
532
- deleteMany(selector) {
533
- return this.collection.then(collection => collection.deleteMany(selector)).then(() => undefined);
534
- }
535
-
536
- cursor(criteria, sort) {
537
- return this.collection.then(collection => collection.find(criteria)).then(sort && (cursor => cursor.sort(sort))).then(cursor => new MongoCursor(this, cursor));
538
- }
539
-
540
- findByKey(key, criteria) {
541
- return this.collection.then(collection => collection.findOne({
542
- _id: key,
543
- ...criteria
544
- })).then(result => result || undefined);
545
- }
546
-
547
- findAll(criteria, sort) {
548
- return this.cursor(criteria, sort).then(cursor => cursor.toArray());
549
- }
550
-
551
- findOne(criteria, sort) {
552
- return this.collection.then(collection => collection.find(criteria)).then(sort && (cursor => cursor.sort(sort))).then(cursor => cursor.limit(1).next());
553
- }
554
-
555
- }
556
-
557
- const logger = new Logger__default('liwi:mongo:MongoConnection');
558
- class MongoConnection extends liwiStore.AbstractConnection {
559
- // TODO interface
560
- constructor(config) {
561
- super();
562
-
563
- if (!config.has('host')) {
564
- config.set('host', 'localhost');
565
- }
566
-
567
- if (!config.has('port')) {
568
- config.set('port', '27017');
569
- }
570
-
571
- if (!config.has('database')) {
572
- throw new Error('Missing config database');
573
- }
574
-
575
- const connectionString = `mongodb://${config.has('user') ? `${config.get('user')}:${config.get('password')}@` : ''}` + `${config.get('host')}:${config.get('port')}/${config.get('database')}`;
576
- this.connect(connectionString);
577
- }
578
-
579
- connect(connectionString) {
580
- logger.info('connecting', {
581
- connectionString
582
- });
583
- const connectPromise = mongodb__default.MongoClient.connect(connectionString, {
584
- useNewUrlParser: true
585
- }).then(connection => {
586
- logger.info('connected', {
587
- connectionString
588
- });
589
- connection.on('close', () => {
590
- logger.warn('close', {
591
- connectionString
592
- });
593
- this.connectionFailed = true;
594
-
595
- this.getConnection = () => {
596
- throw new Error('MongoDB connection closed');
597
- };
598
- });
599
- connection.on('timeout', () => {
600
- logger.warn('timeout', {
601
- connectionString
602
- });
603
- this.connectionFailed = true;
604
-
605
- this.getConnection = () => {
606
- throw new Error('MongoDB connection timeout');
607
- };
608
- });
609
- connection.on('reconnect', () => {
610
- logger.warn('reconnect', {
611
- connectionString
612
- });
613
- this.connectionFailed = false;
614
-
615
- this.getConnection = () => Promise.resolve(this._connection);
616
- });
617
- connection.on('error', err => {
618
- logger.warn('error', {
619
- connectionString,
620
- err
621
- });
622
- });
623
- this._connection = connection;
624
- this._connecting = undefined;
625
-
626
- this.getConnection = () => Promise.resolve(this._connection);
627
-
628
- return connection;
629
- }).catch(err => {
630
- logger.info('not connected', {
631
- connectionString
632
- });
633
- console.error(err.message || err); // throw err;
634
-
635
- process.nextTick(() => {
636
- // eslint-disable-next-line unicorn/no-process-exit
637
- process.exit(1);
638
- });
639
- throw err;
640
- });
641
-
642
- this.getConnection = () => Promise.resolve(connectPromise);
643
-
644
- this._connecting = this.getConnection();
645
- }
646
-
647
- getConnection() {
648
- throw new Error('call connect()');
649
- }
650
-
651
- async close() {
652
- this.getConnection = () => Promise.reject(new Error('Connection closed'));
653
-
654
- if (this._connection) {
655
- await this._connection.close();
656
- this._connection = undefined;
657
- } else if (this._connecting) {
658
- await this._connecting;
659
- await this.close();
660
- }
661
- }
662
-
663
- }
664
-
665
- function createMongoSubscribeStore(mongoStore) {
666
- return new liwiSubscribeStore.SubscribeStore(mongoStore);
667
- }
668
-
669
- exports.MongoConnection = MongoConnection;
670
- exports.MongoStore = MongoStore;
671
- exports.createMongoSubscribeStore = createMongoSubscribeStore;
672
- //# sourceMappingURL=index-node12.cjs.js.map