json-aggregate

What is it?

json-aggregate is a javascript utility to perform aggregations on JSON-formatted data. It is inspired by the MongoDB Aggregation Framework, but in no way pretends to cover all its functionality.

Why?

json-aggregate allows developers to perform aggregation queries on JSON data without the need to store it. Nowadays the average app sends/receives JSON from multiple sources/apis/etc., and this utility can help developers extract some insights from the data without the need to use a database.

How to use

Install via npm / yarn:

npm install json-aggregate

Use the create method with the JSON data to generate a collection:

const jsonAggregate = require('json-aggregate')
const collection = jsonAggregate.create(myJsonData)

Methods

match (options)

options object function

Filters the documents and only those that match the specified criteria are returned.

Data [{ "author" : "dave", "score" : 80, "views" : 100 },
{ "author" : "dave", "score" : 85, "views" : 521 },
{ "author" : "ahn", "score" : 60, "views" : 1000 },
{ "author" : "li", "score" : 55, "views" : 5000 },
{ "author" : "annT", "score" : 60, "views" : 50 },
{ "author" : "li", "score" : 94, "views" : 999 },
{ "author" : "ty", "score" : 95, "views" : 1000 }]
Aggregation collection.match({ author : "dave" }).exec() Result [{ "author" : "dave", "score" : 80, "views" : 100 }
{ "author" : "dave", "score" : 85, "views" : 521 }]

Alternatively, the same result can be achieved with a condition function:

collection.match(item => item.author === "dave").exec()

unwind (field)

field string

Deconstructs an array field and returns a single record for each of its values.

Data { "item" : "ABC1", sizes: [ "S", "M", "L"] } Aggregation collection.unwind("sizes").exec() Results [{ "item" : "ABC1", "sizes" : "S" },
{ "item" : "ABC1", "sizes" : "M" },
{ "item" : "ABC1", "sizes" : "L" }]

group (options)

options string array object

Groups documents by the specified options and returns the aggregated data with each group having a distinct id field.

Data [{ company: "a", employeeCount: 45, category: 1, product: "Product A", price: 120 },
{ company: "a", employeeCount: 45, category: 1, product: "Product B", price: 80 },
{ company: "a", employeeCount: 45, category: 2, product: "Product C", price: 105 },
{ company: "a", employeeCount: 45, category: 2, product: "Product D", price: 95 },
{ company: "b", employeeCount: 30, category: 1, product: "Product A", price: 40 },
{ company: "b", employeeCount: 30, category: 1, product: "Product B", price: 100 },
{ company: "b", employeeCount: 30, category: 2, product: "Product C", price: 60 },
{ company: "b", employeeCount: 30, category: 2, product: "Product D", price: 130 }]
Aggregation #1 collection.group('company').exec()
// or collection.group({ id: 'company' })
Results #1 ["a", "b"]
Aggregation #2 (with operator) collection.group({
 id: 'company',
 lowestPrice: { $min: 'price'}
})
.exec()
Results #2 [{ id: 'a', lowestPrice: 80 },
{ id: 'b', lowestPrice: 40 }]

Aggregation #3 (multiple keys) collection.group({
 id: ['company', 'category'],
 max: { $max: 'price' }
})
.exec()
Results #3 [{ id: { company: 'a', category: 1 }, max: 120 },
{ id: { company: 'a', category: 2 }, max: 105 },
{ id: { company: 'b', category: 1 }, max: 100 },
{ id: { company: 'b', category: 2 }, max: 130 }]

sort (criteria)

criteria object

Sorts values according to the criteria passed.

Data [{ company: 'a', price: 90 },
{ company: 'b', price: 120 },
{ company: 'b', price: 50 }]
Aggregation collection.sort({ company: -1, price: 1 }).exec() Results [{ company: 'b', price: 50 },
{ company: 'b', price: 120 },
{ company: 'a', price: 90 }]

limit (n)

n number

Shrinks the array of results to the desired length. An Array.slice proxy for convenience (in chaining).

Data [{ company: "a", employeeCount: 45, category: 1, product: "Product A", price: 120 },
{ company: "a", employeeCount: 45, category: 1, product: "Product B", price: 80 },
{ company: "a", employeeCount: 45, category: 2, product: "Product C", price: 105 },
{ company: "a", employeeCount: 45, category: 2, product: "Product D", price: 95 },
{ company: "b", employeeCount: 30, category: 1, product: "Product A", price: 40 },
{ company: "b", employeeCount: 30, category: 1, product: "Product B", price: 100 },
{ company: "b", employeeCount: 30, category: 2, product: "Product C", price: 60 },
{ company: "b", employeeCount: 30, category: 2, product: "Product D", price: 130 }]
Aggregation collection.sort({ price: -1 }).limit(2).exec() Results [{ company: "b", employeeCount: 30, category: 2, product: "Product D", price: 130 },
{ company: "a", employeeCount: 45, category: 1, product: "Product A", price: 120 }]

Operators

Data [{ company: "a", employeeCount: 45, category: 1, product: "Product A", price: 120 },
{ company: "a", employeeCount: 45, category: 1, product: "Product B", price: 80 },
{ company: "a", employeeCount: 45, category: 2, product: "Product C", price: 105 },
{ company: "a", employeeCount: 45, category: 2, product: "Product D", price: 95 },
{ company: "b", employeeCount: 30, category: 1, product: "Product A", price: 40 },
{ company: "b", employeeCount: 30, category: 1, product: "Product B", price: 100 },
{ company: "b", employeeCount: 30, category: 2, product: "Product C", price: 60 },
{ company: "b", employeeCount: 30, category: 2, product: "Product D", price: 130 }]

$avg

Returns the average of the selected field for the group of records.

Aggregation collection.group({ id: 'product', count: { $avg: 'price' } }).exec() Result [{ id: 'Product A', count: 80 },
{ id: 'Product B', count: 90 },
{ id: 'Product C', count: 82.5 },
{ id: 'Product D', count: 112.5 }]

$first

Returns the first record found for each group.

Aggregation collection.group({ id: 'company', product: { $first: 'product' } }).exec() Result [{ id: 'a', product: 'Product A'},
{ id: 'b', product: 'Product A'}]

$last

Returns the last record found for each group.

Aggregation collection.group({ id: 'company', product: { $last: 'product' } }).exec() Result [{ id: 'a', product: 'Product D'},
{ id: 'b', product: 'Product D'}]

$min

Returns the minimum value found for the chosen field of each group.

Aggregation collection.group({ id: 'company', lowestPrice: { $min: 'price' } }).exec() Result [{ id: 'a', lowestPrice: 80 },
{ id: 'b', lowestPrice: 40 }]

$max

Returns the maximum value found for the chosen field of each group.

Aggregation collection.group({ id: 'company', highestPrice: { $max: 'price' } }).exec() Result [{ id: 'a', highestPrice: 120 },
{ id: 'b', highestPrice: 130 }]

$sum

Returns the sum of all the values from the chosen field of each group. Admits the value 1 to perform a count of the records within each group.

Aggregation collection.group({ id: 'company', count: { $sum: 1 } }).exec() Result [{ id: 'a', count: 4 },
{ id: 'b', count: 4 }]

$push

Appends the values of specific fields into each group. This operator expects an array.

Aggregation collection.group({ id: 'company', products: { $push: ['product', 'price'] }).exec() Result [{ id: 'a',
 products: [
  { product: 'Product A', price: 120 },
  { product: 'Product B', price: 80 },
  { product: 'Product C', price: 105 },
  { product: 'Product D', price: 95 }
 ]
},
{ id: 'b',
 products: [
  { product: 'Product A', price: 40 },
  { product: 'Product B', price: 100 },
  { product: 'Product C', price: 60 },
  { product: 'Product D', price: 130 }
 ]
}]

$addToSet

Adds unique values present in the chosen field for each group.

Aggregation collection.group({ id: 'company', categories: { $addToSet: 'category' } }).exec() Result [{ id: 'a', categories: [1, 2] }, { id: 'b', categories: [1, 2] }]

Full example

Data [{ shop: "XYZ", "item" : "A", sizes: [ "S", "M", "L"], price: '99' },
{ shop: "XYZ", "item" : "B", sizes: [ "M", "L"], price: '69' },
{ shop: "XYZ", "item" : "C", sizes: [ "S", "L", "XL"], price: '120' },
{ shop: "MNO", "item" : "A", sizes: [ "S" ], price: '110' },
{ shop: "MNO", "item" : "B", sizes: [ "S", "M", "L"], price: '80' },
{ shop: "MNO", "item" : "C", sizes: [ "S", "M", "XL"], price: '100' },
{ shop: "JKL", "item" : "A", sizes: [ "S", "M", "XL" ], price: '105' },
{ shop: "JKL", "item" : "B", sizes: [ "M", "L"], price: '50' },
{ shop: "JKL", "item" : "C", sizes: [ "M", "XL"], price: '110' }]

Aggregation collection
.match(item => item.price > 60)
.unwind('sizes')
.group({
 id: ['item', 'sizes'],
 whereToBuy: { $push: ['shop', 'price'] }
})
.exec()

Result [{
 "id": {"item": "A", "sizes": "L"},
 "whereToBuy": [{"price": "99", "shop": "XYZ"}]
},{
 "id": {"item": "C", "sizes": "L"},
 "whereToBuy": [{"price": "120", "shop": "XYZ"}]
},{
 "id": {"item": "B", "sizes": "S"},
 "whereToBuy": [{"price": "80", "shop": "MNO"}]
},{
 "id": {"item": "B", "sizes": "M"},
 "whereToBuy": [{"price": "69", "shop": "XYZ"},{"price": "80", "shop": "MNO"}]
},{
 "id": {"item": "B", "sizes": "L"},
 "whereToBuy": [{"price": "69", "shop": "XYZ"}, {"price": "80", "shop": "MNO"}]
},{
 "id": {"item": "C", "sizes": "S"},
 "whereToBuy": [{"price": "120", "shop": "XYZ"},{"price": "100", "shop": "MNO"}]
},{
 "id": {"item": "A", "sizes": "S"},
 "whereToBuy": [{"price": "99", "shop": "XYZ"}, {"price": "110", "shop": "MNO"}, {"price": "105", "shop": "JKL"}]
},{
 "id": {"item": "A", "sizes": "M"},
 "whereToBuy": [{"price": "99", "shop": "XYZ"},{"price": "105", "shop": "JKL"}]
},{
 "id": {"item": "A", "sizes": "XL"},
 "whereToBuy": [{"price": "105", "shop": "JKL"}]
},{
 "id": {"item": "C", "sizes": "M"},
 "whereToBuy": [{"price": "100", "shop": "MNO"},{"price": "110", "shop": "JKL"}]
},{
 "id": {"item": "C", "sizes": "XL"},
 "whereToBuy": [{"price": "120", "shop": "XYZ"}, {"price": "100", "shop": "MNO"}, {"price": "110", "shop": "JKL"}]
}]