397 lines
11 KiB
JavaScript
397 lines
11 KiB
JavaScript
// @tags: [
|
|
// requires_getmore,
|
|
// # $slice is not supported on views.
|
|
// incompatible_with_views,
|
|
// # Time series collections (as views) do not support this operation (e.g. $elemMatch, $slice).
|
|
// exclude_from_timeseries_crud_passthrough,
|
|
// ]
|
|
|
|
let t = db.slice1;
|
|
t.drop();
|
|
|
|
assert.commandWorked(t.insert({_id: 1, a: [0, 1, 2, 3, 4, 5, -5, -4, -3, -2, -1], b: 1, c: 1}));
|
|
|
|
// first three
|
|
let out = t.findOne({}, {a: {$slice: 3}});
|
|
assert.eq(out.a, [0, 1, 2], "1");
|
|
|
|
// last three
|
|
out = t.findOne({}, {a: {$slice: -3}});
|
|
assert.eq(out.a, [-3, -2, -1], "2");
|
|
|
|
// skip 2, limit 3
|
|
out = t.findOne({}, {a: {$slice: [2, 3]}});
|
|
assert.eq(out.a, [2, 3, 4], "3");
|
|
|
|
// skip to fifth from last, limit 4
|
|
out = t.findOne({}, {a: {$slice: [-5, 4]}});
|
|
assert.eq(out.a, [-5, -4, -3, -2], "4");
|
|
|
|
// skip to fifth from last, limit 10
|
|
out = t.findOne({}, {a: {$slice: [-5, 10]}});
|
|
assert.eq(out.a, [-5, -4, -3, -2, -1], "5");
|
|
|
|
// interaction with other fields
|
|
|
|
out = t.findOne({}, {a: {$slice: 3}});
|
|
assert.eq(out.a, [0, 1, 2], "A 1");
|
|
assert.eq(out.b, 1, "A 2");
|
|
assert.eq(out.c, 1, "A 3");
|
|
|
|
out = t.findOne({}, {a: {$slice: 3}, b: true});
|
|
assert.eq(out.a, [0, 1, 2], "B 1");
|
|
assert.eq(out.b, 1, "B 2");
|
|
assert.eq(out.c, undefined);
|
|
|
|
out = t.findOne({}, {a: {$slice: 3}, b: false});
|
|
assert.eq(out.a, [0, 1, 2]);
|
|
assert.eq(out.b, undefined);
|
|
assert.eq(out.c, 1);
|
|
|
|
assert(t.drop());
|
|
assert.commandWorked(
|
|
t.insert({
|
|
comments: [
|
|
{id: 0, text: "a"},
|
|
{id: 1, text: "b"},
|
|
{id: 2, text: "c"},
|
|
{id: 3, text: "d"},
|
|
],
|
|
title: "foo",
|
|
}),
|
|
);
|
|
|
|
// $slice in an inclusion projection.
|
|
out = t.findOne({}, {comments: {$slice: 2}, irrelevantField: 1});
|
|
assert.eq(out.comments, [
|
|
{id: 0, text: "a"},
|
|
{id: 1, text: "b"},
|
|
]);
|
|
assert.eq(out.title, undefined);
|
|
|
|
// Check that on its own, $slice defaults to an exclusion projection.
|
|
out = t.findOne({}, {comments: {$slice: 2}});
|
|
assert.eq(out.comments, [
|
|
{id: 0, text: "a"},
|
|
{id: 1, text: "b"},
|
|
]);
|
|
assert.eq(out.title, "foo");
|
|
|
|
// $slice in an exclusion projection (with explicit exclusions).
|
|
out = t.findOne({}, {comments: {$slice: 2}, title: 0});
|
|
assert.eq(out.comments, [
|
|
{id: 0, text: "a"},
|
|
{id: 1, text: "b"},
|
|
]);
|
|
assert.eq(out.title, undefined);
|
|
|
|
// nested arrays
|
|
assert(t.drop());
|
|
assert.commandWorked(
|
|
t.insert({
|
|
_id: 1,
|
|
a: [
|
|
[1, 1, 1],
|
|
[2, 2, 2],
|
|
[3, 3, 3],
|
|
],
|
|
b: 1,
|
|
c: 1,
|
|
}),
|
|
);
|
|
|
|
out = t.findOne({}, {a: {$slice: 1}});
|
|
assert.eq(out.a, [[1, 1, 1]], "n 1");
|
|
|
|
out = t.findOne({}, {a: {$slice: -1}});
|
|
assert.eq(out.a, [[3, 3, 3]], "n 2");
|
|
|
|
out = t.findOne({}, {a: {$slice: [0, 2]}});
|
|
assert.eq(
|
|
out.a,
|
|
[
|
|
[1, 1, 1],
|
|
[2, 2, 2],
|
|
],
|
|
"n 2",
|
|
);
|
|
|
|
// Test that $slice operator goes only 1 level deep into nested arrays.
|
|
assert(t.drop());
|
|
assert.commandWorked(
|
|
t.insert([
|
|
{_id: 1, a: {b: [1, 2, 3]}},
|
|
{_id: 2, a: [{b: [1, 2, 3]}]},
|
|
{_id: 3, a: [[{b: [1, 2, 3]}]]},
|
|
{_id: 4, a: [[{b: [1, 2, 3]}], {b: [1, 2, 3]}, 1, null, {}]},
|
|
]),
|
|
);
|
|
|
|
out = t
|
|
.find({}, {a: {b: {$slice: [1, 1]}}})
|
|
.sort({_id: 1})
|
|
.toArray();
|
|
assert.eq(out, [
|
|
{_id: 1, a: {b: [2]}},
|
|
{_id: 2, a: [{b: [2]}]},
|
|
{_id: 3, a: [[{b: [1, 2, 3]}]]},
|
|
{_id: 4, a: [[{b: [1, 2, 3]}], {b: [2]}, 1, null, {}]},
|
|
]);
|
|
|
|
function testSingleDocument(projection, input, expectedOutput, deleteId = true, assertFn = assert.eq) {
|
|
assert(t.drop());
|
|
assert.commandWorked(t.insert(input));
|
|
const actualOutput = t.findOne({}, projection);
|
|
if (deleteId) {
|
|
delete actualOutput._id;
|
|
}
|
|
assertFn(actualOutput, expectedOutput);
|
|
}
|
|
|
|
// Test nesting objects and arrays.
|
|
testSingleDocument(
|
|
{"a.b.c.d.e": {$slice: [1, 1]}},
|
|
{a: [{b: [{c: [{d: [{e: [1, 2, 3]}]}]}]}]},
|
|
{a: [{b: [{c: [{d: [{e: [2]}]}]}]}]},
|
|
);
|
|
|
|
// Test that inclusion, exclusion and expression projections have unlimited array depth in queries
|
|
// with $slice. We use docEq() for this assertion in particular because of field ordering
|
|
// differences between the classic engine and SBE.
|
|
testSingleDocument(
|
|
{a: {b: {c: {$slice: [1, 1]}, d: 1}}},
|
|
{
|
|
a: [
|
|
{b: {c: [1, 2, 3], d: 123}},
|
|
[{b: {c: [1, 2, 3], d: 123}}, {b: {c: [4, 5, 6], d: 456}}],
|
|
{
|
|
b: [
|
|
{c: [1, 2, 3], d: 123},
|
|
{c: [4, 5, 6], d: 456},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 123}], [{c: [4, 5, 6], d: 456}]]},
|
|
],
|
|
},
|
|
{
|
|
a: [
|
|
{b: {c: [2], d: 123}},
|
|
[{b: {c: [1, 2, 3], d: 123}}, {b: {c: [4, 5, 6], d: 456}}],
|
|
{
|
|
b: [
|
|
{c: [2], d: 123},
|
|
{c: [5], d: 456},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 123}], [{c: [4, 5, 6], d: 456}]]},
|
|
],
|
|
},
|
|
true,
|
|
assert.docEq,
|
|
);
|
|
|
|
testSingleDocument(
|
|
{a: {b: {c: {$slice: [1, 1]}, d: 0}}},
|
|
{
|
|
a: [
|
|
{b: {c: [1, 2, 3], d: 123}},
|
|
[{b: {c: [1, 2, 3], d: 123}}, {b: {c: [4, 5, 6], d: 456}}],
|
|
{
|
|
b: [
|
|
{c: [1, 2, 3], d: 123},
|
|
{c: [4, 5, 6], d: 456},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 123}], [{c: [4, 5, 6], d: 456}]]},
|
|
],
|
|
},
|
|
{
|
|
a: [
|
|
{b: {c: [2]}},
|
|
[{b: {c: [1, 2, 3]}}, {b: {c: [4, 5, 6]}}],
|
|
{b: [{c: [2]}, {c: [5]}]},
|
|
{b: [[{c: [1, 2, 3]}], [{c: [4, 5, 6]}]]},
|
|
],
|
|
},
|
|
);
|
|
|
|
// We use docEq() for this assertion in particular because of field ordering differences between the
|
|
// classic engine and SBE.
|
|
testSingleDocument(
|
|
{a: {b: {c: {$slice: [1, 1]}, d: {$add: [1, 2, 3]}}}},
|
|
{
|
|
a: [
|
|
{b: {c: [1, 2, 3], d: 123}},
|
|
[{b: {c: [1, 2, 3], d: 123}}, {b: {c: [4, 5, 6], d: 456}}],
|
|
{
|
|
b: [
|
|
{c: [1, 2, 3], d: 123},
|
|
{c: [4, 5, 6], d: 456},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 123}], [{c: [4, 5, 6], d: 456}]]},
|
|
],
|
|
},
|
|
{
|
|
a: [
|
|
{b: {c: [2], d: 6}},
|
|
[{b: {c: [1, 2, 3], d: 6}}, {b: {c: [4, 5, 6], d: 6}}],
|
|
{
|
|
b: [
|
|
{c: [2], d: 6},
|
|
{c: [5], d: 6},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 6}], [{c: [4, 5, 6], d: 6}]]},
|
|
],
|
|
},
|
|
true,
|
|
assert.docEq,
|
|
);
|
|
|
|
// We use docEq() for this assertion in particular because of field ordering differences between the
|
|
// classic engine and SBE.
|
|
testSingleDocument(
|
|
{a: {b: {c: {$slice: [1, 1]}, d: {$add: [{$abs: "$e"}, -4]}}}},
|
|
{
|
|
a: [
|
|
{b: {c: [1, 2, 3], d: 123}},
|
|
[{b: {c: [1, 2, 3], d: 123}}, {b: {c: [4, 5, 6], d: 456}}],
|
|
{
|
|
b: [
|
|
{c: [1, 2, 3], d: 123},
|
|
{c: [4, 5, 6], d: 456},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 123}], [{c: [4, 5, 6], d: 456}]]},
|
|
],
|
|
e: -10,
|
|
},
|
|
{
|
|
a: [
|
|
{b: {c: [2], d: 6}},
|
|
[{b: {c: [1, 2, 3], d: 6}}, {b: {c: [4, 5, 6], d: 6}}],
|
|
{
|
|
b: [
|
|
{c: [2], d: 6},
|
|
{c: [5], d: 6},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: 6}], [{c: [4, 5, 6], d: 6}]]},
|
|
],
|
|
},
|
|
true,
|
|
assert.docEq,
|
|
);
|
|
|
|
// Test multiple $slice operators in the same projection.
|
|
testSingleDocument(
|
|
{a: {b: {c: {$slice: [1, 1]}, d: {$slice: -1}}}},
|
|
{
|
|
a: [
|
|
{b: {c: [1, 2, 3], d: [4, 5, 6]}},
|
|
[{b: {c: [1, 2, 3], d: [4, 5, 6]}}, {b: {c: [7, 8, 9], d: [10, 11, 12]}}],
|
|
{
|
|
b: [
|
|
{c: [1, 2, 3], d: [4, 5, 6]},
|
|
{c: [7, 8, 9], d: [10, 11, 12]},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: [4, 5, 6]}], [{c: [7, 8, 9], d: [10, 11, 12]}]]},
|
|
],
|
|
},
|
|
{
|
|
a: [
|
|
{b: {c: [2], d: [6]}},
|
|
[{b: {c: [1, 2, 3], d: [4, 5, 6]}}, {b: {c: [7, 8, 9], d: [10, 11, 12]}}],
|
|
{
|
|
b: [
|
|
{c: [2], d: [6]},
|
|
{c: [8], d: [12]},
|
|
],
|
|
},
|
|
{b: [[{c: [1, 2, 3], d: [4, 5, 6]}], [{c: [7, 8, 9], d: [10, 11, 12]}]]},
|
|
],
|
|
},
|
|
);
|
|
|
|
testSingleDocument(
|
|
{a: {b: {$slice: [1, 1]}}, c: {d: {$slice: -1}}},
|
|
{
|
|
a: [
|
|
{b: [1, 2, 3]},
|
|
[{b: [1, 2, 3]}, {b: [4, 5, 6]}],
|
|
{
|
|
b: [
|
|
[1, 2, 3],
|
|
[4, 5, 6],
|
|
],
|
|
},
|
|
],
|
|
c: [
|
|
{d: [1, 2, 3]},
|
|
[{d: [1, 2, 3]}, {d: [4, 5, 6]}],
|
|
{
|
|
d: [
|
|
[1, 2, 3],
|
|
[4, 5, 6],
|
|
[7, 8, 9],
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
a: [{b: [2]}, [{b: [1, 2, 3]}, {b: [4, 5, 6]}], {b: [[4, 5, 6]]}],
|
|
c: [{d: [3]}, [{d: [1, 2, 3]}, {d: [4, 5, 6]}], {d: [[7, 8, 9]]}],
|
|
},
|
|
);
|
|
|
|
// Test that if $slice cannot be applied, the field value is still included in the output.
|
|
// Case when path for $slice contains object.
|
|
testSingleDocument({a: {$slice: 1}}, {a: {c: 123}}, {a: {c: 123}});
|
|
|
|
// we use docEq() for this assertion in particular because of field ordering differences between the
|
|
// classic engine and SBE.
|
|
testSingleDocument({a: {$slice: 1}, d: 1}, {a: {c: 123}, d: 456}, {a: {c: 123}, d: 456}, true, assert.docEq);
|
|
|
|
testSingleDocument({a: {$slice: 1}, d: 0}, {a: {c: 123}, d: 456}, {a: {c: 123}});
|
|
|
|
// Case when path for $slice does not exist in the document.
|
|
testSingleDocument({a: {$slice: 1}}, {b: {e: 123}}, {b: {e: 123}});
|
|
|
|
testSingleDocument({a: {$slice: 1}, d: 1}, {b: {e: 123}, d: 456}, {d: 456});
|
|
|
|
testSingleDocument({a: {$slice: 1}, d: 0}, {b: {e: 123}, d: 456}, {b: {e: 123}});
|
|
|
|
// Case when path for $slice traverses through deep arrays.
|
|
testSingleDocument({"a.b.c": {$slice: 1}}, {a: [[[{b: [[[{c: [1, 2, 3]}]]]}]]]}, {a: [[[{b: [[[{c: [1, 2, 3]}]]]}]]]});
|
|
|
|
// We use docEq() for this assertion in particular because of field ordering differences between the
|
|
// classic engine and SBE.
|
|
testSingleDocument(
|
|
{"a.b.c": {$slice: 1}, d: 1},
|
|
{a: [[[{b: [[[{c: [1, 2, 3]}]]]}]]], d: 456},
|
|
{a: [[[{b: [[[{c: [1, 2, 3]}]]]}]]], d: 456},
|
|
true,
|
|
assert.docEq,
|
|
);
|
|
|
|
testSingleDocument(
|
|
{"a.b.c": {$slice: 1}, d: 0},
|
|
{a: [[[{b: [[[{c: [1, 2, 3]}]]]}]]], d: 456},
|
|
{a: [[[{b: [[[{c: [1, 2, 3]}]]]}]]]},
|
|
);
|
|
|
|
// Test that even though $slice cannot be applied to deep arrays, fields are still filtered in the
|
|
// objects inside these deep arrays for inclusion projections.
|
|
testSingleDocument(
|
|
{e: 1, "a.b": {$slice: 1}},
|
|
{e: 123, a: [[[{b: [1, 2, 3], d: 4, f: 5}]]]},
|
|
{e: 123, a: [[[{b: [1, 2, 3]}]]]},
|
|
);
|
|
|
|
// Test $slice with an inclusion/exclusion projection for _id field.
|
|
testSingleDocument({_id: 1, a: {$slice: [1, 1]}}, {_id: 123, a: [1, 2, 3]}, {_id: 123, a: [2]}, false /* deleteId */);
|
|
|
|
testSingleDocument({_id: 0, a: {$slice: [1, 1]}}, {_id: 123, a: [1, 2, 3]}, {a: [2]}, false /* deleteId */);
|