Files
mongo/jstests/aggregation/sources/setWindowFields/shift.js
Zac 591928c619 SERVER-108478 JS formatted by prettier and remove clang-format (#39656)
GitOrigin-RevId: 6c8f6aded47f260aa4f7c231b17dae3302cb1e04
2025-08-21 17:27:09 +00:00

357 lines
9.8 KiB
JavaScript

/**
* Test that $shift works as a window function.
*/
import {documentEq} from "jstests/aggregation/extras/utils.js";
const coll = db[jsTestName()];
coll.drop();
const nDocs = 10;
for (let i = 0; i < nDocs; i++) {
assert.commandWorked(
coll.insert({
one: i,
partition: i % 2,
partitionSeq: Math.trunc(i / 2),
}),
);
}
const lastDoc = nDocs - 1;
const lastDocInPartition = nDocs / 2 - 1;
const origDocs = coll.find().sort({_id: 1});
function verifyResults(results, valueFunction) {
for (let i = 0; i < results.length; i++) {
// Use Object.assign to make a copy instead of pass a reference.
const correctDoc = valueFunction(i, Object.assign({}, origDocs[i]));
assert(
documentEq(correctDoc, results[i]),
"Got: " + tojson(results[i]) + "\nExpected: " + tojson(correctDoc) + "\n at position " + i + "\n",
);
}
}
// Run an unpartitioned shift query using the specified offset and default expression.
function runShiftQuery(shiftBy, defaultVal) {
return coll
.aggregate([
{
$setWindowFields: {
sortBy: {one: 1},
output: {a: {$shift: {by: shiftBy, output: "$one", default: defaultVal}}},
},
},
{$sort: {_id: 1}},
])
.toArray();
}
// Run an unpartitioned shift query using the specified offset and the default default expression.
function runShiftQueryWithoutDefault(shiftBy) {
return coll
.aggregate([
{
$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: shiftBy, output: "$one"}}}},
},
{$sort: {_id: 1}},
])
.toArray();
}
// Test left shift with default.
let result = runShiftQuery(-1, -10);
verifyResults(result, function (num, baseObj) {
if (baseObj.one == 0) baseObj.a = -10;
else baseObj.a = baseObj.one - 1;
return baseObj;
});
// Test left shift without default.
result = runShiftQueryWithoutDefault(-1);
verifyResults(result, function (num, baseObj) {
if (baseObj.one == 0) baseObj.a = null;
else baseObj.a = baseObj.one - 1;
return baseObj;
});
// Test 0 shift with default.
result = runShiftQuery(0);
verifyResults(result, function (num, baseObj) {
baseObj.a = baseObj.one;
return baseObj;
});
// Test 0 shift without default.
result = runShiftQueryWithoutDefault(0);
verifyResults(result, function (num, baseObj) {
baseObj.a = baseObj.one;
return baseObj;
});
// Test right shift with default.
result = runShiftQuery(1, -10);
verifyResults(result, function (num, baseObj) {
if (baseObj.one == lastDoc) baseObj.a = -10;
else baseObj.a = baseObj.one + 1;
return baseObj;
});
// Test right shift without default.
result = runShiftQueryWithoutDefault(1);
verifyResults(result, function (num, baseObj) {
if (baseObj.one == lastDoc) baseObj.a = null;
else baseObj.a = baseObj.one + 1;
return baseObj;
});
// Run an unpartitioned shift query using the specified offset with descending order.
function runShiftQueryDescending(shiftBy) {
return coll
.aggregate([
{
$setWindowFields: {sortBy: {one: -1}, output: {a: {$shift: {by: shiftBy, output: "$one"}}}},
},
{$sort: {_id: 1}},
])
.toArray();
}
// Test right shift with descending sort.
result = runShiftQueryDescending(1);
verifyResults(result, function (num, baseObj) {
if (baseObj.one == 0) baseObj.a = null;
else baseObj.a = baseObj.one - 1;
return baseObj;
});
// Test 0 shift with descending sort.
result = runShiftQueryDescending(0);
verifyResults(result, function (num, baseObj) {
baseObj.a = baseObj.one;
return baseObj;
});
// Test left shift with descending sort.
result = runShiftQueryDescending(-1);
verifyResults(result, function (num, baseObj) {
if (baseObj.one == lastDoc) baseObj.a = null;
else baseObj.a = baseObj.one + 1;
return baseObj;
});
// Run a shift query partitioned over "$partition" using the specified shift and default
// default expression.
//
// Partitioning is odd/even.
function runPartitionedShiftQuery(shiftBy) {
return coll
.aggregate([
{
$setWindowFields: {
partitionBy: "$partition",
sortBy: {one: 1},
output: {a: {$shift: {by: shiftBy, output: "$one"}}},
},
},
{$sort: {_id: 1}},
])
.toArray();
}
// Test partitioned left shift.
result = runPartitionedShiftQuery(-1);
verifyResults(result, function (num, baseObj) {
if (baseObj.partitionSeq == 0) baseObj.a = null;
else
// partitioning is even/odd.
baseObj.a = baseObj.one - 2;
return baseObj;
});
// Test partitioned right shift.
result = runPartitionedShiftQuery(1);
verifyResults(result, function (num, baseObj) {
if (baseObj.partitionSeq == lastDocInPartition) baseObj.a = null;
else
// partitioning is even/odd.
baseObj.a = baseObj.one + 2;
return baseObj;
});
// Test partitioned 0 shift.
result = runPartitionedShiftQuery(0);
verifyResults(result, function (num, baseObj) {
baseObj.a = baseObj.one;
return baseObj;
});
// Run a shift query partitioned over "$partition" using the specified shift and default
// default expression with a descending sort.
//
// Partitioning is odd/even.
function runPartitionedShiftQueryDescending(shiftBy) {
return coll
.aggregate([
{
$setWindowFields: {
partitionBy: "$partition",
sortBy: {one: -1},
output: {a: {$shift: {by: shiftBy, output: "$one"}}},
},
},
{$sort: {_id: 1}},
])
.toArray();
}
// Test partitioned left shift with descending sort.
result = runPartitionedShiftQueryDescending(-1);
verifyResults(result, function (num, baseObj) {
if (baseObj.partitionSeq == lastDocInPartition) baseObj.a = null;
else
// partitioning is even/odd.
baseObj.a = baseObj.one + 2;
return baseObj;
});
// Test partitioned right shift with descending sort.
result = runPartitionedShiftQueryDescending(1);
verifyResults(result, function (num, baseObj) {
if (baseObj.partitionSeq == 0) baseObj.a = null;
else
// partitioning is even/odd.
baseObj.a = baseObj.one - 2;
return baseObj;
});
// Test partitioned 0 shift with descending sort.
result = runPartitionedShiftQuery(0);
verifyResults(result, function (num, baseObj) {
baseObj.a = baseObj.one;
return baseObj;
});
// Test $shift with default value.
coll.drop();
assert.commandWorked(coll.insert([{_id: 1}, {_id: 2}]));
result = coll
.aggregate([
{
$setWindowFields: {sortBy: {_id: 1}, output: {a: {$shift: {output: "$b", by: 1, default: "c"}}}},
},
])
.toArray();
assert.eq(
[
{_id: 1, a: "c"},
{_id: 2, a: "c"},
],
result,
result,
);
/* Parsing tests */
// "by" is required.
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [{$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {output: "$one"}}}}}],
cursor: {},
}),
ErrorCodes.FailedToParse,
);
// Can't accept a string for "by".
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [{$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: "1", output: "$one"}}}}}],
cursor: {},
}),
ErrorCodes.FailedToParse,
);
// Can't accept an expression for "by".
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [
{
$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: {$sum: [1, 1]}, output: "$one"}}}},
},
],
cursor: {},
}),
ErrorCodes.FailedToParse,
);
// Can't accept a float for "by".
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [{$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: 1.1, output: "$one"}}}}}],
cursor: {},
}),
ErrorCodes.FailedToParse,
);
// Can't accept a float for "by" ... unless it converts to int without loss of precision.
assert.commandWorked(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [{$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: 1.0, output: "$one"}}}}}],
cursor: {},
}),
);
// "output" is required.
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [{$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: 1}}}}}],
cursor: {},
}),
ErrorCodes.FailedToParse,
);
// "default" must evaluate to a constant.
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [
{
$setWindowFields: {sortBy: {one: 1}, output: {a: {$shift: {by: 1, output: "$one", default: "$one"}}}},
},
],
cursor: {},
}),
ErrorCodes.FailedToParse,
);
// "default" may be an arbitrary expression as long as it evaluates to a constant.
assert.commandWorked(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [
{
$setWindowFields: {
sortBy: {one: 1},
output: {a: {$shift: {by: 1, output: "$one", default: {$add: [1, 1]}}}},
},
},
],
cursor: {},
}),
);
// "sortBy" is required for $shift.
assert.commandFailedWithCode(
coll.runCommand({
aggregate: coll.getName(),
pipeline: [{$setWindowFields: {output: {a: {$shift: {by: 1, output: "$one"}}}}}],
cursor: {},
}),
ErrorCodes.FailedToParse,
);