mirror of https://github.com/mongodb/mongo
205 lines
7.2 KiB
JavaScript
205 lines
7.2 KiB
JavaScript
// Confirm correctness of $concatArrays expression evaluation.
|
|
|
|
// When SBE is enabled, we expect that each $concatArrays expression will be pushed down into the
|
|
// query layer. This does not happen when we wrap aggregations in facets, so we prevent this
|
|
// test from running in the 'aggregation_facet_unwind_passthrough' suite.
|
|
// @tags: [
|
|
// do_not_wrap_aggregations_in_facets,
|
|
//
|
|
// # This test makes assertions about queries erroring or returning null depending on whether SBE
|
|
// # is used or not. SBE may be chosen if an implicit column index is created, so we ban this
|
|
// # tests from implicit index creation suites.
|
|
// assumes_no_implicit_index_creation,
|
|
// ]
|
|
import "jstests/libs/query/sbe_assert_error_override.js";
|
|
|
|
import {assertArrayEq} from "jstests/aggregation/extras/utils.js";
|
|
import {checkSbeFullyEnabled} from "jstests/libs/query/sbe_util.js";
|
|
|
|
const coll = db.projection_expr_concat_arrays;
|
|
coll.drop();
|
|
|
|
assert.commandWorked(
|
|
coll.insertOne({
|
|
int_arr: [1, 2, 3, 4],
|
|
dbl_arr: [10.0, 20.1, 20.4, 50.5],
|
|
nested_arr: [["an", "array"], "arr", [[], [[], "a", "b"]]],
|
|
str_arr: ["a", "b", "c"],
|
|
obj_arr: [{a: 1, b: 2}, {c: 3}, {d: 4, e: 5}],
|
|
null_arr: [null, null, null],
|
|
one_null_arr: [null],
|
|
one_str_arr: ["one"],
|
|
empty_arr: [],
|
|
null_val: null,
|
|
str_val: "a string",
|
|
dbl_val: 2.0,
|
|
int_val: 1,
|
|
obj_val: {a: 1, b: "two"},
|
|
}),
|
|
);
|
|
|
|
function runAndAssert(operands, expectedResult) {
|
|
assertArrayEq({
|
|
actual: coll.aggregate([{$project: {f: {$concatArrays: operands}}}]).map((doc) => doc.f),
|
|
expected: expectedResult,
|
|
});
|
|
}
|
|
function runAndAssertNull(operands) {
|
|
runAndAssert(operands, [null]);
|
|
}
|
|
|
|
function runAndAssertThrows(operands) {
|
|
const error = assert.throws(() => coll.aggregate([{$project: {f: {$concatArrays: operands}}}]).toArray());
|
|
assert.commandFailedWithCode(error, 28664);
|
|
}
|
|
|
|
function runAndAssertThrowsOrNull(operands) {
|
|
try {
|
|
runAndAssertNull(operands);
|
|
} catch (error) {
|
|
assert.commandFailedWithCode(error, 28664);
|
|
}
|
|
}
|
|
|
|
runAndAssert(["$int_arr"], [[1, 2, 3, 4]]);
|
|
runAndAssert([[0], "$int_arr", [5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7]]);
|
|
runAndAssert(["$int_arr", "$str_arr"], [[1, 2, 3, 4, "a", "b", "c"]]);
|
|
runAndAssert(
|
|
["$obj_arr", "$obj_arr", "$null_arr"],
|
|
[[{a: 1, b: 2}, {c: 3}, {d: 4, e: 5}, {a: 1, b: 2}, {c: 3}, {d: 4, e: 5}, null, null, null]],
|
|
);
|
|
runAndAssert(
|
|
["$int_arr", "$str_arr", "$nested_arr"],
|
|
[[1, 2, 3, 4, "a", "b", "c", ["an", "array"], "arr", [[], [[], "a", "b"]]]],
|
|
);
|
|
runAndAssert(["$int_arr", "$obj_arr"], [[1, 2, 3, 4, {a: 1, b: 2}, {c: 3}, {d: 4, e: 5}]]);
|
|
runAndAssert(["$obj_arr"], [[{a: 1, b: 2}, {c: 3}, {d: 4, e: 5}]]);
|
|
runAndAssert(
|
|
[
|
|
"$obj_arr",
|
|
[
|
|
{o: 123, b: 1},
|
|
{y: "o", d: "a"},
|
|
],
|
|
],
|
|
[[{a: 1, b: 2}, {c: 3}, {d: 4, e: 5}, {o: 123, b: 1}, {y: "o", d: "a"}]],
|
|
);
|
|
|
|
// Confirm that arrays containing null can be concatenated.
|
|
runAndAssert(["$null_arr"], [[null, null, null]]);
|
|
runAndAssert([[null], "$null_arr"], [[null, null, null, null]]);
|
|
runAndAssert("$one_null_arr", [[null]]);
|
|
runAndAssert(
|
|
["$null_arr", "$one_null_arr", "$int_arr", "$null_arr"],
|
|
[[null, null, null, null, 1, 2, 3, 4, null, null, null]],
|
|
);
|
|
|
|
// Test operands that form more complex expressions.
|
|
runAndAssert([{$concatArrays: "$int_arr"}], [[1, 2, 3, 4]]);
|
|
runAndAssert(
|
|
[{$concatArrays: "$int_arr"}, {$concatArrays: {$concatArrays: "$str_arr"}}],
|
|
[[1, 2, 3, 4, "a", "b", "c"]],
|
|
);
|
|
runAndAssert(
|
|
[
|
|
"$str_arr",
|
|
{$filter: {input: "$int_arr", as: "num", cond: {$and: [{$gte: ["$$num", 2]}, {$lte: ["$$num", 3]}]}}},
|
|
"$int_arr",
|
|
],
|
|
[["a", "b", "c", 2, 3, 1, 2, 3, 4]],
|
|
);
|
|
|
|
// Confirm that empty arrays can be concatenated with variables.
|
|
runAndAssert(
|
|
["$str_arr", {$filter: {input: [], cond: {$isArray: [{$concatArrays: [[], "$$this"]}]}}}],
|
|
[["a", "b", "c"]],
|
|
);
|
|
|
|
// Concatenation with no arguments results in the empty array.
|
|
runAndAssert([], [[]]);
|
|
|
|
// Confirm that having any combination of null or missing inputs and valid inputs produces null.
|
|
runAndAssertNull(["$int_arr", "$null_val"]);
|
|
runAndAssertNull(["$int_arr", null]);
|
|
runAndAssertNull([null, "$int_arr", "$str_arr"]);
|
|
runAndAssertNull(["$int_arr", null, "$str_arr"]);
|
|
runAndAssertNull(["$null_val", "$str_arr", "$int_arr"]);
|
|
runAndAssertNull(["$str_arr", "$null_val", "$int_arr"]);
|
|
runAndAssertNull(["$int_arr", "$not_a_field"]);
|
|
runAndAssertNull(["$not_a_field", "$str_arr", "$int_arr"]);
|
|
runAndAssertNull(["$not_a_field"]);
|
|
runAndAssertNull(["$null_val"]);
|
|
runAndAssertNull(["$not_a_field", "$null_val"]);
|
|
runAndAssertNull(["$null_val", "$not_a_field"]);
|
|
runAndAssertNull([{$concatArrays: "$int_arr"}, null, {$concatArrays: {$concatArrays: ["$obj_arr", "$str_arr"]}}]);
|
|
|
|
// Confirm edge case where if null precedes non-array input, null is returned.
|
|
runAndAssertNull(["$int_arr", "$null_val", "$int_val"]);
|
|
runAndAssertNull(["$null_val", null, "$null_val"]);
|
|
|
|
//
|
|
// Confirm error cases.
|
|
//
|
|
|
|
// Confirm concatenating non-array and non-values produces an error.
|
|
runAndAssertThrows(["$dbl_val"]);
|
|
runAndAssertThrows(["$str_val"]);
|
|
runAndAssertThrows(["$int_val"]);
|
|
runAndAssertThrows([123]);
|
|
runAndAssertThrows(["some_val", [1, 2, 3]]);
|
|
runAndAssertThrows(["$obj_val"]);
|
|
runAndAssertThrows(["$int_arr", "$int_val"]);
|
|
runAndAssertThrows(["$dbl_arr", "$dbl_val"]);
|
|
|
|
// Confirm edge case where if invalid input precedes null or missing inputs, the command fails.
|
|
// Depending on execution engine null might be returned before we throw an error. A query might
|
|
// execute in Classic or SBE depending on a lot of factors: plan cache, runtime planners and so
|
|
// on, so we can't assert exactly what will happen.
|
|
runAndAssertThrowsOrNull(["$int_arr", "$dbl_val", "$null_val"]);
|
|
runAndAssertThrowsOrNull(["$int_arr", "some_string_value", "$null_val"]);
|
|
runAndAssertThrowsOrNull(["$dbl_val", "$null_val"]);
|
|
runAndAssertThrowsOrNull(["$int_arr", "$int_val", "$not_a_field"]);
|
|
runAndAssertThrowsOrNull(["$int_val", "$not_a_field"]);
|
|
runAndAssertThrowsOrNull(["$int_val", "$not_a_field", "$null_val"]);
|
|
runAndAssertThrows(["$int_arr", 32]);
|
|
|
|
// Clear collection.
|
|
assert(coll.drop());
|
|
|
|
// Test case where find returns multiple documents.
|
|
assert.commandWorked(
|
|
coll.insertMany([
|
|
{arr1: [42, 35.0, 197865432], arr2: ["albatross", "abbacus", "alien"]},
|
|
{arr1: [1], arr2: ["albatross", "abbacus", "alien"]},
|
|
{arr1: [1, 2, 3, 4, 5, 6, 11, 12, 23], arr2: []},
|
|
{arr1: [], arr2: ["foo", "bar"]},
|
|
{arr1: [], arr2: []},
|
|
{arr1: [1, 2, 3, 4, 5, 6, 11, 12, 23], arr2: null},
|
|
{some_field: "foo"},
|
|
]),
|
|
);
|
|
runAndAssert(
|
|
["$arr1", "$arr2"],
|
|
[
|
|
[42, 35.0, 197865432, "albatross", "abbacus", "alien"],
|
|
[1, "albatross", "abbacus", "alien"],
|
|
[1, 2, 3, 4, 5, 6, 11, 12, 23],
|
|
["foo", "bar"],
|
|
[],
|
|
null,
|
|
null,
|
|
],
|
|
);
|
|
runAndAssert(
|
|
["$arr1", [1, 2, 3], "$arr2"],
|
|
[
|
|
[42, 35.0, 197865432, 1, 2, 3, "albatross", "abbacus", "alien"],
|
|
[1, 1, 2, 3, "albatross", "abbacus", "alien"],
|
|
[1, 2, 3, 4, 5, 6, 11, 12, 23, 1, 2, 3],
|
|
["foo", 1, 2, 3, "bar"],
|
|
[1, 2, 3],
|
|
null,
|
|
null,
|
|
],
|
|
);
|