mongo/jstests/aggregation/expressions/concat_arrays.js

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,
],
);