Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions packages/node_modules/pouchdb-mapreduce/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import evalFunction from './evalFunction';
import sum from './sum';
import stats from './stats';
import { NotFoundError } from 'pouchdb-mapreduce-utils';
import createAbstractMapReduce from 'pouchdb-abstract-mapreduce';

Expand All @@ -12,25 +13,7 @@ var builtInReduce = {
return values.length;
},

_stats: function (keys, values) {
// no need to implement rereduce=true, because Pouch
// will never call it
function sumsqr(values) {
var _sumsqr = 0;
for (var i = 0, len = values.length; i < len; i++) {
var num = values[i];
_sumsqr += (num * num);
}
return _sumsqr;
}
return {
sum : sum(values),
min : Math.min.apply(null, values),
max : Math.max.apply(null, values),
count : values.length,
sumsqr : sumsqr(values)
};
}
_stats: stats
};

function getBuiltIn(reduceFunString) {
Expand Down
103 changes: 103 additions & 0 deletions packages/node_modules/pouchdb-mapreduce/src/stats.js
Comment thread
SourceR85 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { BuiltInError } from 'pouchdb-mapreduce-utils';
import sum from './sum';

function getMixedTypeError() {
return new BuiltInError(
'builtin _stats function requires map values to be' +
' numbers or number arrays, not a mix of both.'
);
}

function getMixedLengthError() {
return new BuiltInError(
'builtin _stats function: if the map function outputs' +
' arrays, they need to have consistent length.'
);
}

function sumsqr(values) {
let _sumsqr = 0;
for (let i = 0, len = values.length; i < len; i++) {
const num = values[i];
_sumsqr += (num * num);
}
return _sumsqr;
}

function stats(keys, values) {

// Handle the edge case of an empty values array.
if (!values || values.length === 0) {
return {
sum: 0,
min: null,
max: null,
count: 0,
sumsqr: 0
};
}

const isArrayOfArrays = Array.isArray(values[0]);

if (isArrayOfArrays) {
// --- PATH 1: Handle Array of Arrays ---
// e.g., [[1, 100], [2, 200], [3, 300]]

const firstArrayLength = values[0].length;
const regroupedValues = [];

// Initialize the structure for regrouped ("transposed") values.
for (let i = 0; i < firstArrayLength; i++) {
regroupedValues.push([]);
}

// Validate and regroup the values.
for (const valueArray of values) {
// ERROR CHECK 1: Mixed member types.
if (!Array.isArray(valueArray)) {
throw getMixedTypeError();
}
// ERROR CHECK 2: Inconsistent array lengths.
if (valueArray.length !== firstArrayLength) {
throw getMixedLengthError();
}
// Add each number to its corresponding stats group.
for (let i = 0; i < firstArrayLength; i++) {
regroupedValues[i].push(valueArray[i]);
}
}

// Calculate stats for each regrouped array.
return regroupedValues.map(function (statValues) {
return {
sum: sum(statValues),
min: Math.min.apply(null, statValues),
max: Math.max.apply(null, statValues),
count: statValues.length,
sumsqr: sumsqr(statValues)
};
});

} else {
// --- PATH 2: Handle Array of Numbers ---
// e.g., [1, 2, 3]

// ERROR CHECK: Ensure no arrays are mixed in.
for (const value of values) {
if (typeof value !== 'number') {
throw getMixedTypeError();
}
}

// Apply the original logic.
return {
sum: sum(values),
min: Math.min.apply(null, values),
max: Math.max.apply(null, values),
count: values.length,
sumsqr: sumsqr(values)
};
}
}

export default stats;
66 changes: 63 additions & 3 deletions tests/mapreduce/test.mapreduce.js
Comment thread
SourceR85 marked this conversation as resolved.
Comment thread
SourceR85 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -1065,17 +1065,41 @@ function tests(suiteName, dbName, dbType, viewType) {
});
});

it("Built in _stats reduce function should throw an error with a promise",
it("Built in _stats reduce function can be used with lists of numbers", function () {
const db = new PouchDB(dbName);
return createView(db, {
map: "function(doc){emit(null, doc.val);}",
reduce: "_stats"
}).then(function (queryFun) {
return db.bulkDocs({
docs: [
{ _id: '1', val: [1, 2, 3] },
{ _id: '2', val: [4, 5, 6] },
{ _id: '3', val: [7, 8, 9] },
]
}).then(function () {
return db.query(queryFun, {reduce: true, group_level: 999});
}).then(function (res) {
return res.rows[0].value;
});
}).should.become([
{ sum: 12, count: 3, min: 1, max: 7, sumsqr: 66 },
{ sum: 15, count: 3, min: 2, max: 8, sumsqr: 93 },
{ sum: 18, count: 3, min: 3, max: 9, sumsqr: 126 }
]);
});

it("Built in _stats reduce function should throw an error when confronted with strings",
function () {
var db = new PouchDB(dbName);
const db = new PouchDB(dbName);
return createView(db, {
map: "function(doc){emit(doc.val, 'lala');}",
reduce: "_stats"
}).then(function (queryFun) {
return db.bulkDocs({
docs: [
{ val: 'bar' },
{ val: 'bar' },
{ val: 'bar' },
{ val: 'baz' }
]
}).then(function () {
Expand All @@ -1084,6 +1108,42 @@ function tests(suiteName, dbName, dbType, viewType) {
}).should.be.rejected;
});

it("Built in _stats reduce function should throw an error when confronted with a mix of numbers and arrays",
function () {
const db = new PouchDB(dbName);
return createView(db, {
map: "function(doc){emit(null, doc.val);}",
reduce: "_stats"
}).then(function (queryFun) {
return db.bulkDocs({
docs: [
{ _id: '1', val: [1, 2, 3] },
{ _id: '2', val: 4 }
]
}).then(function () {
return db.query(queryFun, {reduce: true, group_level: 999});
});
}).should.be.rejected;
});

it("Built in _stats reduce function should throw an error when confronted with arrays of inconsistent length",
function () {
const db = new PouchDB(dbName);
return createView(db, {
map: "function(doc){emit(null, doc.val);}",
reduce: "_stats"
}).then(function (queryFun) {
return db.bulkDocs({
docs: [
{ _id: '1', val: [1, 2, 3] },
{ _id: '2', val: [1, 2] }
]
}).then(function () {
return db.query(queryFun, {reduce: true, group_level: 999});
});
}).should.be.rejected;
});

it("Built in _sum reduce function should throw an error with a promise",
function () {
var db = new PouchDB(dbName);
Expand Down