From 65bbf5655d3d408ef652e5447b4720df53ea8761 Mon Sep 17 00:00:00 2001 From: oof1lab Date: Tue, 31 Oct 2017 17:56:45 +0000 Subject: [PATCH] feature addition: aggregate functions --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++ lib/util/cmd.helper.js | 5 +---- lib/xapi.js | 44 ++++++++++++++++++++++++++++++++++++++++-- lib/xsql.js | 1 + package.json | 2 +- tests/tests.js | 22 +++++++++++++++++++++ 6 files changed, 111 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e32e73c949..4abb35cdb1 100644 --- a/README.md +++ b/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. ``` diff --git a/lib/util/cmd.helper.js b/lib/util/cmd.helper.js index e643c52359..1b0f905168 100644 --- a/lib/util/cmd.helper.js +++ b/lib/util/cmd.helper.js @@ -11,7 +11,7 @@ program.on('--help', () => { }) program - .version('0.0.5') + .version('0.0.6') .option('-h, --host ', 'hostname') .option('-d, --database ', 'database schema name') .option('-u, --user ', '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') { diff --git a/lib/xapi.js b/lib/xapi.js index b9e3fc93e9..ba4befbfc6 100644 --- a/lib/xapi.js +++ b/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 ****************/ } diff --git a/lib/xsql.js b/lib/xsql.js index b99eedc8fe..709a7cd8ee 100644 --- a/lib/xsql.js +++ b/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')) diff --git a/package.json b/package.json index a5ce50d3b9..3843faf161 100644 --- a/package.json +++ b/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": { diff --git a/tests/tests.js b/tests/tests.js index fd90f091bb..2173fed8e8 100644 --- a/tests/tests.js +++ b/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(); + + }); + }); + });