Browse Source

Feature addition : WHERE clause

version 0.0.8
pull/8/head
oof1lab 7 years ago
parent
commit
7df4c93a30
  1. 58
      README.md
  2. 2
      lib/util/cmd.helper.js
  3. 56
      lib/util/whereClause.helper.js
  4. 52
      lib/xapi.js
  5. 22
      lib/xsql.js
  6. 2
      package.json
  7. 196
      tests/tests.js

58
README.md

@ -36,18 +36,19 @@ That's it!
* Serves APIs irrespective of naming conventions of primary keys, foreign keys, tables etc * Serves APIs irrespective of naming conventions of primary keys, foreign keys, tables etc
* CRUD : Usual suspects * CRUD : Usual suspects
* Support for composite primary keys * Support for composite primary keys
* Pagination * Pagination :tada:
* Sorting * Sorting :tada:
* Column filtering - Fields * Column filtering - Fields :tada:
* Group By * Row filtering - Where :tada:
* Group By, Order By * Group By :tada:
* Group By, Order By :tada:
* Aggregate functions :tada: * Aggregate functions :tada:
* Relations * Relations :tada: :tada:
* Run dynamic queries * Run dynamic queries
* Upload single file * Upload single file
* Upload multiple files * Upload multiple files
* Download file * Download file
* Row filtering - Where - Work in progress :racehorse: * Group By, Having - Work in Progress - :racehorse::racehorse:
Use HTTP clients like [Postman](https://www.getpostman.com/) or [similar tools](https://chrome.google.com/webstore/search/http%20client?_category=apps) to invoke REST API calls Use HTTP clients like [Postman](https://www.getpostman.com/) or [similar tools](https://chrome.google.com/webstore/search/http%20client?_category=apps) to invoke REST API calls
@ -144,7 +145,48 @@ eg: gets only customerNumber and checkNumber in response of each record
eg: gets all fields in table row but not checkNumber eg: gets all fields in table row but not checkNumber
## Row filtering / Where ## Row filtering / Where
> Work in progress
#### Comparison operators
```
eq - '='
ne - '!='
gt - '>'
gte - '>='
lt - '<'
lte - '<='
```
#### Use of comparison operators
```
/api/payments?_where=(checkNumber,eq,JM555205)
```
#### Logical operators
```
~or - 'or'
~and - 'and'
~xor - 'xor'
```
#### Use of logical operators
```
/api/payments?_where=(checkNumber,eq,JM555205)~or(checkNumber,eq,OM314933)
```
eg: complex parentheses
```
/api/payments?_where=((checkNumber,eq,JM555205)~or(checkNumber,eq,OM314933))~and(amount,gt,100)
```
eg: where with sorting(_sort), pagination(_p), column filtering (_fields)
```
/api/payments?_where=(amount,gte,1000)&_sort=-amount&p=2&&_fields=customerNumber
```
eg: filter of rows using _where is available for relational route URLs too.
```
/api/offices/1/employees?_where=(jobTitle,eq,Sales%20Rep)
```
## Group By ## Group By

2
lib/util/cmd.helper.js

@ -11,7 +11,7 @@ program.on('--help', () => {
}) })
program program
.version('0.0.6') .version('0.0.8')
.option('-h, --host <n>', 'hostname') .option('-h, --host <n>', 'hostname')
.option('-d, --database <n>', 'database schema name') .option('-d, --database <n>', 'database schema name')
.option('-u, --user <n>', 'username of database / root by default') .option('-u, --user <n>', 'username of database / root by default')

56
lib/util/whereClause.helper.js

@ -54,13 +54,29 @@ function getComparisonOperator(operator) {
return '>=' return '>='
break; break;
case 'like': // case 'is':
return ' like ' // return ' is '
break; // break;
case 'nlike': // case 'isnot':
return ' not like ' // return ' is not '
break; // break;
// case 'isnull':
// return ' is NULL '
// break;
// case 'isnnull':
// return ' is not NULL '
// break;
// case 'like':
// return ' like '
// break;
// case 'nlike':
// return ' not like '
// break;
// case 'in': // case 'in':
// return ' in ' // return ' in '
@ -79,19 +95,19 @@ function getLogicalOperator(operator) {
switch (operator) { switch (operator) {
case '+or': case '~or':
return 'or' return 'or'
break; break;
case '+and': case '~and':
return 'and' return 'and'
break; break;
case '+not': // case '~not':
return 'not' // return 'not'
break; // break;
case '+xor': case '~xor':
return 'xor' return 'xor'
break; break;
@ -103,17 +119,22 @@ function getLogicalOperator(operator) {
} }
exports.getWhereClause = function (whereInQueryParams, whereQuery, whereParams) { exports.getWhereClause = function (whereInQueryParams) {
let whereQuery = '';
let whereParams = [];
let grammarErr = 0; let grammarErr = 0;
let numOfConditions = whereInQueryParams.split(/\+or|\+and|\+not|\+xor/); let numOfConditions = whereInQueryParams.split(/~or|~and|~not|~xor/);
let logicalOperatorsInClause = whereInQueryParams.match(/(\+or|\+and|\+not|\+xor)/g) let logicalOperatorsInClause = whereInQueryParams.match(/(~or|~and|~not|~xor)/g)
if (numOfConditions && logicalOperatorsInClause && numOfConditions.length !== logicalOperatorsInClause.length + 1) { if (numOfConditions && logicalOperatorsInClause && numOfConditions.length !== logicalOperatorsInClause.length + 1) {
console.log('conditions and logical operators mismatch', numOfConditions.length, logicalOperatorsInClause.length); console.log('conditions and logical operators mismatch', numOfConditions.length, logicalOperatorsInClause.length);
return return
} }
// console.log('numOfConditions',numOfConditions,whereInQueryParams);
// console.log('logicalOperatorsInClause',logicalOperatorsInClause);
for (var i = 0; i < numOfConditions.length; ++i) { for (var i = 0; i < numOfConditions.length; ++i) {
let variable = '' let variable = ''
@ -178,7 +199,7 @@ exports.getWhereClause = function (whereInQueryParams, whereQuery, whereParams)
} }
whereParams.push(variableValue[1]) whereParams.push(variableValue[1])
// then replace the variableName with ? // then replace the variableValue with ?
temp = replaceWhereParamsToQMarks(false, result[2]) temp = replaceWhereParamsToQMarks(false, result[2])
if (!temp) { if (!temp) {
grammarErr = 1; grammarErr = 1;
@ -220,9 +241,6 @@ exports.getWhereClause = function (whereInQueryParams, whereQuery, whereParams)
obj['params'] = whereParams obj['params'] = whereParams
} }
return obj return obj
} }

52
lib/xapi.js

@ -212,29 +212,45 @@ class Xapi {
async list(req, res) { async list(req, res) {
let queryParamsObj = {}
queryParamsObj.query = '';
queryParamsObj.params = [];
let cols = this.mysql.getColumnsForSelectStmt(req.app.locals._tableName, req.query); let cols = this.mysql.getColumnsForSelectStmt(req.app.locals._tableName, req.query);
let query = 'select ' + cols + ' from ?? ';
let params = [];
params.push(req.app.locals._tableName);
query = query + this.mysql.getOrderByClause(req.query, req.app.locals._tableName); /**************** tableName ****************/
queryParamsObj.query = 'select ' + cols + ' from ?? ';
queryParamsObj.params.push(req.app.locals._tableName);
query = query + ' limit ?,? ' /**************** where clause ****************/
params = params.concat(this.mysql.getLimitClause(req.query)); this.mysql.getWhereClause(req.query, req.app.locals._tableName, queryParamsObj,' where ');
let results = await this.mysql.exec(query, params); /**************** order clause ****************/
queryParamsObj.query += this.mysql.getOrderByClause(req.query, req.app.locals._tableName);
/**************** limit clause ****************/
queryParamsObj.query += ' limit ?,? '
queryParamsObj.params = queryParamsObj.params.concat(this.mysql.getLimitClause(req.query));
//console.log(queryParamsObj.query, queryParamsObj.params);
let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
res.status(200).json(results); res.status(200).json(results);
} }
async nestedList(req, res) { async nestedList(req, res) {
let cols = this.mysql.getColumnsForSelectStmt(req.app.locals._childTable, req.query); let queryParamsObj = {}
let query = 'select ' + cols + ' from ?? where '; queryParamsObj.query = '';
let params = []; queryParamsObj.params = [];
params.push(req.app.locals._childTable); /**************** tableName ****************/
let cols = this.mysql.getColumnsForSelectStmt(req.app.locals._childTable, req.query);
queryParamsObj.query = 'select ' + cols + ' from ?? where ';
queryParamsObj.params.push(req.app.locals._childTable);
/**************** where foreign key ****************/
let whereClause = this.mysql.getForeignKeyWhereClause(req.app.locals._parentTable, let whereClause = this.mysql.getForeignKeyWhereClause(req.app.locals._parentTable,
req.params.id, req.params.id,
req.app.locals._childTable); req.app.locals._childTable);
@ -244,15 +260,19 @@ class Xapi {
error: "Table is made of composite primary keys - all keys were not in input" error: "Table is made of composite primary keys - all keys were not in input"
}) })
} }
queryParamsObj.query += whereClause;
query += whereClause; /**************** where conditions in query ****************/
this.mysql.getWhereClause(req.query, req.app.locals._tableName, queryParamsObj, ' and ');
query = query + this.mysql.getOrderByClause(req.query, req.app.locals._parentTable); /**************** order clause ****************/
queryParamsObj.query = queryParamsObj.query + this.mysql.getOrderByClause(req.query, req.app.locals._parentTable);
query = query + ' limit ?,? ' /**************** limit clause ****************/
params = params.concat(this.mysql.getLimitClause(req.query)); queryParamsObj.query = queryParamsObj.query + ' limit ?,? '
queryParamsObj.params = queryParamsObj.params.concat(this.mysql.getLimitClause(req.query));
let results = await this.mysql.exec(query, params); let results = await this.mysql.exec(queryParamsObj.query, queryParamsObj.params);
res.status(200).json(results); res.status(200).json(results);
} }

22
lib/xsql.js

@ -2,8 +2,10 @@
const mysql = require('mysql'); const mysql = require('mysql');
const dataHelp = require('./util/data.helper.js'); const dataHelp = require('./util/data.helper.js');
const whereHelp = require('./util/whereClause.helper.js');
const assert = require('assert') const assert = require('assert')
//define class //define class
class Xsql { class Xsql {
@ -171,6 +173,24 @@ class Xsql {
} }
getWhereClause(queryparams, tableName, queryParamsObj, appendToWhere) {
let addWhereStr = ''
if (queryparams && queryparams._where) {
let whereClauseObj = whereHelp.getWhereClause(queryparams._where)
if (whereClauseObj.err === 0) {
queryParamsObj.query = queryParamsObj.query + appendToWhere + whereClauseObj.query;
queryParamsObj.params = queryParamsObj.params.concat(whereClauseObj.params)
}
//console.log('> > > after where clause filling up:', queryParamsObj.query, queryParamsObj.params);
}
}
getOrderByClause(queryparams, tableName) { getOrderByClause(queryparams, tableName) {
//defaults //defaults
@ -217,7 +237,7 @@ class Xsql {
// get column name in _fields and mark column name which start with '-' // get column name in _fields and mark column name which start with '-'
for (let i = 0; i < _fieldsInQuery.length; ++i) { for (let i = 0; i < _fieldsInQuery.length; ++i) {
if (_fieldsInQuery[i][0] == '-') { if (_fieldsInQuery[i][0] === '-') {
removeFieldsObj[_fieldsInQuery[i].substring(1, _fieldsInQuery[i].length)] = 1; removeFieldsObj[_fieldsInQuery[i].substring(1, _fieldsInQuery[i].length)] = 1;
} else { } else {
cols.push(_fieldsInQuery[i]) cols.push(_fieldsInQuery[i])

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "xmysql", "name": "xmysql",
"version": "0.0.6", "version": "0.0.8",
"description": "One command to generate REST APIs for any MySql database", "description": "One command to generate REST APIs for any MySql database",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

196
tests/tests.js

@ -504,6 +504,88 @@ describe('xmysql : tests', function () {
}); });
}); });
it('GET /api/offices/1/employees?_where=(jobTitle,eq,Sales%20Rep) should PASS', function (done) {
//post to an url with data
agent.get('/api/offices/1/employees?_where=(jobTitle,eq,Sales%20Rep)') //enter url
.expect(200)//200 for success 4xx for failure
.end(function (err, res) {
// Handle /api/v error
if (err) {
return done(err);
}
//validate response
res.body.length.should.be.equals(2)
return done();
});
});
it('GET /api/payments?_where=(amount,gte,1000)~and(customerNumber,lte,120) should PASS', function (done) {
//post to an url with data
agent.get('/api/payments?_where=(amount,gte,1000)~and(customerNumber,lte,120)') //enter url
.expect(200)//200 for success 4xx for failure
.end(function (err, res) {
// Handle /api/v error
if (err) {
return done(err);
}
//validate response
res.body.length.should.be.equals(13)
return done();
});
});
it('GET /api/payments?_where=(amount,gte,1000)&_sort=-amount should PASS', function (done) {
//post to an url with data
agent.get('/api/payments?_where=(amount,gte,1000)&_sort=-amount') //enter url
.expect(200)//200 for success 4xx for failure
.end(function (err, res) {
// Handle /api/v error
if (err) {
return done(err);
}
//validate response
res.body[0].amount.should.be.equals(120166.58)
return done();
});
});
http://localhost:3000/api/payments?_where=(checkNumber,eq,JM555205)~or(checkNumber,eq,OM314933)
it('GET /api/payments?_where=(checkNumber,eq,JM555205)~or(checkNumber,eq,OM314933) should PASS', function (done) {
//post to an url with data
agent.get('/api/payments?_where=(checkNumber,eq,JM555205)~or(checkNumber,eq,OM314933)') //enter url
.expect(200)//200 for success 4xx for failure
.end(function (err, res) {
// Handle /api/v error
if (err) {
return done(err);
}
//validate response
res.body.length.should.be.equals(2)
return done();
});
});
it('GET /api/employees/1002/employees should PASS', function (done) { it('GET /api/employees/1002/employees should PASS', function (done) {
//post to an url with data //post to an url with data
@ -753,7 +835,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,eq,1234)',query,params) var err = whereClause.getWhereClause('(abc,eq,1234)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??=?)') err.query.should.be.equal('(??=?)')
@ -771,7 +853,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,ne,1234)',query,params) var err = whereClause.getWhereClause('(abc,ne,1234)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??!=?)') err.query.should.be.equal('(??!=?)')
@ -789,7 +871,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,lt,1234)',query,params) var err = whereClause.getWhereClause('(abc,lt,1234)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??<?)') err.query.should.be.equal('(??<?)')
@ -806,7 +888,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,lte,1234)',query,params) var err = whereClause.getWhereClause('(abc,lte,1234)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??<=?)') err.query.should.be.equal('(??<=?)')
@ -823,7 +905,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,gt,1234)',query,params) var err = whereClause.getWhereClause('(abc,gt,1234)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??>?)') err.query.should.be.equal('(??>?)')
@ -840,7 +922,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,gte,1234)',query,params) var err = whereClause.getWhereClause('(abc,gte,1234)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??>=?)') err.query.should.be.equal('(??>=?)')
@ -854,46 +936,46 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=(abc,like,1234) should PASS', function (done) { // it('where clause unit ?_where=(abc,like,1234) should PASS', function (done) {
//
var query = '' // var query = ''
var params = [] // var params = []
var err = whereClause.getWhereClause('(abc,like,1234)',query,params) // var err = whereClause.getWhereClause('(abc,like,1234)')
//
err.err.should.be.equal(0) // err.err.should.be.equal(0)
err.query.should.be.equal('(?? like ?)') // err.query.should.be.equal('(?? like ?)')
err.params[0].should.be.equal('abc') // err.params[0].should.be.equal('abc')
err.params[1].should.be.equal('1234') // err.params[1].should.be.equal('1234')
//
done() // done()
//
//console.log(query,params,err); // //console.log(query,params,err);
//
}); // });
//
//
it('where clause unit ?_where=(abc,nlike,1234) should PASS', function (done) { // it('where clause unit ?_where=(abc,nlike,1234) should PASS', function (done) {
//
var query = '' // var query = ''
var params = [] // var params = []
var err = whereClause.getWhereClause('(abc,nlike,1234)',query,params) // var err = whereClause.getWhereClause('(abc,nlike,1234)')
//
err.err.should.be.equal(0) // err.err.should.be.equal(0)
err.query.should.be.equal('(?? not like ?)') // err.query.should.be.equal('(?? not like ?)')
err.params[0].should.be.equal('abc') // err.params[0].should.be.equal('abc')
err.params[1].should.be.equal('1234') // err.params[1].should.be.equal('1234')
//
done() // done()
//
//console.log(query,params,err); // //console.log(query,params,err);
//
}); // });
it('where clause unit ?_where=abc,eq,1234) should FAIL', function (done) { it('where clause unit ?_where=abc,eq,1234) should FAIL', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('abc,eq,1234)',query,params) var err = whereClause.getWhereClause('abc,eq,1234)')
err.err.should.be.equal(1) err.err.should.be.equal(1)
err.query.should.be.equal('') err.query.should.be.equal('')
@ -909,7 +991,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,eq,1234',query,params) var err = whereClause.getWhereClause('(abc,eq,1234')
err.err.should.be.equal(1) err.err.should.be.equal(1)
err.query.should.be.equal('') err.query.should.be.equal('')
@ -925,7 +1007,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,eq1234)',query,params) var err = whereClause.getWhereClause('(abc,eq1234)')
err.err.should.be.equal(1) err.err.should.be.equal(1)
err.query.should.be.equal('') err.query.should.be.equal('')
@ -941,7 +1023,7 @@ describe('xmysql : tests', function () {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abceq,1234)',query,params) var err = whereClause.getWhereClause('(abceq,1234)')
err.err.should.be.equal(1) err.err.should.be.equal(1)
err.query.should.be.equal('') err.query.should.be.equal('')
@ -954,11 +1036,11 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=(1,eq,1)(1,eq,2)+or should FAIL', function (done) { it('where clause unit ?_where=(1,eq,1)(1,eq,2)~or should FAIL', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(1,eq,1)(1,eq,2)+or',query,params) var err = whereClause.getWhereClause('(1,eq,1)(1,eq,2)~or')
err.err.should.be.equal(1) err.err.should.be.equal(1)
err.query.should.be.equal('') err.query.should.be.equal('')
@ -970,11 +1052,11 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=(1,eq,1)+or+or(1,eq,2)(1,eq,2) should FAIL', function (done) { it('where clause unit ?_where=(1,eq,1)~or~or(1,eq,2)(1,eq,2) should FAIL', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(1,eq,1)+or+or(1,eq,2)(1,eq,2)',query,params) var err = whereClause.getWhereClause('(1,eq,1)~or~or(1,eq,2)(1,eq,2)')
err.err.should.be.equal(1) err.err.should.be.equal(1)
err.query.should.be.equal('') err.query.should.be.equal('')
@ -986,11 +1068,11 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=(abc,eq,1)+or(b,eq,2) should PASS', function (done) { it('where clause unit ?_where=(abc,eq,1)~or(b,eq,2) should PASS', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(abc,eq,1)+or(b,eq,2)',query,params) var err = whereClause.getWhereClause('(abc,eq,1)~or(b,eq,2)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??=?)or(??=?)') err.query.should.be.equal('(??=?)or(??=?)')
@ -1009,11 +1091,11 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=((a,eq,1)+and(b,eq,2))+or(c,eq,3) should PASS', function (done) { it('where clause unit ?_where=((a,eq,1)~and(b,eq,2))~or(c,eq,3) should PASS', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('((abc,eq,1234)+and(b,eq,2))+or(cde,eq,3)',query,params) var err = whereClause.getWhereClause('((abc,eq,1234)~and(b,eq,2))~or(cde,eq,3)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('((??=?)and(??=?))or(??=?)') err.query.should.be.equal('((??=?)and(??=?))or(??=?)')
@ -1032,11 +1114,11 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=((a,eq,1)+and(b,eq,2))+xor(c,eq,3) should PASS', function (done) { it('where clause unit ?_where=((a,eq,1)~and(b,eq,2))~xor(c,eq,3) should PASS', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('((abc,eq,1234)+and(b,eq,2))+xor(cde,eq,3)',query,params) var err = whereClause.getWhereClause('((abc,eq,1234)~and(b,eq,2))~xor(cde,eq,3)')
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('((??=?)and(??=?))xor(??=?)') err.query.should.be.equal('((??=?)and(??=?))xor(??=?)')
@ -1055,11 +1137,13 @@ describe('xmysql : tests', function () {
}); });
it('where clause unit ?_where=(a,eq,1)+and((b,eq,2)+or(c,eq,3)) should PASS', function (done) { it('where clause unit ?_where=(a,eq,1)~and((b,eq,2)~or(c,eq,3)) should PASS', function (done) {
var query = '' var query = ''
var params = [] var params = []
var err = whereClause.getWhereClause('(a,eq,1)+and((b,eq,2)+or(c,eq,3))',query,params) var err = whereClause.getWhereClause('(a,eq,1)~and((b,eq,2)~or(c,eq,3))')
//console.log(query,params);
err.err.should.be.equal(0) err.err.should.be.equal(0)
err.query.should.be.equal('(??=?)and((??=?)or(??=?))') err.query.should.be.equal('(??=?)and((??=?)or(??=?))')

Loading…
Cancel
Save