mongodb 3.6.12 → 3.7.0
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/index.js +4 -0
- package/lib/cmap/connection.js +34 -7
- package/lib/cmap/connection_pool.js +1 -0
- package/lib/collection.js +1 -1
- package/lib/core/connection/connect.js +7 -1
- package/lib/core/index.js +9 -0
- package/lib/core/sdam/monitor.js +7 -1
- package/lib/core/sdam/server.js +2 -0
- package/lib/core/sdam/topology.js +6 -1
- package/lib/core/sessions.js +11 -8
- package/lib/core/transactions.js +5 -1
- package/lib/core/uri_parser.js +5 -0
- package/lib/core/wireprotocol/command.js +24 -0
- package/lib/core/wireprotocol/kill_cursors.js +7 -2
- package/lib/core/wireprotocol/query.js +9 -5
- package/lib/cursor.js +4 -0
- package/lib/explain.js +5 -12
- package/lib/gridfs-stream/index.js +39 -24
- package/lib/gridfs-stream/upload.js +46 -38
- package/lib/mongo_client.js +30 -5
- package/lib/operations/connect.js +1 -0
- package/lib/operations/estimated_document_count.js +46 -18
- package/lib/topologies/native_topology.js +2 -0
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -17,6 +17,9 @@ connect.MongoWriteConcernError = core.MongoWriteConcernError;
|
|
|
17
17
|
connect.MongoBulkWriteError = require('./lib/bulk/common').BulkWriteError;
|
|
18
18
|
connect.BulkWriteError = connect.MongoBulkWriteError;
|
|
19
19
|
|
|
20
|
+
// Expose server versions
|
|
21
|
+
connect.ServerApiVersion = core.ServerApiVersion;
|
|
22
|
+
|
|
20
23
|
// Actual driver classes exported
|
|
21
24
|
connect.Admin = require('./lib/admin');
|
|
22
25
|
connect.MongoClient = require('./lib/mongo_client');
|
|
@@ -47,6 +50,7 @@ connect.Int32 = core.BSON.Int32;
|
|
|
47
50
|
connect.Long = core.BSON.Long;
|
|
48
51
|
connect.MinKey = core.BSON.MinKey;
|
|
49
52
|
connect.MaxKey = core.BSON.MaxKey;
|
|
53
|
+
/** @deprecated Please use `ObjectId` */
|
|
50
54
|
connect.ObjectID = core.BSON.ObjectID;
|
|
51
55
|
connect.ObjectId = core.BSON.ObjectID;
|
|
52
56
|
connect.Symbol = core.BSON.Symbol;
|
package/lib/cmap/connection.js
CHANGED
|
@@ -37,6 +37,8 @@ class Connection extends EventEmitter {
|
|
|
37
37
|
this.port = options.port || 27017;
|
|
38
38
|
this.monitorCommands =
|
|
39
39
|
typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false;
|
|
40
|
+
this.serverApi = options.serverApi;
|
|
41
|
+
|
|
40
42
|
this.closed = false;
|
|
41
43
|
this.destroyed = false;
|
|
42
44
|
|
|
@@ -170,33 +172,58 @@ class Connection extends EventEmitter {
|
|
|
170
172
|
});
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
applyApiVersion(options) {
|
|
176
|
+
if (this.serverApi) {
|
|
177
|
+
options.serverApi = this.serverApi;
|
|
178
|
+
}
|
|
179
|
+
return options;
|
|
180
|
+
}
|
|
181
|
+
|
|
173
182
|
// Wire protocol methods
|
|
174
183
|
command(ns, cmd, options, callback) {
|
|
175
|
-
|
|
184
|
+
if (typeof options === 'function') {
|
|
185
|
+
callback = options;
|
|
186
|
+
options = {};
|
|
187
|
+
}
|
|
188
|
+
wp.command(makeServerTrampoline(this), ns, cmd, this.applyApiVersion(options), callback);
|
|
176
189
|
}
|
|
177
190
|
|
|
178
191
|
query(ns, cmd, cursorState, options, callback) {
|
|
179
|
-
wp.query(
|
|
192
|
+
wp.query(
|
|
193
|
+
makeServerTrampoline(this),
|
|
194
|
+
ns,
|
|
195
|
+
cmd,
|
|
196
|
+
cursorState,
|
|
197
|
+
this.applyApiVersion(options),
|
|
198
|
+
callback
|
|
199
|
+
);
|
|
180
200
|
}
|
|
181
201
|
|
|
182
202
|
getMore(ns, cursorState, batchSize, options, callback) {
|
|
183
|
-
wp.getMore(
|
|
203
|
+
wp.getMore(
|
|
204
|
+
makeServerTrampoline(this),
|
|
205
|
+
ns,
|
|
206
|
+
cursorState,
|
|
207
|
+
batchSize,
|
|
208
|
+
this.applyApiVersion(options),
|
|
209
|
+
callback
|
|
210
|
+
);
|
|
184
211
|
}
|
|
185
212
|
|
|
186
213
|
killCursors(ns, cursorState, callback) {
|
|
187
|
-
wp.killCursors(makeServerTrampoline(this), ns, cursorState, callback);
|
|
214
|
+
wp.killCursors(makeServerTrampoline(this), ns, cursorState, this.applyApiVersion({}), callback);
|
|
188
215
|
}
|
|
189
216
|
|
|
190
217
|
insert(ns, ops, options, callback) {
|
|
191
|
-
wp.insert(makeServerTrampoline(this), ns, ops, options, callback);
|
|
218
|
+
wp.insert(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
update(ns, ops, options, callback) {
|
|
195
|
-
wp.update(makeServerTrampoline(this), ns, ops, options, callback);
|
|
222
|
+
wp.update(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
|
|
196
223
|
}
|
|
197
224
|
|
|
198
225
|
remove(ns, ops, options, callback) {
|
|
199
|
-
wp.remove(makeServerTrampoline(this), ns, ops, options, callback);
|
|
226
|
+
wp.remove(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
|
|
200
227
|
}
|
|
201
228
|
}
|
|
202
229
|
|
package/lib/collection.js
CHANGED
|
@@ -1532,7 +1532,7 @@ Collection.prototype.count = deprecate(function(query, options, callback) {
|
|
|
1532
1532
|
|
|
1533
1533
|
return executeOperation(
|
|
1534
1534
|
this.s.topology,
|
|
1535
|
-
new EstimatedDocumentCountOperation(this, query, options),
|
|
1535
|
+
new EstimatedDocumentCountOperation(this, Object.assign({ query }, options)),
|
|
1536
1536
|
callback
|
|
1537
1537
|
);
|
|
1538
1538
|
}, 'collection.count is deprecated, and will be removed in a future version.' +
|
|
@@ -108,6 +108,11 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
if ('isWritablePrimary' in response) {
|
|
112
|
+
// Provide pre-hello-style response document.
|
|
113
|
+
response.ismaster = response.isWritablePrimary;
|
|
114
|
+
}
|
|
115
|
+
|
|
111
116
|
const supportedServerErr = checkSupportedServer(response, options);
|
|
112
117
|
if (supportedServerErr) {
|
|
113
118
|
callback(supportedServerErr);
|
|
@@ -158,11 +163,12 @@ function performInitialHandshake(conn, options, _callback) {
|
|
|
158
163
|
|
|
159
164
|
function prepareHandshakeDocument(authContext, callback) {
|
|
160
165
|
const options = authContext.options;
|
|
166
|
+
const serverApi = authContext.connection.serverApi;
|
|
161
167
|
const compressors =
|
|
162
168
|
options.compression && options.compression.compressors ? options.compression.compressors : [];
|
|
163
169
|
|
|
164
170
|
const handshakeDoc = {
|
|
165
|
-
ismaster: true,
|
|
171
|
+
[serverApi ? 'hello' : 'ismaster']: true,
|
|
166
172
|
client: options.metadata || makeClientMetadata(options),
|
|
167
173
|
compression: compressors
|
|
168
174
|
};
|
package/lib/core/index.js
CHANGED
|
@@ -15,7 +15,16 @@ try {
|
|
|
15
15
|
}
|
|
16
16
|
} catch (err) {} // eslint-disable-line
|
|
17
17
|
|
|
18
|
+
/** An enumeration of valid server API versions */
|
|
19
|
+
const ServerApiVersion = Object.freeze({
|
|
20
|
+
v1: '1'
|
|
21
|
+
});
|
|
22
|
+
const ValidServerApiVersions = Object.keys(ServerApiVersion).map(key => ServerApiVersion[key]);
|
|
23
|
+
|
|
18
24
|
module.exports = {
|
|
25
|
+
// Versioned API
|
|
26
|
+
ServerApiVersion,
|
|
27
|
+
ValidServerApiVersions,
|
|
19
28
|
// Errors
|
|
20
29
|
MongoError: require('./error').MongoError,
|
|
21
30
|
MongoNetworkError: require('./error').MongoNetworkError,
|
package/lib/core/sdam/monitor.js
CHANGED
|
@@ -204,8 +204,9 @@ function checkServer(monitor, callback) {
|
|
|
204
204
|
const maxAwaitTimeMS = monitor.options.heartbeatFrequencyMS;
|
|
205
205
|
const topologyVersion = monitor[kServer].description.topologyVersion;
|
|
206
206
|
const isAwaitable = topologyVersion != null;
|
|
207
|
+
const serverApi = monitor[kConnection].serverApi;
|
|
207
208
|
|
|
208
|
-
const cmd = { ismaster: true };
|
|
209
|
+
const cmd = { [serverApi ? 'hello' : 'ismaster']: true };
|
|
209
210
|
const options = { socketTimeout: connectTimeoutMS };
|
|
210
211
|
|
|
211
212
|
if (isAwaitable) {
|
|
@@ -229,6 +230,11 @@ function checkServer(monitor, callback) {
|
|
|
229
230
|
const isMaster = result.result;
|
|
230
231
|
const rttPinger = monitor[kRTTPinger];
|
|
231
232
|
|
|
233
|
+
if ('isWritablePrimary' in isMaster) {
|
|
234
|
+
// Provide pre-hello-style response document.
|
|
235
|
+
isMaster.ismaster = isMaster.isWritablePrimary;
|
|
236
|
+
}
|
|
237
|
+
|
|
232
238
|
const duration =
|
|
233
239
|
isAwaitable && rttPinger ? rttPinger.roundTripTime : calculateDurationInMs(start);
|
|
234
240
|
|
package/lib/core/sdam/server.js
CHANGED
|
@@ -114,6 +114,7 @@ class Server extends EventEmitter {
|
|
|
114
114
|
credentials: options.credentials,
|
|
115
115
|
topology
|
|
116
116
|
};
|
|
117
|
+
this.serverApi = options.serverApi;
|
|
117
118
|
|
|
118
119
|
// create the connection pool
|
|
119
120
|
// NOTE: this used to happen in `connect`, we supported overriding pool options there
|
|
@@ -251,6 +252,7 @@ class Server extends EventEmitter {
|
|
|
251
252
|
if (typeof options === 'function') {
|
|
252
253
|
(callback = options), (options = {}), (options = options || {});
|
|
253
254
|
}
|
|
255
|
+
options.serverApi = this.serverApi;
|
|
254
256
|
|
|
255
257
|
if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) {
|
|
256
258
|
callback(new MongoError('server is closed'));
|
|
@@ -200,6 +200,7 @@ class Topology extends EventEmitter {
|
|
|
200
200
|
// timer management
|
|
201
201
|
connectionTimers: new Set()
|
|
202
202
|
};
|
|
203
|
+
this.serverApi = options.serverApi;
|
|
203
204
|
|
|
204
205
|
if (options.srvHost) {
|
|
205
206
|
this.s.srvPoller =
|
|
@@ -494,7 +495,11 @@ class Topology extends EventEmitter {
|
|
|
494
495
|
this.command(
|
|
495
496
|
'admin.$cmd',
|
|
496
497
|
{ endSessions: sessions },
|
|
497
|
-
{
|
|
498
|
+
{
|
|
499
|
+
readPreference: ReadPreference.primaryPreferred,
|
|
500
|
+
noResponse: true,
|
|
501
|
+
serverApi: this.serverApi
|
|
502
|
+
},
|
|
498
503
|
() => {
|
|
499
504
|
// intentionally ignored, per spec
|
|
500
505
|
if (typeof callback === 'function') callback();
|
package/lib/core/sessions.js
CHANGED
|
@@ -472,17 +472,20 @@ function endTransaction(session, commandName, callback) {
|
|
|
472
472
|
if (commandName === 'commitTransaction') {
|
|
473
473
|
session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
|
|
474
474
|
|
|
475
|
-
if (
|
|
476
|
-
|
|
477
|
-
|
|
475
|
+
if (e) {
|
|
476
|
+
if (
|
|
477
|
+
e instanceof MongoNetworkError ||
|
|
478
478
|
e instanceof MongoWriteConcernError ||
|
|
479
479
|
isRetryableError(e) ||
|
|
480
|
-
isMaxTimeMSExpiredError(e)
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
480
|
+
isMaxTimeMSExpiredError(e)
|
|
481
|
+
) {
|
|
482
|
+
if (isUnknownTransactionCommitResult(e)) {
|
|
483
|
+
e.addErrorLabel('UnknownTransactionCommitResult');
|
|
484
484
|
|
|
485
|
-
|
|
485
|
+
// per txns spec, must unpin session in this case
|
|
486
|
+
session.transaction.unpinServer();
|
|
487
|
+
}
|
|
488
|
+
} else if (e.hasErrorLabel('TransientTransactionError')) {
|
|
486
489
|
session.transaction.unpinServer();
|
|
487
490
|
}
|
|
488
491
|
}
|
package/lib/core/transactions.js
CHANGED
|
@@ -150,7 +150,11 @@ class Transaction {
|
|
|
150
150
|
const nextStates = stateMachine[this.state];
|
|
151
151
|
if (nextStates && nextStates.indexOf(nextState) !== -1) {
|
|
152
152
|
this.state = nextState;
|
|
153
|
-
if (
|
|
153
|
+
if (
|
|
154
|
+
this.state === TxnState.NO_TRANSACTION ||
|
|
155
|
+
this.state === TxnState.STARTING_TRANSACTION ||
|
|
156
|
+
this.state === TxnState.TRANSACTION_ABORTED
|
|
157
|
+
) {
|
|
154
158
|
this.unpinServer();
|
|
155
159
|
}
|
|
156
160
|
return;
|
package/lib/core/uri_parser.js
CHANGED
|
@@ -432,6 +432,11 @@ function parseQueryString(query, options) {
|
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
const normalizedKey = key.toLowerCase();
|
|
435
|
+
if (normalizedKey === 'serverapi') {
|
|
436
|
+
throw new MongoParseError(
|
|
437
|
+
'URI cannot contain `serverApi`, it can only be passed to the client'
|
|
438
|
+
);
|
|
439
|
+
}
|
|
435
440
|
const parsedValue = FILE_PATH_OPTIONS.has(normalizedKey)
|
|
436
441
|
? value
|
|
437
442
|
: parseQueryStringItemValue(normalizedKey, value);
|
|
@@ -48,6 +48,18 @@ function _command(server, ns, cmd, options, callback) {
|
|
|
48
48
|
const serverClusterTime = server.clusterTime;
|
|
49
49
|
let clusterTime = serverClusterTime;
|
|
50
50
|
let finalCmd = Object.assign({}, cmd);
|
|
51
|
+
|
|
52
|
+
const serverApi = options.serverApi;
|
|
53
|
+
if (serverApi) {
|
|
54
|
+
finalCmd.apiVersion = serverApi.version || serverApi;
|
|
55
|
+
if (serverApi.strict != null) {
|
|
56
|
+
finalCmd.apiStrict = serverApi.strict;
|
|
57
|
+
}
|
|
58
|
+
if (serverApi.deprecationErrors != null) {
|
|
59
|
+
finalCmd.apiDeprecationErrors = serverApi.deprecationErrors;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
if (hasSessionSupport(server) && session) {
|
|
52
64
|
const sessionClusterTime = session.clusterTime;
|
|
53
65
|
if (
|
|
@@ -60,6 +72,18 @@ function _command(server, ns, cmd, options, callback) {
|
|
|
60
72
|
clusterTime = sessionClusterTime;
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
// We need to unpin any read or write commands that happen outside of a pinned
|
|
76
|
+
// transaction, so we check if we have a pinned transaction that is no longer
|
|
77
|
+
// active, and unpin for all except start or commit.
|
|
78
|
+
if (
|
|
79
|
+
!session.transaction.isActive &&
|
|
80
|
+
session.transaction.isPinned &&
|
|
81
|
+
!finalCmd.startTransaction &&
|
|
82
|
+
!finalCmd.commitTransaction
|
|
83
|
+
) {
|
|
84
|
+
session.transaction.unpinServer();
|
|
85
|
+
}
|
|
86
|
+
|
|
63
87
|
const err = applySession(session, finalCmd, options);
|
|
64
88
|
if (err) {
|
|
65
89
|
return callback(err);
|
|
@@ -8,8 +8,13 @@ const maxWireVersion = require('../utils').maxWireVersion;
|
|
|
8
8
|
const emitWarning = require('../utils').emitWarning;
|
|
9
9
|
const command = require('./command');
|
|
10
10
|
|
|
11
|
-
function killCursors(server, ns, cursorState, callback) {
|
|
11
|
+
function killCursors(server, ns, cursorState, defaultOptions, callback) {
|
|
12
|
+
if (typeof defaultOptions === 'function') {
|
|
13
|
+
callback = defaultOptions;
|
|
14
|
+
defaultOptions = {};
|
|
15
|
+
}
|
|
12
16
|
callback = typeof callback === 'function' ? callback : () => {};
|
|
17
|
+
|
|
13
18
|
const cursorId = cursorState.cursorId;
|
|
14
19
|
|
|
15
20
|
if (maxWireVersion(server) < 4) {
|
|
@@ -45,7 +50,7 @@ function killCursors(server, ns, cursorState, callback) {
|
|
|
45
50
|
cursors: [cursorId]
|
|
46
51
|
};
|
|
47
52
|
|
|
48
|
-
const options = {};
|
|
53
|
+
const options = defaultOptions || {};
|
|
49
54
|
if (typeof cursorState.session === 'object') options.session = cursorState.session;
|
|
50
55
|
|
|
51
56
|
command(server, ns, killCursorCmd, options, (err, result) => {
|
|
@@ -37,9 +37,13 @@ function query(server, ns, cmd, cursorState, options, callback) {
|
|
|
37
37
|
|
|
38
38
|
// If we have explain, we need to rewrite the find command
|
|
39
39
|
// to wrap it in the explain command
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
try {
|
|
41
|
+
const explain = Explain.fromOptions(options);
|
|
42
|
+
if (explain) {
|
|
43
|
+
findCmd = decorateWithExplain(findCmd, explain);
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return callback(err);
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
// NOTE: This actually modifies the passed in cmd, and our code _depends_ on this
|
|
@@ -140,8 +144,8 @@ function prepareFindCommand(server, ns, cmd, cursorState) {
|
|
|
140
144
|
if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
|
|
141
145
|
if (cmd.min) findCmd.min = cmd.min;
|
|
142
146
|
if (cmd.max) findCmd.max = cmd.max;
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
if (typeof cmd.returnKey === 'boolean') findCmd.returnKey = cmd.returnKey;
|
|
148
|
+
if (typeof cmd.showDiskLoc === 'boolean') findCmd.showRecordId = cmd.showDiskLoc;
|
|
145
149
|
if (cmd.snapshot) findCmd.snapshot = cmd.snapshot;
|
|
146
150
|
if (cmd.tailable) findCmd.tailable = cmd.tailable;
|
|
147
151
|
if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
|
package/lib/cursor.js
CHANGED
|
@@ -165,6 +165,10 @@ class Cursor extends CoreCursor {
|
|
|
165
165
|
return this.cmd.sort;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
set session(clientSession) {
|
|
169
|
+
this.cursorState.session = clientSession;
|
|
170
|
+
}
|
|
171
|
+
|
|
168
172
|
_initializeCursor(callback) {
|
|
169
173
|
if (this.operation && this.operation.session != null) {
|
|
170
174
|
this.cursorState.session = this.operation.session;
|
package/lib/explain.js
CHANGED
|
@@ -2,16 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const MongoError = require('./core/error').MongoError;
|
|
4
4
|
|
|
5
|
-
const ExplainVerbosity = {
|
|
6
|
-
queryPlanner: 'queryPlanner',
|
|
7
|
-
queryPlannerExtended: 'queryPlannerExtended',
|
|
8
|
-
executionStats: 'executionStats',
|
|
9
|
-
allPlansExecution: 'allPlansExecution'
|
|
10
|
-
};
|
|
11
|
-
|
|
12
5
|
/**
|
|
13
6
|
* @class
|
|
14
|
-
* @property {
|
|
7
|
+
* @property {string} verbosity The verbosity mode for the explain output, e.g.: 'queryPlanner', 'queryPlannerExtended', 'executionStats', 'allPlansExecution'.
|
|
15
8
|
*/
|
|
16
9
|
class Explain {
|
|
17
10
|
/**
|
|
@@ -21,7 +14,7 @@ class Explain {
|
|
|
21
14
|
* and false as "queryPlanner". Prior to server version 3.6, aggregate()
|
|
22
15
|
* ignores the verbosity parameter and executes in "queryPlanner".
|
|
23
16
|
*
|
|
24
|
-
* @param {
|
|
17
|
+
* @param {string|boolean} [verbosity] The verbosity mode for the explain output.
|
|
25
18
|
*/
|
|
26
19
|
constructor(verbosity) {
|
|
27
20
|
if (typeof verbosity === 'boolean') {
|
|
@@ -35,7 +28,7 @@ class Explain {
|
|
|
35
28
|
* Construct an Explain given an options object.
|
|
36
29
|
*
|
|
37
30
|
* @param {object} [options] The options object from which to extract the explain.
|
|
38
|
-
* @param {
|
|
31
|
+
* @param {string|boolean} [options.explain] The verbosity mode for the explain output.
|
|
39
32
|
* @return {Explain}
|
|
40
33
|
*/
|
|
41
34
|
static fromOptions(options) {
|
|
@@ -44,11 +37,11 @@ class Explain {
|
|
|
44
37
|
}
|
|
45
38
|
|
|
46
39
|
const explain = options.explain;
|
|
47
|
-
if (typeof explain === 'boolean' || explain
|
|
40
|
+
if (typeof explain === 'boolean' || typeof explain === 'string') {
|
|
48
41
|
return new Explain(options.explain);
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
throw new MongoError(`explain must be
|
|
44
|
+
throw new MongoError(`explain must be a string or a boolean`);
|
|
52
45
|
}
|
|
53
46
|
}
|
|
54
47
|
|
|
@@ -7,6 +7,7 @@ var shallowClone = require('../utils').shallowClone;
|
|
|
7
7
|
var toError = require('../utils').toError;
|
|
8
8
|
var util = require('util');
|
|
9
9
|
var executeLegacyOperation = require('../utils').executeLegacyOperation;
|
|
10
|
+
const deprecateOptions = require('../utils').deprecateOptions;
|
|
10
11
|
|
|
11
12
|
var DEFAULT_GRIDFS_BUCKET_OPTIONS = {
|
|
12
13
|
bucketName: 'fs',
|
|
@@ -79,21 +80,28 @@ util.inherits(GridFSBucket, Emitter);
|
|
|
79
80
|
* @param {object} [options.metadata] Optional object to store in the file document's `metadata` field
|
|
80
81
|
* @param {string} [options.contentType] Optional string to store in the file document's `contentType` field
|
|
81
82
|
* @param {array} [options.aliases] Optional array of strings to store in the file document's `aliases` field
|
|
82
|
-
* @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
|
|
83
|
+
* @param {boolean} [options.disableMD5=false] **Deprecated** If true, disables adding an md5 field to file data
|
|
83
84
|
* @return {GridFSBucketWriteStream}
|
|
84
85
|
*/
|
|
85
86
|
|
|
86
|
-
GridFSBucket.prototype.openUploadStream =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
options
|
|
87
|
+
GridFSBucket.prototype.openUploadStream = deprecateOptions(
|
|
88
|
+
{
|
|
89
|
+
name: 'GridFSBucket.openUploadStream',
|
|
90
|
+
deprecatedOptions: ['disableMD5'],
|
|
91
|
+
optionsIndex: 1
|
|
92
|
+
},
|
|
93
|
+
function(filename, options) {
|
|
94
|
+
if (options) {
|
|
95
|
+
options = shallowClone(options);
|
|
96
|
+
} else {
|
|
97
|
+
options = {};
|
|
98
|
+
}
|
|
99
|
+
if (!options.chunkSizeBytes) {
|
|
100
|
+
options.chunkSizeBytes = this.s.options.chunkSizeBytes;
|
|
101
|
+
}
|
|
102
|
+
return new GridFSBucketWriteStream(this, filename, options);
|
|
94
103
|
}
|
|
95
|
-
|
|
96
|
-
};
|
|
104
|
+
);
|
|
97
105
|
|
|
98
106
|
/**
|
|
99
107
|
* Returns a writable stream (GridFSBucketWriteStream) for writing
|
|
@@ -107,25 +115,32 @@ GridFSBucket.prototype.openUploadStream = function(filename, options) {
|
|
|
107
115
|
* @param {object} [options.metadata] Optional object to store in the file document's `metadata` field
|
|
108
116
|
* @param {string} [options.contentType] Optional string to store in the file document's `contentType` field
|
|
109
117
|
* @param {array} [options.aliases] Optional array of strings to store in the file document's `aliases` field
|
|
110
|
-
* @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
|
|
118
|
+
* @param {boolean} [options.disableMD5=false] **Deprecated** If true, disables adding an md5 field to file data
|
|
111
119
|
* @return {GridFSBucketWriteStream}
|
|
112
120
|
*/
|
|
113
121
|
|
|
114
|
-
GridFSBucket.prototype.openUploadStreamWithId =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
122
|
+
GridFSBucket.prototype.openUploadStreamWithId = deprecateOptions(
|
|
123
|
+
{
|
|
124
|
+
name: 'GridFSBucket.openUploadStreamWithId',
|
|
125
|
+
deprecatedOptions: ['disableMD5'],
|
|
126
|
+
optionsIndex: 2
|
|
127
|
+
},
|
|
128
|
+
function(id, filename, options) {
|
|
129
|
+
if (options) {
|
|
130
|
+
options = shallowClone(options);
|
|
131
|
+
} else {
|
|
132
|
+
options = {};
|
|
133
|
+
}
|
|
120
134
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
if (!options.chunkSizeBytes) {
|
|
136
|
+
options.chunkSizeBytes = this.s.options.chunkSizeBytes;
|
|
137
|
+
}
|
|
124
138
|
|
|
125
|
-
|
|
139
|
+
options.id = id;
|
|
126
140
|
|
|
127
|
-
|
|
128
|
-
}
|
|
141
|
+
return new GridFSBucketWriteStream(this, filename, options);
|
|
142
|
+
}
|
|
143
|
+
);
|
|
129
144
|
|
|
130
145
|
/**
|
|
131
146
|
* Returns a readable stream (GridFSBucketReadStream) for streaming file
|
|
@@ -5,10 +5,9 @@ var crypto = require('crypto');
|
|
|
5
5
|
var stream = require('stream');
|
|
6
6
|
var util = require('util');
|
|
7
7
|
var Buffer = require('safe-buffer').Buffer;
|
|
8
|
+
const deprecateOptions = require('../utils').deprecateOptions;
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
module.exports = GridFSBucketWriteStream;
|
|
10
|
+
const ERROR_NAMESPACE_NOT_FOUND = 26;
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* A writable stream that enables you to write buffers to GridFS.
|
|
@@ -31,42 +30,49 @@ module.exports = GridFSBucketWriteStream;
|
|
|
31
30
|
* @fires GridFSBucketWriteStream#finish
|
|
32
31
|
*/
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
33
|
+
const GridFSBucketWriteStream = deprecateOptions(
|
|
34
|
+
{
|
|
35
|
+
name: 'GridFSBucketWriteStream',
|
|
36
|
+
deprecatedOptions: ['disableMD5'],
|
|
37
|
+
optionsIndex: 2
|
|
38
|
+
},
|
|
39
|
+
function(bucket, filename, options) {
|
|
40
|
+
options = options || {};
|
|
41
|
+
stream.Writable.call(this, options);
|
|
42
|
+
this.bucket = bucket;
|
|
43
|
+
this.chunks = bucket.s._chunksCollection;
|
|
44
|
+
this.filename = filename;
|
|
45
|
+
this.files = bucket.s._filesCollection;
|
|
46
|
+
this.options = options;
|
|
47
|
+
// Signals the write is all done
|
|
48
|
+
this.done = false;
|
|
49
|
+
|
|
50
|
+
this.id = options.id ? options.id : core.BSON.ObjectId();
|
|
51
|
+
this.chunkSizeBytes = this.options.chunkSizeBytes;
|
|
52
|
+
this.bufToStore = Buffer.alloc(this.chunkSizeBytes);
|
|
53
|
+
this.length = 0;
|
|
54
|
+
this.md5 = !options.disableMD5 && crypto.createHash('md5');
|
|
55
|
+
this.n = 0;
|
|
56
|
+
this.pos = 0;
|
|
57
|
+
this.state = {
|
|
58
|
+
streamEnd: false,
|
|
59
|
+
outstandingRequests: 0,
|
|
60
|
+
errored: false,
|
|
61
|
+
aborted: false,
|
|
62
|
+
promiseLibrary: this.bucket.s.promiseLibrary
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (!this.bucket.s.calledOpenUploadStream) {
|
|
66
|
+
this.bucket.s.calledOpenUploadStream = true;
|
|
67
|
+
|
|
68
|
+
var _this = this;
|
|
69
|
+
checkIndexes(this, function() {
|
|
70
|
+
_this.bucket.s.checkedIndexes = true;
|
|
71
|
+
_this.bucket.emit('index');
|
|
72
|
+
});
|
|
73
|
+
}
|
|
68
74
|
}
|
|
69
|
-
|
|
75
|
+
);
|
|
70
76
|
|
|
71
77
|
util.inherits(GridFSBucketWriteStream, stream.Writable);
|
|
72
78
|
|
|
@@ -539,3 +545,5 @@ function checkAborted(_this, callback) {
|
|
|
539
545
|
}
|
|
540
546
|
return false;
|
|
541
547
|
}
|
|
548
|
+
|
|
549
|
+
module.exports = GridFSBucketWriteStream;
|
package/lib/mongo_client.js
CHANGED
|
@@ -5,6 +5,7 @@ const Db = require('./db');
|
|
|
5
5
|
const EventEmitter = require('events').EventEmitter;
|
|
6
6
|
const inherits = require('util').inherits;
|
|
7
7
|
const MongoError = require('./core').MongoError;
|
|
8
|
+
const ValidServerApiVersions = require('./core').ValidServerApiVersions;
|
|
8
9
|
const deprecate = require('util').deprecate;
|
|
9
10
|
const WriteConcern = require('./write_concern');
|
|
10
11
|
const MongoDBNamespace = require('./utils').MongoDBNamespace;
|
|
@@ -156,6 +157,7 @@ const validOptions = require('./operations/connect').validOptions;
|
|
|
156
157
|
* @property {number} [numberOfRetries] (**default**: 5) The number of retries for a tailable cursor
|
|
157
158
|
* @property {boolean} [auto_reconnect] (**default**: true) Enable auto reconnecting for single server instances
|
|
158
159
|
* @property {boolean} [monitorCommands] (**default**: false) Enable command monitoring for this client
|
|
160
|
+
* @property {string|ServerApi} [serverApi] (**default**: undefined) The server API version
|
|
159
161
|
* @property {number} [minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
|
|
160
162
|
* @property {boolean} [useNewUrlParser] (**default**: true) Determines whether or not to use the new url parser. Enables the new, spec-compliant, url parser shipped in the core driver. This url parser fixes a number of problems with the original parser, and aims to outright replace that parser in the near future. Defaults to true, and must be explicitly set to false to use the legacy url parser.
|
|
161
163
|
* @property {boolean} [useUnifiedTopology] Enables the new unified topology layer
|
|
@@ -191,16 +193,38 @@ const validOptions = require('./operations/connect').validOptions;
|
|
|
191
193
|
* @param {MongoClientOptions} [options] Optional settings
|
|
192
194
|
*/
|
|
193
195
|
function MongoClient(url, options) {
|
|
196
|
+
options = options || {};
|
|
194
197
|
if (!(this instanceof MongoClient)) return new MongoClient(url, options);
|
|
195
198
|
// Set up event emitter
|
|
196
199
|
EventEmitter.call(this);
|
|
197
200
|
|
|
198
|
-
if (options
|
|
201
|
+
if (options.autoEncryption) require('./encrypter'); // Does CSFLE lib check
|
|
202
|
+
|
|
203
|
+
if (options.serverApi) {
|
|
204
|
+
const serverApiToValidate =
|
|
205
|
+
typeof options.serverApi === 'string' ? { version: options.serverApi } : options.serverApi;
|
|
206
|
+
const versionToValidate = serverApiToValidate && serverApiToValidate.version;
|
|
207
|
+
if (!versionToValidate) {
|
|
208
|
+
throw new MongoError(
|
|
209
|
+
`Invalid \`serverApi\` property; must specify a version from the following enum: ["${ValidServerApiVersions.join(
|
|
210
|
+
'", "'
|
|
211
|
+
)}"]`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
if (!ValidServerApiVersions.some(v => v === versionToValidate)) {
|
|
215
|
+
throw new MongoError(
|
|
216
|
+
`Invalid server API version=${versionToValidate}; must be in the following enum: ["${ValidServerApiVersions.join(
|
|
217
|
+
'", "'
|
|
218
|
+
)}"]`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
options.serverApi = serverApiToValidate;
|
|
222
|
+
}
|
|
199
223
|
|
|
200
224
|
// The internal state
|
|
201
225
|
this.s = {
|
|
202
|
-
url
|
|
203
|
-
options
|
|
226
|
+
url,
|
|
227
|
+
options,
|
|
204
228
|
promiseLibrary: (options && options.promiseLibrary) || Promise,
|
|
205
229
|
dbCache: new Map(),
|
|
206
230
|
sessions: new Set(),
|
|
@@ -364,17 +388,18 @@ MongoClient.prototype.db = function(dbName, options) {
|
|
|
364
388
|
* Check if MongoClient is connected
|
|
365
389
|
*
|
|
366
390
|
* @method
|
|
391
|
+
* @deprecated
|
|
367
392
|
* @param {object} [options] Optional settings.
|
|
368
393
|
* @param {boolean} [options.noListener=false] Do not make the db an event listener to the original connection.
|
|
369
394
|
* @param {boolean} [options.returnNonCachedInstance=false] Control if you want to return a cached instance or have a new one created
|
|
370
395
|
* @return {boolean}
|
|
371
396
|
*/
|
|
372
|
-
MongoClient.prototype.isConnected = function(options) {
|
|
397
|
+
MongoClient.prototype.isConnected = deprecate(function(options) {
|
|
373
398
|
options = options || {};
|
|
374
399
|
|
|
375
400
|
if (!this.topology) return false;
|
|
376
401
|
return this.topology.isConnected(options);
|
|
377
|
-
};
|
|
402
|
+
}, 'isConnected is deprecated and will be removed in the next major version');
|
|
378
403
|
|
|
379
404
|
/**
|
|
380
405
|
* Connect to MongoDB using a url as documented at
|
|
@@ -3,41 +3,69 @@
|
|
|
3
3
|
const Aspect = require('./operation').Aspect;
|
|
4
4
|
const defineAspects = require('./operation').defineAspects;
|
|
5
5
|
const CommandOperationV2 = require('./command_v2');
|
|
6
|
+
const maxWireVersion = require('../core/utils').maxWireVersion;
|
|
7
|
+
const CountDocumentsOperation = require('./count_documents');
|
|
6
8
|
|
|
7
9
|
class EstimatedDocumentCountOperation extends CommandOperationV2 {
|
|
8
|
-
constructor(collection,
|
|
9
|
-
if (typeof options === 'undefined') {
|
|
10
|
-
options = query;
|
|
11
|
-
query = undefined;
|
|
12
|
-
}
|
|
13
|
-
|
|
10
|
+
constructor(collection, options) {
|
|
14
11
|
super(collection, options);
|
|
12
|
+
this.collection = collection;
|
|
15
13
|
this.collectionName = collection.s.namespace.collection;
|
|
16
|
-
if (query) {
|
|
17
|
-
this.query = query;
|
|
18
|
-
}
|
|
19
14
|
}
|
|
20
15
|
|
|
21
16
|
execute(server, callback) {
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
if (maxWireVersion(server) < 12) {
|
|
18
|
+
return this.executeLegacy(server, callback);
|
|
19
|
+
}
|
|
20
|
+
// if the user specifies a filter, use a CountDocumentsOperation instead
|
|
21
|
+
if (this.options.query) {
|
|
22
|
+
const op = new CountDocumentsOperation(this.collection, this.options.query, this.options);
|
|
23
|
+
return op.execute(server, callback);
|
|
24
|
+
}
|
|
25
|
+
const pipeline = [{ $collStats: { count: {} } }, { $group: { _id: 1, n: { $sum: '$count' } } }];
|
|
26
|
+
const cmd = { aggregate: this.collectionName, pipeline, cursor: {} };
|
|
24
27
|
|
|
25
|
-
if (this.
|
|
26
|
-
cmd.
|
|
28
|
+
if (typeof this.options.maxTimeMS === 'number') {
|
|
29
|
+
cmd.maxTimeMS = this.options.maxTimeMS;
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
super.executeCommand(server, cmd, (err, response) => {
|
|
33
|
+
if (err && err.code !== 26) {
|
|
34
|
+
callback(err);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
callback(
|
|
39
|
+
undefined,
|
|
40
|
+
(response &&
|
|
41
|
+
response.cursor &&
|
|
42
|
+
response.cursor.firstBatch &&
|
|
43
|
+
response.cursor.firstBatch[0].n) ||
|
|
44
|
+
0
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
executeLegacy(server, callback) {
|
|
50
|
+
const cmd = { count: this.collectionName };
|
|
51
|
+
|
|
52
|
+
const options = this.options;
|
|
53
|
+
if (options.query) {
|
|
54
|
+
cmd.query = options.query;
|
|
55
|
+
}
|
|
56
|
+
if (options.hint) {
|
|
57
|
+
cmd.hint = options.hint;
|
|
58
|
+
}
|
|
59
|
+
if (typeof options.maxTimeMS === 'number') {
|
|
60
|
+
cmd.maxTimeMS = options.maxTimeMS;
|
|
61
|
+
}
|
|
29
62
|
if (typeof options.skip === 'number') {
|
|
30
63
|
cmd.skip = options.skip;
|
|
31
64
|
}
|
|
32
|
-
|
|
33
65
|
if (typeof options.limit === 'number') {
|
|
34
66
|
cmd.limit = options.limit;
|
|
35
67
|
}
|
|
36
68
|
|
|
37
|
-
if (options.hint) {
|
|
38
|
-
cmd.hint = options.hint;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
69
|
super.executeCommand(server, cmd, (err, response) => {
|
|
42
70
|
if (err) {
|
|
43
71
|
callback(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongodb",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "The official MongoDB driver for Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"check:tls": "mocha --opts '{}' test/manual/tls_support.test.js",
|
|
103
103
|
"format": "npm run check:lint -- --fix",
|
|
104
104
|
"release": "standard-version -i HISTORY.md",
|
|
105
|
-
"test": "npm run lint && mocha --recursive test/functional test/unit"
|
|
105
|
+
"test": "npm run check:lint && mocha --recursive test/functional test/unit"
|
|
106
106
|
},
|
|
107
107
|
"homepage": "https://github.com/mongodb/node-mongodb-native",
|
|
108
108
|
"optionalDependencies": {
|