mongodb 3.3.0-beta1 → 3.3.0-beta2
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.
- package/lib/aggregation_cursor.js +3 -1
- package/lib/change_stream.js +183 -71
- package/lib/collection.js +8 -9
- package/lib/command_cursor.js +6 -4
- package/lib/core/connection/connect.js +1 -1
- package/lib/core/connection/pool.js +66 -41
- package/lib/core/cursor.js +70 -60
- package/lib/core/error.js +24 -1
- package/lib/core/sdam/server.js +84 -9
- package/lib/core/sdam/server_description.js +14 -0
- package/lib/core/sdam/topology.js +17 -4
- package/lib/core/sdam/topology_description.js +6 -14
- package/lib/core/topologies/mongos.js +1 -1
- package/lib/core/topologies/replset.js +1 -1
- package/lib/core/topologies/server.js +36 -1
- package/lib/core/utils.js +38 -1
- package/lib/core/wireprotocol/command.js +2 -2
- package/lib/cursor.js +7 -5
- package/lib/db.js +7 -1
- package/lib/error.js +10 -8
- package/lib/mongo_client.js +9 -13
- package/lib/operations/aggregate.js +17 -9
- package/lib/operations/close.js +47 -0
- package/lib/operations/collection_ops.js +4 -0
- package/lib/operations/command.js +2 -1
- package/lib/operations/command_v2.js +43 -0
- package/lib/operations/connect.js +83 -9
- package/lib/operations/create_collection.js +1 -0
- package/lib/operations/distinct.js +20 -21
- package/lib/operations/drop.js +1 -0
- package/lib/operations/execute_operation.js +81 -2
- package/lib/operations/explain.js +0 -4
- package/lib/operations/operation.js +3 -1
- package/package.json +1 -1
- package/lib/operations/mongo_client_ops.js +0 -731
package/lib/core/utils.js
CHANGED
|
@@ -116,6 +116,41 @@ function isPromiseLike(maybePromise) {
|
|
|
116
116
|
return maybePromise && typeof maybePromise.then === 'function';
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Applies the function `eachFn` to each item in `arr`, in parallel.
|
|
121
|
+
*
|
|
122
|
+
* @param {array} arr an array of items to asynchronusly iterate over
|
|
123
|
+
* @param {function} eachFn A function to call on each item of the array. The callback signature is `(item, callback)`, where the callback indicates iteration is complete.
|
|
124
|
+
* @param {function} callback The callback called after every item has been iterated
|
|
125
|
+
*/
|
|
126
|
+
function eachAsync(arr, eachFn, callback) {
|
|
127
|
+
if (arr.length === 0) {
|
|
128
|
+
callback(null);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const length = arr.length;
|
|
133
|
+
let completed = 0;
|
|
134
|
+
function eachCallback(err) {
|
|
135
|
+
if (err) {
|
|
136
|
+
callback(err, null);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (++completed === length) {
|
|
141
|
+
callback(null);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (let idx = 0; idx < length; ++idx) {
|
|
146
|
+
eachFn(arr[idx], eachCallback);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isUnifiedTopology(topology) {
|
|
151
|
+
return topology.description != null;
|
|
152
|
+
}
|
|
153
|
+
|
|
119
154
|
module.exports = {
|
|
120
155
|
uuidV4,
|
|
121
156
|
calculateDurationInMs,
|
|
@@ -124,5 +159,7 @@ module.exports = {
|
|
|
124
159
|
retrieveEJSON,
|
|
125
160
|
retrieveKerberos,
|
|
126
161
|
maxWireVersion,
|
|
127
|
-
isPromiseLike
|
|
162
|
+
isPromiseLike,
|
|
163
|
+
eachAsync,
|
|
164
|
+
isUnifiedTopology
|
|
128
165
|
};
|
|
@@ -10,7 +10,7 @@ const isTransactionCommand = require('../transactions').isTransactionCommand;
|
|
|
10
10
|
const applySession = require('../sessions').applySession;
|
|
11
11
|
|
|
12
12
|
function isClientEncryptionEnabled(server) {
|
|
13
|
-
return server
|
|
13
|
+
return server.autoEncrypter;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function command(server, ns, cmd, options, callback) {
|
|
@@ -133,7 +133,7 @@ function supportsOpMsg(topologyOrServer) {
|
|
|
133
133
|
|
|
134
134
|
function _cryptCommand(server, ns, cmd, options, callback) {
|
|
135
135
|
const shouldBypassAutoEncryption = !!server.s.options.bypassAutoEncryption;
|
|
136
|
-
const autoEncrypter = server.
|
|
136
|
+
const autoEncrypter = server.autoEncrypter;
|
|
137
137
|
function commandResponseHandler(err, response) {
|
|
138
138
|
if (err || response == null) {
|
|
139
139
|
callback(err, response);
|
package/lib/cursor.js
CHANGED
|
@@ -106,10 +106,12 @@ const fields = ['numberOfRetries', 'tailableRetryInterval'];
|
|
|
106
106
|
*
|
|
107
107
|
* collection.find({}).maxTimeMS(1000).maxScan(100).skip(1).toArray(..)
|
|
108
108
|
*/
|
|
109
|
-
function Cursor(
|
|
109
|
+
function Cursor(topology, ns, cmd, options) {
|
|
110
110
|
CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
|
|
111
111
|
const state = Cursor.INIT;
|
|
112
112
|
const streamOptions = {};
|
|
113
|
+
const bson = topology.s.bson;
|
|
114
|
+
const topologyOptions = topology.s.options;
|
|
113
115
|
|
|
114
116
|
// Tailable cursor options
|
|
115
117
|
const numberOfRetries = options.numberOfRetries || 5;
|
|
@@ -214,9 +216,6 @@ if (SUPPORTS.ASYNC_ITERATOR) {
|
|
|
214
216
|
|
|
215
217
|
// Map core cursor _next method so we can apply mapping
|
|
216
218
|
Cursor.prototype._next = function() {
|
|
217
|
-
if (this._initImplicitSession) {
|
|
218
|
-
this._initImplicitSession();
|
|
219
|
-
}
|
|
220
219
|
return CoreCursor.prototype.next.apply(this, arguments);
|
|
221
220
|
};
|
|
222
221
|
|
|
@@ -224,11 +223,14 @@ for (let name in CoreCursor.prototype) {
|
|
|
224
223
|
Cursor.prototype[name] = CoreCursor.prototype[name];
|
|
225
224
|
}
|
|
226
225
|
|
|
227
|
-
Cursor.prototype.
|
|
226
|
+
Cursor.prototype._initializeCursor = function(callback) {
|
|
227
|
+
// implicitly create a session if one has not been provided
|
|
228
228
|
if (!this.s.explicitlyIgnoreSession && !this.s.session && this.s.topology.hasSessionSupport()) {
|
|
229
229
|
this.s.session = this.s.topology.startSession({ owner: this });
|
|
230
230
|
this.cursorState.session = this.s.session;
|
|
231
231
|
}
|
|
232
|
+
|
|
233
|
+
CoreCursor.prototype._initializeCursor.apply(this, [callback]);
|
|
232
234
|
};
|
|
233
235
|
|
|
234
236
|
Cursor.prototype._endSession = function() {
|
package/lib/db.js
CHANGED
|
@@ -505,7 +505,9 @@ Db.prototype.createCollection = deprecateOptions(
|
|
|
505
505
|
if (typeof options === 'function') (callback = options), (options = {});
|
|
506
506
|
options = options || {};
|
|
507
507
|
options.promiseLibrary = options.promiseLibrary || this.s.promiseLibrary;
|
|
508
|
-
|
|
508
|
+
options.readConcern = options.readConcern
|
|
509
|
+
? new ReadConcern(options.readConcern.level)
|
|
510
|
+
: this.readConcern;
|
|
509
511
|
const createCollectionOperation = new CreateCollectionOperation(this, name, options);
|
|
510
512
|
|
|
511
513
|
return executeOperation(this.s.topology, createCollectionOperation, callback);
|
|
@@ -687,6 +689,10 @@ Db.prototype.renameCollection = function(fromCollection, toCollection, options,
|
|
|
687
689
|
* @method
|
|
688
690
|
* @param {string} name Name of collection to drop
|
|
689
691
|
* @param {Object} [options] Optional settings
|
|
692
|
+
* @param {WriteConcern} [options.writeConcern] A full WriteConcern object
|
|
693
|
+
* @param {(number|string)} [options.w] The write concern
|
|
694
|
+
* @param {number} [options.wtimeout] The write concern timeout
|
|
695
|
+
* @param {boolean} [options.j] The journal write concern
|
|
690
696
|
* @param {ClientSession} [options.session] optional session to use for this operation
|
|
691
697
|
* @param {Db~resultCallback} [callback] The results callback
|
|
692
698
|
* @return {Promise} returns Promise if no callback passed
|
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/
|
|
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
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
package/lib/mongo_client.js
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
const ChangeStream = require('./change_stream');
|
|
4
4
|
const Db = require('./db');
|
|
5
5
|
const EventEmitter = require('events').EventEmitter;
|
|
6
|
-
const
|
|
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
11
|
|
|
12
12
|
// Operations
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const closeOperation = require('./operations/mongo_client_ops').closeOperation;
|
|
13
|
+
const ConnectOperation = require('./operations/connect');
|
|
14
|
+
const CloseOperation = require('./operations/close');
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* @fileOverview The **MongoClient** class is a class that allows for making Connections to MongoDB.
|
|
@@ -67,6 +66,7 @@ const closeOperation = require('./operations/mongo_client_ops').closeOperation;
|
|
|
67
66
|
* @property {string} [kmsProviders.local.key] The master key used to encrypt/decrypt data keys
|
|
68
67
|
* @property {object} [schemaMap] A map of namespaces to a local JSON schema for encryption
|
|
69
68
|
* @property {boolean} [bypassAutoEncryption] Allows the user to bypass auto encryption, maintaining implicit decryption
|
|
69
|
+
* @param {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.
|
|
70
70
|
*/
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -191,16 +191,13 @@ Object.defineProperty(MongoClient.prototype, 'writeConcern', {
|
|
|
191
191
|
* @return {Promise<MongoClient>} returns Promise if no callback passed
|
|
192
192
|
*/
|
|
193
193
|
MongoClient.prototype.connect = function(callback) {
|
|
194
|
-
// Validate options object
|
|
195
|
-
const err = validOptions(this.s.options);
|
|
196
|
-
|
|
197
194
|
if (typeof callback === 'string') {
|
|
198
195
|
throw new TypeError('`connect` only accepts a callback');
|
|
199
196
|
}
|
|
200
197
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
const operation = new ConnectOperation(this);
|
|
199
|
+
|
|
200
|
+
return executeOperation(this, operation, callback);
|
|
204
201
|
};
|
|
205
202
|
|
|
206
203
|
MongoClient.prototype.logout = deprecate(function(options, callback) {
|
|
@@ -217,9 +214,8 @@ MongoClient.prototype.logout = deprecate(function(options, callback) {
|
|
|
217
214
|
*/
|
|
218
215
|
MongoClient.prototype.close = function(force, callback) {
|
|
219
216
|
if (typeof force === 'function') (callback = force), (force = false);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
});
|
|
217
|
+
const operation = new CloseOperation(this, force);
|
|
218
|
+
return executeOperation(this, operation, callback);
|
|
223
219
|
};
|
|
224
220
|
|
|
225
221
|
/**
|
|
@@ -8,9 +8,9 @@ const handleCallback = require('../utils').handleCallback;
|
|
|
8
8
|
const MongoError = require('../core').MongoError;
|
|
9
9
|
const resolveReadPreference = require('../utils').resolveReadPreference;
|
|
10
10
|
const toError = require('../utils').toError;
|
|
11
|
+
const ReadPreference = require('../core').ReadPreference;
|
|
11
12
|
|
|
12
13
|
const DB_AGGREGATE_COLLECTION = 1;
|
|
13
|
-
|
|
14
14
|
const MIN_WIRE_VERSION_$OUT_READ_CONCERN_SUPPORT = 8;
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -27,13 +27,16 @@ function aggregate(db, coll, pipeline, options, callback) {
|
|
|
27
27
|
const isDbAggregate = typeof coll === 'string';
|
|
28
28
|
const target = isDbAggregate ? db : coll;
|
|
29
29
|
const topology = target.s.topology;
|
|
30
|
-
let
|
|
30
|
+
let hasWriteStage = false;
|
|
31
31
|
|
|
32
32
|
if (typeof options.out === 'string') {
|
|
33
33
|
pipeline = pipeline.concat({ $out: options.out });
|
|
34
|
-
|
|
35
|
-
} else if (pipeline.length > 0
|
|
36
|
-
|
|
34
|
+
hasWriteStage = true;
|
|
35
|
+
} else if (pipeline.length > 0) {
|
|
36
|
+
const finalStage = pipeline[pipeline.length - 1];
|
|
37
|
+
if (finalStage.$out || finalStage.$merge) {
|
|
38
|
+
hasWriteStage = true;
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
let command;
|
|
@@ -55,11 +58,11 @@ function aggregate(db, coll, pipeline, options, callback) {
|
|
|
55
58
|
const takesWriteConcern = topology.capabilities().commandsTakeWriteConcern;
|
|
56
59
|
const ismaster = topology.lastIsMaster() || {};
|
|
57
60
|
|
|
58
|
-
if (!
|
|
61
|
+
if (!hasWriteStage || ismaster.maxWireVersion >= MIN_WIRE_VERSION_$OUT_READ_CONCERN_SUPPORT) {
|
|
59
62
|
decorateWithReadConcern(command, target, options);
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
if (
|
|
65
|
+
if (hasWriteStage && takesWriteConcern) {
|
|
63
66
|
applyWriteConcern(command, optionSources, options);
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -82,7 +85,9 @@ function aggregate(db, coll, pipeline, options, callback) {
|
|
|
82
85
|
options = Object.assign({}, options);
|
|
83
86
|
|
|
84
87
|
// Ensure we have the right read preference inheritance
|
|
85
|
-
options.readPreference =
|
|
88
|
+
options.readPreference = hasWriteStage
|
|
89
|
+
? ReadPreference.primary
|
|
90
|
+
: resolveReadPreference(isDbAggregate ? db : coll, options);
|
|
86
91
|
|
|
87
92
|
if (options.explain) {
|
|
88
93
|
if (command.readConcern || command.writeConcern) {
|
|
@@ -99,7 +104,10 @@ function aggregate(db, coll, pipeline, options, callback) {
|
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
options.cursor = options.cursor || {};
|
|
102
|
-
if (options.batchSize && !
|
|
107
|
+
if (options.batchSize && !hasWriteStage) {
|
|
108
|
+
options.cursor.batchSize = options.batchSize;
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
command.cursor = options.cursor;
|
|
104
112
|
|
|
105
113
|
// promiseLibrary
|
|
@@ -0,0 +1,47 @@
|
|
|
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 name in client.s.dbCache) {
|
|
20
|
+
client.s.dbCache[name].emit('close', client);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
client.removeAllListeners('close');
|
|
24
|
+
callback(err, null);
|
|
25
|
+
};
|
|
26
|
+
const mongocryptdClientClose = err => {
|
|
27
|
+
const mongocryptdClient = client.s.mongocryptdClient;
|
|
28
|
+
if (!mongocryptdClient) {
|
|
29
|
+
completeClose(err);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
mongocryptdClient.close(force, err2 => completeClose(err || err2));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if (client.topology == null) {
|
|
37
|
+
mongocryptdClientClose();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
client.topology.close(force, mongocryptdClientClose);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
defineAspects(CloseOperation, [Aspect.SKIP_SESSION]);
|
|
46
|
+
|
|
47
|
+
module.exports = CloseOperation;
|
|
@@ -156,6 +156,10 @@ function bulkWrite(coll, operations, options, callback) {
|
|
|
156
156
|
|
|
157
157
|
// Check the update operation to ensure it has atomic operators.
|
|
158
158
|
function checkForAtomicOperators(update) {
|
|
159
|
+
if (Array.isArray(update)) {
|
|
160
|
+
return update.reduce((err, u) => err || checkForAtomicOperators(u), null);
|
|
161
|
+
}
|
|
162
|
+
|
|
159
163
|
const keys = Object.keys(update);
|
|
160
164
|
|
|
161
165
|
// same errors as the server would give for update doc lacking atomic operators
|
|
@@ -105,7 +105,8 @@ class CommandOperation extends OperationBase {
|
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const namespace =
|
|
108
|
+
const namespace =
|
|
109
|
+
this.namespace != null ? this.namespace : new MongoDBNamespace(dbName, '$cmd');
|
|
109
110
|
|
|
110
111
|
// Execute command
|
|
111
112
|
db.s.topology.command(namespace, command, options, (err, result) => {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const OperationBase = require('./operation').OperationBase;
|
|
4
|
+
const resolveReadPreference = require('../utils').resolveReadPreference;
|
|
5
|
+
|
|
6
|
+
class CommandOperationV2 extends OperationBase {
|
|
7
|
+
constructor(parent, options) {
|
|
8
|
+
super(options);
|
|
9
|
+
|
|
10
|
+
this.ns = parent.s.namespace.withCollection('$cmd');
|
|
11
|
+
this.readPreference = resolveReadPreference(parent, options);
|
|
12
|
+
|
|
13
|
+
// TODO(NODE-2056): make logger another "inheritable" property
|
|
14
|
+
if (parent.s.logger) {
|
|
15
|
+
this.logger = parent.s.logger;
|
|
16
|
+
} else if (parent.s.db && parent.s.db.logger) {
|
|
17
|
+
this.logger = parent.s.db.logger;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
executeCommand(server, cmd, callback) {
|
|
22
|
+
if (this.logger && this.logger.isDebug()) {
|
|
23
|
+
this.logger.debug(`executing command ${JSON.stringify(cmd)} against ${this.ns}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
server.command(this.ns.toString(), cmd, this.options, (err, result) => {
|
|
27
|
+
if (err) {
|
|
28
|
+
callback(err, null);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// full response was requested
|
|
33
|
+
if (this.options.full) {
|
|
34
|
+
callback(null, result);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
callback(null, result.result);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = CommandOperationV2;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const os = require('os');
|
|
3
4
|
const OperationBase = require('./operation').OperationBase;
|
|
5
|
+
const defineAspects = require('./operation').defineAspects;
|
|
4
6
|
const Aspect = require('./operation').Aspect;
|
|
5
7
|
const deprecate = require('util').deprecate;
|
|
6
8
|
const Logger = require('../core').Logger;
|
|
@@ -131,31 +133,58 @@ const validOptionNames = [
|
|
|
131
133
|
'minSize',
|
|
132
134
|
'monitorCommands',
|
|
133
135
|
'retryWrites',
|
|
136
|
+
'retryReads',
|
|
134
137
|
'useNewUrlParser',
|
|
135
138
|
'useUnifiedTopology',
|
|
136
|
-
'serverSelectionTimeoutMS'
|
|
139
|
+
'serverSelectionTimeoutMS',
|
|
140
|
+
'useRecoveryToken',
|
|
141
|
+
'autoEncryption'
|
|
137
142
|
];
|
|
138
143
|
|
|
144
|
+
const ignoreOptionNames = ['native_parser'];
|
|
145
|
+
const legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
|
|
146
|
+
|
|
147
|
+
// Validate options object
|
|
148
|
+
function validOptions(options) {
|
|
149
|
+
const _validOptions = validOptionNames.concat(legacyOptionNames);
|
|
150
|
+
|
|
151
|
+
for (const name in options) {
|
|
152
|
+
if (ignoreOptionNames.indexOf(name) !== -1) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (_validOptions.indexOf(name) === -1) {
|
|
157
|
+
if (options.validateOptions) {
|
|
158
|
+
return new MongoError(`option ${name} is not supported`);
|
|
159
|
+
} else {
|
|
160
|
+
console.warn(`the options [${name}] is not supported`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (legacyOptionNames.indexOf(name) !== -1) {
|
|
165
|
+
console.warn(
|
|
166
|
+
`the server/replset/mongos/db options are deprecated, ` +
|
|
167
|
+
`all their options are supported at the top level of the options object [${validOptionNames}]`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
139
173
|
const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
|
|
140
174
|
obj[name.toLowerCase()] = name;
|
|
141
175
|
return obj;
|
|
142
176
|
}, {});
|
|
143
177
|
|
|
144
178
|
class ConnectOperation extends OperationBase {
|
|
145
|
-
constructor(mongoClient
|
|
179
|
+
constructor(mongoClient) {
|
|
146
180
|
super();
|
|
147
181
|
|
|
148
182
|
this.mongoClient = mongoClient;
|
|
149
|
-
this.err = err;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
static get aspects() {
|
|
153
|
-
return new Set([Aspect.SKIP_SESSIONS]);
|
|
154
183
|
}
|
|
155
184
|
|
|
156
185
|
execute(callback) {
|
|
157
186
|
const mongoClient = this.mongoClient;
|
|
158
|
-
const err =
|
|
187
|
+
const err = validOptions(mongoClient.s.options);
|
|
159
188
|
|
|
160
189
|
// Did we have a validation error
|
|
161
190
|
if (err) return callback(err);
|
|
@@ -166,6 +195,7 @@ class ConnectOperation extends OperationBase {
|
|
|
166
195
|
});
|
|
167
196
|
}
|
|
168
197
|
}
|
|
198
|
+
defineAspects(ConnectOperation, [Aspect.SKIP_SESSION]);
|
|
169
199
|
|
|
170
200
|
function addListeners(mongoClient, topology) {
|
|
171
201
|
topology.on('authenticated', createListener(mongoClient, 'authenticated'));
|
|
@@ -245,6 +275,7 @@ function connect(mongoClient, url, options, callback) {
|
|
|
245
275
|
if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
|
|
246
276
|
if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
|
|
247
277
|
if (_finalOptions.retryWrites == null) _finalOptions.retryWrites = true;
|
|
278
|
+
if (_finalOptions.useRecoveryToken == null) _finalOptions.useRecoveryToken = true;
|
|
248
279
|
|
|
249
280
|
if (_finalOptions.db_options && _finalOptions.db_options.auth) {
|
|
250
281
|
delete _finalOptions.db_options.auth;
|
|
@@ -433,7 +464,50 @@ function createTopology(mongoClient, topologyType, options, callback) {
|
|
|
433
464
|
}
|
|
434
465
|
|
|
435
466
|
assignTopology(mongoClient, newTopology);
|
|
436
|
-
|
|
467
|
+
if (options.autoEncryption == null) {
|
|
468
|
+
callback(null, newTopology);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// setup for client side encryption
|
|
473
|
+
let AutoEncrypter;
|
|
474
|
+
try {
|
|
475
|
+
AutoEncrypter = require('mongodb-client-encryption').AutoEncrypter;
|
|
476
|
+
} catch (err) {
|
|
477
|
+
callback(
|
|
478
|
+
new MongoError(
|
|
479
|
+
'Auto-encryption requested, but the module is not installed. Please add `mongodb-client-encryption` as a dependency of your project'
|
|
480
|
+
)
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const MongoClient = loadClient();
|
|
487
|
+
let connectionString;
|
|
488
|
+
if (options.autoEncryption.extraOptions && options.autoEncryption.extraOptions.mongocryptURI) {
|
|
489
|
+
connectionString = options.autoEncryption.extraOptions.mongocryptURI;
|
|
490
|
+
} else if (os.platform() === 'win32') {
|
|
491
|
+
connectionString = 'mongodb://localhost:27020/?serverSelectionTimeoutMS=1000';
|
|
492
|
+
} else {
|
|
493
|
+
connectionString = 'mongodb://%2Ftmp%2Fmongocryptd.sock/?serverSelectionTimeoutMS=1000';
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const mongocryptdClient = new MongoClient(connectionString, {
|
|
497
|
+
useNewUrlParser: true,
|
|
498
|
+
useUnifiedTopology: true
|
|
499
|
+
});
|
|
500
|
+
mongoClient.s.mongocryptdClient = mongocryptdClient;
|
|
501
|
+
mongocryptdClient.connect(err => {
|
|
502
|
+
if (err) return callback(err, null);
|
|
503
|
+
|
|
504
|
+
const mongoCryptOptions = Object.assign({}, options.autoEncryption, {
|
|
505
|
+
mongocryptdClient
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
topology.s.options.autoEncrypter = new AutoEncrypter(mongoClient, mongoCryptOptions);
|
|
509
|
+
callback(null, newTopology);
|
|
510
|
+
});
|
|
437
511
|
});
|
|
438
512
|
}
|
|
439
513
|
|
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const Aspect = require('./operation').Aspect;
|
|
4
4
|
const defineAspects = require('./operation').defineAspects;
|
|
5
|
-
const
|
|
5
|
+
const CommandOperationV2 = require('./command_v2');
|
|
6
6
|
const decorateWithCollation = require('../utils').decorateWithCollation;
|
|
7
7
|
const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
|
|
8
|
-
const executeCommand = require('./db_ops').executeCommand;
|
|
9
|
-
const handleCallback = require('../utils').handleCallback;
|
|
10
|
-
const resolveReadPreference = require('../utils').resolveReadPreference;
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
10
|
* Return a list of distinct values for the given key across a collection.
|
|
@@ -18,7 +15,7 @@ const resolveReadPreference = require('../utils').resolveReadPreference;
|
|
|
18
15
|
* @property {object} query The query for filtering the set of documents to which we apply the distinct filter.
|
|
19
16
|
* @property {object} [options] Optional settings. See Collection.prototype.distinct for a list of options.
|
|
20
17
|
*/
|
|
21
|
-
class DistinctOperation extends
|
|
18
|
+
class DistinctOperation extends CommandOperationV2 {
|
|
22
19
|
/**
|
|
23
20
|
* Construct a Distinct operation.
|
|
24
21
|
*
|
|
@@ -28,7 +25,7 @@ class DistinctOperation extends OperationBase {
|
|
|
28
25
|
* @param {object} [options] Optional settings. See Collection.prototype.distinct for a list of options.
|
|
29
26
|
*/
|
|
30
27
|
constructor(collection, key, query, options) {
|
|
31
|
-
super(options);
|
|
28
|
+
super(collection, options);
|
|
32
29
|
|
|
33
30
|
this.collection = collection;
|
|
34
31
|
this.key = key;
|
|
@@ -40,14 +37,11 @@ class DistinctOperation extends OperationBase {
|
|
|
40
37
|
*
|
|
41
38
|
* @param {Collection~resultCallback} [callback] The command result callback
|
|
42
39
|
*/
|
|
43
|
-
execute(callback) {
|
|
40
|
+
execute(server, callback) {
|
|
44
41
|
const coll = this.collection;
|
|
45
42
|
const key = this.key;
|
|
46
43
|
const query = this.query;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// maxTimeMS option
|
|
50
|
-
const maxTimeMS = options.maxTimeMS;
|
|
44
|
+
const options = this.options;
|
|
51
45
|
|
|
52
46
|
// Distinct command
|
|
53
47
|
const cmd = {
|
|
@@ -56,12 +50,10 @@ class DistinctOperation extends OperationBase {
|
|
|
56
50
|
query: query
|
|
57
51
|
};
|
|
58
52
|
|
|
59
|
-
options = Object.assign({}, options);
|
|
60
|
-
// Ensure we have the right read preference inheritance
|
|
61
|
-
options.readPreference = resolveReadPreference(coll, options);
|
|
62
|
-
|
|
63
53
|
// Add maxTimeMS if defined
|
|
64
|
-
if (typeof maxTimeMS === 'number')
|
|
54
|
+
if (typeof options.maxTimeMS === 'number') {
|
|
55
|
+
cmd.maxTimeMS = options.maxTimeMS;
|
|
56
|
+
}
|
|
65
57
|
|
|
66
58
|
// Do we have a readConcern specified
|
|
67
59
|
decorateWithReadConcern(cmd, coll, options);
|
|
@@ -73,14 +65,21 @@ class DistinctOperation extends OperationBase {
|
|
|
73
65
|
return callback(err, null);
|
|
74
66
|
}
|
|
75
67
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
super.executeCommand(server, cmd, (err, result) => {
|
|
69
|
+
if (err) {
|
|
70
|
+
callback(err);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
callback(null, this.options.full ? result : result.values);
|
|
80
75
|
});
|
|
81
76
|
}
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
defineAspects(DistinctOperation,
|
|
79
|
+
defineAspects(DistinctOperation, [
|
|
80
|
+
Aspect.READ_OPERATION,
|
|
81
|
+
Aspect.RETRYABLE,
|
|
82
|
+
Aspect.EXECUTE_WITH_SELECTION
|
|
83
|
+
]);
|
|
85
84
|
|
|
86
85
|
module.exports = DistinctOperation;
|