mongoose-reverse-populate
Version:
This module allows you to 'populate' a mongoose model where the relationship ids are stored on another mongoose model that is related to this model
107 lines (84 loc) • 3.09 kB
JavaScript
var _ = require("lodash");
var REQUIRED_FIELDS = ["modelArray", "storeWhere", "arrayPop", "mongooseModel", "idField"];
function reversePopulate(opts, cb) {
// Check required fields have been provided
try { checkRequired(REQUIRED_FIELDS, opts); }
catch (ex) { return cb(ex); }
// If empty array passed, exit!
if (!opts.modelArray.length) return cb(null, opts.modelArray);
// Transform the model array for easy lookups
var modelIndex = _.indexBy(opts.modelArray, "_id");
var popResult = populateResult.bind(this, opts.storeWhere, opts.arrayPop);
var query = buildQuery(opts);
// Do the query
query.exec(function(err, results) {
// If there is an error, callback with error
if (err) return cb(err);
// Map over results (models to be populated)
results.forEach(function(result) {
// Check if the ID field is an array
var isArray = !isNaN(result[opts.idField].length);
// If the idField is an array, map through this
if (isArray) {
result[opts.idField].map(function(resultId) {
var match = modelIndex[resultId];
// If match found, populate the result inside the match
if (match) popResult(match, result);
});
// Id field is not an array
} else {
// So just add the result to the model
var matchId = result[opts.idField];
var match = modelIndex[matchId];
// If match found, populate the result inside the match
if (match) popResult(match, result);
}
});
// Callback with passed modelArray
cb(null, opts.modelArray);
});
}
module.exports = reversePopulate;
// Check all mandatory fields have been provided
function checkRequired(required, opts) {
var msg = "Missing mandatory field ";
required.forEach(function(fieldName) {
if (opts[fieldName] == null) throw new Error(msg + fieldName);
});
}
// Build the query string with user provided options
function buildQuery(opts) {
var conditions = opts.filters || {};
var ids = _.pluck(opts.modelArray, "_id");
conditions[opts.idField] = { $in: ids };
var query = opts.mongooseModel.find(conditions);
// Set query select() parameter
if (opts.select) {
var select = getSelectString(opts.select, opts.idField);
query = query.select(select);
}
if (opts.populate) query = query.populate(opts.populate);
if (opts.sort) query = query.sort(opts.sort);
return query;
}
// Ensure the select option always includes the required id field to populate the relationship
function getSelectString(selectStr, requiredId) {
var selected = selectStr.split(" ");
var idIncluded = !!~selected.indexOf(requiredId);
if (!idIncluded) return selectStr + " " + requiredId;
return selectStr;
}
// Populate the result against the match
function populateResult(storeWhere, arrayPop, match, result) {
// If this is a one to many relationship
if (arrayPop) {
// Check if array exists, if not create one
if (typeof match[storeWhere] === "undefined") match[storeWhere] = [];
// Push the result into the array
match[storeWhere].push(result);
// This is a one to one relationship
} else {
// Save the results
match[storeWhere] = result;
}
}