@contentstack/datasync-mongodb-sdk 1.0.9-beta.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.
Files changed (70) hide show
  1. package/.github/workflows/codeql-analysis.yml +68 -0
  2. package/.github/workflows/jira.yml +33 -0
  3. package/.github/workflows/release.yml +77 -0
  4. package/.github/workflows/sast-scan.yml +11 -0
  5. package/.github/workflows/sca-scan.yml +15 -0
  6. package/.talismanrc +4 -0
  7. package/CODEOWNERS +1 -0
  8. package/LICENCE +21 -0
  9. package/README.md +191 -0
  10. package/SECURITY.md +27 -0
  11. package/contentstack-datasync-mongodb-sdk-1.0.9-beta.1.tgz +0 -0
  12. package/dist/config.js +47 -0
  13. package/dist/index.js +42 -0
  14. package/dist/stack.js +2272 -0
  15. package/dist/util.js +86 -0
  16. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  17. package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
  18. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  19. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  20. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  21. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  22. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  23. package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
  24. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  25. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  26. package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
  27. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  28. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  29. package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  30. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  31. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  32. package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
  33. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  34. package/docs/global.html +7520 -0
  35. package/docs/global.html#Stack +1070 -0
  36. package/docs/index.html +291 -0
  37. package/docs/index.js.html +92 -0
  38. package/docs/scripts/linenumber.js +25 -0
  39. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  40. package/docs/scripts/prettify/lang-css.js +2 -0
  41. package/docs/stack.js.html +2244 -0
  42. package/docs/styles/jsdoc-default.css +358 -0
  43. package/docs/styles/prettify-jsdoc.css +111 -0
  44. package/docs/styles/prettify-tomorrow.css +132 -0
  45. package/example/index.js +56 -0
  46. package/package.json +59 -0
  47. package/test/comparison-operators.ts +257 -0
  48. package/test/conditional-operators.ts +106 -0
  49. package/test/config.ts +12 -0
  50. package/test/core.ts +333 -0
  51. package/test/count.ts +98 -0
  52. package/test/data/assets.ts +35 -0
  53. package/test/data/author.ts +168 -0
  54. package/test/data/blog.ts +138 -0
  55. package/test/data/category.ts +20 -0
  56. package/test/data/content_types.ts +164 -0
  57. package/test/data/products.ts +64 -0
  58. package/test/expressions.ts +108 -0
  59. package/test/include-exclude.ts +176 -0
  60. package/test/logical-operators.ts +140 -0
  61. package/test/projections.ts +109 -0
  62. package/test/queries.ts +143 -0
  63. package/test/references.ts +162 -0
  64. package/test/skip-limit.ts +150 -0
  65. package/test/sorting.ts +177 -0
  66. package/tslint.json +45 -0
  67. package/typings/config.d.ts +42 -0
  68. package/typings/index.d.ts +36 -0
  69. package/typings/stack.d.ts +1097 -0
  70. package/typings/util.d.ts +28 -0
@@ -0,0 +1,2244 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>JSDoc: Source: stack.js</title>
6
+
7
+ <script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js" integrity="sha384-hDHlUtmnjnJimeAhT+DpLqjLdp8vFgSFHhZO1zq2EtqpwFsNM7H5cpSUYqT1Uh2E" crossorigin="anonymous"> </script>
8
+ <script src="scripts/prettify/lang-css.js" integrity="=sha384-ZVW4Q90WP44LAxvcmnOQ3FYYiuBwyy/plPaSRSP9g4z61NRCtowdPA4A2to4Ui9A%" crossorigin="anonymous"> </script>
9
+ <!--[if lt IE 9]>
10
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js" integrity="sha384-hDHlUtmnjnJimeAhT+DpLqjLdp8vFgSFHhZO1zq2EtqpwFsNM7H5cpSUYqT1Uh2E" crossorigin="anonymous"></script>
11
+ <![endif]-->
12
+ <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
13
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
14
+ </head>
15
+
16
+ <body>
17
+
18
+ <div id="main">
19
+
20
+ <h1 class="page-title">Source: stack.js</h1>
21
+
22
+
23
+
24
+
25
+
26
+
27
+ <section>
28
+ <article>
29
+ <pre class="prettyprint source linenums"><code>"use strict";
30
+ /*!
31
+ * Contentstack DataSync Mongodb SDK
32
+ * Copyright (c) 2019 Contentstack LLC
33
+ * MIT Licensed
34
+ */
35
+ var __awaiter = (this &amp;&amp; this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ return new (P || (P = Promise))(function (resolve, reject) {
37
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
38
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
39
+ function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
40
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
41
+ });
42
+ };
43
+ var __importDefault = (this &amp;&amp; this.__importDefault) || function (mod) {
44
+ return (mod &amp;&amp; mod.__esModule) ? mod : { "default": mod };
45
+ };
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ const lodash_1 = require("lodash");
48
+ const mongodb_1 = require("mongodb");
49
+ const sift_1 = __importDefault(require("sift"));
50
+ const config_1 = require("./config");
51
+ const util_1 = require("./util");
52
+ /**
53
+ * @class Stack
54
+ * @descriptionExpose SDK query methods on Stack
55
+ * @constructor
56
+ * @descriptionProvides a range of connection/disconnect, filters and projections on mongodb
57
+ * @returns {Stack} Returns an instance of `Stack`
58
+ */
59
+ class Stack {
60
+ constructor(stackConfig, existingDB) {
61
+ this.config = lodash_1.merge(config_1.config, stackConfig);
62
+ // validates config.locales property
63
+ util_1.validateConfig(this.config);
64
+ this.contentStore = this.config.contentStore;
65
+ this.collectionNames = this.contentStore.collection;
66
+ this.types = this.contentStore.internal.types;
67
+ this.q = {};
68
+ this.internal = {};
69
+ this.db = existingDB;
70
+ }
71
+ /**
72
+ * @public
73
+ * @method ascending
74
+ * @summary Sorts the documents based on the 'sort' key
75
+ * @description
76
+ * The sort function requires that the entire sort be able to complete within 32 megabytes.
77
+ * When the sort option consumes more than 32 megabytes, MongoDB will return an error.
78
+ * @param {string} field The field to sort in ascending order
79
+ * @example
80
+ * Stack
81
+ * .contentType('')
82
+ * .entries()
83
+ * .ascending()
84
+ * .find()
85
+ * .then((result) => {
86
+ * // result sorted in ascending manner with respect to 'published_at' field (by default)
87
+ * })
88
+ * .catch((error) => {
89
+ * // handle query errors
90
+ * })
91
+ *
92
+ * @returns {Stack} Returns an instance of 'stack'
93
+ */
94
+ ascending(field) {
95
+ if (typeof this.q.content_type_uid !== 'string' || typeof field !== 'string' || field.length === 0) {
96
+ throw new Error('Kindly provide valid parameters for .ascending!');
97
+ }
98
+ else if (this.internal.sort &amp;&amp; typeof this.internal.sort === 'object') {
99
+ this.internal.sort[field] = 1;
100
+ }
101
+ else {
102
+ this.internal.sort = {
103
+ [field]: 1,
104
+ };
105
+ }
106
+ return this;
107
+ }
108
+ /**
109
+ * @public
110
+ * @method descending
111
+ * @summary Sorts the documents based on the 'sort' key
112
+ * @description
113
+ * The sort function requires that the entire sort be able to complete within 32 megabytes.
114
+ * When the sort option consumes more than 32 megabytes, MongoDB will return an error.
115
+ *
116
+ * @param {string} field The field to sort in descending order
117
+ * @example
118
+ * Stack
119
+ * .contentType('')
120
+ * .entries()
121
+ * .descending('title')
122
+ * .find()
123
+ * .then((result) => {
124
+ * // result sorted in descending manner with respect to 'title' field
125
+ * })
126
+ * .catch((error) => {
127
+ * // handle query errors
128
+ * })
129
+ *
130
+ * @returns {Stack} Returns an instance of 'stack'
131
+ */
132
+ descending(field) {
133
+ if (typeof this.q.content_type_uid !== 'string' || typeof field !== 'string' || field.length === 0) {
134
+ throw new Error('Kindly provide valid parameters for .descending()!');
135
+ }
136
+ else if (this.internal.sort &amp;&amp; typeof this.internal.sort === 'object') {
137
+ this.internal.sort[field] = -1;
138
+ }
139
+ else {
140
+ this.internal.sort = {
141
+ [field]: -1,
142
+ };
143
+ }
144
+ return this;
145
+ }
146
+ /**
147
+ * @public
148
+ * @method connect
149
+ * @summary
150
+ * Establish connection to mongodb
151
+ *
152
+ * @param {object} overrides Config overrides/mongodb specific config
153
+ * @example
154
+ * Stack
155
+ * .connect({overrides})
156
+ * .then((result) => {
157
+ * // mongodb connection object
158
+ * // indexes will be created on the collection in the background if provided in config
159
+ * })
160
+ * .catch((error) => {
161
+ * // handle query errors
162
+ * })
163
+ *
164
+ * @returns {object} Mongodb 'db' instance
165
+ */
166
+ connect(overrides = {}) {
167
+ return __awaiter(this, void 0, void 0, function* () {
168
+ const dbConfig = lodash_1.merge({}, this.config, overrides).contentStore;
169
+ const url = util_1.validateURI(dbConfig.url);
170
+ const options = dbConfig.options;
171
+ const dbName = dbConfig.dbName;
172
+ const client = new mongodb_1.MongoClient(url, options);
173
+ this.client = client;
174
+ yield client.connect();
175
+ this.db = client.db(dbName);
176
+ return this.db;
177
+ // // Create indexes in the background
178
+ // const bucket: any = []
179
+ // const indexes = this.config.contentStore.indexes
180
+ // const collectionName = this.config.contentStore.collectionName
181
+ // for (let index in indexes) {
182
+ // if (indexes[index] === 1 || indexes[index] === -1) {
183
+ // bucket.push(this.createIndexes(this.config.contentStore.collectionName, index, indexes[index]))
184
+ // }
185
+ // }
186
+ // Promise.all(bucket)
187
+ // .then(() => {
188
+ // console.info(`Indexes created successfully in '${collectionName}' collection`)
189
+ // })
190
+ // .catch((error) => {
191
+ // console.error(`Failed while creating indexes in '${collectionName}' collection`)
192
+ // console.error(error)
193
+ // })
194
+ });
195
+ }
196
+ // private createIndexes(collectionId, index, type) {
197
+ // return this.db.collection(collectionId)
198
+ // .createIndex({
199
+ // [index]: type
200
+ // })
201
+ // .then(() => {
202
+ // return
203
+ // })
204
+ // }
205
+ /**
206
+ * @public
207
+ * @method close
208
+ * @summary Closes connection with mongodb
209
+ */
210
+ close() {
211
+ this.client.close();
212
+ }
213
+ /**
214
+ * @method language
215
+ * @description
216
+ * Locale to query on
217
+ *
218
+ * @param {string} code Query locale's code
219
+ * @example
220
+ * Stack
221
+ * .contentType('')
222
+ * .entries()
223
+ * .language('es-es')
224
+ * .find()
225
+ * .then((result) => {
226
+ * // results in entries fetched from 'es-es' locale
227
+ * // if not provided, defaults to the 1st locale provided in the 'locales' key, provided in config
228
+ * })
229
+ * .catch((error) => {
230
+ * // handle query errors
231
+ * })
232
+ *
233
+ * @returns {Stack} Returns an instance of 'stack'
234
+ */
235
+ language(code) {
236
+ if (typeof code !== 'string' || code.length === 0) {
237
+ throw new Error('Kindly pass valid parameters for .language()!');
238
+ }
239
+ this.q.locale = code;
240
+ return this;
241
+ }
242
+ /**
243
+ * @public
244
+ * @method and
245
+ * @summary Logical AND query wrapper
246
+ * @descriptionAccepts 2 queries and returns only those documents, that satisfy both the query conditions
247
+ * @param {object} queries Query filter
248
+ * @example
249
+ * Stack
250
+ * .contentType('')
251
+ * .entries()
252
+ * .and([
253
+ * {
254
+ * title: 'John'
255
+ * },
256
+ * {
257
+ * age: 30
258
+ * }
259
+ * ])
260
+ * .find()
261
+ * .then((result) => {
262
+ * // filtered entries, where { title: 'John', age: 30 }
263
+ * })
264
+ * .catch((error) => {
265
+ * // handle query errors
266
+ * })
267
+ *
268
+ * @returns {Stack} Returns an instance of 'stack'
269
+ */
270
+ and(queries) {
271
+ if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
272
+ this.q.query = lodash_1.merge(this.q.query, {
273
+ $and: queries,
274
+ });
275
+ }
276
+ else {
277
+ this.q.query = {
278
+ $and: queries,
279
+ };
280
+ }
281
+ return this;
282
+ }
283
+ /**
284
+ * @public
285
+ * @method or
286
+ * @summary Logical OR query wrapper
287
+ * @descriptionAccepts 2 queries and returns only those documents, that satisfy either of the query conditions
288
+ * @param {object} queries Query filter
289
+ * @example
290
+ * Stack
291
+ * .contentType('')
292
+ * .entries()
293
+ * .or([
294
+ * {
295
+ * title: 'John'
296
+ * },
297
+ * {
298
+ * title: 'Jane'
299
+ * }
300
+ * ])
301
+ * .find()
302
+ * .then((result) => {
303
+ * // filtered entries, where { title: 'John' } OR { title: 'Jane' }
304
+ * })
305
+ * .catch((error) => {
306
+ * // handle query errors
307
+ * })
308
+ *
309
+ * @returns {Stack} Returns an instance of 'stack'
310
+ */
311
+ or(queries) {
312
+ if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
313
+ this.q.query = lodash_1.merge(this.q.query, {
314
+ $or: queries,
315
+ });
316
+ }
317
+ else {
318
+ this.q.query = {
319
+ $or: queries,
320
+ };
321
+ }
322
+ return this;
323
+ }
324
+ /**
325
+ * @public
326
+ * @method lessThan
327
+ * @summary Comparison $lt query wrapper
328
+ * @description
329
+ * Compares the field/key provided against the provided value.
330
+ * Only documents that have lower value than the one provided are returned.
331
+ * Check https://docs.mongodb.com/manual/reference/operator/query/lt/
332
+ * and https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing for more info
333
+ * @param {string} key Field to compare against
334
+ * @param {*} value Value to compare with
335
+ * @example
336
+ * Stack
337
+ * .contentType('')
338
+ * .entries()
339
+ * .lessThan('age', 18)
340
+ * .find()
341
+ * .then((result) => {
342
+ * // filtered entries, where { age &lt; 18 }
343
+ * })
344
+ * .catch((error) => {
345
+ * // handle query errors
346
+ * })
347
+ *
348
+ * @returns {Stack} Returns an instance of 'stack'
349
+ */
350
+ lessThan(key, value) {
351
+ if (typeof key !== 'string' || typeof value === 'undefined') {
352
+ throw new Error('Kindly pass valid key and value parameters for \'.lessThan()\'');
353
+ }
354
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
355
+ this.q.query[key] = {
356
+ $lt: value,
357
+ };
358
+ }
359
+ else {
360
+ this.q.query = {
361
+ [key]: {
362
+ $lt: value,
363
+ },
364
+ };
365
+ }
366
+ return this;
367
+ }
368
+ /**
369
+ * @public
370
+ * @method lessThanOrEqualTo
371
+ * @summary Comparison $lte query wrapper
372
+ * @description
373
+ * Compares the field/key provided against the provided value.
374
+ * Only documents that have lower or equal value than the one provided are returned.
375
+ * Check https://docs.mongodb.com/manual/reference/operator/query/lte/
376
+ * and https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing for more info
377
+ * @param {string} key Field to compare against
378
+ * @param {*} value Value to compare with
379
+ * @example
380
+ * Stack
381
+ * .contentType('')
382
+ * .entries()
383
+ * .lessThanOrEqualTo('age', 18)
384
+ * .find()
385
+ * .then((result) => {
386
+ * // filtered entries, where { age &lt;= 18 }
387
+ * })
388
+ * .catch((error) => {
389
+ * // handle query errors
390
+ * })
391
+ *
392
+ * @returns {Stack} Returns an instance of 'stack'
393
+ */
394
+ lessThanOrEqualTo(key, value) {
395
+ if (typeof key !== 'string' || typeof value === 'undefined') {
396
+ throw new Error('Kindly pass valid key and value parameters for \'.lessThanOrEqualTo()\'');
397
+ }
398
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
399
+ this.q.query[key] = {
400
+ $lte: value,
401
+ };
402
+ }
403
+ else {
404
+ this.q.query = {
405
+ [key]: {
406
+ $lte: value,
407
+ },
408
+ };
409
+ }
410
+ return this;
411
+ }
412
+ /**
413
+ * @public
414
+ * @method greaterThan
415
+ * @summary Comparison $gt query wrapper
416
+ * @description
417
+ * Compares the field/key provided against the provided value.
418
+ * Only documents that have greater value than the one provided are returned.
419
+ * Check {@link https://docs.mongodb.com/manual/reference/operator/query/gt/ }
420
+ * and https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing for more info
421
+ * @param {string} key Field to compare against
422
+ * @param {*} value Value to compare with
423
+ * @example
424
+ * Stack
425
+ * .contentType('')
426
+ * .entries()
427
+ * .greaterThan('age', 60)
428
+ * .find()
429
+ * .then((result) => {
430
+ * // filtered entries, where { age > 60 }
431
+ * })
432
+ * .catch((error) => {
433
+ * // handle query errors
434
+ * })
435
+ *
436
+ * @returns {Stack} Returns an instance of 'stack'
437
+ */
438
+ greaterThan(key, value) {
439
+ if (typeof key !== 'string' || typeof value === 'undefined') {
440
+ throw new Error('Kindly pass valid key and value parameters for \'.greaterThan()\'');
441
+ }
442
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
443
+ this.q.query[key] = {
444
+ $gt: value,
445
+ };
446
+ }
447
+ else {
448
+ this.q.query = {
449
+ [key]: {
450
+ $gt: value,
451
+ },
452
+ };
453
+ }
454
+ return this;
455
+ }
456
+ /**
457
+ * @public
458
+ * @method greaterThanOrEqualTo
459
+ * @summary Comparison $gte query wrapper
460
+ * @description
461
+ * Compares the field/key provided against the provided value.
462
+ * Only documents that have greater than or equal value than the one provided are returned.
463
+ * Check https://docs.mongodb.com/manual/reference/operator/query/gte/ and
464
+ * https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing for more info
465
+ * @param {string} key - Field to compare against
466
+ * @param {*} value - Value to compare with
467
+ * @example
468
+ * Stack
469
+ * .contentType('')
470
+ * .entries()
471
+ * .greaterThanOrEqualTo('age', 60)
472
+ * .find()
473
+ * .then((result) => {
474
+ * // filtered entries, where { age >= 60 }
475
+ * })
476
+ * .catch((error) => {
477
+ * // handle query errors
478
+ * })
479
+ *
480
+ * @returns {Stack} Returns an instance of 'stack'
481
+ */
482
+ greaterThanOrEqualTo(key, value) {
483
+ if (typeof key !== 'string' || typeof value === 'undefined') {
484
+ throw new Error('Kindly pass valid key and value parameters for \'.greaterThanOrEqualTo()\'');
485
+ }
486
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
487
+ this.q.query[key] = {
488
+ $gte: value,
489
+ };
490
+ }
491
+ else {
492
+ this.q.query = {
493
+ [key]: {
494
+ $gte: value,
495
+ },
496
+ };
497
+ }
498
+ return this;
499
+ }
500
+ /**
501
+ * @public
502
+ * @method notEqualTo
503
+ * @summary Comparison $ne query wrapper
504
+ * @description
505
+ * Compares the field/key provided against the provided value.
506
+ * Only documents that have value not equals than the one provided are returned.
507
+ *
508
+ * Check mongodb query here: {@link https://docs.mongodb.com/manual/reference/operator/query/ne/}.
509
+ *
510
+ * Res: {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing}.
511
+ *
512
+ * Comparison ordering
513
+ * {@link https://docs.mongodb.com/manual/reference/bson-type-comparison-order/#bson-types-comparison-order}
514
+ * @param {string} key Field to compare against
515
+ * @param {*} value Value to compare with
516
+ * @example
517
+ * Stack
518
+ * .contentType('')
519
+ * .entries()
520
+ * .notEqualTo('age', 25)
521
+ * .find()
522
+ * .then((result) => {
523
+ * // filtered entries, where { age != 25 }
524
+ * })
525
+ * .catch((error) => {
526
+ * // handle query errors
527
+ * })
528
+ *
529
+ * @returns {Stack} Returns an instance of 'stack'
530
+ */
531
+ notEqualTo(key, value) {
532
+ if (typeof key !== 'string' || typeof value === 'undefined') {
533
+ throw new Error('Kindly pass valid key and value parameters for \'.notEqualTo()\'');
534
+ }
535
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
536
+ this.q.query[key] = {
537
+ $ne: value,
538
+ };
539
+ }
540
+ else {
541
+ this.q.query = {
542
+ [key]: {
543
+ $ne: value,
544
+ },
545
+ };
546
+ }
547
+ return this;
548
+ }
549
+ /**
550
+ * @public
551
+ * @method containedIn
552
+ * @summary Comparison $in query wrapper
553
+ * @description
554
+ * Compares the field/key provided against the provided value.
555
+ * Only documents that have value contained in the field/key provided are returned.
556
+ *
557
+ * Check mongodb query here: {@link https://docs.mongodb.com/manual/reference/operator/query/in/}.
558
+ *
559
+ * Res: {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing}.
560
+ *
561
+ * Comparison ordering
562
+ * {@link https://docs.mongodb.com/manual/reference/bson-type-comparison-order/#bson-types-comparison-order}
563
+ * @param {string} key Field to compare against
564
+ * @param {*} value Value to compare with
565
+ *
566
+ * @example
567
+ * Stack
568
+ * .contentType('')
569
+ * .entries()
570
+ * .containedIn('emails', 'john.doe@some.com')
571
+ * .find()
572
+ * .then((result) => {
573
+ * // filtered entries, where 'john.doe@some.com' exists in 'emails' field (array)
574
+ * })
575
+ * .catch((error) => {
576
+ * // handle query errors
577
+ * })
578
+ *
579
+ * @returns {Stack} Returns an instance of 'stack'
580
+ */
581
+ containedIn(key, value) {
582
+ if (typeof key !== 'string' || typeof value !== 'object' || !(value instanceof Array)) {
583
+ throw new Error('Kindly pass valid key and value parameters for \'.containedIn()\'');
584
+ }
585
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
586
+ this.q.query[key] = {
587
+ $in: value,
588
+ };
589
+ }
590
+ else {
591
+ this.q.query = {
592
+ [key]: {
593
+ $in: value,
594
+ },
595
+ };
596
+ }
597
+ return this;
598
+ }
599
+ /**
600
+ * @public
601
+ * @method notContainedIn
602
+ * @summary Comparison $nin query wrapper
603
+ * @description
604
+ * Compares the field/key provided against the provided value.
605
+ * Only documents that have value not contained in the field/key provided are returned.
606
+ *
607
+ * Check mongodb query here: {@link https://docs.mongodb.com/manual/reference/operator/query/nin/}.
608
+ *
609
+ * Res: {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing}.
610
+ *
611
+ * Comparison ordering
612
+ * {@link https://docs.mongodb.com/manual/reference/bson-type-comparison-order/#bson-types-comparison-order}
613
+ * @param {string} key Field to compare against
614
+ * @param {*} value Value to compare with
615
+ *
616
+ * @example
617
+ * Stack
618
+ * .contentType('')
619
+ * .entries()
620
+ * .notContainedIn('emails', 'john.doe@some.com')
621
+ * .find()
622
+ * .then((result) => {
623
+ * // filtered entries, where 'john.doe@some.com' does not exist in 'emails' field (array)
624
+ * })
625
+ * .catch((error) => {
626
+ * // handle query errors
627
+ * })
628
+ *
629
+ * @returns {Stack} Returns an instance of 'stack'
630
+ */
631
+ notContainedIn(key, value) {
632
+ if (typeof key !== 'string' || typeof value !== 'object' || !(value instanceof Array)) {
633
+ throw new Error('Kindly pass valid key and value parameters for \'.notContainedIn()\'');
634
+ }
635
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
636
+ this.q.query[key] = {
637
+ $nin: value,
638
+ };
639
+ }
640
+ else {
641
+ this.q.query = {
642
+ [key]: {
643
+ $nin: value,
644
+ },
645
+ };
646
+ }
647
+ return this;
648
+ }
649
+ /**
650
+ * @public
651
+ * @method exists
652
+ * @summary Element $exists query wrapper, checks if a field exists
653
+ * @description
654
+ * Compares the field / key provided against the provided value.Only documents that have the field /
655
+ * key specified are returned.
656
+ *
657
+ * Check mongodb query here: {@link https://docs.mongodb.com/manual/reference/operator/query/exists/}.
658
+ *
659
+ * Res: {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing}.
660
+ *
661
+ * Comparison ordering{
662
+ * @link https: //docs.mongodb.com/manual/reference/bson-type-comparison-order/#bson-types-comparison-order}
663
+ * @param {string} key Field to compare against
664
+ * @param {*} value Value to compare with
665
+ *
666
+ * @example
667
+ * Stack
668
+ * .contentType('')
669
+ * .entries()
670
+ * .exists('emails')
671
+ * .find()
672
+ * .then((result) => {
673
+ * // filtered entries, where 'emails' property exists
674
+ * })
675
+ * .catch((error) => {
676
+ * // handle query errors
677
+ * })
678
+ *
679
+ * @returns {Stack} Returns an instance of 'stack'
680
+ */
681
+ exists(key) {
682
+ if (typeof key !== 'string') {
683
+ throw new Error('Kindly pass valid key for \'.exists()\'');
684
+ }
685
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
686
+ this.q.query[key] = {
687
+ $exists: true,
688
+ };
689
+ }
690
+ else {
691
+ this.q.query = {
692
+ [key]: {
693
+ $exists: true,
694
+ },
695
+ };
696
+ }
697
+ return this;
698
+ }
699
+ /**
700
+ * @public
701
+ * @method notExists
702
+ * @summary
703
+ * Property $exists query wrapper, checks if a field does not exists
704
+ * @description
705
+ * Compares the field/key provided against the provided value. Only documents that do not have the key are returned.
706
+ *
707
+ * Check mongodb query here: {@link https://docs.mongodb.com/manual/reference/operator/query/exists/}.
708
+ *
709
+ * Res: {@link https://docs.mongodb.com/manual/reference/method/db.collection.find/#type-bracketing}.
710
+ *
711
+ * Comparison ordering{
712
+ * @link https: //docs.mongodb.com/manual/reference/bson-type-comparison-order/#bson-types-comparison-order}
713
+ * @param {string} key Field to compare against
714
+ * @param {*} value Value to compare with
715
+ * @example
716
+ * Stack
717
+ * .contentType('')
718
+ * .entries()
719
+ * .notExists('emails')
720
+ * .find()
721
+ * .then((result) => {
722
+ * // filtered entries, where 'emails' property does not exist
723
+ * })
724
+ * .catch((error) => {
725
+ * // handle query errors
726
+ * })
727
+ *
728
+ * @returns {Stack} Returns an instance of 'stack'
729
+ */
730
+ notExists(key) {
731
+ if (typeof key !== 'string') {
732
+ throw new Error('Kindly pass valid key for \'.notExists()\'');
733
+ }
734
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
735
+ this.q.query[key] = {
736
+ $exists: false,
737
+ };
738
+ }
739
+ else {
740
+ this.q.query = {
741
+ [key]: {
742
+ $exists: false,
743
+ },
744
+ };
745
+ }
746
+ return this;
747
+ }
748
+ /**
749
+ * @public
750
+ * @method contentType
751
+ * @summary Content type to query on
752
+ * @param {string} uid Content type uid
753
+ * @example
754
+ * Stack
755
+ * .contentType('blog')
756
+ * .entries()
757
+ * .find()
758
+ * .then((result) => {
759
+ * // returns entries filtered based on 'blog' content type
760
+ * })
761
+ * .catch((error) => {
762
+ * // handle query errors
763
+ * })
764
+ *
765
+ * @returns {Stack} Returns an instance of 'stack'
766
+ */
767
+ contentType(uid) {
768
+ // create new instances, instead of re-using the old one
769
+ const stack = new Stack(this.config, this.db);
770
+ if (uid &amp;&amp; typeof uid === 'string') {
771
+ stack.q.content_type_uid = uid;
772
+ return stack;
773
+ }
774
+ throw new Error('Kindly pass the content type\'s uid');
775
+ }
776
+ /**
777
+ * @public
778
+ * @method entry
779
+ * @summary Query for a single entry
780
+ * @param {string} uid Entry uid to be found, if not provided,
781
+ * by default returns the 1st element in the content type.
782
+ * Useful for `singleton` content types
783
+ * @example
784
+ * Stack
785
+ * .contentType('blog')
786
+ * .entry()
787
+ * .find()
788
+ * .then((result) => {
789
+ * // returns the entry based on its 'uid',
790
+ * // if not provided, it would return the 1st entry found in 'blog' content type
791
+ * })
792
+ * .catch((error) => {
793
+ * // handle query errors
794
+ * })
795
+ *
796
+ * @returns {Stack} Returns an instance of 'stack'
797
+ */
798
+ entry(uid) {
799
+ if (!(this.q.content_type_uid)) {
800
+ throw new Error('Kindly call \'contentType()\' before \'entry()\'!');
801
+ }
802
+ if (uid &amp;&amp; typeof uid === 'string') {
803
+ this.q.uid = uid;
804
+ }
805
+ this.internal.limit = 1;
806
+ this.internal.single = true;
807
+ return this;
808
+ }
809
+ /**
810
+ * @public
811
+ * @method entries
812
+ * @description
813
+ * Query for a set of entries on a content type
814
+ *
815
+ * @example
816
+ * Stack
817
+ * .contentType('blog')
818
+ * .entries()
819
+ * .find()
820
+ * .then((result) => {
821
+ * // returns entries filtered based on 'blog' content type
822
+ * })
823
+ * .catch((error) => {
824
+ * // handle query errors
825
+ * })
826
+ *
827
+ * @returns {Stack} Returns an instance of 'stack'
828
+ */
829
+ entries() {
830
+ if (this.q.content_type_uid &amp;&amp; typeof this.q.content_type_uid === 'string') {
831
+ return this;
832
+ }
833
+ throw new Error('Kindly call \'contentType()\' before \'entries()\'!');
834
+ }
835
+ /**
836
+ * @public
837
+ * @method asset
838
+ * @description
839
+ * Query for a single asset
840
+ *
841
+ * @param {string} uid Asset uid to be found, if not provided,
842
+ * by default returns the 1st element from assets.
843
+ * @example
844
+ * Stack
845
+ * .asset()
846
+ * .find()
847
+ * .then((result) => {
848
+ * // returns the asset based on its 'uid', if not provided, it would return the 1st asset found
849
+ * })
850
+ * .catch((error) => {
851
+ * // handle query errors
852
+ * })
853
+ *
854
+ * @returns {Stack} Returns an instance of 'stack'
855
+ */
856
+ asset(uid) {
857
+ const stack = new Stack(this.config, this.db);
858
+ if (uid &amp;&amp; typeof uid === 'string') {
859
+ stack.q.uid = uid;
860
+ }
861
+ stack.q.content_type_uid = this.types.assets;
862
+ // stack.collection = stack.db.collection(stack.contentStore.collectionName)
863
+ stack.internal.limit = 1;
864
+ stack.internal.single = true;
865
+ return stack;
866
+ }
867
+ /**
868
+ * @public
869
+ * @method assets
870
+ * @description
871
+ * Query for a set of assets
872
+ *
873
+ * @example
874
+ * Stack
875
+ * .assets()
876
+ * .find()
877
+ * .then((result) => {
878
+ * // returns assets filtered based on 'blog' content type
879
+ * })
880
+ * .catch((error) => {
881
+ * // handle query errors
882
+ * })
883
+ *
884
+ * @returns {Stack} Returns an instance of 'stack'
885
+ */
886
+ assets() {
887
+ const stack = new Stack(this.config, this.db);
888
+ stack.q.content_type_uid = this.types.assets;
889
+ // stack.collection = stack.db.collection(stack.contentStore.collectionName)
890
+ return stack;
891
+ }
892
+ /**
893
+ * @public
894
+ * @method schema
895
+ * @description
896
+ * Query for a single content type's schema
897
+ *
898
+ * @param {string} uid Content type uid to be found, if not provided,
899
+ * by default returns the 1st element from content types
900
+ *
901
+ * @example
902
+ * Stack
903
+ * .schema('blog')
904
+ * .find()
905
+ * .then((result) => {
906
+ * // returns content 'blog' content type's schema
907
+ * })
908
+ * .catch((error) => {
909
+ * // handle query errors
910
+ * })
911
+ *
912
+ * @returns {Stack} Returns an instance of 'stack'
913
+ */
914
+ schema(uid) {
915
+ const stack = new Stack(this.config, this.db);
916
+ if (uid &amp;&amp; typeof uid === 'string') {
917
+ stack.q.uid = uid;
918
+ }
919
+ stack.q.content_type_uid = this.types.content_types;
920
+ // stack.collection = stack.db.collection(stack.contentStore.collectionName)
921
+ stack.internal.limit = 1;
922
+ stack.internal.single = true;
923
+ return stack;
924
+ }
925
+ /**
926
+ * @public
927
+ * @method schemas
928
+ * @description
929
+ * Query for a set of content type schemas
930
+ * @example
931
+ * Stack
932
+ * .schemas()
933
+ * .find()
934
+ * .then((result) => {
935
+ * // returns a set of content type schemas
936
+ * })
937
+ * .catch((error) => {
938
+ * // handle query errors
939
+ * })
940
+ *
941
+ * @returns {Stack} Returns an instance of 'stack'
942
+ */
943
+ schemas() {
944
+ const stack = new Stack(this.config, this.db);
945
+ stack.q.content_type_uid = this.types.content_types;
946
+ // stack.collection = stack.db.collection(stack.contentStore.collectionName)
947
+ return stack;
948
+ }
949
+ /**
950
+ * @public
951
+ * @method contentTypes
952
+ * @description
953
+ * Query for a set of content type schemas
954
+ * @example
955
+ * Stack
956
+ * .contentTypes()
957
+ * .find()
958
+ * .then((result) => {
959
+ * // returns a set of content type schemas
960
+ * })
961
+ * .catch((error) => {
962
+ * // handle query errors
963
+ * })
964
+ *
965
+ * @returns {Stack} Returns an instance of 'stack'
966
+ */
967
+ contentTypes() {
968
+ const stack = new Stack(this.config, this.db);
969
+ stack.q.content_type_uid = this.types.content_types;
970
+ // stack.collection = stack.db.collection(stack.contentStore.collectionName)
971
+ return stack;
972
+ }
973
+ /**
974
+ * @public
975
+ * @method limit
976
+ * @description
977
+ * Parameter - used to limit the total no of items returned/scanned
978
+ * Defaults to 100 (internally, which is overridden)
979
+ * @param {number} no Max count of the 'items' returned
980
+ *
981
+ * @example
982
+ * Stack
983
+ * .contentType('blog')
984
+ * .entries()
985
+ * .limit(20)
986
+ * .find()
987
+ * .then((result) => {
988
+ * // returns a maximum of 20 entries
989
+ * // if not provided, by default - the limit specified in config is returned
990
+ * })
991
+ * .catch((error) => {
992
+ * // handle query errors
993
+ * })
994
+ *
995
+ * @returns {Stack} Returns an instance of 'stack'
996
+ */
997
+ limit(no) {
998
+ if (typeof no === 'number' &amp;&amp; (no >= 0) &amp;&amp; typeof this.q.content_type_uid === 'string') {
999
+ this.internal.limit = no;
1000
+ return this;
1001
+ }
1002
+ throw new Error('Kindly provide a valid \'numeric\' value for \'limit()\'');
1003
+ }
1004
+ /**
1005
+ * @public
1006
+ * @method skip
1007
+ * @description
1008
+ * Parameter - used to skip initial no of items scanned
1009
+ * Defaults to 0 (internally, which is overridden)
1010
+ * @param {number} no Min count of the 'items' to be scanned
1011
+ *
1012
+ * @example
1013
+ * Stack
1014
+ * .contentType('blog')
1015
+ * .entries()
1016
+ * .skip(10)
1017
+ * .find()
1018
+ * .then((result) => {
1019
+ * // returnes entries, after first skipping 20 entries of 'blog' content type
1020
+ * // if not provided, by default - the skip value provided in config is considered
1021
+ * })
1022
+ * .catch((error) => {
1023
+ * // handle query errors
1024
+ * })
1025
+ *
1026
+ * @returns {Stack} Returns an instance of 'stack'
1027
+ */
1028
+ skip(no) {
1029
+ if (typeof no === 'number' &amp;&amp; (no >= 0) &amp;&amp; typeof this.q.content_type_uid === 'string') {
1030
+ this.internal.skip = no;
1031
+ return this;
1032
+ }
1033
+ throw new Error('Kindly provide a valid \'numeric\' value for \'skip()\'');
1034
+ }
1035
+ /**
1036
+ * @public
1037
+ * @method query
1038
+ * @description
1039
+ * Wrapper around a raw query wrapper
1040
+ * @param {object} queryObject Query filter
1041
+ *
1042
+ * @example
1043
+ * Stack
1044
+ * .contentType('blog')
1045
+ * .entries()
1046
+ * .query({"group.heading": "Tab 1"})
1047
+ * .find()
1048
+ * .then((result) => {
1049
+ * // returns entries that have - {"group.heading": "Tab 1"}
1050
+ * })
1051
+ * .catch((error) => {
1052
+ * // handle query errors
1053
+ * })
1054
+ *
1055
+ * @returns {Stack} Returns an instance of 'stack'
1056
+ */
1057
+ query(queryObject = {}) {
1058
+ if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
1059
+ this.q.query = lodash_1.merge(this.q.query, queryObject);
1060
+ }
1061
+ else {
1062
+ this.q.query = queryObject;
1063
+ }
1064
+ return this;
1065
+ }
1066
+ /**
1067
+ * @public
1068
+ * @method only
1069
+ * @description
1070
+ * Projections - returns only the fields passed here
1071
+ *
1072
+ * @param {array} fields Array of 'fields', separated by dot ('.') notation for embedded document query
1073
+ *
1074
+ * @example
1075
+ * Stack
1076
+ * .contentType('blog')
1077
+ * .entries()
1078
+ * .only(["title", "url", "links"])
1079
+ * .find()
1080
+ * .then((result) => {
1081
+ * // returns entries and projects only their - ["title", "url", "links"] properties
1082
+ * })
1083
+ * .catch((error) => {
1084
+ * // handle query errors
1085
+ * })
1086
+ *
1087
+ * @returns {Stack} Returns an instance of 'stack'
1088
+ */
1089
+ only(fields) {
1090
+ if (!fields || typeof fields !== 'object' || !(fields instanceof Array) || fields.length === 0) {
1091
+ throw new Error('Kindly provide valid \'field\' values for \'only()\'');
1092
+ }
1093
+ this.internal.only = this.internal.only || {};
1094
+ this.internal.only._id = 0;
1095
+ fields.forEach((field) => {
1096
+ if (typeof field === 'string') {
1097
+ this.internal.only[field] = 1;
1098
+ }
1099
+ });
1100
+ return this;
1101
+ }
1102
+ /**
1103
+ * @public
1104
+ * @method except
1105
+ * @description
1106
+ * Projections - returns fields except the ones passed here
1107
+ *
1108
+ * @param {array} fields Array of 'fields', separated by dot ('.') notation for embedded document query
1109
+ * @example
1110
+ * Stack
1111
+ * .contentType('blog')
1112
+ * .entries()
1113
+ * .except(["title", "url", "links"])
1114
+ * .find()
1115
+ * .then((result) => {
1116
+ * // returns entries and projects all of their properties, except - ["title", "url", "links"]
1117
+ * })
1118
+ * .catch((error) => {
1119
+ * // handle query errors
1120
+ * })
1121
+ *
1122
+ * @returns {Stack} Returns an instance of 'stack'
1123
+ */
1124
+ except(fields) {
1125
+ if (!fields || typeof fields !== 'object' || !(fields instanceof Array) || fields.length === 0) {
1126
+ throw new Error('Kindly provide valid \'field\' values for \'except()\'');
1127
+ }
1128
+ this.internal.except = this.internal.except || {};
1129
+ fields.forEach((field) => {
1130
+ if (typeof field === 'string') {
1131
+ this.internal.except[field] = 0;
1132
+ }
1133
+ });
1134
+ this.internal.except = lodash_1.merge(this.contentStore.projections, this.internal.except);
1135
+ return this;
1136
+ }
1137
+ /**
1138
+ * @public
1139
+ * @method regex
1140
+ * @description
1141
+ * Raw regex to be applied on a field - wrapper
1142
+ *
1143
+ * @param {string} field Field on which the regex is to be applied on
1144
+ * @param {pattern} pattern Regex pattern
1145
+ * @param {options} options Options to be applied while evaluating the regex
1146
+ * @example
1147
+ * Stack
1148
+ * .contentType('blog')
1149
+ * .entries()
1150
+ * .regex("name", "^J")
1151
+ * .find()
1152
+ * .then((result) => {
1153
+ * // returns entries who's name properties start with "J"
1154
+ * })
1155
+ * .catch((error) => {
1156
+ * // handle query errors
1157
+ * })
1158
+ *
1159
+ * @returns {Stack} Returns an instance of 'stack'
1160
+ */
1161
+ regex(field, pattern, options = 'i') {
1162
+ if (!(field) || !(pattern) || typeof field !== 'string' || typeof pattern !== 'string') {
1163
+ throw new Error('Kindly provide a valid field and pattern value for \'.regex()\'');
1164
+ }
1165
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
1166
+ this.q.query = lodash_1.merge(this.q.query, {
1167
+ [field]: {
1168
+ $options: options,
1169
+ $regex: pattern,
1170
+ },
1171
+ });
1172
+ }
1173
+ else {
1174
+ this.q.query = {
1175
+ [field]: {
1176
+ $options: options,
1177
+ $regex: pattern,
1178
+ },
1179
+ };
1180
+ }
1181
+ return this;
1182
+ }
1183
+ /**
1184
+ * @public
1185
+ * @method tags
1186
+ * @summary Match entries that match a specific tags
1187
+ *
1188
+ * @param {array} values Array of tag values
1189
+ * @example
1190
+ * Stack
1191
+ * .contentType('blog')
1192
+ * .entries()
1193
+ * .tags(["new", "fresh"])
1194
+ * .find()
1195
+ * .then((result) => {
1196
+ * // returns entries filtered based on their tag fields
1197
+ * })
1198
+ * .catch((error) => {
1199
+ * // handle query errors
1200
+ * })
1201
+ *
1202
+ * @returns {Stack} Returns an instance of 'stack'
1203
+ */
1204
+ tags(values) {
1205
+ if (!values || typeof values !== 'object' || !(values instanceof Array) || values.length === 0) {
1206
+ throw new Error('Kindly provide valid \'field\' values for \'tags()\'');
1207
+ }
1208
+ // filter non-string keys
1209
+ lodash_1.remove(values, (value) => {
1210
+ return typeof value !== 'string';
1211
+ });
1212
+ if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
1213
+ this.q.query.tags = {
1214
+ $in: values,
1215
+ };
1216
+ }
1217
+ else {
1218
+ this.q.query = {
1219
+ tags: {
1220
+ $in: values,
1221
+ },
1222
+ };
1223
+ }
1224
+ return this;
1225
+ }
1226
+ /**
1227
+ * @public
1228
+ * @method where
1229
+ * @summary Pass JS expression or a full function to the query system
1230
+ * @description
1231
+ * Use the $where operator to pass either a string containing a JavaScript expression or a full JavaScript
1232
+ * function to the query system.
1233
+ * The $where provides greater flexibility, but requires that the database processes the JavaScript expression or
1234
+ * function for each document in the collection.
1235
+ * Reference the document in the JavaScript expression or function using either this or obj.
1236
+ * Only apply the $where query operator to top-level documents.
1237
+ * The $where query operator will not work inside a nested document, for instance, in an $elemMatch query.
1238
+ * Ref. - https://docs.mongodb.com/manual/reference/operator/query/where/index.html
1239
+ * @param { * } expr Pass either a string containing a JavaScript expression or a full JavaScript
1240
+ * function to the query system.
1241
+ * @example
1242
+ * Stack
1243
+ * .contentType('blog')
1244
+ * .entries()
1245
+ * .where(function() {
1246
+ * return (hex_md5(this.name) === "9b53e667f30cd329dca1ec9e6a83e994")
1247
+ * })
1248
+ * .find()
1249
+ * .then((result) => {
1250
+ * // returns entries filtered based on the $where condition provided
1251
+ * })
1252
+ * .catch((error) => {
1253
+ * // handle query errors
1254
+ * })
1255
+ *
1256
+ * @returns {Stack} Returns an instance of 'stack'
1257
+ */
1258
+ where(expr) {
1259
+ if (!(expr)) {
1260
+ throw new Error('Kindly provide a valid field and expr/fn value for \'.where()\'');
1261
+ }
1262
+ else if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
1263
+ if (typeof expr === 'function') {
1264
+ expr = expr.toString();
1265
+ }
1266
+ this.q.query = lodash_1.merge(this.q.query, {
1267
+ $where: expr,
1268
+ });
1269
+ }
1270
+ else {
1271
+ if (typeof expr === 'function') {
1272
+ expr = expr.toString();
1273
+ }
1274
+ this.q.query = {
1275
+ $where: expr,
1276
+ };
1277
+ }
1278
+ return this;
1279
+ }
1280
+ /**
1281
+ * @public
1282
+ * @method includeCount
1283
+ * @description
1284
+ * Includes 'count' key in response, which is the total count of the items being returned
1285
+ *
1286
+ * @example
1287
+ * Stack
1288
+ * .contentType('blog')
1289
+ * .entries()
1290
+ * .includeCount()
1291
+ * .find()
1292
+ * .then((result) => {
1293
+ * // returns entries, along with a 'count' property, with the total count of entries being returned
1294
+ * })
1295
+ * .catch((error) => {
1296
+ * // handle query errors
1297
+ * })
1298
+ *
1299
+ * @returns {Stack} Returns an instance of 'stack'
1300
+ */
1301
+ includeCount() {
1302
+ this.internal.includeCount = true;
1303
+ return this;
1304
+ }
1305
+ /**
1306
+ * @description
1307
+ * Includes 'content_type' key in response, which is the content type schema of the entries filtered/scanned
1308
+ * @example
1309
+ * Stack
1310
+ * .contentType('blog')
1311
+ * .entries()
1312
+ * .includeSchema()
1313
+ * .find()
1314
+ * .then((result) => {
1315
+ * // returns entries, along with a 'content_type' property, which is 'blog' content type's schema
1316
+ * })
1317
+ * .catch((error) => {
1318
+ * // handle query errors
1319
+ * })
1320
+ *
1321
+ * @returns {Stack} Returns an instance of 'stack'
1322
+ */
1323
+ includeSchema() {
1324
+ this.internal.includeSchema = true;
1325
+ return this;
1326
+ }
1327
+ /**
1328
+ * @public
1329
+ * @method includeContentType
1330
+ * @description
1331
+ * Includes 'content_type' key in response, which is the content type schema of the entries filtered/scanned
1332
+ * @example
1333
+ * Stack
1334
+ * .contentType('blog')
1335
+ * .entries()
1336
+ * .includeContentType()
1337
+ * .find()
1338
+ * .then((result) => {
1339
+ * // returns entries, along with a 'content_type' property, which is 'blog' content type's schema
1340
+ * })
1341
+ * .catch((error) => {
1342
+ * // handle query errors
1343
+ * })
1344
+ *
1345
+ * @returns {Stack} Returns an instance of 'stack'
1346
+ */
1347
+ includeContentType() {
1348
+ this.internal.includeSchema = true;
1349
+ return this;
1350
+ }
1351
+ /**
1352
+ * @public
1353
+ * @method excludeReferences
1354
+ * @description
1355
+ * Excludes all references of the entries being scanned.
1356
+ * Note: On calling this, assets will not be binded in the result being returned.
1357
+ *
1358
+ * @example
1359
+ * Stack
1360
+ * .contentType('blog')
1361
+ * .entries()
1362
+ * .excludeReferences()
1363
+ * .find()
1364
+ * .then((result) => {
1365
+ * // returns entries, without any of its assets Or references
1366
+ * })
1367
+ * .catch((error) => {
1368
+ * // handle query errors
1369
+ * })
1370
+ *
1371
+ * @returns {Stack} Returns an instance of 'stack'
1372
+ */
1373
+ excludeReferences() {
1374
+ this.internal.excludeReferences = true;
1375
+ return this;
1376
+ }
1377
+ /**
1378
+ * @public
1379
+ * @method queryReferences
1380
+ * @description
1381
+ * Wrapper, that allows querying on the entry's references.
1382
+ * Note: This is a slow method, since it scans all documents and fires the `reference`
1383
+ * query on them.Once the references are binded, the query object passed is used
1384
+ * for filtering
1385
+ * Use `.query()` filters to reduce the total no of documents being scanned
1386
+ *
1387
+ * @example
1388
+ * Stack
1389
+ * .contentType('blog')
1390
+ * .entries()
1391
+ * .queryReferences({"authors.name": "John Doe"})
1392
+ * .find()
1393
+ * .then((result) => {
1394
+ * // returns entries, who's reference author's name equals "John Doe"
1395
+ * })
1396
+ * .catch((error) => {
1397
+ * // handle query errors
1398
+ * })
1399
+ *
1400
+ * @returns {Stack} Returns an instance of 'stack'
1401
+ */
1402
+ queryReferences(query) {
1403
+ if (query &amp;&amp; typeof query === 'object') {
1404
+ this.internal.queryReferences = query;
1405
+ return this;
1406
+ }
1407
+ throw new Error('Kindly pass a query object for \'.queryReferences()\'');
1408
+ }
1409
+ /**
1410
+ * @public
1411
+ * @method getQuery
1412
+ * @description
1413
+ * Returns the query build thusfar
1414
+ * @example
1415
+ * const query = Stack
1416
+ * .contentType('blog')
1417
+ * .entries()
1418
+ * .getQuery()
1419
+ * // exposes details of the queries formed inside the SDK
1420
+ *
1421
+ * @returns {Stack} Returns an instance of 'stack'
1422
+ */
1423
+ getQuery() {
1424
+ return Object.assign({}, this.q);
1425
+ }
1426
+ /**
1427
+ * @public
1428
+ * @method includeReferences
1429
+ * @description
1430
+ * This method would return all the references of your queried entries (until depth 2)
1431
+ * Note: If you wish to increase the depth of the references fetched, call pass a numeric parameter
1432
+ * @example
1433
+ * Stack
1434
+ * .contentType('blog')
1435
+ * .entries()
1436
+ * .includeReferences(3)
1437
+ * @returns {Stack} Returns 'this' instance (of Stack)
1438
+ */
1439
+ includeReferences(depth) {
1440
+ console.warn('.includeReferences() is a relatively slow query..!');
1441
+ if (typeof depth === 'number') {
1442
+ this.q.referenceDepth = depth;
1443
+ }
1444
+ this.internal.includeAllReferences = true;
1445
+ return this;
1446
+ }
1447
+ /**
1448
+ * @public
1449
+ * @method include
1450
+ * @description
1451
+ * Pass in reference field uids, that you want included in your result.
1452
+ * If you want all the references, use .includeReferences()
1453
+ * @example
1454
+ * Stack.contentType('blog')
1455
+ * .entries()
1456
+ * .include(['related_blogs', 'authors.blogs']) // here related_blogs and authors.blogs are reference field uids
1457
+ * @param {object} fields An array of reference field uids
1458
+ * @returns {Stack} Returns 'this' instance (of Stack)
1459
+ */
1460
+ include(fields) {
1461
+ if (fields.length === 0) {
1462
+ throw new Error('Kindly pass a valid reference field path to \'.include()\' ');
1463
+ }
1464
+ else if (typeof fields === 'string') {
1465
+ this.internal.includeSpecificReferences = [fields];
1466
+ }
1467
+ else {
1468
+ this.internal.includeSpecificReferences = fields;
1469
+ }
1470
+ return this;
1471
+ }
1472
+ /**
1473
+ * @public
1474
+ * @method find
1475
+ * @description
1476
+ * Queries the db using the query built/passed
1477
+ * Does all the processing, filtering, referencing after querying the DB
1478
+ * @param {object} query Optional query object, that overrides all the
1479
+ * previously build queries
1480
+ * @public
1481
+ * @example
1482
+ * Stack
1483
+ * .contentType('blog')
1484
+ * .entries()
1485
+ * .find()
1486
+ * .then((result) => {
1487
+ * // returns blog content type's entries
1488
+ * })
1489
+ * .catch((error) => {
1490
+ * // handle query errors
1491
+ * })
1492
+ *
1493
+ * @returns {object} - Returns a objects, that have been processed, filtered and referenced
1494
+ */
1495
+ find(query = {}) {
1496
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
1497
+ const queryFilters = this.preProcess(query);
1498
+ if (this.internal.sort) {
1499
+ this.collection = this.collection
1500
+ .find(queryFilters)
1501
+ .sort(this.internal.sort);
1502
+ }
1503
+ else {
1504
+ this.collection = this.collection
1505
+ .find(queryFilters);
1506
+ }
1507
+ if (this.internal.queryReferences) {
1508
+ this.collection = this.collection
1509
+ .project(this.internal.projections)
1510
+ .toArray();
1511
+ }
1512
+ else {
1513
+ this.collection = this.collection
1514
+ .project(this.internal.projections)
1515
+ .limit(this.internal.limit)
1516
+ .skip(this.internal.skip)
1517
+ .toArray();
1518
+ }
1519
+ return this.collection
1520
+ .then((result) => __awaiter(this, void 0, void 0, function* () {
1521
+ // Ignore references include, for empty list, exclude call, content type &amp; assets
1522
+ if (result.length === 0 || this.internal.excludeReferences || this.q.content_type_uid === this
1523
+ .types.content_types || this.q.content_type_uid
1524
+ === this.types.assets || (this.internal.onlyCount &amp;&amp; !this.internal.queryReferences)) {
1525
+ // Do nothing
1526
+ }
1527
+ else if (this.internal.includeSpecificReferences) {
1528
+ yield this.includeSpecificReferences(result, this.q.content_type_uid, this.q.locale, this
1529
+ .internal.includeSpecificReferences);
1530
+ }
1531
+ else if (this.internal.includeAllReferences) {
1532
+ yield this.bindReferences(result, this.q.content_type_uid, this.q.locale);
1533
+ }
1534
+ else {
1535
+ yield this.includeAssetsOnly(result, this.q.content_type_uid, this.q.locale);
1536
+ }
1537
+ if (this.internal.queryReferences) {
1538
+ result = result.filter(sift_1.default(this.internal.queryReferences));
1539
+ if (this.internal.skip) {
1540
+ result = result.splice(this.internal.skip, this.internal.limit);
1541
+ }
1542
+ else if (this.internal.limit) {
1543
+ result = result.splice(0, this.internal.limit);
1544
+ }
1545
+ }
1546
+ result = yield this.postProcess(result);
1547
+ return resolve(result);
1548
+ }))
1549
+ .catch((error) => {
1550
+ this.cleanup();
1551
+ return reject(error);
1552
+ });
1553
+ }));
1554
+ }
1555
+ /**
1556
+ * @public
1557
+ * @method count
1558
+ * @descriptionReturns the count of the entries/assets that match the filter
1559
+ * @param {object} query Optional query filter object
1560
+ * @public
1561
+ * @example
1562
+ * Stack
1563
+ * .contentType('blog')
1564
+ * .entries()
1565
+ * .count()
1566
+ * .then((result) => {
1567
+ * // returns entries, without any of its assets Or references
1568
+ * })
1569
+ * .catch((error) => {
1570
+ * // handle query errors
1571
+ * })
1572
+ *
1573
+ * @returns {object} Returns count of the entries/asset's matched
1574
+ */
1575
+ count(query) {
1576
+ return __awaiter(this, void 0, void 0, function* () {
1577
+ this.internal.onlyCount = true;
1578
+ return this.find(query);
1579
+ });
1580
+ }
1581
+ /**
1582
+ * @public
1583
+ * @method findOne
1584
+ * @deprecated - Use .fetch() instead
1585
+ * @description
1586
+ * Queries the db using the query built/passed. Returns a single entry/asset/content type object
1587
+ * Does all the processing, filtering, referencing after querying the DB
1588
+ * @param {object} query Optional query object, that overrides all the previously build queries
1589
+ *
1590
+ * @example
1591
+ * Stack
1592
+ * .contentType('blog')
1593
+ * .entries()
1594
+ * .findOne()
1595
+ *
1596
+ * @returns {object} - Returns an object, that has been processed, filtered and referenced
1597
+ */
1598
+ findOne(query = {}) {
1599
+ this.internal.single = true;
1600
+ return this.find(query);
1601
+ }
1602
+ /**
1603
+ * @public
1604
+ * @method fetch
1605
+ * @description
1606
+ * Queries the db using the query built/passed. Returns a single entry/asset/content type object
1607
+ * Does all the processing, filtering, referencing after querying the DB
1608
+ * @param {object} query Optional query object, that overrides all the previously build queries
1609
+ *
1610
+ * @example
1611
+ * Stack
1612
+ * .contentType('blog')
1613
+ * .entries()
1614
+ * .fetch()
1615
+ *
1616
+ * @returns {object} - Returns an object, that has been processed, filtered and referenced
1617
+ */
1618
+ fetch(query = {}) {
1619
+ this.internal.single = true;
1620
+ return this.find(query);
1621
+ }
1622
+ /**
1623
+ * @private
1624
+ * @method preProcess
1625
+ * @summary Internal method, that executes and formats the queries built/passed
1626
+ * @param {object} query Query filter/process object
1627
+ * @returns {object} Returns a query object, that has been processed to be queried in mongodb
1628
+ */
1629
+ preProcess(query) {
1630
+ let queryFilters;
1631
+ if (this.q.query &amp;&amp; typeof this.q.query === 'object') {
1632
+ this.q.query = lodash_1.merge(this.q.query, query);
1633
+ }
1634
+ else {
1635
+ this.q.query = {};
1636
+ }
1637
+ // tslint:disable-next-line: max-line-length
1638
+ this.q.referenceDepth = (typeof this.q.referenceDepth === 'number') ? this.q.referenceDepth : this.contentStore.referenceDepth;
1639
+ if (this.internal.only) {
1640
+ this.internal.projections = this.internal.only;
1641
+ }
1642
+ else {
1643
+ this.internal.projections = lodash_1.merge(this.contentStore.projections, this.internal.except);
1644
+ }
1645
+ // set default limit, if .limit() hasn't been called
1646
+ if (!(this.internal.limit)) {
1647
+ this.internal.limit = this.contentStore.limit;
1648
+ }
1649
+ // set default skip, if .skip() hasn't been called
1650
+ if (!(this.internal.skip)) {
1651
+ this.internal.skip = this.contentStore.skip;
1652
+ }
1653
+ // set default locale, if no locale has been passed
1654
+ if (!(this.q.locale)) {
1655
+ this.q.locale = this.contentStore.locale;
1656
+ }
1657
+ // by default, sort by latest content
1658
+ if (!this.internal.sort) {
1659
+ this.internal.sort = {
1660
+ updated_at: -1,
1661
+ };
1662
+ }
1663
+ const filters = Object.assign({ _content_type_uid: this.q.content_type_uid, locale: this.q.locale }, this.q.query);
1664
+ if (this.q.content_type_uid === this.types.assets) {
1665
+ // allow querying only on published assets..!
1666
+ queryFilters = {
1667
+ $and: [
1668
+ filters,
1669
+ {
1670
+ _version: {
1671
+ $exists: true,
1672
+ },
1673
+ },
1674
+ ],
1675
+ };
1676
+ }
1677
+ else {
1678
+ queryFilters = filters;
1679
+ }
1680
+ this.collection = this.db.collection(util_1.getCollectionName({
1681
+ content_type_uid: this.q.content_type_uid,
1682
+ locale: this.q.locale,
1683
+ }, this.collectionNames));
1684
+ return queryFilters;
1685
+ }
1686
+ /**
1687
+ * @private
1688
+ * @method cleanup
1689
+ * @summary Does GC, so memory doesn't stackup
1690
+ */
1691
+ cleanup() {
1692
+ this.collection = null;
1693
+ this.internal = null;
1694
+ this.q = null;
1695
+ }
1696
+ /**
1697
+ * @private
1698
+ * @method postProcess
1699
+ * @summary Internal method, that executes and formats the result, which the user and use
1700
+ * @param {object} result Result, which's to be manipulated
1701
+ * @returns {object} Returns the formatted version of the `result` object
1702
+ */
1703
+ postProcess(result) {
1704
+ return __awaiter(this, void 0, void 0, function* () {
1705
+ const count = (result === null) ? 0 : result.length;
1706
+ const output = {
1707
+ locale: this.q.locale,
1708
+ };
1709
+ if (this.internal.onlyCount) {
1710
+ output.content_type_uid = (this.q.content_type_uid === this.types.assets) ? 'assets' : ((this.q.content_type_uid
1711
+ === this.types.content_types) ? 'content_types' : this.q.content_type_uid);
1712
+ output.count = count;
1713
+ return output;
1714
+ }
1715
+ switch (this.q.content_type_uid) {
1716
+ case this.types.assets:
1717
+ if (this.internal.single) {
1718
+ output.asset = (result === null) ? result : result[0];
1719
+ }
1720
+ else {
1721
+ output.assets = result;
1722
+ }
1723
+ output.content_type_uid = 'assets';
1724
+ break;
1725
+ case this.types.content_types:
1726
+ if (this.internal.single) {
1727
+ output.content_type = (result === null) ? result : result[0];
1728
+ }
1729
+ else {
1730
+ output.content_types = result;
1731
+ }
1732
+ output.content_type_uid = 'content_types';
1733
+ break;
1734
+ default:
1735
+ if (this.internal.single) {
1736
+ output.entry = (result === null) ? result : result[0];
1737
+ }
1738
+ else {
1739
+ output.entries = result;
1740
+ }
1741
+ output.content_type_uid = this.q.content_type_uid;
1742
+ break;
1743
+ }
1744
+ if (this.internal.includeCount) {
1745
+ output.count = yield this.db.collection(util_1.getCollectionName({
1746
+ content_type_uid: this.q.content_type_uid,
1747
+ locale: this.q.locale,
1748
+ }, this.collectionNames))
1749
+ .count({
1750
+ _content_type_uid: this.q.content_type_uid,
1751
+ });
1752
+ }
1753
+ if (this.internal.includeSchema) {
1754
+ output.content_type = yield this.db.collection(util_1.getCollectionName({
1755
+ content_type_uid: this.types.content_types,
1756
+ locale: this.q.locale,
1757
+ }, this.collectionNames))
1758
+ .findOne({
1759
+ uid: this.q.content_type_uid,
1760
+ }, {
1761
+ _assets: 0,
1762
+ _content_type_uid: 0,
1763
+ _id: 0,
1764
+ _references: 0,
1765
+ });
1766
+ }
1767
+ this.cleanup();
1768
+ return output;
1769
+ });
1770
+ }
1771
+ includeAssetsOnly(entries, contentTypeUid, locale) {
1772
+ return __awaiter(this, void 0, void 0, function* () {
1773
+ const schema = yield this.db
1774
+ .collection(util_1.getCollectionName({
1775
+ content_type_uid: this.types.content_types,
1776
+ locale,
1777
+ }, this.collectionNames))
1778
+ .findOne({
1779
+ _content_type_uid: this.types.content_types,
1780
+ uid: contentTypeUid,
1781
+ }, {
1782
+ _assets: 1,
1783
+ _id: 0,
1784
+ });
1785
+ if (schema === null || schema[this.types.assets] !== 'object') {
1786
+ return;
1787
+ }
1788
+ const paths = Object.keys(schema[this.types.assets]);
1789
+ const shelf = [];
1790
+ const queryBucket = {
1791
+ $or: [],
1792
+ };
1793
+ for (let i = 0, j = paths.length; i &lt; j; i++) {
1794
+ this.fetchPathDetails(entries, locale, paths[i].split('.'), queryBucket, shelf, true, entries, 0);
1795
+ }
1796
+ if (shelf.length === 0) {
1797
+ return;
1798
+ }
1799
+ const assets = yield this.db.collection(util_1.getCollectionName({
1800
+ content_type_uid: this.types.assets,
1801
+ locale,
1802
+ }, this.collectionNames))
1803
+ .find(queryBucket)
1804
+ .project({
1805
+ _content_type_uid: 0,
1806
+ _id: 0,
1807
+ })
1808
+ .toArray();
1809
+ for (let l = 0, m = shelf.length; l &lt; m; l++) {
1810
+ for (let n = 0, o = assets.length; n &lt; o; n++) {
1811
+ if (shelf[l].uid === assets[n].uid) {
1812
+ shelf[l].path[shelf[l].position] = assets[n];
1813
+ break;
1814
+ }
1815
+ }
1816
+ }
1817
+ return;
1818
+ });
1819
+ }
1820
+ /**
1821
+ * @summary
1822
+ * Internal method, that iteratively calls itself and binds entries reference
1823
+ * @param {Object} entry - An entry or a collection of entries, who's references are to be found
1824
+ * @param {String} contentTypeUid - Content type uid
1825
+ * @param {String} locale - Locale, in which the reference is to be found
1826
+ * @param {Object} include - Array of field paths, to be included
1827
+ * @returns {Object} - Returns `entries`, that has all of its reference binded
1828
+ */
1829
+ includeSpecificReferences(entries, contentTypeUid, locale, include) {
1830
+ return __awaiter(this, void 0, void 0, function* () {
1831
+ const ctQuery = {
1832
+ _content_type_uid: this.types.content_types,
1833
+ uid: contentTypeUid,
1834
+ };
1835
+ const { paths, // ref. fields in the current content types
1836
+ pendingPath, // left over of *paths*
1837
+ schemaList, } = yield this.getReferencePath(ctQuery, locale, include);
1838
+ const queries = {
1839
+ $or: [],
1840
+ }; // reference field paths
1841
+ const shelf = []; // a mapper object, that holds pointer to the original element
1842
+ // iterate over each path in the entries and fetch the references
1843
+ // while fetching, keep track of their location
1844
+ for (let i = 0, j = paths.length; i &lt; j; i++) {
1845
+ this.fetchPathDetails(entries, locale, paths[i].split('.'), queries, shelf, true, entries, 0);
1846
+ }
1847
+ // even after traversing, if no references were found, simply return the entries found thusfar
1848
+ if (shelf.length === 0) {
1849
+ return entries;
1850
+ }
1851
+ // else, self-recursively iterate and fetch references
1852
+ // Note: Shelf is the one holding `pointers` to the actual entry
1853
+ // Once the pointer has been used, for GC, point the object to null
1854
+ return this.includeReferenceIteration(queries, schemaList, locale, pendingPath, shelf);
1855
+ });
1856
+ }
1857
+ fetchPathDetails(data, locale, pathArr, queryBucket, shelf, assetsOnly = false, parent, pos, counter = 0) {
1858
+ if (counter === (pathArr.length)) {
1859
+ if (data &amp;&amp; typeof data === 'object') {
1860
+ if (data instanceof Array &amp;&amp; data.length) {
1861
+ data.forEach((elem, idx) => {
1862
+ if (typeof elem === 'string') {
1863
+ queryBucket.$or.push({
1864
+ _content_type_uid: this.types.assets,
1865
+ _version: { $exists: true },
1866
+ locale,
1867
+ uid: elem,
1868
+ });
1869
+ shelf.push({
1870
+ path: data,
1871
+ position: idx,
1872
+ uid: elem,
1873
+ });
1874
+ }
1875
+ else if (elem &amp;&amp; typeof elem === 'object' &amp;&amp; elem.hasOwnProperty('_content_type_uid')) {
1876
+ queryBucket.$or.push({
1877
+ _content_type_uid: elem._content_type_uid,
1878
+ locale,
1879
+ uid: elem.uid,
1880
+ });
1881
+ shelf.push({
1882
+ path: data,
1883
+ position: idx,
1884
+ uid: elem.uid,
1885
+ });
1886
+ }
1887
+ });
1888
+ }
1889
+ else if (typeof data === 'object') {
1890
+ if (data.hasOwnProperty('_content_type_uid')) {
1891
+ queryBucket.$or.push({
1892
+ _content_type_uid: data._content_type_uid,
1893
+ locale,
1894
+ uid: data.uid,
1895
+ });
1896
+ shelf.push({
1897
+ path: parent,
1898
+ position: pos,
1899
+ uid: data.uid,
1900
+ });
1901
+ }
1902
+ }
1903
+ }
1904
+ else if (typeof data === 'string') {
1905
+ queryBucket.$or.push({
1906
+ _content_type_uid: this.types.assets,
1907
+ _version: { $exists: true },
1908
+ locale,
1909
+ uid: data,
1910
+ });
1911
+ shelf.push({
1912
+ path: parent,
1913
+ position: pos,
1914
+ uid: data,
1915
+ });
1916
+ }
1917
+ }
1918
+ else {
1919
+ const currentField = pathArr[counter];
1920
+ counter++;
1921
+ if (data instanceof Array) {
1922
+ // tslint:disable-next-line: prefer-for-of
1923
+ for (let i = 0; i &lt; data.length; i++) {
1924
+ if (data[i][currentField]) {
1925
+ this.fetchPathDetails(data[i][currentField], locale, pathArr, queryBucket, shelf, assetsOnly, data[i], currentField, counter);
1926
+ }
1927
+ }
1928
+ }
1929
+ else {
1930
+ if (data[currentField]) {
1931
+ this.fetchPathDetails(data[currentField], locale, pathArr, queryBucket, shelf, assetsOnly, data, currentField, counter);
1932
+ }
1933
+ }
1934
+ }
1935
+ // since we've reached last of the paths, return!
1936
+ return;
1937
+ }
1938
+ includeReferenceIteration(eQuery, ctQuery, locale, include, oldShelf) {
1939
+ return __awaiter(this, void 0, void 0, function* () {
1940
+ if (oldShelf.length === 0 || ctQuery.$or.length === 0) {
1941
+ return;
1942
+ }
1943
+ const { paths, pendingPath, schemaList, } = yield this.getReferencePath(ctQuery, locale, include);
1944
+ const { result, queries, shelf, } = yield this.fetchEntries(eQuery, locale, paths, include);
1945
+ // GC to avoid mem leaks!
1946
+ eQuery = null;
1947
+ for (let i = 0, j = oldShelf.length; i &lt; j; i++) {
1948
+ const element = oldShelf[i];
1949
+ let flag = true;
1950
+ for (let k = 0, l = result.length; k &lt; l; k++) {
1951
+ if (result[k].uid === element.uid) {
1952
+ element.path[element.position] = result[k];
1953
+ flag = false;
1954
+ break;
1955
+ }
1956
+ }
1957
+ if (flag) {
1958
+ for (let e = 0, f = oldShelf[i].path.length; e &lt; f; e++) {
1959
+ // tslint:disable-next-line: max-line-length
1960
+ if (oldShelf[i].path[e].hasOwnProperty('_content_type_uid') &amp;&amp; Object.keys(oldShelf[i].path[e]).length === 2) {
1961
+ oldShelf[i].path.splice(e, 1);
1962
+ break;
1963
+ }
1964
+ }
1965
+ }
1966
+ }
1967
+ // GC to avoid mem leaks!
1968
+ oldShelf = null;
1969
+ // Iterative loops, that traverses paths and binds them onto entries
1970
+ yield this.includeReferenceIteration(queries, schemaList, locale, pendingPath, shelf);
1971
+ return;
1972
+ });
1973
+ }
1974
+ getReferencePath(query, locale, currentInclude) {
1975
+ return __awaiter(this, void 0, void 0, function* () {
1976
+ const schemas = yield this.db.collection(util_1.getCollectionName({
1977
+ content_type_uid: this.types.content_types,
1978
+ locale,
1979
+ }, this.collectionNames))
1980
+ .find(query)
1981
+ .project({
1982
+ _assets: 1,
1983
+ _id: 0,
1984
+ _references: 1,
1985
+ })
1986
+ .toArray();
1987
+ const pendingPath = [];
1988
+ const schemasReferred = [];
1989
+ const paths = [];
1990
+ const schemaList = {
1991
+ $or: [],
1992
+ };
1993
+ if (schemas.length === 0) {
1994
+ return {
1995
+ paths,
1996
+ pendingPath,
1997
+ schemaList,
1998
+ };
1999
+ }
2000
+ let entryReferences = {};
2001
+ schemas.forEach((schema) => {
2002
+ // Entry references
2003
+ entryReferences = lodash_1.merge(entryReferences, schema[this.types.references]);
2004
+ // tslint:disable-next-line: forin
2005
+ for (const path in schema[this.types.assets]) {
2006
+ paths.push(path);
2007
+ }
2008
+ });
2009
+ for (let i = 0, j = currentInclude.length; i &lt; j; i++) {
2010
+ const includePath = currentInclude[i];
2011
+ // tslint:disable-next-line: forin
2012
+ for (const path in entryReferences) {
2013
+ const subStr = includePath.slice(0, path.length);
2014
+ if (subStr === path) {
2015
+ let subPath;
2016
+ // Its the complete path!! Hurrah!
2017
+ if (path.length !== includePath.length) {
2018
+ subPath = subStr;
2019
+ pendingPath.push(includePath.slice(path.length + 1));
2020
+ }
2021
+ else {
2022
+ subPath = includePath;
2023
+ }
2024
+ if (typeof entryReferences[path] === 'string') {
2025
+ schemasReferred.push({
2026
+ _content_type_uid: this.types.content_types,
2027
+ uid: entryReferences[path],
2028
+ });
2029
+ }
2030
+ else if (entryReferences[path].length) {
2031
+ entryReferences[path].forEach((contentTypeUid) => {
2032
+ schemasReferred.push({
2033
+ _content_type_uid: this.types.content_types,
2034
+ uid: contentTypeUid,
2035
+ });
2036
+ });
2037
+ }
2038
+ paths.push(subPath);
2039
+ break;
2040
+ }
2041
+ }
2042
+ }
2043
+ schemaList.$or = schemasReferred;
2044
+ return {
2045
+ // path, that's possible in the current schema
2046
+ paths,
2047
+ // paths, that's yet to be traversed
2048
+ pendingPath,
2049
+ // schemas, to be loaded!
2050
+ schemaList,
2051
+ };
2052
+ });
2053
+ }
2054
+ fetchEntries(query, locale, paths, include, includeAll = false) {
2055
+ return __awaiter(this, void 0, void 0, function* () {
2056
+ const result = yield this.db.collection(util_1.getCollectionName({
2057
+ content_type_uid: 'entries',
2058
+ locale,
2059
+ }, this.collectionNames))
2060
+ .find(query)
2061
+ .project({
2062
+ _content_type_uid: 0,
2063
+ _id: 0,
2064
+ _synced_at: 0,
2065
+ event_at: 0,
2066
+ })
2067
+ .toArray();
2068
+ const queries = {
2069
+ $or: [],
2070
+ };
2071
+ const shelf = [];
2072
+ if (result.length === 0) {
2073
+ return {
2074
+ queries,
2075
+ result,
2076
+ shelf,
2077
+ };
2078
+ }
2079
+ if (include.length || includeAll) {
2080
+ paths.forEach((path) => {
2081
+ this.fetchPathDetails(result, locale, path.split('.'), queries, shelf, false, result, 0);
2082
+ });
2083
+ }
2084
+ else {
2085
+ // if there are no includes, only fetch assets)
2086
+ paths.forEach((path) => {
2087
+ this.fetchPathDetails(result, locale, path.split('.'), queries, shelf, true, result, 0);
2088
+ });
2089
+ }
2090
+ return {
2091
+ queries,
2092
+ result,
2093
+ shelf,
2094
+ };
2095
+ });
2096
+ }
2097
+ bindReferences(entries, contentTypeUid, locale) {
2098
+ return __awaiter(this, void 0, void 0, function* () {
2099
+ const ctQuery = {
2100
+ $or: [{
2101
+ _content_type_uid: this.types.content_types,
2102
+ uid: contentTypeUid,
2103
+ }],
2104
+ };
2105
+ const { paths, // ref. fields in the current content types
2106
+ ctQueries, } = yield this.getAllReferencePaths(ctQuery, locale);
2107
+ const queries = {
2108
+ $or: [],
2109
+ }; // reference field paths
2110
+ const objectPointerList = []; // a mapper object, that holds pointer to the original element
2111
+ // iterate over each path in the entries and fetch the references
2112
+ // while fetching, keep track of their location
2113
+ for (let i = 0, j = paths.length; i &lt; j; i++) {
2114
+ this.fetchPathDetails(entries, locale, paths[i].split('.'), queries, objectPointerList, true, entries, 0);
2115
+ }
2116
+ // even after traversing, if no references were found, simply return the entries found thusfar
2117
+ if (objectPointerList.length === 0) {
2118
+ return entries;
2119
+ }
2120
+ // else, self-recursively iterate and fetch references
2121
+ // Note: Shelf is the one holding `pointers` to the actual entry
2122
+ // Once the pointer has been used, for GC, point the object to null
2123
+ return this.includeAllReferencesIteration(queries, ctQueries, locale, objectPointerList);
2124
+ });
2125
+ }
2126
+ includeAllReferencesIteration(oldEntryQueries, oldCtQueries, locale, oldObjectPointerList, depth = 0) {
2127
+ return __awaiter(this, void 0, void 0, function* () {
2128
+ if (depth > this.q.referenceDepth || oldObjectPointerList.length === 0 || oldCtQueries.$or.length === 0) {
2129
+ return;
2130
+ }
2131
+ const { ctQueries, paths, } = yield this.getAllReferencePaths(oldCtQueries, locale);
2132
+ // GC to aviod mem leaks
2133
+ oldCtQueries = null;
2134
+ const { result, queries, shelf, } = yield this.fetchEntries(oldEntryQueries, locale, paths, [], true);
2135
+ // GC to avoid mem leaks!
2136
+ oldEntryQueries = null;
2137
+ for (let i = 0, j = oldObjectPointerList.length; i &lt; j; i++) {
2138
+ const element = oldObjectPointerList[i];
2139
+ let flag = true;
2140
+ for (let k = 0, l = result.length; k &lt; l; k++) {
2141
+ if (result[k].uid === element.uid) {
2142
+ element.path[element.position] = result[k];
2143
+ flag = false;
2144
+ break;
2145
+ }
2146
+ }
2147
+ if (flag) {
2148
+ for (let e = 0, f = oldObjectPointerList[i].path.length; e &lt; f; e++) {
2149
+ // tslint:disable-next-line: max-line-length
2150
+ if (oldObjectPointerList[i].path[e].hasOwnProperty('_content_type_uid') &amp;&amp; Object.keys(oldObjectPointerList[i].path[e]).length === 2) {
2151
+ oldObjectPointerList[i].path.splice(e, 1);
2152
+ break;
2153
+ }
2154
+ }
2155
+ }
2156
+ }
2157
+ // GC to avoid mem leaks!
2158
+ oldObjectPointerList = null;
2159
+ ++depth;
2160
+ // Iterative loops, that traverses paths and binds them onto entries
2161
+ yield this.includeAllReferencesIteration(queries, ctQueries, locale, shelf, depth);
2162
+ return;
2163
+ });
2164
+ }
2165
+ getAllReferencePaths(contentTypeQueries, locale) {
2166
+ return __awaiter(this, void 0, void 0, function* () {
2167
+ const contents = yield this.db
2168
+ .collection(util_1.getCollectionName({
2169
+ content_type_uid: this.types.content_types,
2170
+ locale,
2171
+ }, this.collectionNames))
2172
+ .find(contentTypeQueries)
2173
+ .project({
2174
+ _assets: 1,
2175
+ _references: 1,
2176
+ })
2177
+ .toArray();
2178
+ const ctQueries = {
2179
+ $or: [],
2180
+ };
2181
+ let paths = [];
2182
+ for (let i = 0, j = contents.length; i &lt; j; i++) {
2183
+ let assetFieldPaths;
2184
+ let entryReferencePaths;
2185
+ if (contents[i].hasOwnProperty(this.types.assets)) {
2186
+ assetFieldPaths = Object.keys(contents[i][this.types.assets]);
2187
+ paths = paths.concat(assetFieldPaths);
2188
+ }
2189
+ if (contents[i].hasOwnProperty('_references')) {
2190
+ entryReferencePaths = Object.keys(contents[i][this.types.references]);
2191
+ paths = paths.concat(entryReferencePaths);
2192
+ for (let k = 0, l = entryReferencePaths.length; k &lt; l; k++) {
2193
+ if (typeof contents[i][this.types.references][entryReferencePaths[k]] === 'string') {
2194
+ ctQueries.$or.push({
2195
+ _content_type_uid: this.types.content_types,
2196
+ // this would probably make it slow in FS, avoid this there?
2197
+ // locale,
2198
+ uid: contents[i][this.types.references][entryReferencePaths[k]],
2199
+ });
2200
+ }
2201
+ else if (contents[i][this.types.references][entryReferencePaths[k]].length) {
2202
+ contents[i][this.types.references][entryReferencePaths[k]].forEach((uid) => {
2203
+ ctQueries.$or.push({
2204
+ _content_type_uid: this.types.content_types,
2205
+ // avoiding locale here, not sure if its required
2206
+ // locale,
2207
+ uid,
2208
+ });
2209
+ });
2210
+ }
2211
+ }
2212
+ }
2213
+ }
2214
+ return {
2215
+ ctQueries,
2216
+ paths,
2217
+ };
2218
+ });
2219
+ }
2220
+ }
2221
+ exports.Stack = Stack;
2222
+ </code></pre>
2223
+ </article>
2224
+ </section>
2225
+
2226
+
2227
+
2228
+
2229
+ </div>
2230
+
2231
+ <nav>
2232
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="global.html#Stack">Stack</a></li></ul><h3>Global</h3><ul><li><a href="global.html#and">and</a></li><li><a href="global.html#ascending">ascending</a></li><li><a href="global.html#asset">asset</a></li><li><a href="global.html#assets">assets</a></li><li><a href="global.html#close">close</a></li><li><a href="global.html#connect">connect</a></li><li><a href="global.html#containedIn">containedIn</a></li><li><a href="global.html#contentType">contentType</a></li><li><a href="global.html#contentTypes">contentTypes</a></li><li><a href="global.html#count">count</a></li><li><a href="global.html#descending">descending</a></li><li><a href="global.html#entries">entries</a></li><li><a href="global.html#entry">entry</a></li><li><a href="global.html#except">except</a></li><li><a href="global.html#excludeReferences">excludeReferences</a></li><li><a href="global.html#exists">exists</a></li><li><a href="global.html#fetch">fetch</a></li><li><a href="global.html#find">find</a></li><li><a href="global.html#findOne">findOne</a></li><li><a href="global.html#getQuery">getQuery</a></li><li><a href="global.html#greaterThan">greaterThan</a></li><li><a href="global.html#greaterThanOrEqualTo">greaterThanOrEqualTo</a></li><li><a href="global.html#include">include</a></li><li><a href="global.html#includeContentType">includeContentType</a></li><li><a href="global.html#includeCount">includeCount</a></li><li><a href="global.html#includeReferences">includeReferences</a></li><li><a href="global.html#language">language</a></li><li><a href="global.html#lessThan">lessThan</a></li><li><a href="global.html#lessThanOrEqualTo">lessThanOrEqualTo</a></li><li><a href="global.html#limit">limit</a></li><li><a href="global.html#notContainedIn">notContainedIn</a></li><li><a href="global.html#notEqualTo">notEqualTo</a></li><li><a href="global.html#notExists">notExists</a></li><li><a href="global.html#only">only</a></li><li><a href="global.html#or">or</a></li><li><a href="global.html#query">query</a></li><li><a href="global.html#queryReferences">queryReferences</a></li><li><a href="global.html#regex">regex</a></li><li><a href="global.html#schema">schema</a></li><li><a href="global.html#schemas">schemas</a></li><li><a href="global.html#skip">skip</a></li><li><a href="global.html#tags">tags</a></li><li><a href="global.html#where">where</a></li></ul>
2233
+ </nav>
2234
+
2235
+ <br class="clear">
2236
+
2237
+ <footer>
2238
+ Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.3</a> on Sat Aug 03 2019 21:26:39 GMT+0530 (India Standard Time)
2239
+ </footer>
2240
+
2241
+ <script> prettyPrint(); </script>
2242
+ <script src="scripts/linenumber.js" integrity="gjKEaAtJoBN94tFHTJO/QMWm2iZN7DSXY/EAGrHzx30=%" crossorigin="anonymous"> </script>
2243
+ </body>
2244
+ </html>