wbyoung/maguey

View on GitHub
lib/query/mixins/join.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';

var _ = require('lodash');
var Mixin = require('corazon/mixin');
var Condition = require('../../condition');
var w = Condition.w;

/**
 * Check if an argument is a join type.
 *
 * @function Join~isType
 * @private
 * @param {?} arg The argument to check.
 * @return {Boolean}
 */
var isType = function(str) {
  var types = ['inner', 'left', 'right', 'full'];
  return types.indexOf(str) !== -1;
};


/**
 * Join support for queries.
 *
 * @mixin Join
 */
module.exports = Mixin.create(/** @lends Join# */ {

  /**
   * Mixin initialization.
   */
  init: function() {
    this._super();
    this._joins = {};
  },

  /**
   * Duplication implementation.
   *
   * @method
   * @protected
   * @see {@link BaseQuery#_take}
   */
  _take: function(orig) {
    this._super(orig);
    this._joins = _.clone(orig._joins);
  },

  /**
   * Specify a join for a query.
   *
   *     select('cities', ['cities.name', 'countries.name'])
   *       .join('countries', 'cities.country_id=countries.id')
   *     // -> select cities.name, countries.name from cities
   *     // -> inner join countries on cities.country_id = countries.id
   *
   * You can also specify a condition with object syntax to match a field
   * by using the {@link Condition.f} helper. Where clauses can be added as
   * well:
   *
   *     select('cities')
   *       .join('countries', 'left', {
   *         'cities.country_id': f('countries.id')
   *        })
   *       .where({ 'cities.name$icontains': 'city' })
   *     // -> select * from cities
   *     // -> left join countries on cities.country_id = countries.id
   *     // -> where cities.name ilike ?
   *     // !> ['%city%']
   *
   * To make a cross join, you can specify no condition an an inner join will
   * be performed with a `true` condition:
   *
   *     select('cities')
   *       .join('countries')
   *     // -> select * from cities
   *     // -> inner join countries on true
   *
   * @param {String|Object} table The table on which to join. If provided as an
   * object, the key is the table name, and the value is the alias name.
   * @param {String} [type] The join type. This can be one of:
   *   - `'inner'` An inner join (the default)
   *   - `'left'` A left outer join
   *   - `'right'` A right outer join
   *   - `'full'` A full outer join
   * @param {...(Condition|Object|String)} conditions The conditions on which to
   * join. If provided as an object or string, it will be converted to a proper
   * {@link Condition}.
   * @see {@link goo.gl/3Cdu1t|A Visual Explanation of SQL Joins}
   * @return {ChainedQuery} The newly configured query.
   */
  join: function(/*table, [type], ...[condition]*/) {
    var dup = this._dup();
    var args = _.toArray(arguments);

    var alias;
    var table = args.shift();
    var key = table;
    var type = isType(args[0]) ? args.shift() : 'inner';
    var condition = args.length > 0 ? w.apply(null, args) : null;

    if (!_.isString(table)) {
      alias = _.values(table)[0];
      table = _.keys(table)[0];
      key = alias;
    }

    dup._joins[key] = {
      table: table,
      alias: alias,
      type: type,
      condition: condition,
    };

    return dup;
  },
});