/** * Basic tests for the $concatArrays accumulator. * @tags: [requires_fcv_81] */ import {assertErrorCode} from "jstests/aggregation/extras/utils.js"; const coll = db["accumulators_concat_arrays"]; assert(coll.drop()); // Test that $concatArrays correctly concatenates arrays, preserves sort order and preserves order // of the elements inside the concatenated arrays. assert.commandWorked( coll.insert([ {_id: 0, author: "Adrian", publisher: "Pub1", books: ["Adrian Book 0", "Adrian Book 1"]}, {_id: 1, author: "Militsa", publisher: "Pub1", books: ["Militsa Book 0"]}, {_id: 2, author: "Hana", publisher: "Pub1", books: ["Hana Book 0", "Hana Book 1"]}, ]), ); let result = coll .aggregate([ {$match: {_id: {$in: [0, 1, 2]}}}, {$sort: {_id: 1}}, {$group: {_id: null, allBooks: {$concatArrays: "$books"}}}, ]) .toArray(); assert.eq(result, [ { _id: null, allBooks: ["Adrian Book 0", "Adrian Book 1", "Militsa Book 0", "Hana Book 0", "Hana Book 1"], }, ]); // Redo the aggregation with the reverse sort to ensure sort order is really preserverd and we're // not just looking at the docs in the order they are found in the collection. result = coll .aggregate([ {$match: {_id: {$in: [0, 1, 2]}}}, {$sort: {_id: -1}}, {$group: {_id: null, allBooks: {$concatArrays: "$books"}}}, ]) .toArray(); assert.eq(result, [ { _id: null, allBooks: ["Hana Book 0", "Hana Book 1", "Militsa Book 0", "Adrian Book 0", "Adrian Book 1"], }, ]); // $concatArrays should concatenate arrays, and any nested arrays should remain array values. assert.commandWorked( coll.insert({ _id: 3, author: "Ben", publisher: "Pub2", books: [["Ben Series 0 Book 0", "Ben Series 0 Book 1"], ["Ben Series 1 Book 0"], "Ben Book 4"], }), ); result = coll .aggregate([ {$match: {_id: {$in: [1, 3]}}}, {$sort: {_id: 1}}, {$group: {_id: null, allBooks: {$concatArrays: "$books"}}}, ]) .toArray(); assert.eq(result, [ { _id: null, allBooks: [ "Militsa Book 0", ["Ben Series 0 Book 0", "Ben Series 0 Book 1"], ["Ben Series 1 Book 0"], "Ben Book 4", ], }, ]); // $concatArrays should 'skip over' documents that do not have field. // Importantly, do not create a null element for documents that do not have the referenced field. assert.commandWorked( coll.insert([ {_id: 4, author: "Nick", publisher: "Pub3", books: ["Smile :)"]}, {_id: 5, author: "Santiago", publisher: "Pub3"}, {_id: 6, author: "Matt", publisher: "Pub3", books: ["Happy!"]}, ]), ); result = coll .aggregate([ {$match: {_id: {$in: [4, 5, 6]}}}, {$sort: {_id: 1}}, {$group: {_id: null, allBooks: {$concatArrays: "$books"}}}, ]) .toArray(); assert.eq(result, [{_id: null, allBooks: ["Smile :)", "Happy!"]}]); // Test for errors on non-array types ($concatArrays only supports arrays). const notArrays = [1, "string", {object: "object"}, null]; for (const notAnArray of notArrays) { assert.commandWorked( coll.insert([ { _id: "doesNotMatter", author: "doesNotMatter", publisher: "doesNotMatter", books: notAnArray, }, ]), ); assertErrorCode(coll, [{$group: {_id: null, allBooks: {$concatArrays: "$books"}}}], ErrorCodes.TypeMismatch); assert.commandWorked(coll.deleteOne({_id: "doesNotMatter"})); } // Basic test of $concatArrays with grouping. result = coll .aggregate([ {$match: {_id: {$in: [0, 1, 3]}}}, {$sort: {_id: 1}}, {$group: {_id: "$publisher", booksByPublisher: {$concatArrays: "$books"}}}, {$sort: {_id: 1}}, ]) .toArray(); assert.eq(result, [ {_id: "Pub1", booksByPublisher: ["Adrian Book 0", "Adrian Book 1", "Militsa Book 0"]}, { _id: "Pub2", booksByPublisher: [["Ben Series 0 Book 0", "Ben Series 0 Book 1"], ["Ben Series 1 Book 0"], "Ben Book 4"], }, ]); // $concatArrays dotted field. assert(coll.drop()); assert.commandWorked( coll.insert([ {_id: 1, a: {b: [1, 2, 3]}}, {_id: 2, a: {b: [4, 5, 6]}}, {_id: 3, a: {b: [7, 8, 9]}}, ]), ); result = coll.aggregate([{$sort: {_id: 1}}, {$group: {_id: null, nums: {$concatArrays: "$a.b"}}}]).toArray(); assert.eq(result, [{_id: null, nums: [1, 2, 3, 4, 5, 6, 7, 8, 9]}]); assert(coll.drop()); // $concatArrays dotted field, array halfway on path. assert(coll.drop()); assert.commandWorked( coll.insert([ {_id: 1, a: [{b: [1, 2, 3]}, {b: [4, 5, 6]}]}, {_id: 2, a: [{b: [7, 8, 9]}, {b: [10, 11, 12]}]}, {_id: 3, a: [{b: [7, 8, 9]}]}, ]), ); result = coll.aggregate([{$sort: {_id: 1}}, {$group: {_id: null, nums: {$concatArrays: "$a.b"}}}]).toArray(); assert.eq(result, [ { _id: null, nums: [ [1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [7, 8, 9], ], }, ]); assert(coll.drop()); // Basic correctness test for $concatArrays used in $bucket (see bucketAuto_concatArrays.js for // $bucketAuto tests). Though $bucket and $bucketAuto use accumulators in the same way that $group // does, the test below verifies that everything works properly with serialization and reporting // results. assert(coll.drop()); const docs = []; for (let i = 0; i < 10; i++) { docs.push({_id: i, arr: [i]}); } coll.insertMany(docs); result = coll .aggregate([ {$sort: {_id: 1}}, { $bucket: { groupBy: "$_id", boundaries: [0, 5, 10], output: {nums: {$concatArrays: "$arr"}}, }, }, ]) .toArray(); assert.eq(result, [ {"_id": 0, "nums": [0, 1, 2, 3, 4]}, {"_id": 5, "nums": [5, 6, 7, 8, 9]}, ]); assert(coll.drop());