loopback-datasource-juggler/lib/include.js

155 lines
4.2 KiB
JavaScript
Raw Normal View History

2014-03-13 23:43:38 +00:00
var async = require('async');
var utils = require('./utils');
var isPlainObject = utils.isPlainObject;
var defineCachedRelations = utils.defineCachedRelations;
2014-03-12 23:28:46 +00:00
/*!
2013-04-11 23:23:34 +00:00
* Include mixin for ./model.js
*/
2013-05-28 05:20:30 +00:00
module.exports = Inclusion;
2014-03-12 23:28:46 +00:00
/**
* Inclusion - Model mixin.
*
* @class
*/
2013-05-28 05:20:30 +00:00
function Inclusion() {
}
2013-04-11 23:23:34 +00:00
/**
2014-03-12 23:28:46 +00:00
* Enables you to load relations of several objects and optimize numbers of requests.
2013-04-11 23:23:34 +00:00
*
* Examples:
*
2014-03-12 23:28:46 +00:00
* Load all users' posts with only one additional request:
* `User.include(users, 'posts', function() {});`
* Or
* `User.include(users, ['posts'], function() {});`
*
* Load all users posts and passports with two additional requests:
* `User.include(users, ['posts', 'passports'], function() {});`
*
* Load all passports owner (users), and all posts of each owner loaded:
*```Passport.include(passports, {owner: 'posts'}, function() {});
*``` Passport.include(passports, {owner: ['posts', 'passports']});
*``` Passport.include(passports, {owner: [{posts: 'images'}, 'passports']});
2013-04-11 23:23:34 +00:00
*
2014-03-12 23:28:46 +00:00
* @param {Array} objects Array of instances
2014-06-18 23:42:00 +00:00
* @param {String|Object|Array} include Which relations to load.
2014-03-12 23:28:46 +00:00
* @param {Function} cb Callback called when relations are loaded
*
2013-04-11 23:23:34 +00:00
*/
2013-05-28 05:20:30 +00:00
Inclusion.include = function (objects, include, cb) {
2014-01-24 17:09:53 +00:00
var self = this;
2014-03-13 23:43:38 +00:00
if (!include || (Array.isArray(include) && include.length === 0) ||
(isPlainObject(include) && Object.keys(include).length === 0)) {
// The objects are empty
return process.nextTick(function() {
cb && cb(null, objects);
});
2014-01-24 17:09:53 +00:00
}
2014-03-13 23:43:38 +00:00
include = normalizeInclude(include);
async.each(include, function(item, callback) {
processIncludeItem(objects, item, callback);
}, function(err) {
cb && cb(err, objects);
});
/*!
* Normalize the include to be an array
* @param include
* @returns {*}
*/
function normalizeInclude(include) {
if (typeof include === 'string') {
return [include];
} else if (isPlainObject(include)) {
// Build an array of key/value pairs
var newInclude = [];
for (var key in include) {
2014-01-24 17:09:53 +00:00
var obj = {};
2014-03-13 23:43:38 +00:00
obj[key] = include[key];
newInclude.push(obj);
2014-01-24 17:09:53 +00:00
}
2014-03-13 23:43:38 +00:00
return newInclude;
} else {
return include;
2013-04-11 23:23:34 +00:00
}
2014-01-24 17:09:53 +00:00
}
2014-03-13 23:43:38 +00:00
function processIncludeItem(objs, include, cb) {
2014-01-24 17:09:53 +00:00
var relations = self.relations;
var relationName, subInclude;
if (isPlainObject(include)) {
relationName = Object.keys(include)[0];
subInclude = include[relationName];
2014-01-24 17:09:53 +00:00
} else {
relationName = include;
2014-03-13 23:43:38 +00:00
subInclude = null;
2014-01-24 17:09:53 +00:00
}
var relation = relations[relationName];
2014-07-29 11:57:49 +00:00
2014-01-24 17:09:53 +00:00
if (!relation) {
cb(new Error('Relation "' + relationName + '" is not defined for '
+ self.modelName + ' model'));
return;
2013-04-11 23:23:34 +00:00
}
// Just skip if inclusion is disabled
if (relation.options.disableInclude) {
cb();
return;
}
2014-07-29 11:57:49 +00:00
2014-03-13 23:43:38 +00:00
// Calling the relation method for each object
async.each(objs, function (obj, callback) {
if(relation.type === 'belongsTo') {
// If the belongsTo relation doesn't have an owner
if(obj[relation.keyFrom] === null || obj[relation.keyFrom] === undefined) {
defineCachedRelations(obj);
// Set to null if the owner doesn't exist
obj.__cachedRelations[relationName] = null;
if(obj === inst) {
obj.__data[relationName] = null;
} else {
obj[relationName] = null;
}
2014-03-13 23:43:38 +00:00
return callback();
2013-04-11 23:23:34 +00:00
}
2014-01-24 17:09:53 +00:00
}
2014-03-13 23:43:38 +00:00
var inst = (obj instanceof self) ? obj : new self(obj);
// Calling the relation method on the instance
inst[relationName](function (err, result) {
2014-03-13 23:43:38 +00:00
if (err) {
return callback(err);
} else {
defineCachedRelations(obj);
obj.__cachedRelations[relationName] = result;
if(obj === inst) {
obj.__data[relationName] = result;
2014-07-29 11:57:49 +00:00
obj.__strict = false;
} else {
obj[relationName] = result;
}
2014-07-29 11:57:49 +00:00
2014-03-13 23:43:38 +00:00
if (subInclude && result) {
var subItems = relation.multiple ? result : [result];
// Recursively include the related models
relation.modelTo.include(subItems, subInclude, callback);
} else {
callback(null, result);
2014-01-24 17:09:53 +00:00
}
2014-03-13 23:43:38 +00:00
}
});
}, cb);
2014-01-24 17:09:53 +00:00
}
};
2013-04-11 23:23:34 +00:00