UNPKG

vitamin

Version:

Data Mapper library for Node.js applications

641 lines (511 loc) 16.3 kB
'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; } }); });