all files / lib/waterline/query/integrator/ _join.js

100% Statements 25/25
95.83% Branches 23/24
100% Functions 3/3
100% Lines 24/24
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                                                        104× 104×         104×               102×     102×     102×     102×       102×     102×     94×       163×   475×                           475× 185× 185×   290×         163× 22×     163×       94×      
/**
 * Module dependencies
 */
var anchor = require('anchor');
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 || anchor(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 || anchor(options.parent).to({
    type: 'array'
  });
  invalid = invalid || anchor(options.child).to({
    type: 'array'
  });
  invalid = invalid || anchor(options.parentKey).to({
    type: 'string'
  });
  invalid = invalid || anchor(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
      });
 
      // console.log('PARENT ROW: ', parentRow);
      // console.log('CHILD ROW: ', childRow);
      // console.log('JOIN ROW: ', newRow);
 
      // 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;
  }, []);
 
  // console.log('JOIN RESULT SET::', resultSet);
  return resultSet;
 
};