Skip to content

Commit 7f19744

Browse files
committed
move explain cmd/options to separate file
1 parent 7090c39 commit 7f19744

File tree

12 files changed

+84
-69
lines changed

12 files changed

+84
-69
lines changed

src/cmap/wire_protocol/command.ts

-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import type { Topology } from '../../sdam/topology';
1010
import type { ReadPreferenceLike } from '../../read_preference';
1111
import type { WriteConcernOptions, WriteConcern, W } from '../../write_concern';
1212
import type { WriteCommandOptions } from './write_command';
13-
import { decorateWithExplain } from '../../explain';
1413

1514
/** @internal */
1615
export interface CommandOptions extends BSONSerializeOptions {
@@ -69,11 +68,6 @@ export function command(
6968
return callback(new MongoError(`command ${JSON.stringify(cmd)} does not return a cursor`));
7069
}
7170

72-
// TODO: should not modify the command here
73-
if (cmd.explain !== undefined) {
74-
cmd = decorateWithExplain(cmd, cmd.explain);
75-
}
76-
7771
if (!isClientEncryptionEnabled(server)) {
7872
_command(server, ns, cmd, options, callback);
7973
return;

src/cmap/wire_protocol/query.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Document, pluckBSONSerializeOptions } from '../../bson';
77
import type { Server } from '../../sdam/server';
88
import type { ReadPreferenceLike } from '../../read_preference';
99
import type { FindOptions } from '../../operations/find';
10-
import { Explain } from '../../explain';
1110

1211
/** @internal */
1312
export interface QueryOptions extends CommandOptions {
@@ -63,7 +62,7 @@ export function query(
6362
}
6463

6564
function prepareFindCommand(server: Server, ns: string, cmd: Document) {
66-
const findCmd: Document = {
65+
let findCmd: Document = {
6766
find: collectionNamespace(ns)
6867
};
6968

@@ -147,9 +146,11 @@ function prepareFindCommand(server: Server, ns: string, cmd: Document) {
147146
if (cmd.collation) findCmd.collation = cmd.collation;
148147
if (cmd.readConcern) findCmd.readConcern = cmd.readConcern;
149148

150-
// TODO: Quick fix to make tests pass; will be updated during NODE-2853
151-
if (cmd.explain !== undefined) {
152-
findCmd.explain = Explain.fromOptions({ explain: cmd.explain });
149+
// If we have explain, we need to rewrite the find command
150+
if (cmd.explain) {
151+
findCmd = {
152+
explain: findCmd
153+
};
153154
}
154155

155156
return findCmd;

src/cmap/wire_protocol/write_command.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { command, CommandOptions } from './command';
44
import type { Server } from '../../sdam/server';
55
import type { Document, BSONSerializeOptions } from '../../bson';
66
import type { WriteConcern } from '../../write_concern';
7-
import { Explain, ExplainOptions } from '../../explain';
7+
import { decorateWithExplain, Explain } from '../../explain';
8+
import type { ExplainOptions } from '../../operations/explainable_command';
89

910
/** @public */
1011
export interface CollationOptions {
@@ -44,7 +45,7 @@ export function writeCommand(
4445
options = options || {};
4546
const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
4647
const writeConcern = options.writeConcern;
47-
const writeCommand: Document = {};
48+
let writeCommand: Document = {};
4849
writeCommand[type] = collectionNamespace(ns);
4950
writeCommand[opsField] = ops;
5051
writeCommand.ordered = ordered;
@@ -65,8 +66,13 @@ export function writeCommand(
6566
writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;
6667
}
6768

69+
// If a command is to be explained, we need to reformat the command after
70+
// the other command properties are specified.
6871
if (options.explain !== undefined) {
69-
writeCommand.explain = Explain.fromOptions(options);
72+
const explain = Explain.fromOptions(options);
73+
if (explain) {
74+
writeCommand = decorateWithExplain(writeCommand, explain);
75+
}
7076
}
7177

7278
const commandOptions = Object.assign(

src/explain.ts

+3-43
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { Callback, Document } from '.';
2-
import { MongoError } from './error';
3-
import { CommandOperation, CommandOperationOptions, OperationParent } from './operations/command';
1+
import type { Document } from '.';
2+
import type { ExplainOptions } from './operations/explainable_command';
43
import type { Server } from './sdam/server';
54
import { maxWireVersion } from './utils';
65

@@ -10,45 +9,6 @@ const SUPPORTS_EXPLAIN_WITH_DISTINCT = 3.2;
109
const SUPPORTS_EXPLAIN_WITH_FIND_AND_MODIFY = 3.2;
1110
const SUPPORTS_EXPLAIN_WITH_MAP_REDUCE = 4.4;
1211

13-
/** @internal */
14-
export abstract class ExplainableCommand<
15-
T extends ExplainOptions = ExplainOptions,
16-
TResult = Document
17-
> extends CommandOperation<T, TResult> {
18-
explain?: Explain;
19-
20-
constructor(parent?: OperationParent, options?: T) {
21-
super(parent, options);
22-
23-
if (!Explain.explainOptionsValid(options)) {
24-
throw new MongoError(`explain must be one of ${Object.keys(Verbosity)} or a boolean`);
25-
}
26-
27-
this.explain = Explain.fromOptions(options);
28-
}
29-
30-
get canRetryWrite(): boolean {
31-
return this.explain === undefined;
32-
}
33-
34-
executeCommand(server: Server, cmd: Document, callback: Callback): void {
35-
if (this.explain) {
36-
if (!Explain.explainSupportedOnCmd(server, cmd)) {
37-
callback(new MongoError(`server ${server.name} does not support explain on this command`));
38-
return;
39-
}
40-
41-
cmd.explain = this.explain;
42-
}
43-
super.executeCommand(server, cmd, callback);
44-
}
45-
}
46-
47-
/** @public */
48-
export interface ExplainOptions extends CommandOperationOptions {
49-
explain?: VerbosityLike;
50-
}
51-
5212
/** @public */
5313
export enum Verbosity {
5414
queryPlanner = 'queryPlanner',
@@ -81,7 +41,7 @@ export class Explain {
8141
return new Explain(options.explain);
8242
}
8343

84-
static explainOptionsValid(options?: ExplainOptions): boolean {
44+
static valid(options?: ExplainOptions): boolean {
8545
if (options == null || options.explain === undefined) {
8646
return true;
8747
}

src/operations/command.ts

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { Server } from '../sdam/server';
1010
import { BSONSerializeOptions, Document, resolveBSONOptions } from '../bson';
1111
import type { CollationOptions } from '../cmap/wire_protocol/write_command';
1212
import type { ReadConcernLike } from './../read_concern';
13+
import { decorateWithExplain } from '../explain';
1314

1415
const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5;
1516

@@ -139,6 +140,12 @@ export abstract class CommandOperation<
139140
this.logger.debug(`executing command ${JSON.stringify(cmd)} against ${this.ns}`);
140141
}
141142

143+
// If a command is to be explained, we need to reformat the command after
144+
// the other command properties are specified.
145+
if (cmd.explain) {
146+
cmd = decorateWithExplain(cmd, cmd.explain);
147+
}
148+
142149
server.command(
143150
this.ns.toString(),
144151
cmd,

src/operations/delete.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import type { Server } from '../sdam/server';
77
import type { Collection } from '../collection';
88
import type { WriteCommandOptions } from '../cmap/wire_protocol/write_command';
99
import type { Connection } from '../cmap/connection';
10-
import { ExplainableCommand, ExplainOptions } from '../explain';
10+
import { ExplainableCommand, ExplainOptions } from '../operations/explainable_command';
11+
import type { CommandOperationOptions } from './command';
1112

1213
/** @public */
13-
export interface DeleteOptions extends ExplainOptions {
14+
export interface DeleteOptions extends CommandOperationOptions, ExplainOptions {
1415
single?: boolean;
1516
hint?: Hint;
1617
}

src/operations/distinct.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import { decorateWithCollation, decorateWithReadConcern, Callback } from '../uti
33
import type { Document } from '../bson';
44
import type { Server } from '../sdam/server';
55
import type { Collection } from '../collection';
6-
import { ExplainableCommand, ExplainOptions } from '../explain';
6+
import { ExplainableCommand, ExplainOptions } from '../operations/explainable_command';
7+
import type { CommandOperationOptions } from './command';
78

89
/** @public */
9-
export type DistinctOptions = ExplainOptions;
10+
export interface DistinctOptions extends CommandOperationOptions, ExplainOptions {}
1011

1112
/**
1213
* Return a list of distinct values for the given key across a collection.

src/operations/explainable_command.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { CommandOperation, OperationParent, CommandOperationOptions } from './command';
2+
import { Explain, Verbosity, VerbosityLike } from '../explain';
3+
import { Callback, Document, MongoError, Server } from '..';
4+
5+
/** @public */
6+
export interface ExplainOptions {
7+
explain?: VerbosityLike;
8+
}
9+
10+
/** @internal */
11+
export abstract class ExplainableCommand<
12+
T extends ExplainOptions & CommandOperationOptions,
13+
TResult = Document
14+
> extends CommandOperation<T, TResult> {
15+
explain?: Explain;
16+
17+
constructor(parent?: OperationParent, options?: T) {
18+
super(parent, options);
19+
20+
if (!Explain.valid(options)) {
21+
throw new MongoError(`explain must be one of ${Object.keys(Verbosity)} or a boolean`);
22+
}
23+
24+
this.explain = Explain.fromOptions(options);
25+
}
26+
27+
get canRetryWrite(): boolean {
28+
return this.explain === undefined;
29+
}
30+
31+
executeCommand(server: Server, cmd: Document, callback: Callback): void {
32+
if (this.explain) {
33+
if (!Explain.explainSupportedOnCmd(server, cmd)) {
34+
callback(new MongoError(`server ${server.name} does not support explain on this command`));
35+
return;
36+
}
37+
38+
cmd.explain = this.explain;
39+
}
40+
super.executeCommand(server, cmd, callback);
41+
}
42+
}

src/operations/find_and_modify.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import type { Document } from '../bson';
1313
import type { Server } from '../sdam/server';
1414
import type { Collection } from '../collection';
1515
import { Sort, formatSort } from '../sort';
16-
import { ExplainableCommand, ExplainOptions } from '../explain';
16+
import { ExplainableCommand, ExplainOptions } from '../operations/explainable_command';
17+
import type { CommandOperationOptions } from './command';
1718

1819
/** @public */
19-
export interface FindAndModifyOptions extends ExplainOptions {
20+
export interface FindAndModifyOptions extends CommandOperationOptions, ExplainOptions {
2021
/** When false, returns the updated document rather than the original. The default is true. */
2122
returnOriginal?: boolean;
2223
/** Upsert the document if it does not exist. */

src/operations/map_reduce.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import type { Collection } from '../collection';
1313
import type { Sort } from '../sort';
1414
import { MongoError } from '../error';
1515
import type { ObjectId } from '../bson';
16-
import { ExplainableCommand, ExplainOptions } from '../explain';
16+
import { ExplainableCommand, ExplainOptions } from '../operations/explainable_command';
17+
import type { CommandOperationOptions } from './command';
1718

1819
const exclusionList = [
1920
'explain',
@@ -35,7 +36,7 @@ export type ReduceFunction = (key: string, values: Document[]) => Document;
3536
export type FinalizeFunction = (key: string, reducedValue: Document) => Document;
3637

3738
/** @public */
38-
export interface MapReduceOptions extends ExplainOptions {
39+
export interface MapReduceOptions extends CommandOperationOptions, ExplainOptions {
3940
/** Sets the output target for the map reduce job. */
4041
out?: 'inline' | { inline: 1 } | { replace: string } | { merge: string } | { reduce: string };
4142
/** Query filter object. */

src/operations/update.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import type { Server } from '../sdam/server';
55
import type { Collection } from '../collection';
66
import type { CollationOptions, WriteCommandOptions } from '../cmap/wire_protocol/write_command';
77
import type { ObjectId, Document } from '../bson';
8-
import { ExplainableCommand, ExplainOptions } from '../explain';
8+
import { ExplainableCommand, ExplainOptions } from '../operations/explainable_command';
9+
import type { CommandOperationOptions } from './command';
910

1011
/** @public */
11-
export interface UpdateOptions extends ExplainOptions {
12+
export interface UpdateOptions extends CommandOperationOptions, ExplainOptions {
1213
/** A set of filters specifying to which array elements an update should apply */
1314
arrayFilters?: Document[];
1415
/** If true, allows the write to opt-out of document level validation */

test/functional/explain.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ describe('Explain', function () {
256256
expect(res).to.exist;
257257

258258
// Verify explanation result contains properties of executionStats output
259-
collection.deleteOne({ a: 1 }, { explain: 'executionStats' }, (err, explanation) => {
259+
collection.findOneAndDelete({ a: 1 }, { explain: 'executionStats' }, (err, explanation) => {
260260
expect(err).to.not.exist;
261261
expect(explanation).to.exist;
262262
expect(explanation).property('queryPlanner').to.exist;
@@ -283,7 +283,7 @@ describe('Explain', function () {
283283
expect(res).to.exist;
284284

285285
// Verify explanation result contains properties of allPlansExecution output
286-
collection.deleteOne({ a: 1 }, { explain: 'allPlansExecution' }, (err, explanation) => {
286+
collection.distinct('a', {}, { explain: 'allPlansExecution' }, (err, explanation) => {
287287
expect(err).to.not.exist;
288288
expect(explanation).to.exist;
289289
expect(explanation).property('queryPlanner').to.exist;

0 commit comments

Comments
 (0)