/** * Tests that a batch size of zero can be used for aggregation commands, and all data can be * retrieved via getMores. */ (function() { "use strict"; const mongodOptions = {}; const conn = MongoRunner.runMongod(mongodOptions); assert.neq(null, conn, "mongod failed to start with options " + tojson(mongodOptions)); const testDB = conn.getDB("test"); const coll = testDB[jsTest.name]; coll.drop(); // Test that an aggregate is successful on a non-existent collection. assert.eq(0, coll.aggregate([]).toArray().length, "expected no results from an aggregation on an empty collection"); // Test that an aggregate is successful on a non-existent collection with a batchSize of 0, and // that a getMore will succeed with an empty result set. let res = assert.commandWorked( testDB.runCommand({aggregate: coll.getName(), pipeline: [], cursor: {batchSize: 0}})); let cursor = new DBCommandCursor(testDB, res); assert.eq( 0, cursor.itcount(), "expected no results from getMore of aggregation on empty collection"); // Test that an aggregation can return *all* matching data via getMores if the initial aggregate // used a batchSize of 0. const nDocs = 1000; const bulk = coll.initializeUnorderedBulkOp(); for (let i = 0; i < nDocs; i++) { bulk.insert({_id: i, stringField: "string"}); } assert.commandWorked(bulk.execute()); res = assert.commandWorked( testDB.runCommand({aggregate: coll.getName(), pipeline: [], cursor: {batchSize: 0}})); cursor = new DBCommandCursor(testDB, res); assert.eq(nDocs, cursor.itcount(), "expected all results to be returned via getMores"); // Test that an error in a getMore will destroy the cursor. function assertNumOpenCursors(nExpectedOpen) { let serverStatus = assert.commandWorked(testDB.adminCommand({serverStatus: 1})); assert.eq(nExpectedOpen, serverStatus.metrics.cursor.open.total, "expected to find " + nExpectedOpen + " open cursor(s): " + tojson(serverStatus.metrics.cursor)); } // Issue an aggregate command that will fail *at runtime*, so the error will happen in a // getMore. assertNumOpenCursors(0); res = assert.commandWorked(testDB.runCommand({ aggregate: coll.getName(), pipeline: [{$project: {invalidComputation: {$add: [1, "$stringField"]}}}], cursor: {batchSize: 0} })); cursor = new DBCommandCursor(testDB, res); assertNumOpenCursors(1); assert.throws(() => cursor.itcount(), [], "expected getMore to fail"); assertNumOpenCursors(0); // Test that an error in a getMore using a $out stage will destroy the cursor. This test is // intended to reproduce SERVER-26608. // Issue an aggregate command that will fail *at runtime*, so the error will happen in a // getMore. res = assert.commandWorked(testDB.runCommand({ aggregate: coll.getName(), pipeline: [{$out: "validated_collection"}], cursor: {batchSize: 0} })); cursor = new DBCommandCursor(testDB, res); assertNumOpenCursors(1); // Add a document validation rule to the $out collection so that insertion will fail. assert.commandWorked( testDB.runCommand({create: "validated_collection", validator: {stringField: {$type: "int"}}})); assert.throws(() => cursor.itcount(), [], "expected getMore to fail"); assertNumOpenCursors(0); MongoRunner.stopMongod(conn); }());