mongodb 3.5.11 → 3.6.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 (68) hide show
  1. package/HISTORY.md +42 -21
  2. package/lib/admin.js +1 -0
  3. package/lib/bulk/common.js +48 -4
  4. package/lib/change_stream.js +0 -1
  5. package/lib/cmap/connection.js +9 -6
  6. package/lib/cmap/connection_pool.js +4 -10
  7. package/lib/collection.js +59 -81
  8. package/lib/core/auth/auth_provider.js +29 -132
  9. package/lib/core/auth/defaultAuthProviders.js +2 -2
  10. package/lib/core/auth/gssapi.js +124 -214
  11. package/lib/core/auth/mongo_credentials.js +29 -3
  12. package/lib/core/auth/mongocr.js +6 -12
  13. package/lib/core/auth/mongodb_aws.js +256 -0
  14. package/lib/core/auth/plain.js +5 -12
  15. package/lib/core/auth/scram.js +229 -212
  16. package/lib/core/auth/x509.js +25 -16
  17. package/lib/core/connection/connect.js +97 -161
  18. package/lib/core/connection/connection.js +71 -3
  19. package/lib/core/connection/pool.js +2 -2
  20. package/lib/core/cursor.js +18 -11
  21. package/lib/core/error.js +82 -8
  22. package/lib/core/sdam/common.js +8 -0
  23. package/lib/core/sdam/monitor.js +240 -78
  24. package/lib/core/sdam/server.js +81 -15
  25. package/lib/core/sdam/server_description.js +37 -2
  26. package/lib/core/sdam/topology.js +41 -8
  27. package/lib/core/sdam/topology_description.js +21 -3
  28. package/lib/core/sessions.js +38 -51
  29. package/lib/core/topologies/mongos.js +18 -6
  30. package/lib/core/topologies/read_preference.js +15 -3
  31. package/lib/core/topologies/replset.js +4 -4
  32. package/lib/core/topologies/server.js +1 -1
  33. package/lib/core/topologies/shared.js +39 -16
  34. package/lib/core/uri_parser.js +41 -6
  35. package/lib/core/utils.js +30 -0
  36. package/lib/core/wireprotocol/command.js +1 -4
  37. package/lib/core/wireprotocol/constants.js +2 -2
  38. package/lib/core/wireprotocol/query.js +4 -0
  39. package/lib/cursor.js +0 -1
  40. package/lib/db.js +6 -5
  41. package/lib/error.js +6 -1
  42. package/lib/gridfs-stream/download.js +13 -2
  43. package/lib/mongo_client.js +6 -4
  44. package/lib/operations/collection_ops.js +0 -20
  45. package/lib/operations/command_v2.js +1 -1
  46. package/lib/operations/common_functions.js +3 -0
  47. package/lib/operations/connect.js +11 -14
  48. package/lib/operations/create_collection.js +37 -52
  49. package/lib/operations/create_indexes.js +111 -35
  50. package/lib/operations/find.js +7 -1
  51. package/lib/operations/find_and_modify.js +17 -0
  52. package/lib/operations/find_one_and_delete.js +5 -0
  53. package/lib/operations/find_one_and_replace.js +13 -0
  54. package/lib/operations/find_one_and_update.js +13 -0
  55. package/lib/operations/map_reduce.js +1 -1
  56. package/lib/operations/re_index.js +22 -17
  57. package/lib/operations/replace_one.js +11 -4
  58. package/lib/operations/update_many.js +5 -0
  59. package/lib/operations/update_one.js +5 -0
  60. package/lib/operations/validate_collection.js +1 -2
  61. package/lib/topologies/mongos.js +1 -1
  62. package/lib/topologies/replset.js +1 -1
  63. package/lib/topologies/server.js +1 -1
  64. package/lib/utils.js +18 -1
  65. package/lib/write_concern.js +10 -0
  66. package/package.json +1 -1
  67. package/lib/core/auth/sspi.js +0 -131
  68. package/lib/operations/create_index.js +0 -92
@@ -2,60 +2,136 @@
2
2
 
3
3
  const Aspect = require('./operation').Aspect;
4
4
  const defineAspects = require('./operation').defineAspects;
5
- const OperationBase = require('./operation').OperationBase;
6
- const executeCommand = require('./db_ops').executeCommand;
5
+ const CommandOperationV2 = require('./command_v2');
7
6
  const MongoError = require('../core').MongoError;
8
- const ReadPreference = require('../core').ReadPreference;
7
+ const parseIndexOptions = require('../utils').parseIndexOptions;
8
+ const maxWireVersion = require('../core/utils').maxWireVersion;
9
9
 
10
- class CreateIndexesOperation extends OperationBase {
11
- constructor(collection, indexSpecs, options) {
12
- super(options);
10
+ const VALID_INDEX_OPTIONS = new Set([
11
+ 'background',
12
+ 'unique',
13
+ 'name',
14
+ 'partialFilterExpression',
15
+ 'sparse',
16
+ 'expireAfterSeconds',
17
+ 'storageEngine',
18
+ 'collation',
13
19
 
20
+ // text indexes
21
+ 'weights',
22
+ 'default_language',
23
+ 'language_override',
24
+ 'textIndexVersion',
25
+
26
+ // 2d-sphere indexes
27
+ '2dsphereIndexVersion',
28
+
29
+ // 2d indexes
30
+ 'bits',
31
+ 'min',
32
+ 'max',
33
+
34
+ // geoHaystack Indexes
35
+ 'bucketSize',
36
+
37
+ // wildcard indexes
38
+ 'wildcardProjection'
39
+ ]);
40
+
41
+ class CreateIndexesOperation extends CommandOperationV2 {
42
+ /**
43
+ * @ignore
44
+ */
45
+ constructor(parent, collection, indexes, options) {
46
+ super(parent, options);
14
47
  this.collection = collection;
15
- this.indexSpecs = indexSpecs;
48
+
49
+ // createIndex can be called with a variety of styles:
50
+ // coll.createIndex('a');
51
+ // coll.createIndex({ a: 1 });
52
+ // coll.createIndex([['a', 1]]);
53
+ // createIndexes is always called with an array of index spec objects
54
+ if (!Array.isArray(indexes) || Array.isArray(indexes[0])) {
55
+ this.onlyReturnNameOfCreatedIndex = true;
56
+ // TODO: remove in v4 (breaking change); make createIndex return full response as createIndexes does
57
+
58
+ const indexParameters = parseIndexOptions(indexes);
59
+ // Generate the index name
60
+ const name = typeof options.name === 'string' ? options.name : indexParameters.name;
61
+ // Set up the index
62
+ const indexSpec = { name, key: indexParameters.fieldHash };
63
+ // merge valid index options into the index spec
64
+ for (let optionName in options) {
65
+ if (VALID_INDEX_OPTIONS.has(optionName)) {
66
+ indexSpec[optionName] = options[optionName];
67
+ }
68
+ }
69
+ this.indexes = [indexSpec];
70
+ return;
71
+ }
72
+
73
+ this.indexes = indexes;
16
74
  }
17
75
 
18
- execute(callback) {
19
- const coll = this.collection;
20
- const indexSpecs = this.indexSpecs;
21
- let options = this.options;
76
+ /**
77
+ * @ignore
78
+ */
79
+ execute(server, callback) {
80
+ const options = this.options;
81
+ const indexes = this.indexes;
22
82
 
23
- const capabilities = coll.s.topology.capabilities();
83
+ const serverWireVersion = maxWireVersion(server);
24
84
 
25
85
  // Ensure we generate the correct name if the parameter is not set
26
- for (let i = 0; i < indexSpecs.length; i++) {
27
- if (indexSpecs[i].name == null) {
28
- const keys = [];
86
+ for (let i = 0; i < indexes.length; i++) {
87
+ // Did the user pass in a collation, check if our write server supports it
88
+ if (indexes[i].collation && serverWireVersion < 5) {
89
+ callback(
90
+ new MongoError(
91
+ `Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation`
92
+ )
93
+ );
94
+ return;
95
+ }
29
96
 
30
- // Did the user pass in a collation, check if our write server supports it
31
- if (indexSpecs[i].collation && capabilities && !capabilities.commandsTakeCollation) {
32
- return callback(new MongoError('server/primary/mongos does not support collation'));
33
- }
97
+ if (indexes[i].name == null) {
98
+ const keys = [];
34
99
 
35
- for (let name in indexSpecs[i].key) {
36
- keys.push(`${name}_${indexSpecs[i].key[name]}`);
100
+ for (let name in indexes[i].key) {
101
+ keys.push(`${name}_${indexes[i].key[name]}`);
37
102
  }
38
103
 
39
104
  // Set the name
40
- indexSpecs[i].name = keys.join('_');
105
+ indexes[i].name = keys.join('_');
41
106
  }
42
107
  }
43
108
 
44
- options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
45
-
46
- // Execute the index
47
- executeCommand(
48
- coll.s.db,
49
- {
50
- createIndexes: coll.collectionName,
51
- indexes: indexSpecs
52
- },
53
- options,
54
- callback
55
- );
109
+ const cmd = { createIndexes: this.collection, indexes };
110
+
111
+ if (options.commitQuorum != null) {
112
+ if (serverWireVersion < 9) {
113
+ callback(
114
+ new MongoError('`commitQuorum` option for `createIndexes` not supported on servers < 4.4')
115
+ );
116
+ return;
117
+ }
118
+ cmd.commitQuorum = options.commitQuorum;
119
+ }
120
+
121
+ // collation is set on each index, it should not be defined at the root
122
+ this.options.collation = undefined;
123
+
124
+ super.executeCommand(server, cmd, (err, result) => {
125
+ if (err) {
126
+ callback(err);
127
+ return;
128
+ }
129
+
130
+ callback(null, this.onlyReturnNameOfCreatedIndex ? indexes[0].name : result);
131
+ });
56
132
  }
57
133
  }
58
134
 
59
- defineAspects(CreateIndexesOperation, Aspect.WRITE_OPERATION);
135
+ defineAspects(CreateIndexesOperation, [Aspect.WRITE_OPERATION, Aspect.EXECUTE_WITH_SELECTION]);
60
136
 
61
137
  module.exports = CreateIndexesOperation;
@@ -4,6 +4,8 @@ const OperationBase = require('./operation').OperationBase;
4
4
  const Aspect = require('./operation').Aspect;
5
5
  const defineAspects = require('./operation').defineAspects;
6
6
  const ReadPreference = require('../core').ReadPreference;
7
+ const maxWireVersion = require('../core/utils').maxWireVersion;
8
+ const MongoError = require('../core/error').MongoError;
7
9
 
8
10
  class FindOperation extends OperationBase {
9
11
  constructor(collection, ns, command, options) {
@@ -18,9 +20,13 @@ class FindOperation extends OperationBase {
18
20
  // copied from `CommandOperationV2`, to be subclassed in the future
19
21
  this.server = server;
20
22
 
21
- const cursorState = this.cursorState || {};
23
+ if (typeof this.cmd.allowDiskUse !== 'undefined' && maxWireVersion(server) < 4) {
24
+ callback(new MongoError('The `allowDiskUse` option is not supported on MongoDB < 3.2'));
25
+ return;
26
+ }
22
27
 
23
28
  // TOOD: use `MongoDBNamespace` through and through
29
+ const cursorState = this.cursorState || {};
24
30
  server.query(this.ns.toString(), this.cmd, cursorState, this.options, callback);
25
31
  }
26
32
  }
@@ -8,6 +8,8 @@ const executeCommand = require('./db_ops').executeCommand;
8
8
  const formattedOrderClause = require('../utils').formattedOrderClause;
9
9
  const handleCallback = require('../utils').handleCallback;
10
10
  const ReadPreference = require('../core').ReadPreference;
11
+ const maxWireVersion = require('../core/utils').maxWireVersion;
12
+ const MongoError = require('../error').MongoError;
11
13
 
12
14
  class FindAndModifyOperation extends OperationBase {
13
15
  constructor(collection, query, sort, doc, options) {
@@ -86,6 +88,21 @@ class FindAndModifyOperation extends OperationBase {
86
88
  return callback(err, null);
87
89
  }
88
90
 
91
+ if (options.hint) {
92
+ // TODO: once this method becomes a CommandOperationV2 we will have the server
93
+ // in place to check.
94
+ const unacknowledgedWrite = options.writeConcern && options.writeConcern.w === 0;
95
+ if (unacknowledgedWrite || maxWireVersion(coll.s.topology) < 8) {
96
+ callback(
97
+ new MongoError('The current topology does not support a hint on findAndModify commands')
98
+ );
99
+
100
+ return;
101
+ }
102
+
103
+ queryObject.hint = options.hint;
104
+ }
105
+
89
106
  // Execute the command
90
107
  executeCommand(coll.s.db, queryObject, options, (err, result) => {
91
108
  if (err) return handleCallback(callback, err, null);
@@ -9,6 +9,11 @@ class FindOneAndDeleteOperation extends FindAndModifyOperation {
9
9
  finalOptions.fields = options.projection;
10
10
  finalOptions.remove = true;
11
11
 
12
+ // Basic validation
13
+ if (filter == null || typeof filter !== 'object') {
14
+ throw new TypeError('Filter parameter must be an object');
15
+ }
16
+
12
17
  super(collection, filter, finalOptions.sort, null, finalOptions);
13
18
  }
14
19
  }
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const FindAndModifyOperation = require('./find_and_modify');
4
+ const hasAtomicOperators = require('../utils').hasAtomicOperators;
4
5
 
5
6
  class FindOneAndReplaceOperation extends FindAndModifyOperation {
6
7
  constructor(collection, filter, replacement, options) {
@@ -11,6 +12,18 @@ class FindOneAndReplaceOperation extends FindAndModifyOperation {
11
12
  finalOptions.new = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
12
13
  finalOptions.upsert = options.upsert !== void 0 ? !!options.upsert : false;
13
14
 
15
+ if (filter == null || typeof filter !== 'object') {
16
+ throw new TypeError('Filter parameter must be an object');
17
+ }
18
+
19
+ if (replacement == null || typeof replacement !== 'object') {
20
+ throw new TypeError('Replacement parameter must be an object');
21
+ }
22
+
23
+ if (hasAtomicOperators(replacement)) {
24
+ throw new TypeError('Replacement document must not contain atomic operators');
25
+ }
26
+
14
27
  super(collection, filter, finalOptions.sort, replacement, finalOptions);
15
28
  }
16
29
  }
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const FindAndModifyOperation = require('./find_and_modify');
4
+ const hasAtomicOperators = require('../utils').hasAtomicOperators;
4
5
 
5
6
  class FindOneAndUpdateOperation extends FindAndModifyOperation {
6
7
  constructor(collection, filter, update, options) {
@@ -12,6 +13,18 @@ class FindOneAndUpdateOperation extends FindAndModifyOperation {
12
13
  typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
13
14
  finalOptions.upsert = typeof options.upsert === 'boolean' ? options.upsert : false;
14
15
 
16
+ if (filter == null || typeof filter !== 'object') {
17
+ throw new TypeError('Filter parameter must be an object');
18
+ }
19
+
20
+ if (update == null || typeof update !== 'object') {
21
+ throw new TypeError('Update parameter must be an object');
22
+ }
23
+
24
+ if (!hasAtomicOperators(update)) {
25
+ throw new TypeError('Update document requires atomic operators');
26
+ }
27
+
15
28
  super(collection, filter, finalOptions.sort, update, finalOptions);
16
29
  }
17
30
  }
@@ -60,7 +60,7 @@ class MapReduceOperation extends OperationBase {
60
60
  let options = this.options;
61
61
 
62
62
  const mapCommandHash = {
63
- mapreduce: coll.collectionName,
63
+ mapReduce: coll.collectionName,
64
64
  map: map,
65
65
  reduce: reduce
66
66
  };
@@ -1,28 +1,33 @@
1
1
  'use strict';
2
2
 
3
- const CommandOperation = require('./command');
4
- const handleCallback = require('../utils').handleCallback;
3
+ const Aspect = require('./operation').Aspect;
4
+ const defineAspects = require('./operation').defineAspects;
5
+ const CommandOperationV2 = require('./command_v2');
6
+ const serverType = require('../core/sdam/common').serverType;
7
+ const ServerType = require('../core/sdam/common').ServerType;
8
+ const MongoError = require('../core').MongoError;
5
9
 
6
- class ReIndexOperation extends CommandOperation {
10
+ class ReIndexOperation extends CommandOperationV2 {
7
11
  constructor(collection, options) {
8
- super(collection.s.db, options, collection);
12
+ super(collection, options);
13
+ this.collectionName = collection.collectionName;
9
14
  }
10
15
 
11
- _buildCommand() {
12
- const collection = this.collection;
13
-
14
- const cmd = { reIndex: collection.collectionName };
15
-
16
- return cmd;
17
- }
18
-
19
- execute(callback) {
20
- super.execute((err, result) => {
21
- if (callback == null) return;
22
- if (err) return handleCallback(callback, err, null);
23
- handleCallback(callback, null, result.ok ? true : false);
16
+ execute(server, callback) {
17
+ if (serverType(server) !== ServerType.Standalone) {
18
+ callback(new MongoError(`reIndex can only be executed on standalone servers.`));
19
+ return;
20
+ }
21
+ super.executeCommand(server, { reIndex: this.collectionName }, (err, result) => {
22
+ if (err) {
23
+ callback(err);
24
+ return;
25
+ }
26
+ callback(null, !!result.ok);
24
27
  });
25
28
  }
26
29
  }
27
30
 
31
+ defineAspects(ReIndexOperation, [Aspect.EXECUTE_WITH_SELECTION]);
32
+
28
33
  module.exports = ReIndexOperation;
@@ -2,27 +2,34 @@
2
2
 
3
3
  const OperationBase = require('./operation').OperationBase;
4
4
  const updateDocuments = require('./common_functions').updateDocuments;
5
+ const hasAtomicOperators = require('../utils').hasAtomicOperators;
5
6
 
6
7
  class ReplaceOneOperation extends OperationBase {
7
- constructor(collection, filter, doc, options) {
8
+ constructor(collection, filter, replacement, options) {
8
9
  super(options);
9
10
 
11
+ if (hasAtomicOperators(replacement)) {
12
+ throw new TypeError('Replacement document must not contain atomic operators');
13
+ }
14
+
10
15
  this.collection = collection;
11
16
  this.filter = filter;
12
- this.doc = doc;
17
+ this.replacement = replacement;
13
18
  }
14
19
 
15
20
  execute(callback) {
16
21
  const coll = this.collection;
17
22
  const filter = this.filter;
18
- const doc = this.doc;
23
+ const replacement = this.replacement;
19
24
  const options = this.options;
20
25
 
21
26
  // Set single document update
22
27
  options.multi = false;
23
28
 
24
29
  // Execute update
25
- updateDocuments(coll, filter, doc, options, (err, r) => replaceCallback(err, r, doc, callback));
30
+ updateDocuments(coll, filter, replacement, options, (err, r) =>
31
+ replaceCallback(err, r, replacement, callback)
32
+ );
26
33
  }
27
34
  }
28
35
 
@@ -3,11 +3,16 @@
3
3
  const OperationBase = require('./operation').OperationBase;
4
4
  const updateCallback = require('./common_functions').updateCallback;
5
5
  const updateDocuments = require('./common_functions').updateDocuments;
6
+ const hasAtomicOperators = require('../utils').hasAtomicOperators;
6
7
 
7
8
  class UpdateManyOperation extends OperationBase {
8
9
  constructor(collection, filter, update, options) {
9
10
  super(options);
10
11
 
12
+ if (!hasAtomicOperators(update)) {
13
+ throw new TypeError('Update document requires atomic operators');
14
+ }
15
+
11
16
  this.collection = collection;
12
17
  this.filter = filter;
13
18
  this.update = update;
@@ -2,11 +2,16 @@
2
2
 
3
3
  const OperationBase = require('./operation').OperationBase;
4
4
  const updateDocuments = require('./common_functions').updateDocuments;
5
+ const hasAtomicOperators = require('../utils').hasAtomicOperators;
5
6
 
6
7
  class UpdateOneOperation extends OperationBase {
7
8
  constructor(collection, filter, update, options) {
8
9
  super(options);
9
10
 
11
+ if (!hasAtomicOperators(update)) {
12
+ throw new TypeError('Update document requires atomic operators');
13
+ }
14
+
10
15
  this.collection = collection;
11
16
  this.filter = filter;
12
17
  this.update = update;
@@ -14,8 +14,7 @@ class ValidateCollectionOperation extends CommandOperation {
14
14
  }
15
15
 
16
16
  super(admin.s.db, options, null, command);
17
-
18
- this.collectionName;
17
+ this.collectionName = collectionName;
19
18
  }
20
19
 
21
20
  execute(callback) {
@@ -82,7 +82,7 @@ var legalOptionNames = [
82
82
  * @param {object} [options.socketOptions] Socket options
83
83
  * @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
84
84
  * @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
85
- * @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
85
+ * @param {number} [options.socketOptions.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
86
86
  * @param {number} [options.socketOptions.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
87
87
  * @param {number} [options.socketOptions.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
88
88
  * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
@@ -92,7 +92,7 @@ var legalOptionNames = [
92
92
  * @param {object} [options.socketOptions] Socket options
93
93
  * @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
94
94
  * @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
95
- * @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
95
+ * @param {number} [options.socketOptions.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
96
96
  * @param {number} [options.socketOptions.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
97
97
  * @param {number} [options.socketOptions.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
98
98
  * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
@@ -84,7 +84,7 @@ var legalOptionNames = [
84
84
  * @param {boolean} [options.socketOptions.autoReconnect=true] Reconnect on error.
85
85
  * @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
86
86
  * @param {boolean} [options.socketOptions.keepAlive=true] TCP Connection keep alive enabled
87
- * @param {number} [options.socketOptions.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
87
+ * @param {number} [options.socketOptions.keepAliveInitialDelay=120000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
88
88
  * @param {number} [options.socketOptions.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
89
89
  * @param {number} [options.socketOptions.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
90
90
  * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
package/lib/utils.js CHANGED
@@ -721,6 +721,12 @@ function makeInterruptableAsyncInterval(fn, options) {
721
721
  const timeUntilNextCall = Math.max(interval - timeSinceLastCall, 0);
722
722
  lastWakeTime = currentTime;
723
723
 
724
+ // For the streaming protocol: there is nothing obviously stopping this
725
+ // interval from being woken up again while we are waiting "infinitely"
726
+ // for `fn` to be called again`. Since the function effectively
727
+ // never completes, the `timeUntilNextCall` will continue to grow
728
+ // negatively unbounded, so it will never trigger a reschedule here.
729
+
724
730
  // debounce multiple calls to wake within the `minInterval`
725
731
  if (timeSinceLastWake < minInterval) {
726
732
  return;
@@ -753,6 +759,7 @@ function makeInterruptableAsyncInterval(fn, options) {
753
759
  function executeAndReschedule() {
754
760
  lastWakeTime = 0;
755
761
  lastCallTime = now();
762
+
756
763
  fn(err => {
757
764
  if (err) throw err;
758
765
  reschedule(interval);
@@ -769,6 +776,15 @@ function makeInterruptableAsyncInterval(fn, options) {
769
776
  return { wake, stop };
770
777
  }
771
778
 
779
+ function hasAtomicOperators(doc) {
780
+ if (Array.isArray(doc)) {
781
+ return doc.reduce((err, u) => err || hasAtomicOperators(u), null);
782
+ }
783
+
784
+ const keys = Object.keys(doc);
785
+ return keys.length > 0 && keys[0][0] === '$';
786
+ }
787
+
772
788
  module.exports = {
773
789
  filterOptions,
774
790
  mergeOptions,
@@ -800,5 +816,6 @@ module.exports = {
800
816
  maybePromise,
801
817
  now,
802
818
  calculateDurationInMs,
803
- makeInterruptableAsyncInterval
819
+ makeInterruptableAsyncInterval,
820
+ hasAtomicOperators
804
821
  };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const kWriteConcernKeys = new Set(['w', 'wtimeout', 'j', 'fsync']);
4
+
3
5
  /**
4
6
  * The **WriteConcern** class is a class that represents a MongoDB WriteConcern.
5
7
  * @class
@@ -51,6 +53,14 @@ class WriteConcern {
51
53
  }
52
54
 
53
55
  if (options.writeConcern) {
56
+ if (typeof options.writeConcern === 'string') {
57
+ return new WriteConcern(options.writeConcern);
58
+ }
59
+
60
+ if (!Object.keys(options.writeConcern).some(key => kWriteConcernKeys.has(key))) {
61
+ return;
62
+ }
63
+
54
64
  return new WriteConcern(
55
65
  options.writeConcern.w,
56
66
  options.writeConcern.wtimeout,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mongodb",
3
- "version": "3.5.11",
3
+ "version": "3.6.2",
4
4
  "description": "The official MongoDB driver for Node.js",
5
5
  "main": "index.js",
6
6
  "files": [