index.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const ArrayMethods = require('../array/methods');
  6. const DocumentArrayMethods = require('./methods');
  7. const Document = require('../../document');
  8. const arrayAtomicsSymbol = require('../../helpers/symbols').arrayAtomicsSymbol;
  9. const arrayAtomicsBackupSymbol = require('../../helpers/symbols').arrayAtomicsBackupSymbol;
  10. const arrayParentSymbol = require('../../helpers/symbols').arrayParentSymbol;
  11. const arrayPathSymbol = require('../../helpers/symbols').arrayPathSymbol;
  12. const arraySchemaSymbol = require('../../helpers/symbols').arraySchemaSymbol;
  13. const _basePush = Array.prototype.push;
  14. const numberRE = /^\d+$/;
  15. /**
  16. * DocumentArray constructor
  17. *
  18. * @param {Array} values
  19. * @param {String} path the path to this array
  20. * @param {Document} doc parent document
  21. * @api private
  22. * @return {MongooseDocumentArray}
  23. * @inherits MongooseArray
  24. * @see https://bit.ly/f6CnZU
  25. */
  26. function MongooseDocumentArray(values, path, doc) {
  27. const __array = [];
  28. const internals = {
  29. [arrayAtomicsSymbol]: {},
  30. [arrayAtomicsBackupSymbol]: void 0,
  31. [arrayPathSymbol]: path,
  32. [arraySchemaSymbol]: void 0,
  33. [arrayParentSymbol]: void 0
  34. };
  35. if (Array.isArray(values)) {
  36. if (values[arrayPathSymbol] === path &&
  37. values[arrayParentSymbol] === doc) {
  38. internals[arrayAtomicsSymbol] = Object.assign({}, values[arrayAtomicsSymbol]);
  39. }
  40. values.forEach(v => {
  41. _basePush.call(__array, v);
  42. });
  43. }
  44. internals[arrayPathSymbol] = path;
  45. internals.__array = __array;
  46. // Because doc comes from the context of another function, doc === global
  47. // can happen if there was a null somewhere up the chain (see #3020 && #3034)
  48. // RB Jun 17, 2015 updated to check for presence of expected paths instead
  49. // to make more proof against unusual node environments
  50. if (doc && doc instanceof Document) {
  51. internals[arrayParentSymbol] = doc;
  52. internals[arraySchemaSymbol] = doc.schema.path(path);
  53. // `schema.path()` doesn't drill into nested arrays properly yet, see
  54. // gh-6398, gh-6602. This is a workaround because nested arrays are
  55. // always plain non-document arrays, so once you get to a document array
  56. // nesting is done. Matryoshka code.
  57. while (internals[arraySchemaSymbol] != null &&
  58. internals[arraySchemaSymbol].$isMongooseArray &&
  59. !internals[arraySchemaSymbol].$isMongooseDocumentArray) {
  60. internals[arraySchemaSymbol] = internals[arraySchemaSymbol].casterConstructor;
  61. }
  62. }
  63. const proxy = new Proxy(__array, {
  64. get: function(target, prop) {
  65. if (prop === 'isMongooseArray' ||
  66. prop === 'isMongooseArrayProxy' ||
  67. prop === 'isMongooseDocumentArray' ||
  68. prop === 'isMongooseDocumentArrayProxy') {
  69. return true;
  70. }
  71. if (internals.hasOwnProperty(prop)) {
  72. return internals[prop];
  73. }
  74. if (DocumentArrayMethods.hasOwnProperty(prop)) {
  75. return DocumentArrayMethods[prop];
  76. }
  77. if (ArrayMethods.hasOwnProperty(prop)) {
  78. return ArrayMethods[prop];
  79. }
  80. return __array[prop];
  81. },
  82. set: function(target, prop, value) {
  83. if (typeof prop === 'string' && numberRE.test(prop)) {
  84. DocumentArrayMethods.set.call(proxy, prop, value, false);
  85. } else if (internals.hasOwnProperty(prop)) {
  86. internals[prop] = value;
  87. } else {
  88. __array[prop] = value;
  89. }
  90. return true;
  91. }
  92. });
  93. return proxy;
  94. }
  95. /*!
  96. * Module exports.
  97. */
  98. module.exports = MongooseDocumentArray;