offshore
Version:
An ORM for Node.js
101 lines (83 loc) • 2.71 kB
JavaScript
/**
* Module dependencies
*/
var validator = require('offshore-validator');
var _ = require('lodash');
var partialJoin = require('./_partialJoin');
/**
* _join
*
* @api private
*
* Helper method- can perform and inner -OR- outer join.
*
* @option {String|Boolean} outer [whether to do an outer join, and if so the direction ("left"|"right")]
* @option {Array} parent [rows from the "lefthand table"]
* @option {Array} child [rows from the "righthand table"]
* @option {String} parentKey [primary key of the "lefthand table"]
* @option {String} childKey [foreign key from the "righthand table" to the "lefthand table"]
* @option {String} childNamespace [string prepended to child attribute keys (default='.')]
*
* @return {Array} new joined row data
*
* @throws {Error} on invalid input
*
* @synchronous
*/
module.exports = function _join(options) {
// Usage
var invalid = false;
invalid = invalid || validator(options).to({
type: 'object'
});
// Tolerate `right` and `left` usage
_.defaults(options, {
parent: options.left,
child: options.right,
parentKey: options.leftKey,
childKey: options.rightKey,
childNamespace: options.childNamespace || '.'
});
invalid = invalid || validator(options.parent).to({
type: 'array'
});
invalid = invalid || validator(options.child).to({
type: 'array'
});
invalid = invalid || validator(options.parentKey).to({
type: 'string'
});
invalid = invalid || validator(options.childKey).to({
type: 'string'
});
invalid = invalid || (options.outer === 'right' ?
new Error('Right joins not supported yet.') : false);
if (invalid) throw invalid;
var resultSet = _.reduce(options.parent, function eachParentRow(memo, parentRow) {
// For each childRow whose childKey matches
// this parentRow's parentKey...
var foundMatch = _.reduce(options.child, function eachChildRow(hasFoundMatchYet, childRow) {
var newRow = partialJoin({
parentRow: parentRow,
childRow: childRow,
parentKey: options.parentKey,
childKey: options.childKey,
childNamespace: options.childNamespace
});
// Save the new row for the join result if it exists
// and mark the match as found
if (newRow) {
memo.push(newRow);
return true;
}
return hasFoundMatchYet;
}, false);
// If this is a left outer join and we didn't find a match
// for this parentRow, add it to the result set anyways
if (!foundMatch && options.outer === 'left') {
memo.push(_.cloneDeep(parentRow));
}
return memo;
}, []);
return resultSet;
};