mongodb 3.2.5 → 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.
Files changed (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -1,16 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ const os = require('os');
4
+ const OperationBase = require('./operation').OperationBase;
5
+ const defineAspects = require('./operation').defineAspects;
6
+ const Aspect = require('./operation').Aspect;
3
7
  const deprecate = require('util').deprecate;
4
- const Logger = require('mongodb-core').Logger;
5
- const MongoError = require('mongodb-core').MongoError;
8
+ const Logger = require('../core').Logger;
9
+ const MongoCredentials = require('../core').MongoCredentials;
10
+ const MongoError = require('../core').MongoError;
6
11
  const Mongos = require('../topologies/mongos');
7
- const parse = require('mongodb-core').parseConnectionString;
8
- const ReadPreference = require('mongodb-core').ReadPreference;
12
+ const NativeTopology = require('../topologies/native_topology');
13
+ const parse = require('../core').parseConnectionString;
14
+ const ReadConcern = require('../read_concern');
15
+ const ReadPreference = require('../core').ReadPreference;
9
16
  const ReplSet = require('../topologies/replset');
10
17
  const Server = require('../topologies/server');
11
- const ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool;
12
- const NativeTopology = require('../topologies/native_topology');
13
- const MongoCredentials = require('mongodb-core').MongoCredentials;
18
+ const ServerSessionPool = require('../core').Sessions.ServerSessionPool;
14
19
 
15
20
  let client;
16
21
  function loadClient() {
@@ -20,6 +25,21 @@ function loadClient() {
20
25
  return client;
21
26
  }
22
27
 
28
+ const legacyParse = deprecate(
29
+ require('../url_parser'),
30
+ 'current URL string parser is deprecated, and will be removed in a future version. ' +
31
+ 'To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.'
32
+ );
33
+
34
+ const AUTH_MECHANISM_INTERNAL_MAP = {
35
+ DEFAULT: 'default',
36
+ 'MONGODB-CR': 'mongocr',
37
+ PLAIN: 'plain',
38
+ 'MONGODB-X509': 'x509',
39
+ 'SCRAM-SHA-1': 'scram-sha-1',
40
+ 'SCRAM-SHA-256': 'scram-sha-256'
41
+ };
42
+
23
43
  const monitoringEvents = [
24
44
  'timeout',
25
45
  'close',
@@ -43,13 +63,17 @@ const monitoringEvents = [
43
63
  'fullsetup',
44
64
  'open'
45
65
  ];
46
- const ignoreOptionNames = ['native_parser'];
47
- const legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
48
- const legacyParse = deprecate(
49
- require('../url_parser'),
50
- 'current URL string parser is deprecated, and will be removed in a future version. ' +
51
- 'To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.'
52
- );
66
+
67
+ const VALID_AUTH_MECHANISMS = new Set([
68
+ 'DEFAULT',
69
+ 'MONGODB-CR',
70
+ 'PLAIN',
71
+ 'MONGODB-X509',
72
+ 'SCRAM-SHA-1',
73
+ 'SCRAM-SHA-256',
74
+ 'GSSAPI'
75
+ ]);
76
+
53
77
  const validOptionNames = [
54
78
  'poolSize',
55
79
  'ssl',
@@ -109,12 +133,70 @@ const validOptionNames = [
109
133
  'minSize',
110
134
  'monitorCommands',
111
135
  'retryWrites',
136
+ 'retryReads',
112
137
  'useNewUrlParser',
113
138
  'useUnifiedTopology',
114
139
  'serverSelectionTimeoutMS',
115
- 'useRecoveryToken'
140
+ 'useRecoveryToken',
141
+ 'autoEncryption'
116
142
  ];
117
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
+
173
+ const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
174
+ obj[name.toLowerCase()] = name;
175
+ return obj;
176
+ }, {});
177
+
178
+ class ConnectOperation extends OperationBase {
179
+ constructor(mongoClient) {
180
+ super();
181
+
182
+ this.mongoClient = mongoClient;
183
+ }
184
+
185
+ execute(callback) {
186
+ const mongoClient = this.mongoClient;
187
+ const err = validOptions(mongoClient.s.options);
188
+
189
+ // Did we have a validation error
190
+ if (err) return callback(err);
191
+ // Fallback to callback based connect
192
+ connect(mongoClient, mongoClient.s.url, mongoClient.s.options, err => {
193
+ if (err) return callback(err);
194
+ callback(null, mongoClient);
195
+ });
196
+ }
197
+ }
198
+ defineAspects(ConnectOperation, [Aspect.SKIP_SESSION]);
199
+
118
200
  function addListeners(mongoClient, topology) {
119
201
  topology.on('authenticated', createListener(mongoClient, 'authenticated'));
120
202
  topology.on('error', createListener(mongoClient, 'error'));
@@ -160,19 +242,6 @@ function collectEvents(mongoClient, topology) {
160
242
  return collectedEvents;
161
243
  }
162
244
 
163
- /**
164
- * Connect to MongoDB using a url as documented at
165
- *
166
- * docs.mongodb.org/manual/reference/connection-string/
167
- *
168
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
169
- *
170
- * @method
171
- * @param {MongoClient} mongoClient The MongoClient instance with which to connect.
172
- * @param {string} url The connection URI string
173
- * @param {object} [options] Optional settings. See MongoClient.prototype.connect for a list of options.
174
- * @param {MongoClient~connectCallback} [callback] The command result callback
175
- */
176
245
  function connect(mongoClient, url, options, callback) {
177
246
  options = Object.assign({}, options);
178
247
 
@@ -205,6 +274,8 @@ function connect(mongoClient, url, options, callback) {
205
274
  // Check if we have connection and socket timeout set
206
275
  if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
207
276
  if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
277
+ if (_finalOptions.retryWrites == null) _finalOptions.retryWrites = true;
278
+ if (_finalOptions.useRecoveryToken == null) _finalOptions.useRecoveryToken = true;
208
279
 
209
280
  if (_finalOptions.db_options && _finalOptions.db_options.auth) {
210
281
  delete _finalOptions.db_options.auth;
@@ -266,27 +337,6 @@ function connect(mongoClient, url, options, callback) {
266
337
  }
267
338
  }
268
339
 
269
- /**
270
- * Connect to MongoDB using a url as documented at
271
- *
272
- * docs.mongodb.org/manual/reference/connection-string/
273
- *
274
- * Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
275
- *
276
- * @method
277
- * @param {MongoClient} mongoClient The MongoClient instance with which to connect.
278
- * @param {MongoClient~connectCallback} [callback] The command result callback
279
- */
280
- function connectOp(mongoClient, err, callback) {
281
- // Did we have a validation error
282
- if (err) return callback(err);
283
- // Fallback to callback based connect
284
- connect(mongoClient, mongoClient.s.url, mongoClient.s.options, err => {
285
- if (err) return callback(err);
286
- callback(null, mongoClient);
287
- });
288
- }
289
-
290
340
  function connectWithUrl(mongoClient, url, options, connectCallback) {
291
341
  // Set the topology
292
342
  assignTopology(mongoClient, url);
@@ -414,7 +464,50 @@ function createTopology(mongoClient, topologyType, options, callback) {
414
464
  }
415
465
 
416
466
  assignTopology(mongoClient, newTopology);
417
- callback(null, newTopology);
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
+ });
418
511
  });
419
512
  }
420
513
 
@@ -453,6 +546,42 @@ function createUnifiedOptions(finalOptions, options) {
453
546
  return finalOptions;
454
547
  }
455
548
 
549
+ function generateCredentials(client, username, password, options) {
550
+ options = Object.assign({}, options);
551
+
552
+ // the default db to authenticate against is 'self'
553
+ // if authententicate is called from a retry context, it may be another one, like admin
554
+ const source = options.authSource || options.authdb || options.dbName;
555
+
556
+ // authMechanism
557
+ const authMechanismRaw = options.authMechanism || 'DEFAULT';
558
+ const authMechanism = authMechanismRaw.toUpperCase();
559
+
560
+ if (!VALID_AUTH_MECHANISMS.has(authMechanism)) {
561
+ throw MongoError.create({
562
+ message: `authentication mechanism ${authMechanismRaw} not supported', options.authMechanism`,
563
+ driver: true
564
+ });
565
+ }
566
+
567
+ if (authMechanism === 'GSSAPI') {
568
+ return new MongoCredentials({
569
+ mechanism: process.platform === 'win32' ? 'sspi' : 'gssapi',
570
+ mechanismProperties: options,
571
+ source,
572
+ username,
573
+ password
574
+ });
575
+ }
576
+
577
+ return new MongoCredentials({
578
+ mechanism: AUTH_MECHANISM_INTERNAL_MAP[authMechanism],
579
+ source,
580
+ username,
581
+ password
582
+ });
583
+ }
584
+
456
585
  function legacyTransformUrlOptions(object) {
457
586
  return mergeOptions(createUnifiedOptions({}, object), object, false);
458
587
  }
@@ -505,11 +634,6 @@ function replayEvents(mongoClient, events) {
505
634
  }
506
635
  }
507
636
 
508
- const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
509
- obj[name.toLowerCase()] = name;
510
- return obj;
511
- }, {});
512
-
513
637
  function transformUrlOptions(_object) {
514
638
  let object = Object.assign({ servers: _object.hosts }, _object.options);
515
639
  for (let name in object) {
@@ -541,13 +665,17 @@ function transformUrlOptions(_object) {
541
665
  }
542
666
 
543
667
  if (object.readconcernlevel) {
544
- object.readConcern = { level: object.readconcernlevel };
668
+ object.readConcern = new ReadConcern(object.readconcernlevel);
545
669
  }
546
670
 
547
671
  if (object.wtimeoutms) {
548
672
  object.wtimeout = object.wtimeoutms;
549
673
  }
550
674
 
675
+ if (_object.srvHost) {
676
+ object.srvHost = _object.srvHost;
677
+ }
678
+
551
679
  return object;
552
680
  }
553
681
 
@@ -585,104 +713,4 @@ function translateOptions(options, translationOptions) {
585
713
  });
586
714
  }
587
715
 
588
- // Validate options object
589
- function validOptions(options) {
590
- const _validOptions = validOptionNames.concat(legacyOptionNames);
591
-
592
- for (const name in options) {
593
- if (ignoreOptionNames.indexOf(name) !== -1) {
594
- continue;
595
- }
596
-
597
- if (_validOptions.indexOf(name) === -1) {
598
- if (options.validateOptions) {
599
- return new MongoError(`option ${name} is not supported`);
600
- } else {
601
- console.warn(`the options [${name}] is not supported`);
602
- }
603
- }
604
-
605
- if (legacyOptionNames.indexOf(name) !== -1) {
606
- console.warn(
607
- `the server/replset/mongos/db options are deprecated, ` +
608
- `all their options are supported at the top level of the options object [${validOptionNames}]`
609
- );
610
- }
611
- }
612
- }
613
-
614
- const VALID_AUTH_MECHANISMS = new Set([
615
- 'DEFAULT',
616
- 'MONGODB-CR',
617
- 'PLAIN',
618
- 'MONGODB-X509',
619
- 'SCRAM-SHA-1',
620
- 'SCRAM-SHA-256',
621
- 'GSSAPI'
622
- ]);
623
-
624
- const AUTH_MECHANISM_INTERNAL_MAP = {
625
- DEFAULT: 'default',
626
- 'MONGODB-CR': 'mongocr',
627
- PLAIN: 'plain',
628
- 'MONGODB-X509': 'x509',
629
- 'SCRAM-SHA-1': 'scram-sha-1',
630
- 'SCRAM-SHA-256': 'scram-sha-256'
631
- };
632
-
633
- function generateCredentials(client, username, password, options) {
634
- options = Object.assign({}, options);
635
-
636
- // the default db to authenticate against is 'self'
637
- // if authenticate is called from a retry context, it may be another one, like admin
638
- const source = options.authSource || options.authdb || options.dbName;
639
-
640
- // authMechanism
641
- const authMechanismRaw = options.authMechanism || 'DEFAULT';
642
- const authMechanism = authMechanismRaw.toUpperCase();
643
-
644
- if (!VALID_AUTH_MECHANISMS.has(authMechanism)) {
645
- throw MongoError.create({
646
- message: `authentication mechanism ${authMechanismRaw} not supported', options.authMechanism`,
647
- driver: true
648
- });
649
- }
650
-
651
- if (authMechanism === 'GSSAPI') {
652
- return new MongoCredentials({
653
- mechanism: process.platform === 'win32' ? 'sspi' : 'gssapi',
654
- mechanismProperties: options,
655
- source,
656
- username,
657
- password
658
- });
659
- }
660
-
661
- return new MongoCredentials({
662
- mechanism: AUTH_MECHANISM_INTERNAL_MAP[authMechanism],
663
- source,
664
- username,
665
- password
666
- });
667
- }
668
-
669
- function closeOperation(client, force, callback) {
670
- const completeClose = err => {
671
- client.emit('close', client);
672
- for (const name in client.s.dbCache) {
673
- client.s.dbCache[name].emit('close', client);
674
- }
675
-
676
- client.removeAllListeners('close');
677
- callback(err, null);
678
- };
679
-
680
- if (client.topology == null) {
681
- completeClose();
682
- return;
683
- }
684
-
685
- client.topology.close(force, completeClose);
686
- }
687
-
688
- module.exports = { connectOp, validOptions, closeOperation };
716
+ module.exports = ConnectOperation;
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ const Aspect = require('./operation').Aspect;
4
+ const buildCountCommand = require('./common_functions').buildCountCommand;
5
+ const defineAspects = require('./operation').defineAspects;
6
+ const OperationBase = require('./operation').OperationBase;
7
+
8
+ class CountOperation extends OperationBase {
9
+ constructor(cursor, applySkipLimit, options) {
10
+ super(options);
11
+
12
+ this.cursor = cursor;
13
+ this.applySkipLimit = applySkipLimit;
14
+ }
15
+
16
+ execute(callback) {
17
+ const cursor = this.cursor;
18
+ const applySkipLimit = this.applySkipLimit;
19
+ const options = this.options;
20
+
21
+ if (applySkipLimit) {
22
+ if (typeof cursor.cursorSkip() === 'number') options.skip = cursor.cursorSkip();
23
+ if (typeof cursor.cursorLimit() === 'number') options.limit = cursor.cursorLimit();
24
+ }
25
+
26
+ // Ensure we have the right read preference inheritance
27
+ if (options.readPreference) {
28
+ cursor.setReadPreference(options.readPreference);
29
+ }
30
+
31
+ if (
32
+ typeof options.maxTimeMS !== 'number' &&
33
+ cursor.s.cmd &&
34
+ typeof cursor.s.cmd.maxTimeMS === 'number'
35
+ ) {
36
+ options.maxTimeMS = cursor.s.cmd.maxTimeMS;
37
+ }
38
+
39
+ let finalOptions = {};
40
+ finalOptions.skip = options.skip;
41
+ finalOptions.limit = options.limit;
42
+ finalOptions.hint = options.hint;
43
+ finalOptions.maxTimeMS = options.maxTimeMS;
44
+
45
+ // Command
46
+ finalOptions.collectionName = cursor.s.namespace.collection;
47
+
48
+ let command;
49
+ try {
50
+ command = buildCountCommand(cursor, cursor.s.cmd.query, finalOptions);
51
+ } catch (err) {
52
+ return callback(err);
53
+ }
54
+
55
+ // Set cursor server to the same as the topology
56
+ cursor.server = cursor.topology.s.coreTopology;
57
+
58
+ // Execute the command
59
+ cursor.s.topology.command(
60
+ cursor.s.namespace.withCollection('$cmd'),
61
+ command,
62
+ cursor.s.options,
63
+ (err, result) => {
64
+ callback(err, result ? result.result.n : null);
65
+ }
66
+ );
67
+ }
68
+ }
69
+
70
+ defineAspects(CountOperation, Aspect.SKIP_SESSION);
71
+
72
+ module.exports = CountOperation;
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const OperationBase = require('./operation').OperationBase;
4
+ const handleCallback = require('../utils').handleCallback;
5
+
6
+ class CountDocumentsOperation extends OperationBase {
7
+ constructor(collection, query, options) {
8
+ super(options);
9
+
10
+ this.collection = collection;
11
+ this.query = query;
12
+ }
13
+
14
+ execute(callback) {
15
+ const coll = this.collection;
16
+ const query = this.query;
17
+ let options = this.options;
18
+
19
+ const skip = options.skip;
20
+ const limit = options.limit;
21
+ options = Object.assign({}, options);
22
+
23
+ const pipeline = [{ $match: query }];
24
+
25
+ // Add skip and limit if defined
26
+ if (typeof skip === 'number') {
27
+ pipeline.push({ $skip: skip });
28
+ }
29
+
30
+ if (typeof limit === 'number') {
31
+ pipeline.push({ $limit: limit });
32
+ }
33
+
34
+ pipeline.push({ $group: { _id: 1, n: { $sum: 1 } } });
35
+
36
+ delete options.limit;
37
+ delete options.skip;
38
+
39
+ coll.aggregate(pipeline, options).toArray((err, docs) => {
40
+ if (err) return handleCallback(callback, err);
41
+ handleCallback(callback, null, docs.length ? docs[0].n : 0);
42
+ });
43
+ }
44
+ }
45
+
46
+ module.exports = CountDocumentsOperation;
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ const Aspect = require('./operation').Aspect;
4
+ const defineAspects = require('./operation').defineAspects;
5
+ const CommandOperation = require('./command');
6
+ const applyWriteConcern = require('../utils').applyWriteConcern;
7
+ const handleCallback = require('../utils').handleCallback;
8
+ const loadCollection = require('../dynamic_loaders').loadCollection;
9
+ const MongoError = require('../core').MongoError;
10
+ const ReadPreference = require('../core').ReadPreference;
11
+
12
+ // Filter out any write concern options
13
+ const illegalCommandFields = [
14
+ 'w',
15
+ 'wtimeout',
16
+ 'j',
17
+ 'fsync',
18
+ 'autoIndexId',
19
+ 'strict',
20
+ 'serializeFunctions',
21
+ 'pkFactory',
22
+ 'raw',
23
+ 'readPreference',
24
+ 'session',
25
+ 'readConcern',
26
+ 'writeConcern'
27
+ ];
28
+
29
+ class CreateCollectionOperation extends CommandOperation {
30
+ constructor(db, name, options) {
31
+ super(db, options);
32
+
33
+ this.name = name;
34
+ }
35
+
36
+ _buildCommand() {
37
+ const name = this.name;
38
+ const options = this.options;
39
+
40
+ // Create collection command
41
+ const cmd = { create: name };
42
+ // Add all optional parameters
43
+ for (let n in options) {
44
+ if (
45
+ options[n] != null &&
46
+ typeof options[n] !== 'function' &&
47
+ illegalCommandFields.indexOf(n) === -1
48
+ ) {
49
+ cmd[n] = options[n];
50
+ }
51
+ }
52
+
53
+ return cmd;
54
+ }
55
+
56
+ execute(callback) {
57
+ const db = this.db;
58
+ const name = this.name;
59
+ const options = this.options;
60
+
61
+ let Collection = loadCollection();
62
+
63
+ // Did the user destroy the topology
64
+ if (db.serverConfig && db.serverConfig.isDestroyed()) {
65
+ return callback(new MongoError('topology was destroyed'));
66
+ }
67
+
68
+ let listCollectionOptions = Object.assign({}, options, { nameOnly: true });
69
+ listCollectionOptions = applyWriteConcern(listCollectionOptions, { db }, listCollectionOptions);
70
+
71
+ // Check if we have the name
72
+ db
73
+ .listCollections({ name }, listCollectionOptions)
74
+ .setReadPreference(ReadPreference.PRIMARY)
75
+ .toArray((err, collections) => {
76
+ if (err != null) return handleCallback(callback, err, null);
77
+ if (collections.length > 0 && listCollectionOptions.strict) {
78
+ return handleCallback(
79
+ callback,
80
+ MongoError.create({
81
+ message: `Collection ${name} already exists. Currently in strict mode.`,
82
+ driver: true
83
+ }),
84
+ null
85
+ );
86
+ } else if (collections.length > 0) {
87
+ try {
88
+ return handleCallback(
89
+ callback,
90
+ null,
91
+ new Collection(db, db.s.topology, db.databaseName, name, db.s.pkFactory, options)
92
+ );
93
+ } catch (err) {
94
+ return handleCallback(callback, err);
95
+ }
96
+ }
97
+
98
+ // Execute command
99
+ super.execute(err => {
100
+ if (err) return handleCallback(callback, err);
101
+
102
+ try {
103
+ return handleCallback(
104
+ callback,
105
+ null,
106
+ new Collection(db, db.s.topology, db.databaseName, name, db.s.pkFactory, options)
107
+ );
108
+ } catch (err) {
109
+ return handleCallback(callback, err);
110
+ }
111
+ });
112
+ });
113
+ }
114
+ }
115
+
116
+ defineAspects(CreateCollectionOperation, Aspect.WRITE_OPERATION);
117
+
118
+ module.exports = CreateCollectionOperation;