// Tests that the sort order is obeyed when an aggregation requests sorted results that are // scattered across multiple shards. (function() { 'use strict'; const shardingTest = new ShardingTest({shards: 2}); const db = shardingTest.getDB("test"); const coll = db.sharded_agg_sort; coll.drop(); assert.commandWorked(shardingTest.s0.adminCommand({enableSharding: db.getName()})); shardingTest.ensurePrimaryShard(db.getName(), shardingTest.shard1.shardName); assert.commandWorked( shardingTest.s0.adminCommand({shardCollection: coll.getFullName(), key: {_id: 1}})); const nDocs = 10; const yValues = [ "abc", "ABC", null, 1, NumberLong(2), NumberDecimal(-20), MinKey, MaxKey, BinData(0, ""), [3, 4], ]; const bulkOp = coll.initializeOrderedBulkOp(); for (var i = 0; i < nDocs; ++i) { bulkOp.insert({_id: i, x: Math.floor(i / 2), y: yValues[i]}); } assert.writeOK(bulkOp.execute()); // Split the data into 3 chunks assert.commandWorked( shardingTest.s0.adminCommand({split: coll.getFullName(), middle: {_id: 3}})); assert.commandWorked( shardingTest.s0.adminCommand({split: coll.getFullName(), middle: {_id: 6}})); // Migrate the middle chunk to another shard assert.commandWorked(shardingTest.s0.adminCommand({ movechunk: coll.getFullName(), find: {_id: 5}, to: shardingTest.getOther(shardingTest.getPrimaryShard(db.getName())).name })); function assertResultsEqual({actual, expected}) { const resultsAsString = " actual: " + tojson(actual) + "\n expected: " + tojson(expected); assert.eq( actual.length, expected.length, `different number of results:\n${resultsAsString}`); for (let i = 0; i < actual.length; i++) { assert.eq( actual[i], expected[i], `different results at index ${i}:\n${resultsAsString}`); } } function testSorts() { // Test a basic sort by _id. assertResultsEqual({ actual: coll.aggregate([{$sort: {_id: 1}}]).toArray(), expected: [ {_id: 0, x: 0, y: "abc"}, {_id: 1, x: 0, y: "ABC"}, {_id: 2, x: 1, y: null}, {_id: 3, x: 1, y: 1}, {_id: 4, x: 2, y: NumberLong(2)}, {_id: 5, x: 2, y: NumberDecimal(-20)}, {_id: 6, x: 3, y: MinKey}, {_id: 7, x: 3, y: MaxKey}, {_id: 8, x: 4, y: BinData(0, "")}, {_id: 9, x: 4, y: [3, 4]}, ], }); assertResultsEqual({ actual: coll.aggregate([{$sort: {_id: 1}}, {$project: {_id: 1}}]).toArray(), expected: new Array(nDocs).fill().map(function(_, index) { return {_id: index}; }), }); // Test a compound sort. assertResultsEqual({ actual: coll.aggregate([{$sort: {x: 1, y: 1}}]).toArray(), expected: [ {_id: 1, x: 0, y: "ABC"}, {_id: 0, x: 0, y: "abc"}, {_id: 2, x: 1, y: null}, {_id: 3, x: 1, y: 1}, {_id: 5, x: 2, y: NumberDecimal(-20)}, {_id: 4, x: 2, y: NumberLong(2)}, {_id: 6, x: 3, y: MinKey}, {_id: 7, x: 3, y: MaxKey}, {_id: 9, x: 4, y: [3, 4]}, {_id: 8, x: 4, y: BinData(0, "")}, ], }); assertResultsEqual({ actual: coll.aggregate([{$sort: {x: 1, y: 1}}, {$project: {_id: 0, x: 1, y: 1}}]).toArray(), expected: [ {x: 0, y: "ABC"}, {x: 0, y: "abc"}, {x: 1, y: null}, {x: 1, y: 1}, {x: 2, y: NumberDecimal(-20)}, {x: 2, y: NumberLong(2)}, {x: 3, y: MinKey}, {x: 3, y: MaxKey}, {x: 4, y: [3, 4]}, {x: 4, y: BinData(0, "")}, ], }); // Test a compound sort with a missing field. assertResultsEqual({ actual: coll.aggregate({$sort: {missing: -1, x: 1, _id: -1}}).toArray(), expected: [ {_id: 1, x: 0, y: "ABC"}, {_id: 0, x: 0, y: "abc"}, {_id: 3, x: 1, y: 1}, {_id: 2, x: 1, y: null}, {_id: 5, x: 2, y: NumberDecimal(-20)}, {_id: 4, x: 2, y: NumberLong(2)}, {_id: 7, x: 3, y: MaxKey}, {_id: 6, x: 3, y: MinKey}, {_id: 9, x: 4, y: [3, 4]}, {_id: 8, x: 4, y: BinData(0, "")}, ] }); } testSorts(); assert.commandWorked(coll.createIndex({x: 1})); testSorts(); assert.commandWorked(coll.createIndex({x: 1, y: 1})); testSorts(); assert.commandWorked(coll.createIndex({missing: 1, x: -1})); testSorts(); assert.commandWorked(coll.createIndex({missing: -1, x: 1, _id: -1})); testSorts(); // Test that a sort including the text score is merged properly in a sharded cluster. const textColl = db.sharded_agg_sort_text; assert.commandWorked( shardingTest.s0.adminCommand({shardCollection: textColl.getFullName(), key: {_id: 1}})); assert.writeOK(textColl.insert([ {_id: 0, text: "apple"}, {_id: 1, text: "apple orange banana apple"}, {_id: 2, text: "apple orange"}, {_id: 3, text: "apple orange banana apple apple banana"}, {_id: 4, text: "apple orange banana"}, {_id: 5, text: "apple orange banana apple apple"}, ])); // Split the data into 3 chunks assert.commandWorked( shardingTest.s0.adminCommand({split: textColl.getFullName(), middle: {_id: 2}})); assert.commandWorked( shardingTest.s0.adminCommand({split: textColl.getFullName(), middle: {_id: 4}})); // Migrate the middle chunk to another shard assert.commandWorked(shardingTest.s0.adminCommand({ movechunk: textColl.getFullName(), find: {_id: 3}, to: shardingTest.getOther(shardingTest.getPrimaryShard(db.getName())).name })); assert.commandWorked(textColl.createIndex({text: "text"})); assertResultsEqual({ actual: textColl .aggregate([ {$match: {$text: {$search: "apple banana orange"}}}, {$sort: {x: {$meta: "textScore"}}} ]) .toArray(), expected: [ {_id: 3, text: "apple orange banana apple apple banana"}, {_id: 5, text: "apple orange banana apple apple"}, {_id: 1, text: "apple orange banana apple"}, {_id: 4, text: "apple orange banana"}, {_id: 2, text: "apple orange"}, {_id: 0, text: "apple"}, ], }); function assertSortedByMetaField(results) { for (let i = 0; i < results.length - 1; ++i) { assert(results[i].hasOwnProperty("meta"), `Expected all results to have "meta" field, found one without it at index ${i}`); assert.gte( results[i].meta, results[i + 1].meta, `Expected results to be sorted by "meta" field, descending. Detected unsorted` + ` results at index ${i}, entire result set: ${tojson(results)}`); } } assertSortedByMetaField(textColl .aggregate([ {$match: {$text: {$search: "apple banana orange"}}}, {$sort: {x: {$meta: "textScore"}}}, {$project: {_id: 0, meta: {$meta: "textScore"}}}, ]) .toArray()); assertSortedByMetaField(textColl .aggregate([ {$match: {$text: {$search: "apple banana orange"}}}, {$project: {_id: 0, meta: {$meta: "textScore"}}}, {$sort: {meta: -1}}, ]) .toArray()); assertSortedByMetaField(textColl .aggregate([ {$sample: {size: 10}}, {$project: {_id: 0, meta: {$meta: "randVal"}}}, ]) .toArray()); shardingTest.stop(); })();