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

100% Statements 37/37
100% Branches 8/8
100% Functions 7/7
100% Lines 36/36
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 107 108 109 110                                                62× 62× 62×   62× 62× 62× 62×   62×   62×     91×             91×     91×         135×       131× 131×       131× 830×         131× 1296× 437×   859×       131×     131× 131× 131× 570× 570×       131× 131×     91×                     91× 91× 91×    
/**
 * Module dependencies
 */
var _ = require('lodash');
 
 
/**
 * populate()
 *
 * Destructive mapping of `parentRows` to include a new key, `alias`,
 * which is an ordered array of child rows.
 *
 * @option [{Object}] parentRows    - the parent rows the joined rows will be folded into
 * @option {String} alias           - the alias of the association
 * @option [{Object}] childRows     - the unfolded result set from the joins
 *
 * @option {String} parentPK        - the primary key of the parent table (optional- only needed for M..N associations)
 * @option {String} fkToChild       - the foreign key associating a row with the child table
 * @option {String} childPK         - the primary key of the child table
 *
 * @option [{String}] childNamespace- attributes to keep
 *
 * @return {*Object} reference to `parentRows`
 */
module.exports = function populate(options) {
 
  var parentRows = options.parentRows;
  var alias = options.alias;
  var childRows = options.childRows;
 
  var parentPK = options.parentPK;
  var childPK = options.childPK;
  var fkToChild = options.fkToChild;
  var fkToParent = parentPK;// At least for all use cases currently, `fkToParent` <=> `parentPK`
 
  var childNamespace = options.childNamespace || '';
 
  return _.map(parentRows, function _insertJoinedResults(parentRow) {
 
    // Gather the subset of child rows associated with the current parent row
    var associatedChildRows = _.where(childRows,
      // { (parentPK): (parentRow[(parentPK)]) }, e.g. { id: 3 }
      _cons(fkToParent, parentRow[parentPK])
    );
 
    // Clone the `associatedChildRows` to avoid mutating the original
    // `childRows` in the cache.
    associatedChildRows = _.cloneDeep(associatedChildRows);
 
    // Stuff the sanitized associated child rows into the parent row.
    parentRow[alias] =
    _.reduce(associatedChildRows, function(memo, childRow) {
 
      // Ignore child rows without an appropriate foreign key
      // to an instance in the REAL child collection.
      if (!childRow[childNamespace + childPK] && !childRow[childPK]) return memo;
 
      // Rename childRow's [fkToChild] key to [childPK]
      // (so that it will have the proper primary key attribute for its collection)
      var childPKValue = childRow[fkToChild];
      childRow[childPK] = childPKValue;
 
      // Determine if we have any double nested attributes.
      // These would come from m:m joins
      var doubleNested = _.find(childRow, function(name, key) {
        return _.startsWith(key, '..');
      });
 
      // Grab all the keys that start with a dot or double dot depending on
      // the status of doubleNested
      childRow = _.pick(childRow, function(name, key) {
        if (doubleNested) {
          return _.startsWith(key, '..');
        } else {
          return _.startsWith(key, '.');
        }
      });
 
      var _origChildRow = childRow;
 
      // Strip off childNamespace prefix
      childRow = {};
      var PREFIX_REGEXP = new RegExp('^' + childNamespace + '');
      _.each(_origChildRow, function(attrValue, attrName) {
        var unprefixedKey = attrName.replace(PREFIX_REGEXP, '');
        childRow[unprefixedKey] = attrValue;
      });
 
      // Build the set of rows to stuff into our parent row.
      memo.push(childRow);
      return memo;
    }, []);
 
    return parentRow;
  });
};
 
 
/**
 * Dumb little helper because I hate naming anonymous objects just to use them once.
 *
 * @return {Object} [a tuple]
 * @api private
 */
function _cons(key, value) {
  var obj = {};
  obj[key] = value;
  return obj;
}