diff --git a/src/collection.ts b/src/collection.ts index abc1ca6da3..9d452f760d 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -77,7 +77,6 @@ import { RenameOperation, RenameOptions } from './operations/rename'; import { ReplaceOneOperation, ReplaceOptions } from './operations/replace_one'; import { CollStatsOperation, CollStatsOptions } from './operations/stats'; import { executeOperation } from './operations/execute_operation'; -import { EvalGroupOperation, GroupOperation } from './operations/group'; import type { Db } from './db'; import type { OperationOptions, Hint } from './operations/operation'; import type { IndexInformationOptions } from './operations/common_functions'; @@ -1510,76 +1509,6 @@ export class Collection implements OperationParent { ); } - /** - * Run a group command across a collection - * - * @deprecated MongoDB 3.6 or higher no longer supports the group command. We recommend rewriting using the aggregation framework. - * @param keys - An object, array or function expressing the keys to group by. - * @param condition - An optional condition that must be true for a row to be considered. - * @param initial - Initial value of the aggregation counter object. - * @param reduce - The reduce function aggregates (reduces) the objects iterated - * @param finalize - An optional function to be run on each item in the result set just before the item is returned. - * @param command - Specify if you wish to run using the internal group command or using eval, default is true. - * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided - */ - group( - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - keys: any, - condition: Document, - initial: Document, - // TODO: Use labeled tuples when api-extractor supports TS 4.0 - ...args: [/*reduce?:*/ any, /*finalize?:*/ any, /*command?:*/ any, /*callback?:*/ Callback] - ): Promise | void { - const callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined; - let reduce = args.length ? args.shift() : undefined; - let finalize = args.length ? args.shift() : undefined; - let command = args.length ? args.shift() : undefined; - let options = args.length ? args.shift() || {} : {}; - - // Make sure we are backward compatible - if (!(typeof finalize === 'function')) { - command = finalize; - finalize = undefined; - } - - if ( - !Array.isArray(keys) && - keys instanceof Object && - typeof keys !== 'function' && - !(keys._bsontype === 'Code') - ) { - keys = Object.keys(keys); - } - - if (typeof reduce === 'function') { - reduce = reduce.toString(); - } - - if (typeof finalize === 'function') { - finalize = finalize.toString(); - } - - // Set up the command as default - command = command == null ? true : command; - - options = resolveOptions(this, options); - - if (command == null) { - return executeOperation( - getTopology(this), - new EvalGroupOperation(this, keys, condition, initial, reduce, finalize, options), - callback - ); - } - - return executeOperation( - getTopology(this), - new GroupOperation(this, keys, condition, initial, reduce, finalize, options), - callback - ); - } - /** * Find and modify a document. * @@ -1694,8 +1623,3 @@ Collection.prototype.findAndRemove = deprecate( Collection.prototype.findAndRemove, 'collection.findAndRemove is deprecated. Use findOneAndDelete instead.' ); - -Collection.prototype.group = deprecate( - Collection.prototype.group, - 'MongoDB 3.6 or higher no longer supports the group command. We recommend rewriting using the aggregation framework.' -); diff --git a/src/operations/group.ts b/src/operations/group.ts deleted file mode 100644 index 3e5dee473b..0000000000 --- a/src/operations/group.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { CommandOperation, CommandOperationOptions } from './command'; -import { EvalOperation } from './eval'; -import { Code, Document } from '../bson'; -import type { Callback } from '../utils'; -import type { Server } from '../sdam/server'; -import type { Collection } from '../collection'; - -export type GroupOptions = CommandOperationOptions; - -/** @internal */ -export class GroupOperation extends CommandOperation { - collectionName: string; - keys: any; - condition: Document; - initial: any; - reduceFunction: Code; - finalize: Code; - - constructor( - collection: Collection, - keys: any, - condition: Document, - initial: Document, - reduce: any, - finalize: Code, - options?: GroupOptions - ) { - super(collection, options); - this.collectionName = collection.collectionName; - this.keys = keys; - this.condition = condition; - this.initial = initial; - this.finalize = finalize; - this.reduceFunction = reduce && reduce._bsontype === 'Code' ? reduce : new Code(reduce); - } - - execute(server: Server, callback: Callback): void { - const cmd: Document = { - group: { - ns: this.collectionName, - $reduce: this.reduceFunction, - cond: this.condition, - initial: this.initial, - out: 'inline' - } - }; - - // if finalize is defined - if (this.finalize != null) { - cmd.group.finalize = this.finalize; - } - - // Set up group selector - if ('function' === typeof this.keys || (this.keys && this.keys._bsontype === 'Code')) { - cmd.group.$keyf = - this.keys && this.keys._bsontype === 'Code' ? this.keys : new Code(this.keys); - } else { - const hash: { [key: string]: 1 } = {}; - this.keys.forEach((key: string) => { - hash[key] = 1; - }); - - cmd.group.key = hash; - } - - // Execute command - super.executeCommand(server, cmd, (err, result) => { - if (err) return callback(err); - callback(undefined, result.retval); - }); - } -} - -const groupFunction = - 'function () {\nvar c = db[ns].find(condition);\nvar map = new Map();\nvar reduce_function = reduce;\n\nwhile (c.hasNext()) {\nvar obj = c.next();\nvar key = {};\n\nfor (var i = 0, len = keys.length; i < len; ++i) {\nvar k = keys[i];\nkey[k] = obj[k];\n}\n\nvar aggObj = map.get(key);\n\nif (aggObj == null) {\nvar newObj = Object.extend({}, key);\naggObj = Object.extend(newObj, initial);\nmap.put(key, aggObj);\n}\n\nreduce_function(obj, aggObj);\n}\n\nreturn { "result": map.values() };\n}'; - -export class EvalGroupOperation extends EvalOperation { - constructor( - collection: Collection, - keys: string[], - condition: Document, - initial: Document, - reduce: any, - finalize: Code, - options?: GroupOptions - ) { - // Create execution scope - const scope = reduce != null && reduce._bsontype === 'Code' ? reduce.scope : {}; - - scope.ns = collection.collectionName; - scope.keys = keys; - scope.condition = condition; - scope.initial = initial; - - // Pass in the function text to execute within mongodb. - const groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';'); - - super(collection, new Code(groupfn, scope), undefined, options); - } -} diff --git a/src/sessions.ts b/src/sessions.ts index abff68c0bb..ca89d82dc1 100644 --- a/src/sessions.ts +++ b/src/sessions.ts @@ -717,14 +717,7 @@ class ServerSessionPool { // TODO: this should be codified in command construction // @see https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.rst#read-concern function commandSupportsReadConcern(command: Document, options?: Document): boolean { - if ( - command.aggregate || - command.count || - command.distinct || - command.find || - command.geoNear || - command.group - ) { + if (command.aggregate || command.count || command.distinct || command.find || command.geoNear) { return true; } diff --git a/test/functional/collations.test.js b/test/functional/collations.test.js index e313044e1d..9ee544d822 100644 --- a/test/functional/collations.test.js +++ b/test/functional/collations.test.js @@ -166,50 +166,6 @@ describe('Collation', function () { } }); - it('Successfully pass through collation to group command', { - metadata: { requires: { generators: true, topology: 'single', mongodb: '<=4.1.0' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; - - let commandResult; - testContext.server.setMessageHandler(request => { - var doc = request.document; - if (doc.ismaster) { - request.reply(primary[0]); - } else if (doc.group) { - commandResult = doc; - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .collection('test') - .group( - [], - { a: { $gt: 1 } }, - { count: 0 }, - 'function (obj, prev) { prev.count++; }', - 'function (obj, prev) { prev.count++; }', - true, - { collation: { caseLevel: true } } - ) - .then(() => { - expect(commandResult).to.have.property('collation'); - expect(commandResult.collation).to.eql({ caseLevel: true }); - return client.close(); - }); - }); - } - }); - it('Successfully pass through collation to mapReduce command', { metadata: { requires: { generators: true, topology: 'single' } }, diff --git a/test/functional/mapreduce.test.js b/test/functional/mapreduce.test.js index ddce7d624e..34200b56f1 100644 --- a/test/functional/mapreduce.test.js +++ b/test/functional/mapreduce.test.js @@ -9,62 +9,6 @@ describe('MapReduce', function () { return setupDatabase(this.configuration, ['outputCollectionDb']); }); - it('shouldCorrectlyExecuteGroupFunctionWithFinalizeFunction', { - metadata: { - requires: { - mongodb: '<=4.1.0', - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] - } - }, - - test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); - client.connect(function (err, client) { - var db = client.db(configuration.db); - db.createCollection('test_group2', function (err, collection) { - collection.group( - [], - {}, - { count: 0 }, - 'function (obj, prev) { prev.count++; }', - true, - function (err, results) { - test.deepEqual([], results); - - // Trigger some inserts - collection.insert( - [{ a: 2 }, { b: 5, a: 0 }, { a: 1 }, { c: 2, a: 0 }], - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - collection.group( - [], - {}, - { count: 0, running_average: 0 }, - function (doc, out) { - out.count++; - out.running_average += doc.a; - }, - function (out) { - out.average = out.running_average / out.count; - }, - true, - function (err, results) { - test.equal(3, results[0].running_average); - test.equal(0.75, results[0].average); - client.close(done); - } - ); - } - ); - } - ); - }); - }); - } - }); - /** * Mapreduce tests */ @@ -386,68 +330,6 @@ describe('MapReduce', function () { } }); - it('shouldCorrectlyReturnNestedKeys', { - metadata: { - requires: { - mongodb: '<=4.1.0', // Because of use of `group` command - topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] - } - }, - - test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); - client.connect(function (err, client) { - var db = client.db(configuration.db); - var start = new Date().setTime(new Date().getTime() - 10000); - var end = new Date().setTime(new Date().getTime() + 10000); - - var keys = { - 'data.lastname': true - }; - - var condition = { - 'data.date': { - $gte: start, - $lte: end - } - }; - - condition = {}; - - var initial = { - count: 0 - }; - - var reduce = function (doc, output) { - output.count++; - }; - - // Execute the group - db.createCollection('data', function (err, collection) { - collection.insert( - { - data: { - lastname: 'smith', - date: new Date() - } - }, - configuration.writeConcernMax(), - function (err) { - expect(err).to.not.exist; - // Execute the group - collection.group(keys, condition, initial, reduce, true, function (err, r) { - test.equal(1, r[0].count); - test.equal('smith', r[0]['data.lastname']); - client.close(done); - }); - } - ); - }); - }); - } - }); - /** * Mapreduce tests */ diff --git a/test/functional/readconcern.test.js b/test/functional/readconcern.test.js index 9bf6e03de5..e911626388 100644 --- a/test/functional/readconcern.test.js +++ b/test/functional/readconcern.test.js @@ -183,12 +183,6 @@ describe('ReadConcern', function () { commandName: 'count', mongodbVersion: '>= 3.2', readConcern: { level: 'majority' } - }, - { - description: 'Should set majority readConcern group command', - commandName: 'group', - mongodbVersion: '>= 3.2 <=4.1.0', - readConcern: { level: 'majority' } } ]; @@ -246,23 +240,6 @@ describe('ReadConcern', function () { validateTestResults(started, succeeded, test.commandName, test.readConcern.level); done(); }); - } else if (test.commandName === 'group') { - collection.group( - [], - {}, - { count: 0 }, - 'function (obj, prev) { prev.count++; }', - err => { - expect(err).to.not.exist; - validateTestResults( - started, - succeeded, - test.commandName, - test.readConcern.level - ); - done(); - } - ); } } ); diff --git a/test/functional/readpreference.test.js b/test/functional/readpreference.test.js index 3879aab52b..780c4ae3a9 100644 --- a/test/functional/readpreference.test.js +++ b/test/functional/readpreference.test.js @@ -112,45 +112,6 @@ describe('ReadPreference', function () { } }); - it('Should correctly apply collection level read Preference to group', { - metadata: { requires: { mongodb: '>=2.6.0,<=4.0.x', topology: ['single', 'ssl'] } }, - - test: function (done) { - var configuration = this.configuration; - var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); - client.connect(function (err, client) { - var db = client.db(configuration.db); - expect(err).to.not.exist; - // Set read preference - var collection = db.collection('read_pref_1', { - readPreference: ReadPreference.SECONDARY_PREFERRED - }); - - // Save checkout function - var command = client.topology.command; - // Set up our checker method - client.topology.command = function () { - var args = Array.prototype.slice.call(arguments, 0); - if (args[0] === 'integration_tests.$cmd') { - test.equal(ReadPreference.SECONDARY_PREFERRED, args[2].readPreference.mode); - } - - return command.apply(db.s.topology, args); - }; - - // Execute count - collection.group([], {}, { count: 0 }, 'function (obj, prev) { prev.count++; }', function ( - err - ) { - expect(err).to.not.exist; - client.topology.command = command; - - client.close(done); - }); - }); - } - }); - it('Should correctly apply collection level read Preference to mapReduce', { metadata: { requires: { mongodb: '>=2.6.0', topology: ['single', 'ssl'] } },