Browse Source

feature addition: aggregate functions

pull/8/head
oof1lab 7 years ago
parent
commit
65bbf5655d
  1. 44
      README.md
  2. 5
      lib/util/cmd.helper.js
  3. 44
      lib/xapi.js
  4. 1
      lib/xsql.js
  5. 2
      package.json
  6. 22
      tests/tests.js

44
README.md

@ -41,6 +41,7 @@ That's it!
* Fields
* Group By
* Group By, Order By
* Aggregate functions :tada:
* Relations
* Run dynamic queries
* Upload single file
@ -170,6 +171,49 @@ eg: SELECT country,city,count(*) FROM offices GROUP BY country,city ORDER BY cit
eg: SELECT country,city,count(*) FROM offices GROUP BY country,city ORDER BY city ASC, country DESC
## Aggregate functions :jack_o_lantern: :sunglasses:
```
http://localhost:3000/api/payments/aggregate?_fields=amount
[
{
"min_of_amount": 615.45,
"max_of_amount": 120166.58,
"avg_of_amount": 32431.645531,
"sum_of_amount": 8853839.23,
"stddev_of_amount": 20958.625377426568,
"variance_of_amount": 439263977.71130896
}
]
```
eg: retrieves all numeric aggregate of a column in a table
```
http://localhost:3000/api/orderDetails/aggregate?_fields=priceEach,quantityOrdered
[
{
"min_of_priceEach": 26.55,
"max_of_priceEach": 214.3,
"avg_of_priceEach": 90.769499,
"sum_of_priceEach": 271945.42,
"stddev_of_priceEach": 36.576811252187795,
"variance_of_priceEach": 1337.8631213781719,
"min_of_quantityOrdered": 6,
"max_of_quantityOrdered": 97,
"avg_of_quantityOrdered": 35.219,
"sum_of_quantityOrdered": 105516,
"stddev_of_quantityOrdered": 9.832243813502942,
"variance_of_quantityOrdered": 96.67301840816688
}
]
```
eg: retrieves numeric aggregate can be done for multiple columns too
## Relational Tables
xmysql identifies foreign key relations automatically and provides GET api.
```

5
lib/util/cmd.helper.js

@ -11,7 +11,7 @@ program.on('--help', () => {
})
program
.version('0.0.5')
.version('0.0.6')
.option('-h, --host <n>', 'hostname')
.option('-d, --database <n>', 'database schema name')
.option('-u, --user <n>', 'username of database / root by default')
@ -52,9 +52,6 @@ exports.handle = program => {
program.host = program.host || 'localhost';
program.storageFolder = program.storageFolder || process.cwd()
console.log('storage::', program.storageFolder);
program.connectionLimit = 10;
if (program.host === 'localhost' || program.host === '127.0.0.1' || program.host === '::1') {

44
lib/xapi.js

@ -168,6 +168,12 @@ class Xapi {
.get(this.asyncMiddleware(this.groupBy.bind(this)));
break;
case 'aggregate':
this.app.route(routes[i]['routeUrl'])
.get(this.asyncMiddleware(this.aggregate.bind(this)));
break;
}
}
}
@ -189,8 +195,6 @@ class Xapi {
}
}
async create(req, res) {
@ -431,6 +435,41 @@ class Xapi {
}
async aggregate(req, res) {
let tableName = req.app.locals._tableName;
let query = 'select '
let params = []
let fields = req.query._fields.split(',');
for (var i = 0; i < fields.length; ++i) {
if (i) {
query = query + ','
}
query = query + ' min(??) as ?,max(??) as ?,avg(??) as ?,sum(??) as ?,stddev(??) as ?,variance(??) as ? '
params.push(fields[i]);
params.push('min_of_' + fields[i]);
params.push(fields[i]);
params.push('max_of_' + fields[i]);
params.push(fields[i]);
params.push('avg_of_' + fields[i]);
params.push(fields[i]);
params.push('sum_of_' + fields[i]);
params.push(fields[i]);
params.push('stddev_of_' + fields[i]);
params.push(fields[i]);
params.push('variance_of_' + fields[i]);
}
query = query + ' from ??'
params.push(tableName)
var results = await this.mysql.exec(query, params);
res.status(200).json(results);
}
/**************** START : files related ****************/
downloadFile(req, res) {
@ -463,6 +502,7 @@ class Xapi {
}
}
/**************** END : files related ****************/
}

1
lib/xsql.js

@ -391,6 +391,7 @@ class Xsql {
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/describe', 'describe'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/count', 'count'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/groupby', 'groupby'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/aggregate', 'aggregate'))
routes.push(this.prepareRoute(internal, 'post', apiPrefix, tableName, 'create'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName, 'list'))
routes.push(this.prepareRoute(internal, 'get', apiPrefix, tableName + '/:id', 'read'))

2
package.json

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

22
tests/tests.js

@ -700,5 +700,27 @@ describe('xmysql : tests', function () {
});
});
it('GET /api/orders/aggregate?_fields=orderNumber,customerNumber should PASS', function (done) {
//post to an url with data
agent.get('/api/orders/aggregate?_fields=orderNumber,customerNumber') //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]['min_of_orderNumber'].should.be.equals(10100)
res.body[0]['max_of_orderNumber'].should.be.equals(10425)
res.body[0]['sum_of_orderNumber'].should.be.equals(3345575)
Object.keys(res.body[0]).length.should.be.equals(12)
return done();
});
});
});

Loading…
Cancel
Save