Skip to content

Commit

Permalink
Aggregates (#85)
Browse files Browse the repository at this point in the history
Aggregates
  • Loading branch information
lroal authored May 11, 2024
1 parent 3b08006 commit 16a22e4
Show file tree
Hide file tree
Showing 23 changed files with 3,562 additions and 3,205 deletions.
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1756,7 +1756,7 @@ async function getRows() {
<details><summary><strong>Aggregate functions</strong></summary>
You can count records and aggregate numerical columns. This can either be done for all rows or for associcated columns for each row.
You can count records and aggregate numerical columns. This can either be done across rows or separately for each row.
Supported functions include:
- count
- sum
Expand All @@ -1765,7 +1765,7 @@ Supported functions include:
- avg
__On each row__
For each row we are counting the number of lines.
In this example, we are counting the number of lines for each order. This is represented as the property <i>numberOfLines</i>. You can call these aggregated properties whatever you want.
You can also elevate associated data to the a parent level for easier access. In the example below, <i>balance</i> of the customer is elevated to the root level.
```javascript
Expand All @@ -1782,8 +1782,28 @@ async function getRows() {
}
```
__Across all rows__
<p>Currently there is only the <strong><i>count</i></strong> aggregate available.</p>
The aggregate function effeciently groups data together.
In this particular example , for each customer, it counts the number of lines associated with their orders and calculates the total amount of these lines.
Under the hood, it will run an sql group by customerId and customerName.
```javascript
import map from './map';
const db = map.sqlite('demo.db');

getAggregates();

async function getAggregates() {
const orders = await db.order.aggregate({
where: x => x.orderDate.greaterThan(new Date(2022, 0, 11, 9, 24, 47)),
customerId: x => x.customerId,
customerName: x => x.customer.name,
numberOfLines: x => x.count(x => x.lines.id),
totals: x => x.sum(x => lines.amount)
});
}
```
__Count__
For convenience, you can use the <i>count</i> directly on the table instead of using the aggregated query syntax.
```javascript
import map from './map';
const db = map.sqlite('demo.db');
Expand Down
140 changes: 118 additions & 22 deletions src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ function rdbClient(options = {}) {
let c = {
count,
getMany,
aggregate: groupBy,
getAll,
getOne,
getById,
Expand Down Expand Up @@ -229,6 +230,16 @@ function rdbClient(options = {}) {
return proxify(rows, strategy);
}

async function groupBy(strategy) {
let args = negotiateGroupBy(null, strategy);
let body = stringify({
path: 'aggregate',
args
});
let adapter = netAdapter(url, tableName, { axios: axiosInterceptor, tableOptions });
return adapter.post(body);
}

async function count(_) {
let args = [_].concat(Array.prototype.slice.call(arguments).slice(1));
let body = stringify({
Expand Down Expand Up @@ -281,30 +292,66 @@ function rdbClient(options = {}) {
return [_, where(strategy), ...rest];
else
return args;
}

function where(_strategy, path = '') {
if (typeof _strategy !== 'object' || _strategy === null)
return _strategy;
function where(_strategy, path = '') {
if (typeof _strategy !== 'object' || _strategy === null)
return _strategy;

if (Array.isArray(_strategy)) {
return _strategy.map(item => where(item, path));
}

if (Array.isArray(_strategy)) {
return _strategy.map(item => where(item, path));
const strategy = { ..._strategy };
for (let name in _strategy) {
if (name === 'where' && typeof strategy[name] === 'function')
strategy.where = column(path + 'where')(strategy.where); // Assuming `column` is defined elsewhere.
else if (typeof strategy[name] === 'function') {
strategy[name] = aggregate(path, strategy[name]);
}
else
strategy[name] = where(_strategy[name], path + name + '.');
}
return strategy;
}

const strategy = { ..._strategy };
for (let name in _strategy) {
if (name === 'where' && typeof strategy[name] === 'function')
strategy.where = column(path + 'where')(strategy.where); // Assuming `column` is defined elsewhere.
else if (typeof strategy[name] === 'function') {
strategy[name] = aggregate(path, strategy[name]);
}



function negotiateGroupBy(_, strategy, ...rest) {
const args = Array.prototype.slice.call(arguments);
if (strategy)
return [_, where(strategy), ...rest];
else
return args;

function where(_strategy, path = '') {
if (typeof _strategy !== 'object' || _strategy === null)
return _strategy;

if (Array.isArray(_strategy)) {
return _strategy.map(item => where(item, path));
}
else
strategy[name] = where(_strategy[name], path + name + '.');

const strategy = { ..._strategy };
for (let name in _strategy) {
if (name === 'where' && typeof strategy[name] === 'function')
strategy.where = column(path + 'where')(strategy.where); // Assuming `column` is defined elsewhere.
else if (typeof strategy[name] === 'function') {
strategy[name] = groupByAggregate(path, strategy[name]);
}
else
strategy[name] = where(_strategy[name], path + name + '.');
}
return strategy;
}
return strategy;

}





async function _delete() {
let args = Array.prototype.slice.call(arguments);
let body = stringify({
Expand Down Expand Up @@ -821,7 +868,56 @@ function aggregate(path, arg) {
if (property in c)
return Reflect.get(...arguments);
else {
subColumn = column(path + 'aggregate');
subColumn = column(path + '_aggregate');
return column(property);
}
}

};
let subColumn;
const proxy = new Proxy(c, handler);

const result = arg(proxy);

if (subColumn)
return subColumn(result.self());
else
return result;


function sum(fn) {
return column(path + '_aggregate')(fn(column('')).groupSum());
}
function avg(fn) {
return column(path + '_aggregate')(fn(column('')).groupAvg());
}
function max(fn) {
return column(path + '_aggregate')(fn(column('')).groupMax());
}
function min(fn) {
return column(path + '_aggregate')(fn(column('')).groupMin());
}
function count(fn) {
return column(path + '_aggregate')(fn(column('')).groupCount());
}
}

function groupByAggregate(path, arg) {

const c = {
sum,
count,
avg,
max,
min
};

let handler = {
get(_target, property,) {
if (property in c)
return Reflect.get(...arguments);
else {
subColumn = column(path + '_aggregate');
return column(property);
}
}
Expand All @@ -830,7 +926,7 @@ function aggregate(path, arg) {
let subColumn;
const proxy = new Proxy(c, handler);

const result = arg(proxy);
const result = arg(proxy);

if (subColumn)
return subColumn(result.self());
Expand All @@ -839,19 +935,19 @@ function aggregate(path, arg) {


function sum(fn) {
return column(path + 'aggregate')(fn(column('')).sum());
return column(path + '_aggregate')(fn(column('')).sum());
}
function avg(fn) {
return column(path + 'aggregate')(fn(column('')).avg());
return column(path + '_aggregate')(fn(column('')).avg());
}
function max(fn) {
return column(path + 'aggregate')(fn(column('')).max());
return column(path + '_aggregate')(fn(column('')).max());
}
function min(fn) {
return column(path + 'aggregate')(fn(column('')).min());
return column(path + '_aggregate')(fn(column('')).min());
}
function count(fn) {
return column(path + 'aggregate')(fn(column('')).count());
return column(path + '_aggregate')(fn(column('')).count());
}
}

Expand Down
Loading

0 comments on commit 16a22e4

Please sign in to comment.