diff --git a/lib/helpers/document/applyVirtuals.js b/lib/helpers/document/applyVirtuals.js index e3869440b9..43cb0a047c 100644 --- a/lib/helpers/document/applyVirtuals.js +++ b/lib/helpers/document/applyVirtuals.js @@ -4,7 +4,16 @@ const mpath = require('mpath'); module.exports = applyVirtuals; -function applyVirtuals(schema, doc, virtuals, parent) { +/** + * Apply a given schema's virtuals to a given POJO + * + * @param {Schema} schema + * @param {Object} doc + * @param {Array} [virtuals] optional whitelist of virtuals to apply + * @returns + */ + +function applyVirtuals(schema, doc, virtuals) { if (doc == null) { return doc; } @@ -15,9 +24,7 @@ function applyVirtuals(schema, doc, virtuals, parent) { if (Array.isArray(virtuals)) { virtualsForChildren = []; toApply = []; - const len = virtuals.length; - for (let i = 0; i < len; ++i) { - const virtual = virtuals[i]; + for (const virtual of virtuals) { if (virtual.length === 1) { toApply.push(virtual[0]); } else { @@ -26,28 +33,23 @@ function applyVirtuals(schema, doc, virtuals, parent) { } } - applyVirtualsToChildren(this, schema, doc, virtualsForChildren, parent); - return applyVirtualsToDocs(schema, doc, toApply); + applyVirtualsToChildren(schema, doc, virtualsForChildren); + return applyVirtualsToDoc(schema, doc, toApply); } -function applyVirtualsToDocs(schema, res, toApply) { - if (Array.isArray(res)) { - const len = res.length; - for (let i = 0; i < len; ++i) { - applyVirtualsToDoc(schema, res[i], toApply); - } - return res; - } else { - return applyVirtualsToDoc(schema, res, toApply); - } -} +/** + * Apply virtuals to any subdocuments + * + * @param {Schema} schema subdocument schema + * @param {Object} res subdocument + * @param {Array} [virtuals] optional whitelist of virtuals to apply + */ -function applyVirtualsToChildren(doc, schema, res, virtuals, parent) { - const len = schema.childSchemas.length; +function applyVirtualsToChildren(schema, res, virtuals) { let attachedVirtuals = false; - for (let i = 0; i < len; ++i) { - const _path = schema.childSchemas[i].model.path; - const _schema = schema.childSchemas[i].schema; + for (const childSchema of schema.childSchemas) { + const _path = childSchema.model.path; + const _schema = childSchema.schema; if (!_path) { continue; } @@ -59,9 +61,7 @@ function applyVirtualsToChildren(doc, schema, res, virtuals, parent) { let virtualsForChild = null; if (Array.isArray(virtuals)) { virtualsForChild = []; - const len = virtuals.length; - for (let i = 0; i < len; ++i) { - const virtual = virtuals[i]; + for (const virtual of virtuals) { if (virtual[0] == _path) { virtualsForChild.push(virtual.slice(1)); } @@ -72,22 +72,31 @@ function applyVirtualsToChildren(doc, schema, res, virtuals, parent) { } } - applyVirtuals.call(doc, _schema, _doc, virtualsForChild, res); + applyVirtuals(_schema, _doc, virtualsForChild); attachedVirtuals = true; } if (virtuals && virtuals.length && !attachedVirtuals) { - applyVirtualsToDoc(schema, res, virtuals, parent); + applyVirtualsToDoc(schema, res, virtuals); } } +/** + * Apply virtuals to a given document. Does not apply virtuals to subdocuments: use `applyVirtualsToChildren` instead + * + * @param {Schema} schema + * @param {Object} doc + * @param {Array} [virtuals] optional whitelist of virtuals to apply + * @returns + */ + function applyVirtualsToDoc(schema, doc, virtuals) { if (doc == null || typeof doc !== 'object') { return; } if (Array.isArray(doc)) { - for (let i = 0; i < doc.length; ++i) { - applyVirtualsToDoc(schema, doc[i], virtuals); + for (const el of doc) { + applyVirtualsToDoc(schema, el, virtuals); } return; } @@ -107,25 +116,25 @@ function applyVirtualsToDoc(schema, doc, virtuals) { if (virtuals == null) { virtuals = Object.keys(schema.virtuals); } - const numVirtuals = virtuals.length; - for (let i = 0; i < numVirtuals; ++i) { - const virtual = virtuals[i]; + for (const virtual of virtuals) { if (schema.virtuals[virtual] == null) { continue; } const virtualType = schema.virtuals[virtual]; const sp = Array.isArray(virtual) - ? virtual : - virtual.indexOf('.') === -1 + ? virtual + : virtual.indexOf('.') === -1 ? [virtual] : virtual.split('.'); let cur = doc; - for (let j = 0; j < sp.length - 1; ++j) { - cur[sp[j]] = sp[j] in cur ? cur[sp[j]] : {}; - cur = cur[sp[j]]; + for (let i = 0; i < sp.length - 1; ++i) { + cur[sp[i]] = sp[i] in cur ? cur[sp[i]] : {}; + cur = cur[sp[i]]; } let val = virtualType.applyGetters(cur[sp[sp.length - 1]], doc); - if (isPopulateVirtual(virtualType) && val === undefined) { + const isPopulateVirtual = + virtualType.options && (virtualType.options.ref || virtualType.options.refPath); + if (isPopulateVirtual && val === undefined) { if (virtualType.options.justOne) { val = null; } else { @@ -135,7 +144,3 @@ function applyVirtualsToDoc(schema, doc, virtuals) { cur[sp[sp.length - 1]] = val; } } - -function isPopulateVirtual(virtualType) { - return virtualType.options && (virtualType.options.ref || virtualType.options.refPath); -} diff --git a/lib/model.js b/lib/model.js index 8978718d5b..1c361bcb49 100644 --- a/lib/model.js +++ b/lib/model.js @@ -3521,24 +3521,24 @@ Model.applyDefaults = function applyDefaults(doc) { * obj.name; // 'John' * obj.upper; // 'JOHN', Mongoose applied the return value of the virtual to the given object * - * @param {Object} doc object or document to apply virtuals on + * @param {Object} obj object or document to apply virtuals on * @param {Array} [virtualsToApply] optional whitelist of virtuals to apply - * @returns {Object} doc + * @returns {Object} obj * @api public */ -Model.applyVirtuals = function applyVirtuals(doc, virtualsToApply) { - if (doc == null) { - return doc; +Model.applyVirtuals = function applyVirtuals(obj, virtualsToApply) { + if (obj == null) { + return obj; } // Nothing to do if this is already a hydrated document - it should already have virtuals - if (doc.$__ != null) { - return doc; + if (obj.$__ != null) { + return obj; } - applyVirtualsHelper(this.schema, doc, virtualsToApply, null); + applyVirtualsHelper(this.schema, obj, virtualsToApply); - return doc; + return obj; }; /**