UNPKG

mongoose-advanced-populate

Version:

Advanced populate logic for Mongoose with nested and flexible path support.

79 lines (67 loc) 1.67 kB
const mongoose = require('mongoose'); function setNested(doc, path, value) { const keys = path.split('.'); let current = doc; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (i === keys.length - 1) { current[key] = value; } else { current[key] = current[key] || {}; current = current[key]; } } } const advancedPopulate = async ({ model, match = {}, populates = [] }) => { const pipeline = [{ $match: match }]; const unwindPaths = new Set(populates.map(p => p.as.split('.')[0])); for (const path of unwindPaths) { pipeline.push({ $unwind: { path: `$${path}`, preserveNullAndEmptyArrays: true, }, }); } for (const populate of populates) { const { from, localField, foreignField, as, pipeline: subPipeline = [], isArray = true, } = populate; const tempField = as.replace(/\./g, '__'); pipeline.push({ $lookup: { from, localField, foreignField, as: tempField, pipeline: subPipeline, }, }); if (!isArray) { pipeline.push({ $unwind: { path: `$${tempField}`, preserveNullAndEmptyArrays: true, }, }); } } const flatResults = await model.aggregate(pipeline); return flatResults.map(doc => { const reconstructed = {}; for (const key in doc) { if (key.includes('__')) { setNested(reconstructed, key.replace(/__/g, '.'), doc[key]); } else { reconstructed[key] = doc[key]; } } return reconstructed; }); }; module.exports = advancedPopulate;