mongodb 3.3.0-beta1 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/HISTORY.md +117 -0
  2. package/index.js +1 -0
  3. package/lib/admin.js +6 -6
  4. package/lib/aggregation_cursor.js +180 -226
  5. package/lib/change_stream.js +233 -121
  6. package/lib/collection.js +74 -39
  7. package/lib/command_cursor.js +87 -165
  8. package/lib/core/connection/connect.js +1 -1
  9. package/lib/core/connection/msg.js +2 -1
  10. package/lib/core/connection/pool.js +66 -41
  11. package/lib/core/cursor.js +605 -461
  12. package/lib/core/error.js +70 -1
  13. package/lib/core/index.js +1 -1
  14. package/lib/core/sdam/server.js +109 -10
  15. package/lib/core/sdam/server_description.js +14 -0
  16. package/lib/core/sdam/topology.js +34 -14
  17. package/lib/core/sdam/topology_description.js +6 -14
  18. package/lib/core/sessions.js +68 -12
  19. package/lib/core/topologies/mongos.js +10 -3
  20. package/lib/core/topologies/replset.js +49 -11
  21. package/lib/core/topologies/server.js +38 -3
  22. package/lib/core/topologies/shared.js +22 -0
  23. package/lib/core/transactions.js +1 -0
  24. package/lib/core/utils.js +45 -1
  25. package/lib/core/wireprotocol/command.js +2 -2
  26. package/lib/core/wireprotocol/get_more.js +4 -0
  27. package/lib/core/wireprotocol/query.js +8 -1
  28. package/lib/cursor.js +780 -840
  29. package/lib/db.js +31 -73
  30. package/lib/error.js +10 -8
  31. package/lib/gridfs-stream/download.js +12 -5
  32. package/lib/mongo_client.js +33 -21
  33. package/lib/operations/aggregate.js +77 -95
  34. package/lib/operations/close.js +46 -0
  35. package/lib/operations/collection_ops.js +7 -916
  36. package/lib/operations/command.js +2 -1
  37. package/lib/operations/command_v2.js +109 -0
  38. package/lib/operations/common_functions.js +48 -14
  39. package/lib/operations/connect.js +73 -9
  40. package/lib/operations/count.js +8 -8
  41. package/lib/operations/count_documents.js +24 -29
  42. package/lib/operations/create_collection.js +1 -0
  43. package/lib/operations/cursor_ops.js +22 -32
  44. package/lib/operations/db_ops.js +0 -178
  45. package/lib/operations/distinct.js +20 -21
  46. package/lib/operations/drop.js +1 -0
  47. package/lib/operations/estimated_document_count.js +43 -18
  48. package/lib/operations/execute_operation.js +115 -3
  49. package/lib/operations/explain.js +2 -6
  50. package/lib/operations/find.js +35 -0
  51. package/lib/operations/insert_one.js +1 -37
  52. package/lib/operations/list_collections.js +106 -0
  53. package/lib/operations/list_databases.js +38 -0
  54. package/lib/operations/list_indexes.js +28 -52
  55. package/lib/operations/operation.js +7 -1
  56. package/lib/operations/rename.js +1 -1
  57. package/lib/operations/to_array.js +3 -5
  58. package/lib/read_concern.js +7 -1
  59. package/lib/topologies/mongos.js +1 -1
  60. package/lib/topologies/replset.js +1 -1
  61. package/lib/topologies/server.js +2 -2
  62. package/lib/topologies/topology_base.js +4 -5
  63. package/lib/utils.js +4 -4
  64. package/package.json +1 -1
  65. package/lib/operations/aggregate_operation.js +0 -127
  66. package/lib/operations/mongo_client_ops.js +0 -731
package/lib/db.js CHANGED
@@ -22,16 +22,16 @@ const MongoDBNamespace = require('./utils').MongoDBNamespace;
22
22
  const CONSTANTS = require('./constants');
23
23
  const WriteConcern = require('./write_concern');
24
24
  const ReadConcern = require('./read_concern');
25
+ const AggregationCursor = require('./aggregation_cursor');
25
26
 
26
27
  // Operations
27
- const aggregate = require('./operations/aggregate').aggregate;
28
28
  const createListener = require('./operations/db_ops').createListener;
29
29
  const ensureIndex = require('./operations/db_ops').ensureIndex;
30
30
  const evaluate = require('./operations/db_ops').evaluate;
31
- const listCollectionsTransforms = require('./operations/db_ops').listCollectionsTransforms;
32
31
  const profilingInfo = require('./operations/db_ops').profilingInfo;
33
32
  const validateDatabaseName = require('./operations/db_ops').validateDatabaseName;
34
33
 
34
+ const AggregateOperation = require('./operations/aggregate');
35
35
  const AddUserOperation = require('./operations/add_user');
36
36
  const CollectionsOperation = require('./operations/collections');
37
37
  const CommandOperation = require('./operations/command');
@@ -41,6 +41,7 @@ const DropCollectionOperation = require('./operations/drop').DropCollectionOpera
41
41
  const DropDatabaseOperation = require('./operations/drop').DropDatabaseOperation;
42
42
  const ExecuteDbAdminCommandOperation = require('./operations/execute_db_admin_command');
43
43
  const IndexInformationOperation = require('./operations/index_information');
44
+ const ListCollectionsOperation = require('./operations/list_collections');
44
45
  const ProfilingLevelOperation = require('./operations/profiling_level');
45
46
  const RemoveUserOperation = require('./operations/remove_user');
46
47
  const RenameOperation = require('./operations/rename');
@@ -329,7 +330,19 @@ Db.prototype.aggregate = function(pipeline, options, callback) {
329
330
  options = {};
330
331
  }
331
332
 
332
- return aggregate(this, '1', pipeline, options, callback);
333
+ const cursor = new AggregationCursor(
334
+ this.s.topology,
335
+ new AggregateOperation(this, pipeline, options),
336
+ options
337
+ );
338
+
339
+ // TODO: remove this when NODE-2074 is resolved
340
+ if (typeof callback === 'function') {
341
+ callback(null, cursor);
342
+ return;
343
+ }
344
+
345
+ return cursor;
333
346
  };
334
347
 
335
348
  /**
@@ -489,7 +502,7 @@ Db.prototype.collection = function(name, options, callback) {
489
502
  * @param {string} [options.validationAction] Determines whether to error on invalid documents or just warn about the violations but allow invalid documents to be inserted on MongoDB 3.2 or higher.
490
503
  * @param {object} [options.indexOptionDefaults] Allows users to specify a default configuration for indexes when creating a collection on MongoDB 3.2 or higher.
491
504
  * @param {string} [options.viewOn] The name of the source collection or view from which to create the view. The name is not the full namespace of the collection or view; i.e. does not include the database name and implies the same database as the view to create on MongoDB 3.4 or higher.
492
- * @param {array} [options.pipeline] An array that consists of the aggregation pipeline stage. create creates the view by applying the specified pipeline to the viewOn collection or view on MongoDB 3.4 or higher.
505
+ * @param {array} [options.pipeline] An array that consists of the aggregation pipeline stage. Creates the view by applying the specified pipeline to the viewOn collection or view on MongoDB 3.4 or higher.
493
506
  * @param {object} [options.collation] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
494
507
  * @param {ClientSession} [options.session] optional session to use for this operation
495
508
  * @param {Db~collectionResultCallback} [callback] The results callback
@@ -505,7 +518,9 @@ Db.prototype.createCollection = deprecateOptions(
505
518
  if (typeof options === 'function') (callback = options), (options = {});
506
519
  options = options || {};
507
520
  options.promiseLibrary = options.promiseLibrary || this.s.promiseLibrary;
508
-
521
+ options.readConcern = options.readConcern
522
+ ? new ReadConcern(options.readConcern.level)
523
+ : this.readConcern;
509
524
  const createCollectionOperation = new CreateCollectionOperation(this, name, options);
510
525
 
511
526
  return executeOperation(this.s.topology, createCollectionOperation, callback);
@@ -557,72 +572,11 @@ Db.prototype.listCollections = function(filter, options) {
557
572
  filter = filter || {};
558
573
  options = options || {};
559
574
 
560
- // Shallow clone the object
561
- options = Object.assign({}, options);
562
- // Set the promise library
563
- options.promiseLibrary = this.s.promiseLibrary;
564
-
565
- // Ensure valid readPreference
566
- options.readPreference = resolveReadPreference(this, options);
567
-
568
- // Cursor options
569
- let cursor = options.batchSize ? { batchSize: options.batchSize } : {};
570
-
571
- // We have a list collections command
572
- if (this.serverConfig.capabilities().hasListCollectionsCommand) {
573
- const nameOnly = typeof options.nameOnly === 'boolean' ? options.nameOnly : false;
574
- // Build the command
575
- const command = { listCollections: true, filter, cursor, nameOnly };
576
- // Set the AggregationCursor constructor
577
- options.cursorFactory = CommandCursor;
578
- // Create the cursor
579
- cursor = this.s.topology.cursor(
580
- this.s.namespace.withCollection('$cmd').toString(),
581
- command,
582
- options
583
- );
584
- // Do we have a readPreference, apply it
585
- if (options.readPreference) {
586
- cursor.setReadPreference(options.readPreference);
587
- }
588
- // Return the cursor
589
- return cursor;
590
- }
591
-
592
- // We cannot use the listCollectionsCommand
593
- if (!this.serverConfig.capabilities().hasListCollectionsCommand) {
594
- // If we have legacy mode and have not provided a full db name filter it
595
- if (
596
- typeof filter.name === 'string' &&
597
- !new RegExp('^' + this.databaseName + '\\.').test(filter.name)
598
- ) {
599
- filter = Object.assign({}, filter);
600
- filter.name = this.s.namespace.withCollection(filter.name).toString();
601
- }
602
- }
603
-
604
- // No filter, filter by current database
605
- if (filter == null) {
606
- filter.name = `/${this.databaseName}/`;
607
- }
608
-
609
- // Rewrite the filter to use $and to filter out indexes
610
- if (filter.name) {
611
- filter = { $and: [{ name: filter.name }, { name: /^((?!\$).)*$/ }] };
612
- } else {
613
- filter = { name: /^((?!\$).)*$/ };
614
- }
615
-
616
- // Return options
617
- const _options = { transforms: listCollectionsTransforms(this.databaseName) };
618
- // Get the cursor
619
- cursor = this.collection(CONSTANTS.SYSTEM_NAMESPACE_COLLECTION).find(filter, _options);
620
- // Do we have a readPreference, apply it
621
- if (options.readPreference) cursor.setReadPreference(options.readPreference);
622
- // Set the passed in batch size if one was provided
623
- if (options.batchSize) cursor = cursor.batchSize(options.batchSize);
624
- // We have a fallback mode using legacy systems collections
625
- return cursor;
575
+ return new CommandCursor(
576
+ this.s.topology,
577
+ new ListCollectionsOperation(this, filter, options),
578
+ options
579
+ );
626
580
  };
627
581
 
628
582
  /**
@@ -687,6 +641,10 @@ Db.prototype.renameCollection = function(fromCollection, toCollection, options,
687
641
  * @method
688
642
  * @param {string} name Name of collection to drop
689
643
  * @param {Object} [options] Optional settings
644
+ * @param {WriteConcern} [options.writeConcern] A full WriteConcern object
645
+ * @param {(number|string)} [options.w] The write concern
646
+ * @param {number} [options.wtimeout] The write concern timeout
647
+ * @param {boolean} [options.j] The journal write concern
690
648
  * @param {ClientSession} [options.session] optional session to use for this operation
691
649
  * @param {Db~resultCallback} [callback] The results callback
692
650
  * @return {Promise} returns Promise if no callback passed
@@ -777,7 +735,7 @@ Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
777
735
  * @param {number} [options.max] For geospatial indexes set the high bound for the co-ordinates.
778
736
  * @param {number} [options.v] Specify the format version of the indexes.
779
737
  * @param {number} [options.expireAfterSeconds] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
780
- * @param {number} [options.name] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
738
+ * @param {string} [options.name] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
781
739
  * @param {object} [options.partialFilterExpression] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
782
740
  * @param {ClientSession} [options.session] optional session to use for this operation
783
741
  * @param {Db~resultCallback} [callback] The command result callback
@@ -975,7 +933,7 @@ Db.prototype.unref = function() {
975
933
  * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
976
934
  * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
977
935
  * @param {ReadPreference} [options.readPreference] The read preference. Defaults to the read preference of the database. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
978
- * @param {Timestamp} [options.startAtClusterTime] receive change events that occur after the specified timestamp
936
+ * @param {Timestamp} [options.startAtOperationTime] receive change events that occur after the specified timestamp
979
937
  * @param {ClientSession} [options.session] optional session to use for this operation
980
938
  * @return {ChangeStream} a ChangeStream instance.
981
939
  */
package/lib/error.js CHANGED
@@ -9,15 +9,15 @@ const GET_MORE_NON_RESUMABLE_CODES = new Set([
9
9
  11601 // Interrupted
10
10
  ]);
11
11
 
12
- // From spec@https://github.com/mongodb/specifications/blob/35e466ddf25059cb30e4113de71cdebd3754657f/source/change-streams.rst#resumable-error:
12
+ // From spec@https://github.com/mongodb/specifications/blob/7a2e93d85935ee4b1046a8d2ad3514c657dc74fa/source/change-streams/change-streams.rst#resumable-error:
13
13
  //
14
14
  // An error is considered resumable if it meets any of the following criteria:
15
15
  // - any error encountered which is not a server error (e.g. a timeout error or network error)
16
- // - any server error response from a getMore command excluding those containing the following error codes
16
+ // - any server error response from a getMore command excluding those containing the error label
17
+ // NonRetryableChangeStreamError and those containing the following error codes:
17
18
  // - Interrupted: 11601
18
19
  // - CappedPositionLost: 136
19
20
  // - CursorKilled: 237
20
- // - a server error response with an error message containing the substring "not master" or "node is recovering"
21
21
  //
22
22
  // An error on an aggregate command is not a resumable error. Only errors on a getMore command may be considered resumable errors.
23
23
 
@@ -32,11 +32,13 @@ function isResumableError(error) {
32
32
  return false;
33
33
  }
34
34
 
35
- return !!(
36
- error instanceof MongoNetworkError ||
37
- !GET_MORE_NON_RESUMABLE_CODES.has(error.code) ||
38
- error.message.match(/not master/) ||
39
- error.message.match(/node is recovering/)
35
+ if (error instanceof MongoNetworkError) {
36
+ return true;
37
+ }
38
+
39
+ return !(
40
+ GET_MORE_NON_RESUMABLE_CODES.has(error.code) ||
41
+ error.hasErrorLabel('NonRetryableChangeStreamError')
40
42
  );
41
43
  }
42
44
 
@@ -185,12 +185,19 @@ function doRead(_this) {
185
185
  }
186
186
  if (!doc) {
187
187
  _this.push(null);
188
- return _this.s.cursor.close(function(error) {
189
- if (error) {
190
- return __handleError(_this, error);
191
- }
192
- _this.emit('close');
188
+
189
+ process.nextTick(() => {
190
+ _this.s.cursor.close(function(error) {
191
+ if (error) {
192
+ __handleError(_this, error);
193
+ return;
194
+ }
195
+
196
+ _this.emit('close');
197
+ });
193
198
  });
199
+
200
+ return;
194
201
  }
195
202
 
196
203
  var bytesRemaining = _this.s.file.length - _this.s.bytesRead;
@@ -3,16 +3,17 @@
3
3
  const ChangeStream = require('./change_stream');
4
4
  const Db = require('./db');
5
5
  const EventEmitter = require('events').EventEmitter;
6
- const executeLegacyOperation = require('./utils').executeLegacyOperation;
6
+ const executeOperation = require('./operations/execute_operation');
7
7
  const inherits = require('util').inherits;
8
8
  const MongoError = require('./core').MongoError;
9
9
  const deprecate = require('util').deprecate;
10
10
  const WriteConcern = require('./write_concern');
11
+ const MongoDBNamespace = require('./utils').MongoDBNamespace;
12
+ const ReadPreference = require('./core/topologies/read_preference');
11
13
 
12
14
  // Operations
13
- const connectOp = require('./operations/mongo_client_ops').connectOp;
14
- const validOptions = require('./operations/mongo_client_ops').validOptions;
15
- const closeOperation = require('./operations/mongo_client_ops').closeOperation;
15
+ const ConnectOperation = require('./operations/connect');
16
+ const CloseOperation = require('./operations/close');
16
17
 
17
18
  /**
18
19
  * @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
@@ -54,7 +55,9 @@ const closeOperation = require('./operations/mongo_client_ops').closeOperation;
54
55
  */
55
56
 
56
57
  /**
57
- * Configuration options for a automatic client encryption
58
+ * Configuration options for a automatic client encryption.
59
+ *
60
+ * **NOTE**: Support for client side encryption is in beta. Backwards-breaking changes may be made before the final release.
58
61
  *
59
62
  * @typedef {Object} AutoEncryptionOptions
60
63
  * @property {MongoClient} [keyVaultClient] A `MongoClient` used to fetch keys from a key vault
@@ -67,6 +70,11 @@ const closeOperation = require('./operations/mongo_client_ops').closeOperation;
67
70
  * @property {string} [kmsProviders.local.key] The master key used to encrypt/decrypt data keys
68
71
  * @property {object} [schemaMap] A map of namespaces to a local JSON schema for encryption
69
72
  * @property {boolean} [bypassAutoEncryption] Allows the user to bypass auto encryption, maintaining implicit decryption
73
+ * @property {object} [extraOptions] Extra options related to the mongocryptd process
74
+ * @property {string} [extraOptions.mongocryptURI] A local process the driver communicates with to determine how to encrypt values in a command. Defaults to "mongodb://%2Fvar%2Fmongocryptd.sock" if domain sockets are available or "mongodb://localhost:27020" otherwise
75
+ * @property {boolean} [extraOptions.mongocryptdBypassSpawn=false] If true, autoEncryption will not attempt to spawn a mongocryptd before connecting
76
+ * @property {string} [extraOptions.mongocryptdSpawnPath] The path to the mongocryptd executable on the system
77
+ * @property {string[]} [extraOptions.mongocryptdSpawnArgs] Command line arguments to use when auto-spawning a mongocryptd
70
78
  */
71
79
 
72
80
  /**
@@ -148,9 +156,10 @@ function MongoClient(url, options) {
148
156
  url: url,
149
157
  options: options || {},
150
158
  promiseLibrary: null,
151
- dbCache: {},
152
- sessions: [],
153
- writeConcern: WriteConcern.fromOptions(options)
159
+ dbCache: new Map(),
160
+ sessions: new Set(),
161
+ writeConcern: WriteConcern.fromOptions(options),
162
+ namespace: new MongoDBNamespace('admin')
154
163
  };
155
164
 
156
165
  // Get the promiseLibrary
@@ -172,6 +181,13 @@ Object.defineProperty(MongoClient.prototype, 'writeConcern', {
172
181
  }
173
182
  });
174
183
 
184
+ Object.defineProperty(MongoClient.prototype, 'readPreference', {
185
+ enumerable: true,
186
+ get: function() {
187
+ return ReadPreference.primary;
188
+ }
189
+ });
190
+
175
191
  /**
176
192
  * The callback format for results
177
193
  * @callback MongoClient~connectCallback
@@ -191,16 +207,13 @@ Object.defineProperty(MongoClient.prototype, 'writeConcern', {
191
207
  * @return {Promise<MongoClient>} returns Promise if no callback passed
192
208
  */
193
209
  MongoClient.prototype.connect = function(callback) {
194
- // Validate options object
195
- const err = validOptions(this.s.options);
196
-
197
210
  if (typeof callback === 'string') {
198
211
  throw new TypeError('`connect` only accepts a callback');
199
212
  }
200
213
 
201
- return executeLegacyOperation(this, connectOp, [this, err, callback], {
202
- skipSessions: true
203
- });
214
+ const operation = new ConnectOperation(this);
215
+
216
+ return executeOperation(this, operation, callback);
204
217
  };
205
218
 
206
219
  MongoClient.prototype.logout = deprecate(function(options, callback) {
@@ -217,9 +230,8 @@ MongoClient.prototype.logout = deprecate(function(options, callback) {
217
230
  */
218
231
  MongoClient.prototype.close = function(force, callback) {
219
232
  if (typeof force === 'function') (callback = force), (force = false);
220
- return executeLegacyOperation(this, closeOperation, [this, force, callback], {
221
- skipSessions: true
222
- });
233
+ const operation = new CloseOperation(this, force);
234
+ return executeOperation(this, operation, callback);
223
235
  };
224
236
 
225
237
  /**
@@ -247,8 +259,8 @@ MongoClient.prototype.db = function(dbName, options) {
247
259
  const finalOptions = Object.assign({}, this.s.options, options);
248
260
 
249
261
  // Do we have the db in the cache already
250
- if (this.s.dbCache[dbName] && finalOptions.returnNonCachedInstance !== true) {
251
- return this.s.dbCache[dbName];
262
+ if (this.s.dbCache.has(dbName) && finalOptions.returnNonCachedInstance !== true) {
263
+ return this.s.dbCache.get(dbName);
252
264
  }
253
265
 
254
266
  // Add promiseLibrary
@@ -263,7 +275,7 @@ MongoClient.prototype.db = function(dbName, options) {
263
275
  const db = new Db(dbName, this.topology, finalOptions);
264
276
 
265
277
  // Add the db to the cache
266
- this.s.dbCache[dbName] = db;
278
+ this.s.dbCache.set(dbName, db);
267
279
  // Return the database
268
280
  return db;
269
281
  };
@@ -437,7 +449,7 @@ MongoClient.prototype.withSession = function(options, operation) {
437
449
  * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
438
450
  * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
439
451
  * @param {ReadPreference} [options.readPreference] The read preference. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
440
- * @param {Timestamp} [options.startAtClusterTime] receive change events that occur after the specified timestamp
452
+ * @param {Timestamp} [options.startAtOperationTime] receive change events that occur after the specified timestamp
441
453
  * @param {ClientSession} [options.session] optional session to use for this operation
442
454
  * @return {ChangeStream} a ChangeStream instance.
443
455
  */
@@ -1,124 +1,106 @@
1
1
  'use strict';
2
2
 
3
- const AggregationCursor = require('../aggregation_cursor');
4
- const applyWriteConcern = require('../utils').applyWriteConcern;
5
- const decorateWithCollation = require('../utils').decorateWithCollation;
6
- const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
7
- const handleCallback = require('../utils').handleCallback;
3
+ const CommandOperationV2 = require('./command_v2');
8
4
  const MongoError = require('../core').MongoError;
9
- const resolveReadPreference = require('../utils').resolveReadPreference;
10
- const toError = require('../utils').toError;
5
+ const maxWireVersion = require('../core/utils').maxWireVersion;
6
+ const ReadPreference = require('../core').ReadPreference;
7
+ const Aspect = require('./operation').Aspect;
8
+ const defineAspects = require('./operation').defineAspects;
11
9
 
12
10
  const DB_AGGREGATE_COLLECTION = 1;
13
-
14
11
  const MIN_WIRE_VERSION_$OUT_READ_CONCERN_SUPPORT = 8;
15
12
 
16
- /**
17
- * Perform an aggregate operation. See Collection.prototype.aggregate or Db.prototype.aggregate for more information.
18
- *
19
- * @method
20
- * @param {Db} db A Db instance.
21
- * @param {Collection|string} coll A collection instance or the string '1', used for db.aggregate.
22
- * @param {object} [pipeline=[]] Array containing all the aggregation framework commands for the execution.
23
- * @param {object} [options] Optional settings. See Collection.prototype.aggregate or Db.prototype.aggregate for a list of options.
24
- * @param {Db~aggregationCallback|Collection~aggregationCallback} callback The command result callback
25
- */
26
- function aggregate(db, coll, pipeline, options, callback) {
27
- const isDbAggregate = typeof coll === 'string';
28
- const target = isDbAggregate ? db : coll;
29
- const topology = target.s.topology;
30
- let hasOutStage = false;
31
-
32
- if (typeof options.out === 'string') {
33
- pipeline = pipeline.concat({ $out: options.out });
34
- hasOutStage = true;
35
- } else if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out']) {
36
- hasOutStage = true;
37
- }
38
-
39
- let command;
40
- let namespace;
41
- let optionSources;
42
-
43
- if (isDbAggregate) {
44
- command = { aggregate: DB_AGGREGATE_COLLECTION, pipeline: pipeline };
45
- namespace = db.s.namespace.withCollection(DB_AGGREGATE_COLLECTION);
46
-
47
- optionSources = { db };
48
- } else {
49
- command = { aggregate: coll.collectionName, pipeline: pipeline };
50
- namespace = coll.s.namespace;
51
-
52
- optionSources = { db: coll.s.db, collection: coll };
53
- }
13
+ class AggregateOperation extends CommandOperationV2 {
14
+ constructor(parent, pipeline, options) {
15
+ super(parent, options, { fullResponse: true });
16
+
17
+ this.target =
18
+ parent.s.namespace && parent.s.namespace.collection
19
+ ? parent.s.namespace.collection
20
+ : DB_AGGREGATE_COLLECTION;
21
+
22
+ this.pipeline = pipeline;
23
+
24
+ // determine if we have a write stage, override read preference if so
25
+ this.hasWriteStage = false;
26
+ if (typeof options.out === 'string') {
27
+ this.pipeline = this.pipeline.concat({ $out: options.out });
28
+ this.hasWriteStage = true;
29
+ } else if (pipeline.length > 0) {
30
+ const finalStage = pipeline[pipeline.length - 1];
31
+ if (finalStage.$out || finalStage.$merge) {
32
+ this.hasWriteStage = true;
33
+ }
34
+ }
54
35
 
55
- const takesWriteConcern = topology.capabilities().commandsTakeWriteConcern;
56
- const ismaster = topology.lastIsMaster() || {};
36
+ if (this.hasWriteStage) {
37
+ this.readPreference = ReadPreference.primary;
38
+ }
57
39
 
58
- if (!hasOutStage || ismaster.maxWireVersion >= MIN_WIRE_VERSION_$OUT_READ_CONCERN_SUPPORT) {
59
- decorateWithReadConcern(command, target, options);
60
- }
40
+ if (options.explain && (this.readConcern || this.writeConcern)) {
41
+ throw new MongoError(
42
+ '"explain" cannot be used on an aggregate call with readConcern/writeConcern'
43
+ );
44
+ }
61
45
 
62
- if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out'] && takesWriteConcern) {
63
- applyWriteConcern(command, optionSources, options);
46
+ if (options.cursor != null && typeof options.cursor !== 'object') {
47
+ throw new MongoError('cursor options must be an object');
48
+ }
64
49
  }
65
50
 
66
- try {
67
- decorateWithCollation(command, target, options);
68
- } catch (err) {
69
- if (typeof callback === 'function') return callback(err, null);
70
- throw err;
51
+ get canRetryRead() {
52
+ return !this.hasWriteStage;
71
53
  }
72
54
 
73
- if (options.bypassDocumentValidation === true) {
74
- command.bypassDocumentValidation = options.bypassDocumentValidation;
55
+ addToPipeline(stage) {
56
+ this.pipeline.push(stage);
75
57
  }
76
58
 
77
- if (typeof options.allowDiskUse === 'boolean') command.allowDiskUse = options.allowDiskUse;
78
- if (typeof options.maxTimeMS === 'number') command.maxTimeMS = options.maxTimeMS;
59
+ execute(server, callback) {
60
+ const options = this.options;
61
+ const serverWireVersion = maxWireVersion(server);
62
+ const command = { aggregate: this.target, pipeline: this.pipeline };
79
63
 
80
- if (options.hint) command.hint = options.hint;
81
-
82
- options = Object.assign({}, options);
83
-
84
- // Ensure we have the right read preference inheritance
85
- options.readPreference = resolveReadPreference(isDbAggregate ? db : coll, options);
86
-
87
- if (options.explain) {
88
- if (command.readConcern || command.writeConcern) {
89
- throw toError('"explain" cannot be used on an aggregate call with readConcern/writeConcern');
64
+ if (this.hasWriteStage && serverWireVersion < MIN_WIRE_VERSION_$OUT_READ_CONCERN_SUPPORT) {
65
+ this.readConcern = null;
90
66
  }
91
- command.explain = options.explain;
92
- }
93
67
 
94
- if (typeof options.comment === 'string') command.comment = options.comment;
68
+ if (serverWireVersion >= 5) {
69
+ if (this.hasWriteStage && this.writeConcern) {
70
+ Object.assign(command, { writeConcern: this.writeConcern });
71
+ }
72
+ }
95
73
 
96
- // Validate that cursor options is valid
97
- if (options.cursor != null && typeof options.cursor !== 'object') {
98
- throw toError('cursor options must be an object');
99
- }
74
+ if (options.bypassDocumentValidation === true) {
75
+ command.bypassDocumentValidation = options.bypassDocumentValidation;
76
+ }
100
77
 
101
- options.cursor = options.cursor || {};
102
- if (options.batchSize && !hasOutStage) options.cursor.batchSize = options.batchSize;
103
- command.cursor = options.cursor;
78
+ if (typeof options.allowDiskUse === 'boolean') {
79
+ command.allowDiskUse = options.allowDiskUse;
80
+ }
104
81
 
105
- // promiseLibrary
106
- options.promiseLibrary = target.s.promiseLibrary;
82
+ if (options.hint) {
83
+ command.hint = options.hint;
84
+ }
107
85
 
108
- // Set the AggregationCursor constructor
109
- options.cursorFactory = AggregationCursor;
86
+ if (options.explain) {
87
+ options.full = false;
88
+ command.explain = options.explain;
89
+ }
110
90
 
111
- if (typeof callback !== 'function') {
112
- if (!topology.capabilities()) {
113
- throw new MongoError('cannot connect to server');
91
+ command.cursor = options.cursor || {};
92
+ if (options.batchSize && !this.hasWriteStage) {
93
+ command.cursor.batchSize = options.batchSize;
114
94
  }
115
95
 
116
- return topology.cursor(namespace.toString(), command, options);
96
+ super.executeCommand(server, command, callback);
117
97
  }
118
-
119
- return handleCallback(callback, null, topology.cursor(namespace.toString(), command, options));
120
98
  }
121
99
 
122
- module.exports = {
123
- aggregate
124
- };
100
+ defineAspects(AggregateOperation, [
101
+ Aspect.READ_OPERATION,
102
+ Aspect.RETRYABLE,
103
+ Aspect.EXECUTE_WITH_SELECTION
104
+ ]);
105
+
106
+ module.exports = AggregateOperation;
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const Aspect = require('./operation').Aspect;
4
+ const defineAspects = require('./operation').defineAspects;
5
+ const OperationBase = require('./operation').OperationBase;
6
+
7
+ class CloseOperation extends OperationBase {
8
+ constructor(client, force) {
9
+ super();
10
+ this.client = client;
11
+ this.force = force;
12
+ }
13
+
14
+ execute(callback) {
15
+ const client = this.client;
16
+ const force = this.force;
17
+ const completeClose = err => {
18
+ client.emit('close', client);
19
+ for (const item of client.s.dbCache) {
20
+ item[1].emit('close', client);
21
+ }
22
+
23
+ client.removeAllListeners('close');
24
+ callback(err, null);
25
+ };
26
+
27
+ if (client.topology == null) {
28
+ completeClose();
29
+ return;
30
+ }
31
+
32
+ client.topology.close(force, err => {
33
+ const autoEncrypter = client.topology.s.options.autoEncrypter;
34
+ if (!autoEncrypter) {
35
+ completeClose(err);
36
+ return;
37
+ }
38
+
39
+ autoEncrypter.teardown(force, err2 => completeClose(err || err2));
40
+ });
41
+ }
42
+ }
43
+
44
+ defineAspects(CloseOperation, [Aspect.SKIP_SESSION]);
45
+
46
+ module.exports = CloseOperation;