275 lines
9.0 KiB
JavaScript
275 lines
9.0 KiB
JavaScript
/**
|
|
* Test the behavior of $integral.
|
|
*/
|
|
(function() {
|
|
"use strict";
|
|
|
|
load("jstests/aggregation/extras/window_function_helpers.js");
|
|
|
|
const coll = db.setWindowFields_integral;
|
|
|
|
// Like most other window functions, the default window for $integral is [unbounded, unbounded].
|
|
coll.drop();
|
|
assert.commandWorked(coll.insert([
|
|
{x: 0, y: 0},
|
|
{x: 1, y: 42},
|
|
{x: 3, y: 67},
|
|
{x: 7, y: 99},
|
|
{x: 10, y: 20},
|
|
]));
|
|
let result = coll.runCommand({
|
|
aggregate: coll.getName(),
|
|
cursor: {},
|
|
pipeline: [
|
|
{
|
|
$setWindowFields: {
|
|
sortBy: {x: 1},
|
|
output: {
|
|
integral: {$integral: {input: "$y"}},
|
|
}
|
|
}
|
|
},
|
|
]
|
|
});
|
|
assert.commandWorked(result);
|
|
|
|
// $integral never compares values from separate partitions.
|
|
coll.drop();
|
|
assert.commandWorked(coll.insert([
|
|
{partitionID: 1, x: 0, y: 1},
|
|
{partitionID: 1, x: 1, y: 2},
|
|
{partitionID: 1, x: 2, y: 1},
|
|
{partitionID: 1, x: 3, y: 4},
|
|
|
|
{partitionID: 2, x: 0, y: 100},
|
|
{partitionID: 2, x: 2, y: 105},
|
|
{partitionID: 2, x: 4, y: 107},
|
|
{partitionID: 2, x: 6, y: -100},
|
|
]));
|
|
result = coll.aggregate([
|
|
{
|
|
$setWindowFields: {
|
|
partitionBy: "$partitionID",
|
|
sortBy: {x: 1},
|
|
output: {
|
|
integral: {$integral: {input: "$y"}, window: {documents: [-1, 0]}},
|
|
}
|
|
}
|
|
},
|
|
{$unset: "_id"},
|
|
])
|
|
.toArray();
|
|
assert.sameMembers(result, [
|
|
{partitionID: 1, x: 0, y: 1, integral: 0},
|
|
{partitionID: 1, x: 1, y: 2, integral: 1.5}, // (1 + 2) * (1 - 0) / 2 = 1.5
|
|
{partitionID: 1, x: 2, y: 1, integral: 1.5}, // (1 + 2) * (2 - 1) / 2 = 1.5
|
|
{partitionID: 1, x: 3, y: 4, integral: 2.5}, // (4 + 1) * (3 - 2) / 2 = 2.5
|
|
|
|
{partitionID: 2, x: 0, y: 100, integral: 0}, //
|
|
{partitionID: 2, x: 2, y: 105, integral: 205}, // (100 + 105) * 2 / 2 = 205
|
|
{partitionID: 2, x: 4, y: 107, integral: 212}, // (105 + 107) * 2 / 2 = 212
|
|
{partitionID: 2, x: 6, y: -100, integral: 7}, // (107 - 100) * 2 / 2 = 7
|
|
]);
|
|
// Because the integral from a to b is the same as the inverse of the integral from b to a, we can
|
|
// invert the input, sort order, and bounds so that the results are the same as the previous
|
|
// integral.
|
|
const resultDesc = coll.aggregate([
|
|
{
|
|
$setWindowFields: {
|
|
partitionBy: "$partitionID",
|
|
sortBy: {x: -1},
|
|
output: {
|
|
integral: {
|
|
$integral: {input: {$subtract: [0, "$y"]}},
|
|
window: {documents: [0, +1]}
|
|
},
|
|
}
|
|
}
|
|
},
|
|
{$unset: "_id"},
|
|
])
|
|
.toArray();
|
|
assert.sameMembers(result, resultDesc);
|
|
|
|
// 'unit' only supports 'week' and smaller.
|
|
coll.drop();
|
|
function explainUnit(unit) {
|
|
return coll.runCommand({
|
|
explain: {
|
|
aggregate: coll.getName(),
|
|
cursor: {},
|
|
pipeline: [{
|
|
$setWindowFields: {
|
|
sortBy: {x: 1},
|
|
output: {
|
|
integral: {
|
|
$integral: {
|
|
input: "$y",
|
|
unit: unit,
|
|
},
|
|
window: {documents: [-1, 1]}
|
|
},
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
});
|
|
}
|
|
assert.commandFailedWithCode(explainUnit('year'), 5490704);
|
|
assert.commandFailedWithCode(explainUnit('quarter'), 5490704);
|
|
assert.commandFailedWithCode(explainUnit('month'), 5490704);
|
|
assert.commandWorked(explainUnit('week'));
|
|
assert.commandWorked(explainUnit('day'));
|
|
assert.commandWorked(explainUnit('hour'));
|
|
assert.commandWorked(explainUnit('minute'));
|
|
assert.commandWorked(explainUnit('second'));
|
|
assert.commandWorked(explainUnit('millisecond'));
|
|
|
|
// Test if 'unit' is specified. Date type input is supported.
|
|
coll.drop();
|
|
assert.commandWorked(coll.insert([
|
|
{x: ISODate("2020-01-01T00:00:00.000Z"), y: 0},
|
|
{x: ISODate("2020-01-01T00:00:00.002Z"), y: 2},
|
|
{x: ISODate("2020-01-01T00:00:00.004Z"), y: 4},
|
|
{x: ISODate("2020-01-01T00:00:00.006Z"), y: 6},
|
|
]));
|
|
|
|
const pipelineWithUnit = [
|
|
{
|
|
$setWindowFields: {
|
|
sortBy: {x: 1},
|
|
output: {
|
|
integral: {$integral: {input: "$y", unit: 'second'}, window: {documents: [-1, 1]}},
|
|
}
|
|
}
|
|
},
|
|
{$unset: "_id"},
|
|
];
|
|
result = coll.aggregate(pipelineWithUnit).toArray();
|
|
assert.sameMembers(result, [
|
|
// We should scale the result by 'millisecond/second'.
|
|
{x: ISODate("2020-01-01T00:00:00.000Z"), y: 0, integral: 0.002},
|
|
{x: ISODate("2020-01-01T00:00:00.002Z"), y: 2, integral: 0.008},
|
|
{x: ISODate("2020-01-01T00:00:00.004Z"), y: 4, integral: 0.016},
|
|
{x: ISODate("2020-01-01T00:00:00.006Z"), y: 6, integral: 0.010},
|
|
]);
|
|
|
|
const pipelineWithNoUnit = [
|
|
{
|
|
$setWindowFields: {
|
|
sortBy: {x: 1},
|
|
output: {
|
|
integral: {$integral: {input: "$y"}, window: {documents: [-1, 1]}},
|
|
}
|
|
}
|
|
},
|
|
{$unset: "_id"},
|
|
];
|
|
// 'unit' is only valid if the 'sortBy' values are ISODate objects.
|
|
// Dates are only valid if 'unit' is specified.
|
|
coll.drop();
|
|
assert.commandWorked(coll.insert([
|
|
{x: 0, y: 100},
|
|
{x: 1, y: 100},
|
|
{x: ISODate("2020-01-01T00:00:00.000Z"), y: 5},
|
|
{x: ISODate("2020-01-01T00:00:00.001Z"), y: 4},
|
|
]));
|
|
assert.commandFailedWithCode(db.runCommand({
|
|
aggregate: "setWindowFields_integral",
|
|
pipeline: pipelineWithUnit,
|
|
cursor: {},
|
|
}),
|
|
5423901);
|
|
|
|
assert.commandFailedWithCode(db.runCommand({
|
|
aggregate: "setWindowFields_integral",
|
|
pipeline: pipelineWithNoUnit,
|
|
cursor: {},
|
|
}),
|
|
5423902);
|
|
|
|
// Test various type of document-based window. Only test the stability not testing the actual
|
|
// result.
|
|
coll.drop();
|
|
assert.commandWorked(coll.insert([
|
|
{x: 0, y: 0},
|
|
{x: 1, y: 42},
|
|
{x: 3, y: 67},
|
|
]));
|
|
documentBounds.forEach(function(bounds) {
|
|
const res = assert.commandWorked(coll.runCommand({
|
|
aggregate: coll.getName(),
|
|
cursor: {},
|
|
pipeline: [
|
|
{
|
|
$setWindowFields: {
|
|
sortBy: {x: 1},
|
|
output: {
|
|
integral: {$integral: {input: "$y"}, window: {documents: bounds}},
|
|
}
|
|
}
|
|
},
|
|
]
|
|
}));
|
|
assert.eq(res.cursor.firstBatch.length, 3);
|
|
});
|
|
|
|
//
|
|
// Testing range-based $integral.
|
|
//
|
|
coll.drop();
|
|
assert.commandWorked(coll.insert([
|
|
{time: ISODate("2020-01-01T00:00:00.000Z"), y: 0.0, integral: 0.002},
|
|
{time: ISODate("2020-01-01T00:00:04.000Z"), y: 2.4, integral: 0.008},
|
|
{time: ISODate("2020-01-01T00:00:10.000Z"), y: 5.6, integral: 0.016},
|
|
{time: ISODate("2020-01-01T00:00:18.000Z"), y: 6.8, integral: 0.010},
|
|
]));
|
|
|
|
function runRangeBasedIntegral(bounds) {
|
|
return coll
|
|
.aggregate([
|
|
{
|
|
$setWindowFields: {
|
|
sortBy: {time: 1},
|
|
output: {
|
|
integral: {
|
|
$integral: {input: "$y", unit: "second"},
|
|
window: {range: bounds, unit: "second"}
|
|
},
|
|
}
|
|
}
|
|
},
|
|
{$unset: "_id"},
|
|
])
|
|
.toArray();
|
|
}
|
|
|
|
// Empty window.
|
|
assert.sameMembers(runRangeBasedIntegral([-1, -1]), [
|
|
{time: ISODate("2020-01-01T00:00:00.000Z"), y: 0.0, integral: null},
|
|
{time: ISODate("2020-01-01T00:00:04.000Z"), y: 2.4, integral: null},
|
|
{time: ISODate("2020-01-01T00:00:10.000Z"), y: 5.6, integral: null},
|
|
{time: ISODate("2020-01-01T00:00:18.000Z"), y: 6.8, integral: null},
|
|
]);
|
|
|
|
// Window contains only one doc.
|
|
assert.sameMembers(runRangeBasedIntegral([-2, 0]), [
|
|
{time: ISODate("2020-01-01T00:00:00.000Z"), y: 0.0, integral: 0},
|
|
{time: ISODate("2020-01-01T00:00:04.000Z"), y: 2.4, integral: 0},
|
|
{time: ISODate("2020-01-01T00:00:10.000Z"), y: 5.6, integral: 0},
|
|
{time: ISODate("2020-01-01T00:00:18.000Z"), y: 6.8, integral: 0},
|
|
]);
|
|
|
|
// Window contains multiple docs.
|
|
assert.sameMembers(runRangeBasedIntegral([-6, 6]), [
|
|
// doc[0] and doc[1] are in the window.
|
|
{time: ISODate("2020-01-01T00:00:00.000Z"), y: 0.0, integral: 4.8},
|
|
// doc[0], doc[1] and doc[2] are in the window.
|
|
{time: ISODate("2020-01-01T00:00:04.000Z"), y: 2.4, integral: 28.8},
|
|
// doc[1] and doc[2] are in the window.
|
|
{time: ISODate("2020-01-01T00:00:10.000Z"), y: 5.6, integral: 24.0},
|
|
// Empty window.
|
|
{time: ISODate("2020-01-01T00:00:18.000Z"), y: 6.8, integral: 0.0},
|
|
]);
|
|
})();
|