UNPKG

mongoose-lean-getters

Version:

Apply getters to the results of mongoose queries when using `.lean()`

144 lines (128 loc) 4.29 kB
'use strict'; const mpath = require('mpath'); module.exports = function mongooseLeanGetters(schema) { const fn = applyGettersMiddleware(schema); // Use `pre('find')` so this also works with `cursor()` // and `eachAsync()`, because those do not call `post('find')` schema.pre('find', function() { if (typeof this.map === 'function') { this.map((res) => { fn.call(this, res); return res; }); } else if (typeof this.transform === 'function') { this.transform((res) => { fn.call(this, res); return res; }); } else { this.options.transform = (res) => { fn.call(this, res); return res; }; } }); schema.post('findOne', fn); schema.post('findOneAndUpdate', fn); schema.post('findOneAndDelete', fn); schema.post('findOneAndReplace', fn); }; function applyGettersMiddleware(schema) { return function(res) { applyGetters.call(this, schema, res); }; } function applyGetters(schema, res, path) { if (res == null) { return; } if (this._mongooseOptions.lean && this._mongooseOptions.lean.getters) { if (Array.isArray(res)) { const len = res.length; for (let i = 0; i < len; ++i) { applyGettersToDoc.call(this, schema, res[i], this._fields, path); } } else { applyGettersToDoc.call(this, schema, res, this._fields, path); } for (let i = 0; i < schema.childSchemas.length; ++i) { const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path; const _schema = schema.childSchemas[i].schema; const doc = mpath.get(schema.childSchemas[i].model.path, res); if (doc == null) { continue; } applyGetters.call(this, _schema, doc, childPath); } return res; } else { return res; } } function getSchemaForDoc(schema, res) { if (!schema.discriminatorMapping || !schema.discriminatorMapping.key || !schema.discriminators) { return schema; } const discriminatorValue = res[schema.discriminatorMapping.key]; let childSchema = undefined; for (const name of Object.keys(schema.discriminators)) { const matchValue = schema.discriminators[name].discriminatorMapping.value; if (matchValue === discriminatorValue) { childSchema = schema.discriminators[name]; break; } } return childSchema; } function applyGettersToDoc(schema, doc, fields, prefix) { if (doc == null) { return; } if (Array.isArray(doc)) { for (let i = 0; i < doc.length; ++i) { const currentDoc = doc[i]; // If the current doc is null/undefined, there's nothing to do if (currentDoc == null) continue; // If it is a nested array, apply getters to each subdocument (otherwise it would attempt to apply getters to the array itself) if (Array.isArray(currentDoc)) { applyGettersToDoc.call(this, schema, currentDoc, fields, prefix); continue; } const schemaForDoc = getSchemaForDoc(schema, currentDoc); applyGettersToDoc.call(this, schemaForDoc, currentDoc, fields, prefix); } return; } const schemaForDoc = getSchemaForDoc(schema, doc); schemaForDoc.eachPath((path, schematype) => { const pathWithPrefix = prefix ? prefix + '.' + path : path; if (this.selectedInclusively() && fields && fields[pathWithPrefix] == null && !this.isPathSelectedInclusive(pathWithPrefix)) { // fields[pathWithPrefix] should return false return; } if (this.selectedExclusively() && fields && fields[pathWithPrefix] != null && !this.isPathSelectedInclusive(pathWithPrefix)) { return; } const pathExists = mpath.has(path, doc); if (pathExists) { if (schematype.$isMongooseArray && !schematype.$isMongooseDocumentArray) { mpath.set( path, schematype.applyGetters(mpath.get(path, doc), doc, true).map(subdoc => { return schematype.caster.applyGetters(subdoc, doc); }), doc ); } else { mpath.set(path, schematype.applyGetters(mpath.get(path, doc), doc, true), doc); } } }); } module.exports.default = module.exports; module.exports.mongooseLeanGetters = module.exports;