diff --git a/lib/sql.js b/lib/sql.js index aa19aa8..f1bb9a0 100644 --- a/lib/sql.js +++ b/lib/sql.js @@ -1185,6 +1185,45 @@ SQLConnector.prototype.find = function(model, id, options, cb) { // Alias to `find`. Juggler checks `findById` only. Connector.defineAliases(SQLConnector.prototype, 'find', ['findById']); +/** + * Build a SQL SELECT statement to count rows + * @param {String} model Model name + * @param {Object} where Where object + * @param {Object} options Options object + * @returns {ParameterizedSQL} Statement object {sql: ..., params: [...]} + */ +SQLConnector.prototype.buildCount = function(model, where, options) { + var haveRelationFilters = false; + if (where) { + var relations = this.getModelDefinition(model).model.relations; + if (relations) { + for (var key in where) { + if (key in relations) { + haveRelationFilters = true; + break; + } + } + } + } + + var count = 'count(*)'; + if (haveRelationFilters) { + var idColumn = this.columnEscaped(model, this.idColumn(model)); + count = 'count(DISTINCT ' + idColumn + ')'; + } + + var stmt = new ParameterizedSQL('SELECT ' + count + + ' as "cnt" FROM ' + this.tableEscaped(model)); + + if (haveRelationFilters) { + var joinsStmts = this.buildJoins(model, where); + stmt = stmt.merge(joinsStmts); + } + + stmt = stmt.merge(this.buildWhere(model, where)); + return this.parameterize(stmt); +}; + /** * Count all model instances by the where filter * @@ -1202,10 +1241,8 @@ SQLConnector.prototype.count = function(model, where, options, cb) { where = tmp; } - var stmt = new ParameterizedSQL('SELECT count(*) as "cnt" FROM ' + - this.tableEscaped(model)); - stmt = stmt.merge(this.buildWhere(model, where)); - stmt = this.parameterize(stmt); + var stmt = this.buildCount(model, where, options); + this.execute(stmt.sql, stmt.params, function(err, res) { if (err) { diff --git a/test/sql.test.js b/test/sql.test.js index 9e0f3c2..b0186cc 100644 --- a/test/sql.test.js +++ b/test/sql.test.js @@ -420,6 +420,40 @@ describe('sql connector', function() { }); }); + it('builds count', function() { + var sql = connector.buildCount('customer'); + expect(sql.toJSON()).to.eql({ + sql: 'SELECT count(*) as "cnt" FROM `CUSTOMER` ', + params: [] + }); + }); + + it('builds count with WHERE', function() { + var sql = connector.buildCount('customer', {name: 'John'}); + expect(sql.toJSON()).to.eql({ + sql: 'SELECT count(*) as "cnt" FROM `CUSTOMER` WHERE `CUSTOMER`.`NAME`=$1', + params: ['John'] + }); + }); + + it('builds count with WHERE and JOIN', function() { + var sql = connector.buildCount('customer', { + name: 'John', + orders: { + where: { + date: {between: ['2015-01-01', '2015-01-31']} + } + } + }); + expect(sql.toJSON()).to.eql({ + sql: 'SELECT count(DISTINCT `CUSTOMER`.`NAME`) as "cnt" FROM `CUSTOMER` ' + + 'INNER JOIN ( SELECT `ORDER`.`CUSTOMER_NAME` FROM `ORDER` WHERE ' + + '`ORDER`.`DATE` BETWEEN $1 AND $2 ORDER BY `ORDER`.`ID` ) AS `ORDER` ' + + 'ON `CUSTOMER`.`NAME`=`ORDER`.`CUSTOMER_NAME` WHERE `CUSTOMER`.`NAME`=$3', + params: ['2015-01-01', '2015-01-31', 'John'] + }); + }); + it('normalizes a SQL statement from string', function() { var sql = 'SELECT * FROM `CUSTOMER`'; var stmt = new ParameterizedSQL(sql);