mirror of
https://github.com/knex/knex.git
synced 2025-09-28 17:45:40 +00:00
516 lines
15 KiB
JavaScript
516 lines
15 KiB
JavaScript
// Builder
|
|
// -------
|
|
(function(define) {
|
|
|
|
"use strict";
|
|
|
|
// The `Builder` is the interface for constructing a regular
|
|
// `select`, `insert`, `update`, or `delete` query, as well
|
|
// as some aggregate helpers.
|
|
define(function(require, exports) {
|
|
|
|
var _ = require('underscore');
|
|
|
|
var Raw = require('./raw').Raw;
|
|
var Common = require('./common').Common;
|
|
var Helpers = require('./helpers').Helpers;
|
|
var JoinClause = require('./builder/joinclause').JoinClause;
|
|
|
|
var array = [];
|
|
var push = array.push;
|
|
|
|
// Constructor for the builder instance, typically called from
|
|
// `knex.builder`, accepting the current `knex` instance,
|
|
// and pulling out the `client` and `grammar` from the current
|
|
// knex instance.
|
|
var Builder = function(knex) {
|
|
this.knex = knex;
|
|
this.client = knex.client;
|
|
this.grammar = knex.grammar;
|
|
this.reset();
|
|
_.bindAll(this, 'handleResponse');
|
|
};
|
|
|
|
// All operators used in the `where` clause generation.
|
|
var operators = ['=', '<', '>', '<=', '>=', '<>', '!=', 'like', 'not like', 'between', 'ilike'];
|
|
|
|
_.extend(Builder.prototype, Common, {
|
|
|
|
_source: 'Builder',
|
|
|
|
// Sets the `tableName` on the query.
|
|
from: function(tableName) {
|
|
if (!tableName) return this.table;
|
|
this.table = tableName;
|
|
return this;
|
|
},
|
|
|
|
// Alias to from, for "insert" statements
|
|
// e.g. builder.insert({a: value}).into('tableName')
|
|
into: function(tableName) {
|
|
this.table = tableName;
|
|
return this;
|
|
},
|
|
|
|
// Adds a column or columns to the list of "columns"
|
|
// being selected on the query.
|
|
column: function(columns) {
|
|
if (columns) {
|
|
push.apply(this.columns, _.isArray(columns) ? columns : _.toArray(arguments));
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Adds a `distinct` clause to the query.
|
|
distinct: function(column) {
|
|
this.column(column);
|
|
this.flags.distinct = true;
|
|
return this;
|
|
},
|
|
|
|
// Clones the current query builder, including any
|
|
// pieces that have been set thus far.
|
|
clone: function() {
|
|
var item = new Builder(this.knex);
|
|
item.table = this.table;
|
|
var items = [
|
|
'joins', 'wheres', 'orders', 'columns', 'bindings',
|
|
'grammar', 'transaction', 'unions', 'flags', 'type'
|
|
];
|
|
for (var i = 0, l = items.length; i < l; i++) {
|
|
var k = items[i];
|
|
item[k] = this[k];
|
|
}
|
|
return item;
|
|
},
|
|
|
|
// Resets all attributes on the query builder.
|
|
reset: function() {
|
|
this.joins = [];
|
|
this.values = [];
|
|
this.unions = [];
|
|
this.wheres = [];
|
|
this.orders = [];
|
|
this.columns = [];
|
|
this.bindings = [];
|
|
this.flags = {};
|
|
},
|
|
|
|
// Adds a join clause to the query, allowing for advanced joins
|
|
// with an anonymous function as the second argument.
|
|
join: function(table, first, operator, second, type) {
|
|
var join;
|
|
if (_.isFunction(first)) {
|
|
type = operator;
|
|
join = new JoinClause(type || 'inner', table);
|
|
first.call(join, join);
|
|
} else {
|
|
join = new JoinClause(type || 'inner', table);
|
|
join.on(first, operator, second);
|
|
}
|
|
this.joins.push(join);
|
|
return this;
|
|
},
|
|
|
|
// The where function can be used in several ways:
|
|
// The most basic is `where(key, value)`, which expands to
|
|
// where key = value.
|
|
where: function(column, operator, value) {
|
|
var bool = this._boolFlag || 'and';
|
|
this._boolFlag = 'and';
|
|
|
|
// Check if the column is a function, in which case it's
|
|
// a grouped where statement (wrapped in parens).
|
|
if (_.isFunction(column)) {
|
|
return this._whereNested(column, bool);
|
|
}
|
|
|
|
// Allow a raw statement to be passed along to the query.
|
|
if (column instanceof Raw) {
|
|
return this.whereRaw(column.sql, column.bindings, bool);
|
|
}
|
|
|
|
// Allows `where({id: 2})` syntax.
|
|
if (_.isObject(column)) {
|
|
for (var key in column) {
|
|
value = column[key];
|
|
this[bool + 'Where'](key, '=', value);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
// Enable the where('key', value) syntax, only when there
|
|
// are explicitly two arguments passed, so it's not possible to
|
|
// do where('key', '!=') and have that turn into where key != null
|
|
if (arguments.length === 2) {
|
|
value = operator;
|
|
operator = '=';
|
|
}
|
|
|
|
// If the value is null, and the operator is equals, assume that we're
|
|
// going for a `whereNull` statement here.
|
|
if (value == null && operator === '=') {
|
|
return this.whereNull(column, bool);
|
|
}
|
|
|
|
// If the value is a function, assume it's for building a sub-select.
|
|
if (_.isFunction(value)) {
|
|
return this._whereSub(column, operator, value, bool);
|
|
}
|
|
|
|
this.wheres.push({
|
|
type: 'Basic',
|
|
column: column,
|
|
operator: operator,
|
|
value: value,
|
|
bool: bool
|
|
});
|
|
this.bindings.push(value);
|
|
|
|
return this;
|
|
},
|
|
|
|
// Alias to `where`, for internal builder consistency.
|
|
andWhere: function(column, operator, value) {
|
|
return this.where.apply(this, arguments);
|
|
},
|
|
|
|
// Adds an `or where` clause to the query.
|
|
orWhere: function(column, operator, value) {
|
|
this._boolFlag = 'or';
|
|
return this.where.apply(this, arguments);
|
|
},
|
|
|
|
// Adds a raw `where` clause to the query.
|
|
whereRaw: function(sql, bindings, bool) {
|
|
bindings = _.isArray(bindings) ? bindings : (bindings ? [bindings] : []);
|
|
this.wheres.push({type: 'Raw', sql: sql, bool: bool || 'and'});
|
|
push.apply(this.bindings, bindings);
|
|
return this;
|
|
},
|
|
|
|
// Adds a raw `or where` clause to the query.
|
|
orWhereRaw: function(sql, bindings) {
|
|
return this.whereRaw(sql, bindings, 'or');
|
|
},
|
|
|
|
// Adds a `where exists` clause to the query.
|
|
whereExists: function(callback, bool, type) {
|
|
var query = new Builder(this.knex);
|
|
callback.call(query, query);
|
|
this.wheres.push({
|
|
type: (type || 'Exists'),
|
|
query: query,
|
|
bool: (bool || 'and')
|
|
});
|
|
push.apply(this.bindings, query.bindings);
|
|
return this;
|
|
},
|
|
|
|
// Adds an `or where exists` clause to the query.
|
|
orWhereExists: function(callback) {
|
|
return this.whereExists(callback, 'or');
|
|
},
|
|
|
|
// Adds a `where not exists` clause to the query.
|
|
whereNotExists: function(callback) {
|
|
return this.whereExists(callback, 'and', 'NotExists');
|
|
},
|
|
|
|
// Adds a `or where not exists` clause to the query.
|
|
orWhereNotExists: function(callback) {
|
|
return this.whereExists(callback, 'or', 'NotExists');
|
|
},
|
|
|
|
// Adds a `where in` clause to the query.
|
|
whereIn: function(column, values, bool, condition) {
|
|
bool || (bool = 'and');
|
|
if (_.isFunction(values)) {
|
|
return this._whereInSub(column, values, bool, (condition || 'In'));
|
|
}
|
|
this.wheres.push({
|
|
type: (condition || 'In'),
|
|
column: column,
|
|
value: values,
|
|
bool: bool
|
|
});
|
|
push.apply(this.bindings, values);
|
|
return this;
|
|
},
|
|
|
|
// Adds a `or where in` clause to the query.
|
|
orWhereIn: function(column, values) {
|
|
return this.whereIn(column, values, 'or');
|
|
},
|
|
|
|
// Adds a `where not in` clause to the query.
|
|
whereNotIn: function(column, values) {
|
|
return this.whereIn(column, values, 'and', 'NotIn');
|
|
},
|
|
|
|
// Adds a `or where not in` clause to the query.
|
|
orWhereNotIn: function(column, values) {
|
|
return this.whereIn(column, values, 'or', 'NotIn');
|
|
},
|
|
|
|
// Adds a `where null` clause to the query.
|
|
whereNull: function(column, bool, type) {
|
|
this.wheres.push({type: (type || 'Null'), column: column, bool: (bool || 'and')});
|
|
return this;
|
|
},
|
|
|
|
// Adds a `or where null` clause to the query.
|
|
orWhereNull: function(column) {
|
|
return this.whereNull(column, 'or', 'Null');
|
|
},
|
|
|
|
// Adds a `where not null` clause to the query.
|
|
whereNotNull: function(column) {
|
|
return this.whereNull(column, 'and', 'NotNull');
|
|
},
|
|
|
|
// Adds a `or where not null` clause to the query.
|
|
orWhereNotNull: function(column) {
|
|
return this.whereNull(column, 'or', 'NotNull');
|
|
},
|
|
|
|
// Adds a `where between` clause to the query.
|
|
whereBetween: function(column, values) {
|
|
this.wheres.push({column: column, type: 'Between', bool: 'and'});
|
|
push.apply(this.bindings, values);
|
|
return this;
|
|
},
|
|
|
|
// Adds a `or where between` clause to the query.
|
|
orWhereBetween: function(column, values) {
|
|
this.wheres.push({column: column, type: 'Between', bool: 'or'});
|
|
push.apply(this.bindings, values);
|
|
return this;
|
|
},
|
|
|
|
// Adds a `group by` clause to the query.
|
|
groupBy: function() {
|
|
this.groups = (this.groups || []).concat(_.toArray(arguments));
|
|
return this;
|
|
},
|
|
|
|
// Adds a `order by` clause to the query.
|
|
orderBy: function(column, direction) {
|
|
this.orders.push({column: column, direction: (direction || 'asc')});
|
|
return this;
|
|
},
|
|
|
|
// Add a union statement to the query.
|
|
union: function(callback) {
|
|
this._union(callback, false);
|
|
return this;
|
|
},
|
|
|
|
// Adds a union all statement to the query.
|
|
unionAll: function(callback) {
|
|
this._union(callback, true);
|
|
return this;
|
|
},
|
|
|
|
// Adds a `having` clause to the query.
|
|
having: function(column, operator, value, bool) {
|
|
if (column instanceof Raw) {
|
|
return this.havingRaw(column.value, bool);
|
|
}
|
|
this.havings.push({column: column, operator: (operator || ''), value: (value || ''), bool: bool || 'and'});
|
|
this.bindings.push(value);
|
|
return this;
|
|
},
|
|
|
|
// Adds an `or having` clause to the query.
|
|
orHaving: function(column, operator, value) {
|
|
return this.having(column, operator, value, 'or');
|
|
},
|
|
|
|
// Adds a raw `having` clause to the query.
|
|
havingRaw: function(sql, bool) {
|
|
this.havings.push({type: 'Raw', sql: sql, bool: bool || 'and'});
|
|
return this;
|
|
},
|
|
|
|
// Adds a raw `or having` clause to the query.
|
|
orHavingRaw: function(sql) {
|
|
return this.havingRaw(sql, 'or');
|
|
},
|
|
|
|
offset: function(value) {
|
|
if (value == null) return this.flags.offset;
|
|
this.flags.offset = value;
|
|
return this;
|
|
},
|
|
|
|
limit: function(value) {
|
|
if (value == null) return this.flags.limit;
|
|
this.flags.limit = value;
|
|
return this;
|
|
},
|
|
|
|
// Retrieve the "count" result of the query.
|
|
count: function(column) {
|
|
return this._aggregate('count', column);
|
|
},
|
|
|
|
// Retrieve the minimum value of a given column.
|
|
min: function(column) {
|
|
return this._aggregate('min', column);
|
|
},
|
|
|
|
// Retrieve the maximum value of a given column.
|
|
max: function(column) {
|
|
return this._aggregate('max', column);
|
|
},
|
|
|
|
// Retrieve the sum of the values of a given column.
|
|
sum: function(column) {
|
|
return this._aggregate('sum', column);
|
|
},
|
|
|
|
// Increments a column's value by the specified amount.
|
|
increment: function(column, amount) {
|
|
return this._counter(column, amount);
|
|
},
|
|
|
|
// Decrements a column's value by the specified amount.
|
|
decrement: function(column, amount) {
|
|
return this._counter(column, amount, '-');
|
|
},
|
|
|
|
// Sets the values for a `select` query.
|
|
select: function(columns) {
|
|
if (columns) {
|
|
push.apply(this.columns, _.isArray(columns) ? columns : _.toArray(arguments));
|
|
}
|
|
return this._setType('select');
|
|
},
|
|
|
|
// Sets the values for an `insert` query.
|
|
insert: function(values, returning) {
|
|
if (returning) this.returning(returning);
|
|
this.values = this.prepValues(_.clone(values));
|
|
return this._setType('insert');
|
|
},
|
|
|
|
// Sets the returning value for the query.
|
|
returning: function(returning) {
|
|
this.flags.returning = returning;
|
|
return this;
|
|
},
|
|
|
|
// Sets the values for an `update` query.
|
|
update: function(values) {
|
|
var obj = Helpers.sortObject(values);
|
|
var bindings = [];
|
|
for (var i = 0, l = obj.length; i < l; i++) {
|
|
bindings[i] = obj[i][1];
|
|
}
|
|
this.bindings = bindings.concat(this.bindings || []);
|
|
this.values = obj;
|
|
return this._setType('update');
|
|
},
|
|
|
|
// Alias to del.
|
|
"delete": function() {
|
|
return this._setType('delete');
|
|
},
|
|
|
|
// Executes a delete statement on the query;
|
|
del: function() {
|
|
return this._setType('delete');
|
|
},
|
|
|
|
option: function(opts) {
|
|
this.opts = _.extend(this.opts, opts);
|
|
return this;
|
|
},
|
|
|
|
// Truncate
|
|
truncate: function() {
|
|
return this._setType('truncate');
|
|
},
|
|
|
|
// Set by `transacting` - contains the object with the connection
|
|
// needed to execute a transaction
|
|
transaction: false,
|
|
|
|
// Preps the values for `insert` or `update`.
|
|
prepValues: function(values) {
|
|
if (!_.isArray(values)) values = values ? [values] : [];
|
|
for (var i = 0, l = values.length; i<l; i++) {
|
|
var obj = values[i] = Helpers.sortObject(values[i]);
|
|
for (var i2 = 0, l2 = obj.length; i2 < l2; i2++) {
|
|
this.bindings.push(obj[i2][1]);
|
|
}
|
|
}
|
|
return values;
|
|
},
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Helper for compiling any advanced `where in` queries.
|
|
_whereInSub: function(column, callback, bool, condition) {
|
|
condition += 'Sub';
|
|
var query = new Builder(this.knex);
|
|
callback.call(query, query);
|
|
this.wheres.push({type: condition, column: column, query: query, bool: bool});
|
|
push.apply(this.bindings, query.bindings);
|
|
return this;
|
|
},
|
|
|
|
// Helper for compiling any advanced `where` queries.
|
|
_whereNested: function(callback, bool) {
|
|
var query = new Builder(this.knex);
|
|
callback.call(query, query);
|
|
this.wheres.push({type: 'Nested', query: query, bool: bool});
|
|
push.apply(this.bindings, query.bindings);
|
|
return this;
|
|
},
|
|
|
|
// Helper for compiling any of the `where` advanced queries.
|
|
_whereSub: function(column, operator, callback, bool) {
|
|
var query = new Builder(this.knex);
|
|
callback.call(query, query);
|
|
this.wheres.push({
|
|
type: 'Sub',
|
|
column: column,
|
|
operator: operator,
|
|
query: query,
|
|
bool: bool
|
|
});
|
|
push.apply(this.bindings, query.bindings);
|
|
return this;
|
|
},
|
|
|
|
// Helper for compiling any aggregate queries.
|
|
_aggregate: function(type, columns) {
|
|
if (!_.isArray(columns)) columns = [columns];
|
|
this.aggregate = {type: type, columns: columns};
|
|
return this._setType('select');
|
|
},
|
|
|
|
// Helper for the incrementing/decrementing queries.
|
|
_counter: function(column, amount, symbol) {
|
|
return this.update({column: this.knex.raw('' + this.grammar.wrap(column) + ' ' + (symbol || '+') + ' ' + amount)});
|
|
},
|
|
|
|
// Helper for compiling any `union` queries.
|
|
_union: function(callback, bool) {
|
|
var query = new Builder(this.knex);
|
|
callback.call(query, query);
|
|
this.unions.push({query: query, all: bool});
|
|
push.apply(this.bindings, query.bindings);
|
|
}
|
|
|
|
});
|
|
|
|
exports.Builder = Builder;
|
|
|
|
});
|
|
|
|
})(
|
|
typeof define === 'function' && define.amd ? define : function (factory) { factory(require, exports); }
|
|
); |