From 9eb2439eb74ab7491833f6f2d9eed65488fefc9a Mon Sep 17 00:00:00 2001 From: Muhammad Aaqil Date: Thu, 28 Dec 2023 08:49:33 +0500 Subject: [PATCH] feat: provide support for groupby Signed-off-by: Muhammad Aaqil --- lib/connectors/memory.js | 34 ++++++++++++++++++++++++++++++++++ lib/dao.js | 12 ++++++++++++ lib/utils.js | 14 ++++++++++++++ test/crud-with-options.test.js | 22 ++++++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/lib/connectors/memory.js b/lib/connectors/memory.js index 5c6be09c..bf175d67 100644 --- a/lib/connectors/memory.js +++ b/lib/connectors/memory.js @@ -502,7 +502,41 @@ Memory.prototype._findAllSkippingIncludes = function(model, filter) { // limit/skip const skip = filter.skip || filter.offset || 0; const limit = filter.limit || nodes.length; + // groupBy nodes = nodes.slice(skip, skip + limit); + if (filter.groupBy) { + nodes = utils.groupBy(nodes, filter.groupBy); + const tempNodes = []; + Object.keys(nodes).forEach(nodeKey => { + let count = undefined; + const tempNode = {...nodes[nodeKey][0]}; + if (filter.count) { + count = nodes[nodeKey].filter((obj) => { + const id = obj[filter.count]; + return obj[filter.count] === id; + }).length; + tempNode.count = count; + } + if (filter.max) { + tempNode.max = Math.max(...nodes[nodeKey].map(o => o[filter.max])); + } + if (filter.min) { + tempNode.min = Math.min(...nodes[nodeKey].map(o => o[filter.min])); + } + if (filter.sum) { + tempNode.sum = nodes[nodeKey].reduce((accumulator, object) => { + return accumulator + object[filter.sum]; + }, 0); + } + if (filter.avg) { + tempNode.avg = nodes[nodeKey].reduce((accumulator, object) => { + return accumulator + object[filter.avg]; + }, 0); + tempNode.avg = tempNode.avg / nodes[nodeKey].length; + } + }); + nodes = tempNodes; + } } return nodes; diff --git a/lib/dao.js b/lib/dao.js index f34ed8b3..555fd779 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -1930,6 +1930,18 @@ DataAccessObject.find = function find(query, options, cb) { } } + const keys = Object.keys(data); + keys.forEach(key => { + if ( + key.includes('sumOf') || + key.includes('countOf') || + key.includes('avgOf') || + key.includes('minOf') || + key.includes('maxOf') + ) { + obj.__data[key] = data[key]; + } + }); callback(null, obj); } diff --git a/lib/utils.js b/lib/utils.js index 4b80cb68..e64a33ad 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,6 +31,7 @@ exports.idsHaveDuplicates = idsHaveDuplicates; exports.isClass = isClass; exports.escapeRegExp = escapeRegExp; exports.applyParentProperty = applyParentProperty; +exports.groupBy = groupBy; const g = require('strong-globalize')(); const traverse = require('traverse'); @@ -893,3 +894,16 @@ function applyParentProperty(element, parent) { }); } } + +function groupBy(items, key) { + return items.reduce( + (result, item) => ({ + ...result, + [item[key]]: [ + ...(result[item[key]] || []), + item, + ], + }), + {}, + ); +} diff --git a/test/crud-with-options.test.js b/test/crud-with-options.test.js index c1a9bd5c..f9728ef7 100644 --- a/test/crud-with-options.test.js +++ b/test/crud-with-options.test.js @@ -272,6 +272,28 @@ describe('crud-with-options', function() { User.find({limit: 3}); }); + it('should allow filter with groupBy, count, max, min, sum & avg', function(done) { + User.find({ + groupBy: ['vip'], + count: 'vip', + max: 'id', + min: 'id', + sum: 'id', + avg: 'id', + }, options, function(err, users) { + should.not.exist(err); + should.exist(users); + users.forEach(user => { + user.should.have.property('count', user.count); + user.should.have.property('max'); + user.should.have.property('min'); + user.should.have.property('sum'); + user.should.have.property('avg'); + }); + done(); + }); + }); + it('should skip trailing undefined args', function(done) { User.find({limit: 3}, function(err, users) { should.exists(users);