ArraySubdocument.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*!
  2. * Module dependencies.
  3. */
  4. 'use strict';
  5. const EventEmitter = require('events').EventEmitter;
  6. const Subdocument = require('./subdocument');
  7. const utils = require('../utils');
  8. const documentArrayParent = require('../helpers/symbols').documentArrayParent;
  9. /*!
  10. * A constructor.
  11. *
  12. * @param {Object} obj js object returned from the db
  13. * @param {MongooseDocumentArray} parentArr the parent array of this document
  14. * @param {Boolean} skipId
  15. * @inherits Document
  16. * @api private
  17. */
  18. function ArraySubdocument(obj, parentArr, skipId, fields, index) {
  19. if (utils.isMongooseDocumentArray(parentArr)) {
  20. this.__parentArray = parentArr;
  21. this[documentArrayParent] = parentArr.$parent();
  22. } else {
  23. this.__parentArray = undefined;
  24. this[documentArrayParent] = undefined;
  25. }
  26. this.$setIndex(index);
  27. this.$__parent = this[documentArrayParent];
  28. Subdocument.call(this, obj, fields, this[documentArrayParent], skipId, { isNew: true });
  29. }
  30. /*!
  31. * Inherit from Subdocument
  32. */
  33. ArraySubdocument.prototype = Object.create(Subdocument.prototype);
  34. ArraySubdocument.prototype.constructor = ArraySubdocument;
  35. Object.defineProperty(ArraySubdocument.prototype, '$isSingleNested', {
  36. configurable: false,
  37. writable: false,
  38. value: false
  39. });
  40. Object.defineProperty(ArraySubdocument.prototype, '$isDocumentArrayElement', {
  41. configurable: false,
  42. writable: false,
  43. value: true
  44. });
  45. for (const i in EventEmitter.prototype) {
  46. ArraySubdocument[i] = EventEmitter.prototype[i];
  47. }
  48. /*!
  49. * ignore
  50. */
  51. ArraySubdocument.prototype.$setIndex = function(index) {
  52. this.__index = index;
  53. if (this.$__ != null && this.$__.validationError != null) {
  54. const keys = Object.keys(this.$__.validationError.errors);
  55. for (const key of keys) {
  56. this.invalidate(key, this.$__.validationError.errors[key]);
  57. }
  58. }
  59. };
  60. /*!
  61. * ignore
  62. */
  63. ArraySubdocument.prototype.populate = function() {
  64. throw new Error('Mongoose does not support calling populate() on nested ' +
  65. 'docs. Instead of `doc.arr[0].populate("path")`, use ' +
  66. '`doc.populate("arr.0.path")`');
  67. };
  68. /*!
  69. * ignore
  70. */
  71. ArraySubdocument.prototype.$__removeFromParent = function() {
  72. const _id = this._doc._id;
  73. if (!_id) {
  74. throw new Error('For your own good, Mongoose does not know ' +
  75. 'how to remove an ArraySubdocument that has no _id');
  76. }
  77. this.__parentArray.pull({ _id: _id });
  78. };
  79. /**
  80. * Returns the full path to this document. If optional `path` is passed, it is appended to the full path.
  81. *
  82. * @param {String} [path]
  83. * @param {Boolean} [skipIndex] Skip adding the array index. For example `arr.foo` instead of `arr.0.foo`.
  84. * @return {String}
  85. * @api private
  86. * @method $__fullPath
  87. * @memberOf ArraySubdocument
  88. * @instance
  89. */
  90. ArraySubdocument.prototype.$__fullPath = function(path, skipIndex) {
  91. if (this.__index == null) {
  92. return null;
  93. }
  94. if (!this.$__.fullPath) {
  95. this.ownerDocument();
  96. }
  97. if (skipIndex) {
  98. return path ?
  99. this.$__.fullPath + '.' + path :
  100. this.$__.fullPath;
  101. }
  102. return path ?
  103. this.$__.fullPath + '.' + this.__index + '.' + path :
  104. this.$__.fullPath + '.' + this.__index;
  105. };
  106. /*!
  107. * Given a path relative to this document, return the path relative
  108. * to the top-level document.
  109. */
  110. ArraySubdocument.prototype.$__pathRelativeToParent = function(path, skipIndex) {
  111. if (this.__index == null) {
  112. return null;
  113. }
  114. if (skipIndex) {
  115. return path == null ? this.__parentArray.$path() : this.__parentArray.$path() + '.' + path;
  116. }
  117. if (path == null) {
  118. return this.__parentArray.$path() + '.' + this.__index;
  119. }
  120. return this.__parentArray.$path() + '.' + this.__index + '.' + path;
  121. };
  122. /*!
  123. * Returns this sub-documents parent document.
  124. */
  125. ArraySubdocument.prototype.$parent = function() {
  126. return this[documentArrayParent];
  127. };
  128. /**
  129. * Returns this subdocument's parent array.
  130. *
  131. * #### Example:
  132. *
  133. * const Test = mongoose.model('Test', new Schema({
  134. * docArr: [{ name: String }]
  135. * }));
  136. * const doc = new Test({ docArr: [{ name: 'test subdoc' }] });
  137. *
  138. * doc.docArr[0].parentArray() === doc.docArr; // true
  139. *
  140. * @api public
  141. * @method parentArray
  142. * @returns DocumentArray
  143. */
  144. ArraySubdocument.prototype.parentArray = function() {
  145. return this.__parentArray;
  146. };
  147. /*!
  148. * Module exports.
  149. */
  150. module.exports = ArraySubdocument;