command_v2.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 'use strict';
  2. const Aspect = require('./operation').Aspect;
  3. const OperationBase = require('./operation').OperationBase;
  4. const ReadPreference = require('../core').ReadPreference;
  5. const ReadConcern = require('../read_concern');
  6. const WriteConcern = require('../write_concern');
  7. const maxWireVersion = require('../core/utils').maxWireVersion;
  8. const decorateWithExplain = require('../utils').decorateWithExplain;
  9. const commandSupportsReadConcern = require('../core/sessions').commandSupportsReadConcern;
  10. const MongoError = require('../core/error').MongoError;
  11. const extractCommand = require('../command_utils').extractCommand;
  12. const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5;
  13. class CommandOperationV2 extends OperationBase {
  14. constructor(parent, options, operationOptions) {
  15. super(options);
  16. this.ns = parent.s.namespace.withCollection('$cmd');
  17. const propertyProvider = this.hasAspect(Aspect.NO_INHERIT_OPTIONS) ? undefined : parent;
  18. this.readPreference = this.hasAspect(Aspect.WRITE_OPERATION)
  19. ? ReadPreference.primary
  20. : ReadPreference.resolve(propertyProvider, this.options);
  21. this.readConcern = resolveReadConcern(propertyProvider, this.options);
  22. this.writeConcern = resolveWriteConcern(propertyProvider, this.options);
  23. if (operationOptions && typeof operationOptions.fullResponse === 'boolean') {
  24. this.fullResponse = true;
  25. }
  26. // TODO: A lot of our code depends on having the read preference in the options. This should
  27. // go away, but also requires massive test rewrites.
  28. this.options.readPreference = this.readPreference;
  29. // TODO(NODE-2056): make logger another "inheritable" property
  30. if (parent.s.logger) {
  31. this.logger = parent.s.logger;
  32. } else if (parent.s.db && parent.s.db.logger) {
  33. this.logger = parent.s.db.logger;
  34. }
  35. }
  36. executeCommand(server, cmd, callback) {
  37. // TODO: consider making this a non-enumerable property
  38. this.server = server;
  39. const options = this.options;
  40. const serverWireVersion = maxWireVersion(server);
  41. const inTransaction = this.session && this.session.inTransaction();
  42. if (this.readConcern && commandSupportsReadConcern(cmd) && !inTransaction) {
  43. Object.assign(cmd, { readConcern: this.readConcern });
  44. }
  45. if (options.collation && serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) {
  46. callback(
  47. new MongoError(
  48. `Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation`
  49. )
  50. );
  51. return;
  52. }
  53. if (serverWireVersion >= SUPPORTS_WRITE_CONCERN_AND_COLLATION) {
  54. if (this.writeConcern && this.hasAspect(Aspect.WRITE_OPERATION)) {
  55. Object.assign(cmd, { writeConcern: this.writeConcern });
  56. }
  57. if (options.collation && typeof options.collation === 'object') {
  58. Object.assign(cmd, { collation: options.collation });
  59. }
  60. }
  61. if (typeof options.maxTimeMS === 'number') {
  62. cmd.maxTimeMS = options.maxTimeMS;
  63. }
  64. if (typeof options.comment === 'string') {
  65. cmd.comment = options.comment;
  66. }
  67. if (this.hasAspect(Aspect.EXPLAINABLE) && this.explain) {
  68. if (serverWireVersion < 6 && cmd.aggregate) {
  69. // Prior to 3.6, with aggregate, verbosity is ignored, and we must pass in "explain: true"
  70. cmd.explain = true;
  71. } else {
  72. cmd = decorateWithExplain(cmd, this.explain);
  73. }
  74. }
  75. if (this.logger && this.logger.isDebug()) {
  76. const extractedCommand = extractCommand(cmd);
  77. this.logger.debug(
  78. `executing command ${JSON.stringify(
  79. extractedCommand.shouldRedact ? `${extractedCommand.name} details REDACTED` : cmd
  80. )} against ${this.ns}`
  81. );
  82. }
  83. server.command(this.ns.toString(), cmd, this.options, (err, result) => {
  84. if (err) {
  85. callback(err, null);
  86. return;
  87. }
  88. if (this.fullResponse) {
  89. callback(null, result);
  90. return;
  91. }
  92. callback(null, result.result);
  93. });
  94. }
  95. }
  96. function resolveWriteConcern(parent, options) {
  97. return WriteConcern.fromOptions(options) || (parent && parent.writeConcern);
  98. }
  99. function resolveReadConcern(parent, options) {
  100. return ReadConcern.fromOptions(options) || (parent && parent.readConcern);
  101. }
  102. module.exports = CommandOperationV2;