vitamin
Version:
Data Mapper library for Node.js applications
641 lines (511 loc) • 16.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _model = require('./model');
var _model2 = _interopRequireDefault(_model);
var _mapper = require('./mapper');
var _mapper2 = _interopRequireDefault(_mapper);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _modelNotFound = require('./errors/model-not-found');
var _modelNotFound2 = _interopRequireDefault(_modelNotFound);
var _underscore = require('underscore');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* @class Query
*/
var Query = function () {
/**
* Query class constructor
*
* @param {QueryBuilder} qb knex query builder
* @constructor
*/
function Query(qb) {
_classCallCheck(this, Query);
this.columns = [];
this.table = null;
this.alias = null;
this.rels = {};
this.__qb = qb;
}
/**
* Get the base query builder
*
* @var {QueryBuilder}
*/
_createClass(Query, [{
key: 'toBase',
/**
* Get the base query builder
*
* @return query builder
* @deprecated
*/
value: function toBase() {
// TODO display a warning message
return this.builder;
}
/**
* Get the selected columns prefixed by the table name
*
* @return array
*/
}, {
key: 'getQualifiedColumns',
value: function getQualifiedColumns() {
var _this = this;
return ((0, _underscore.isEmpty)(this.columns) ? ['*'] : this.columns).map(function (name) {
return (0, _underscore.isString)(name) && name.indexOf('.') === -1 ? _this.getQualifiedColumn(name) : name;
});
}
/**
* Set the table name for this query
*
* @param {String} table
* @param {String} alias
* @return this query
* @deprecated
*/
}, {
key: 'from',
value: function from(table, alias) {
return this.setTable(table, alias);
}
/**
* Set the table name for this query
*
* @param {String} name
* @param {String} alias
* @return this query
*/
}, {
key: 'setTable',
value: function setTable(name) {
var alias = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
if (!(0, _underscore.isString)(name)) throw new TypeError("Invalid table name");
this.table = name;
if ((0, _underscore.isString)(alias) && !(0, _underscore.isEmpty)(alias)) {
name += ' as ' + alias;
this.alias = alias;
}
this.builder.from(name);
return this;
}
/**
* Get the column name prefixed by the table name
*
* @param {String} name
* @return string
* @deprecated
*/
}, {
key: 'getQualifiedColumn',
value: function getQualifiedColumn(name) {
return this.qualifyColumn(name);
}
/**
* Get the column name prefixed by the table name
*
* @param {String} name
* @return string
*/
}, {
key: 'qualifyColumn',
value: function qualifyColumn(name) {
return (this.alias || this.table) + '.' + name;
}
/**
* Set the columns of the query
*
* @param {Array} columns
* @return this query
*/
}, {
key: 'select',
value: function select(columns) {
var _columns;
if (!(0, _underscore.isArray)(columns)) columns = (0, _underscore.toArray)(arguments);
if (!(0, _underscore.isEmpty)(columns)) (_columns = this.columns).push.apply(_columns, _toConsumableArray(columns));
return this;
}
/**
* Set the model being queried
*
* @param {Mapper} mapper
* @return this query
*/
}, {
key: 'setModel',
value: function setModel(mapper) {
this.model = mapper;
return this;
}
/**
* Set the relationships that should be eager loaded
*
* @param {Array} relations
* @return this query
*/
}, {
key: 'withRelated',
value: function withRelated(relations) {
if (!(0, _underscore.isArray)(relations)) relations = (0, _underscore.toArray)(arguments);
(0, _underscore.extend)(this.rels, this.parseWithRelated(relations));
return this;
}
/**
* Load the relationships of the models
*
* @param {Array} models
* @return promise
*/
}, {
key: 'loadRelated',
value: function loadRelated(models) {
var _this2 = this;
// no need to load related, if there is no parent models
if ((0, _underscore.isEmpty)(models) || (0, _underscore.isEmpty)(this.rels)) return _bluebird2.default.resolve();
return _bluebird2.default.map((0, _underscore.keys)(this.rels), function (name) {
return _this2.eagerLoad(name, models);
});
}
/**
* Fetch many models from th database
*
* @param {String|Array} columns
* @return promise
*/
}, {
key: 'fetch',
value: function fetch(columns) {
var _this3 = this;
this.select.apply(this, arguments);
return _bluebird2.default.resolve(this.builder.select(this.getQualifiedColumns())).then(function (res) {
return _this3.model.createModels(res);
}).tap(function (res) {
return _this3.loadRelated(res.toArray());
});
}
/**
* Fetch the first model from the database
*
* @param {String|Array} columns
* @return promise
*/
}, {
key: 'first',
value: function first(columns) {
var _limit;
return (_limit = this.limit(1)).fetch.apply(_limit, arguments).then(function (res) {
return res.first();
});
}
/**
* Fetch the first model or fail if not found
*
* @param {String|Array} columns
* @return promise
*/
}, {
key: 'firstOrFail',
value: function firstOrFail(columns) {
return this.first.apply(this, arguments).then(function (model) {
if (model) return model;
throw new _modelNotFound2.default();
});
}
/**
* Get the first record matching the attributes or instantiate it
*
* @param {Object} attrs
* @return promise
*/
}, {
key: 'firstOrNew',
value: function firstOrNew(attrs) {
var _this4 = this;
return this.where(attrs).firstOrFail().catch(function (err) {
if (!(err instanceof _modelNotFound2.default)) throw err;
return _this4.model.newInstance(attrs);
});
}
/**
* Get the first record matching the attributes or create it
*
* @param {Object} attrs
* @return promise
*/
}, {
key: 'firstOrCreate',
value: function firstOrCreate(attrs) {
var returning = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['*'];
return this.firstOrNew(attrs).then(function (res) {
return res.save(returning);
});
}
/**
* Find a model by its primary key
*
* @param {Any} id
* @param {Array} columns
* @return promise
*/
}, {
key: 'find',
value: function find(id) {
var _where;
var columns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['*'];
if ((0, _underscore.isArray)(id)) return this.findMany(id, columns);
var pk = this.getQualifiedColumn(this.model.primaryKey);
return (_where = this.where(pk, id)).first.apply(_where, _toConsumableArray(columns));
}
/**
* Find a model by its primary key or fail
*
* @param {Any} id
* @param {Array} columns
* @return promise
*/
}, {
key: 'findOrFail',
value: function findOrFail(id) {
var columns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['*'];
return this.find(id, columns).then(function (res) {
// it will throw a error if there is no result,
// or the models found are different than the given ids,
// in case of an array of ids passed in
if (!res || (0, _underscore.isArray)(id) && (0, _underscore.uniq)(id).length === res.length) throw new _modelNotFound2.default();
return res;
});
}
/**
* Find a model by its primary key or instantiate it
*
* @param {Any} id
* @param {Array} columns
* @return promise
*/
}, {
key: 'findOrNew',
value: function findOrNew(id) {
var _this5 = this;
var columns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['*'];
return this.findOrFail(id, columns).catch(function (err) {
if (!(err instanceof _modelNotFound2.default)) throw err;else return _this5.model.newInstance();
});
}
/**
* Find multiple models by their primary keys
*
* @param {Array} ids
* @param {Array} columns
* @return promise
*/
}, {
key: 'findMany',
value: function findMany(ids) {
var _whereIn;
var columns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['*'];
if ((0, _underscore.isEmpty)(ids)) return _bluebird2.default.resolve(this.model.newCollection());
var pk = this.getQualifiedColumn(this.model.primaryKey);
return (_whereIn = this.whereIn(pk, ids)).fetch.apply(_whereIn, _toConsumableArray(columns));
}
/**
* Insert records into the database
*
* @param {Object} data
* @param {Array} returning
* @return promise
*/
}, {
key: 'insert',
value: function insert(data) {
var _builder;
var returning = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['*'];
return _bluebird2.default.resolve((_builder = this.builder).insert.apply(_builder, arguments));
}
/**
* Update records in the database
*
* @param {String} key
* @param {Any} value
* @param {Array} returning
* @return promise
*/
}, {
key: 'update',
value: function update(key, value) {
var _builder2;
var returning = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ['*'];
return _bluebird2.default.resolve((_builder2 = this.builder).update.apply(_builder2, arguments));
}
/**
* Get an array with the values of the given column
*
* @param {String} column
* @return promise
*/
}, {
key: 'pluck',
value: function pluck(column) {
return _bluebird2.default.resolve(this.builder.pluck(column));
}
/**
* Delete a record from the database
*
* @return promise
*/
}, {
key: 'destroy',
value: function destroy() {
return _bluebird2.default.resolve(this.builder.del());
}
/**
* Get the minimum value of a given column
*
* @param {String} column
* @return promise
*/
}, {
key: 'min',
value: function min(column) {
return this.aggregate('min', column);
}
/**
* Get the maximum value of a given column
*
* @param {String} column
* @return promise
*/
}, {
key: 'max',
value: function max(column) {
return this.aggregate('max', column);
}
/**
* Get the average of the values of a given column
*
* @param {String} column
* @return promise
*/
}, {
key: 'avg',
value: function avg(column) {
return this.aggregate('avg', column);
}
/**
* Execute an aggregate function on the database
*
* @param {String} method
* @param {String} column
* @return promise
*/
}, {
key: 'aggregate',
value: function aggregate(method, column) {
column += ' as aggregate';
return this.builder[method](column).then(function (res) {
return (0, _underscore.first)(res)['aggregate'];
});
}
/**
* Get a single column's value from the first result of a query
*
* @param {String} column
* @return promise
*/
}, {
key: 'value',
value: function value(column) {
return this.builder.first(column).then(function (res) {
return res[column];
});
}
/**
* Simple pagination of the given query
*
* @param {Integer} page
* @param {Integer} count
* @param {Array} columns
* @return promise
*/
}, {
key: 'paginate',
value: function paginate() {
var page = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 15;
var columns = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ['*'];
return this.offset((page - 1) * count).limit(count).fetch(columns);
}
/**
* Eager laod a relationhip
*
* @param {String} name
* @param {Array} models
* @return promise
* @private
*/
}, {
key: 'eagerLoad',
value: function eagerLoad(name, models) {
var config = this.rels[name];
var relation = this.model.getRelation(name);
// add custom constraints
if ((0, _underscore.isFunction)(config)) relation.modify(config);
// set nested models
if ((0, _underscore.isArray)(config)) relation.modify(function (q) {
return q.withRelated(config);
});
return relation.eagerLoad(models);
}
/**
* Reduce an array of relations into a plain object
*
* @param {Array} relations
* @return plain object
* @private
*/
}, {
key: 'parseWithRelated',
value: function parseWithRelated(relations) {
return (0, _underscore.reduce)(relations, function (memo, value) {
if ((0, _underscore.isString)(value)) value = (0, _underscore.object)([[value, function () {}]]);
if ((0, _underscore.isObject)(value)) {
(0, _underscore.each)(value, function (val, key) {
var parts = key.split('.');
var parent = parts.shift();
var child = parts.join('.');
if ((0, _underscore.isEmpty)(child)) {
if (!(0, _underscore.isArray)(val)) memo[parent] = val;else memo[parent] = val.concat(memo[parent] || []);
} else (memo[parent] = memo[parent] || []).push((0, _underscore.object)([[child, val]]));
});
}
return memo;
}, {});
}
}, {
key: 'builder',
get: function get() {
return this.__qb;
}
}]);
return Query;
}();
// query builder methods
exports.default = Query;
['increment', 'decrement', 'distinct', 'union', 'unionAll', 'where', 'orWhere', 'whereRaw', 'whereNot', 'whereIn', 'orWhereIn', 'whereNotIn', 'orWhereNotIn', 'whereNull', 'orWhereNull', 'whereNotNull', 'orWhereNotNull', 'whereExists', 'orWhereExists', 'whereNotExists', 'orWhereNotExists', 'whereBetween', 'orWhereBetween', 'whereNotBetween', 'orWhereNotBetween', 'offset', 'limit', 'groupBy', 'groupByRaw', 'having', 'orderBy', 'orderByRaw', 'join', 'innerJoin', 'leftJoin', 'rightJoin', 'outerJoin', 'crossJoin', 'joinRaw'].forEach(function (name) {
return Object.defineProperty(Query.prototype, name, {
writable: true,
configurable: true,
value: function value() {
var _builder3;
(_builder3 = this.builder)[name].apply(_builder3, arguments);
return this;
}
});
});