mongodb-explain-compat 2.8.0 → 2.10.1

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/.esm-wrapper.mjs CHANGED
@@ -1,3 +1,7 @@
1
1
  import mod from "./lib/index.js";
2
2
 
3
3
  export default mod;
4
+ export const getStageCursorKey = mod.getStageCursorKey;
5
+ export const isAggregationExplain = mod.isAggregationExplain;
6
+ export const isShardedAggregationExplain = mod.isShardedAggregationExplain;
7
+ export const isShardedFindExplain = mod.isShardedFindExplain;
package/lib/index.js CHANGED
@@ -72,18 +72,148 @@ function mapStages(queryPlan, sbeExecutionStages) {
72
72
  });
73
73
  }
74
74
 
75
- module.exports = function(explain) {
75
+ function isAggregationExplain(explain) {
76
+ return (
77
+ isUnshardedAggregationExplain(explain) ||
78
+ isShardedAggregationExplain(explain)
79
+ );
80
+ }
81
+ // Only unshared aggregation has stages property
82
+ function isUnshardedAggregationExplain(explain) {
83
+ return !!explain.stages;
84
+ }
85
+ // Only shared aggregation has shards property
86
+ function isShardedAggregationExplain(explain) {
87
+ return !!explain.shards;
88
+ }
89
+
90
+ function isShardedFindExplain(explain) {
91
+ const { mongosPlannerVersion } = explain.queryPlanner ?? {};
92
+ return !isNaN(mongosPlannerVersion);
93
+ }
94
+
95
+ function getStageCursorKey(stage) {
96
+ return Object.keys(stage).find((x) => x.match(/^\$.*cursor/i));
97
+ }
98
+
99
+ function isCursorStage(stage) {
100
+ return !!getStageCursorKey(stage);
101
+ }
102
+
103
+ /**
104
+ * Converts SBE explain plan to a format that is compatible with the query planner.
105
+ * https://wiki.corp.mongodb.com/display/QUERY/Explain+Notes
106
+ *
107
+ * @param {Object} explain Raw explain object.
108
+ * @returns Classic explain object
109
+ */
110
+ module.exports = function (explain) {
76
111
  explain = JSON.parse(JSON.stringify(explain));
77
- if ((explain.explainVersion || 0) < 2) {
112
+
113
+ // In a sharded aggregation, we don't have explainVersion :(
114
+ // In a sharded find, its represented by explain.queryPlanner.mongosPlannerVersion
115
+
116
+ // return explain that uses classic engine (except for sharded response)
117
+ if (explain.explainVersion && explain.explainVersion < 2) {
78
118
  return explain;
79
119
  }
80
120
  delete explain.explainVersion;
81
- explain.queryPlanner.plannerVersion = 1;
82
- const winningPlan = explain.queryPlanner.winningPlan.queryPlan;
83
- explain.queryPlanner.winningPlan = winningPlan;
84
-
85
- explain.executionStats.executionStages =
86
- mapStages(winningPlan, explain.executionStats.executionStages);
87
121
 
122
+ if (isAggregationExplain(explain)) {
123
+ if (isShardedAggregationExplain(explain)) {
124
+ explain = mapShardedAggregation(explain);
125
+ } else {
126
+ explain = mapUnshardedAggregation(explain);
127
+ }
128
+ } else {
129
+ if (isShardedFindExplain(explain)) {
130
+ explain = mapShardedFind(explain);
131
+ } else {
132
+ explain = mapUnshardedFind(explain);
133
+ }
134
+ }
88
135
  return JSON.parse(JSON.stringify(explain));
89
136
  };
137
+
138
+ function mapUnshardedAggregation(explain) {
139
+ if (!explain.stages || explain.stages.length === 0) {
140
+ return explain;
141
+ }
142
+ const stages = explain.stages.map((stage) => {
143
+ if (!isCursorStage(stage)) {
144
+ return stage;
145
+ }
146
+ const stageKey = getStageCursorKey(stage);
147
+ stage[stageKey] = _mapPlannerStage(stage[stageKey]);
148
+ return stage;
149
+ });
150
+ explain.stages = stages;
151
+ return explain;
152
+ }
153
+ function mapShardedAggregation(explain) {
154
+ if (!explain.shards) {
155
+ return explain;
156
+ }
157
+ const shards = {};
158
+ for (const shardName in explain.shards) {
159
+ // Shard with stages
160
+ if (explain.shards[shardName].stages) {
161
+ shards[shardName] = mapUnshardedAggregation(explain.shards[shardName]);
162
+ } else {
163
+ shards[shardName] = mapUnshardedFind(explain.shards[shardName]);
164
+ }
165
+ }
166
+ explain.shards = shards;
167
+ return explain;
168
+ }
169
+
170
+ function mapShardedFind(explain) {
171
+ const queryPlanner = explain.queryPlanner;
172
+ const executionStats = explain.executionStats;
173
+ if (!queryPlanner.winningPlan.shards) {
174
+ return explain;
175
+ }
176
+ queryPlanner.winningPlan.shards.forEach((shard, index) => {
177
+ const winningPlan = shard.winningPlan.queryPlan;
178
+ if (winningPlan) {
179
+ const executionStages = mapStages(
180
+ winningPlan,
181
+ executionStats.executionStages.shards[index].executionStages
182
+ );
183
+
184
+ queryPlanner.winningPlan.shards[index].winningPlan = winningPlan;
185
+ executionStats.executionStages.shards[index].executionStages =
186
+ executionStages;
187
+ }
188
+ });
189
+ explain.queryPlanner = queryPlanner;
190
+ explain.executionStats = executionStats;
191
+ return explain;
192
+ }
193
+ function mapUnshardedFind(explain) {
194
+ return _mapPlannerStage(explain);
195
+ }
196
+
197
+ function _mapPlannerStage(planner) {
198
+ if (
199
+ planner.queryPlanner &&
200
+ planner.queryPlanner.winningPlan &&
201
+ planner.queryPlanner.winningPlan.queryPlan
202
+ ) {
203
+ planner.queryPlanner.plannerVersion = 1;
204
+ const winningPlan = planner.queryPlanner.winningPlan.queryPlan;
205
+ planner.queryPlanner.winningPlan = winningPlan;
206
+
207
+ planner.executionStats.executionStages = mapStages(
208
+ winningPlan,
209
+ planner.executionStats.executionStages
210
+ );
211
+ }
212
+ return planner;
213
+ }
214
+
215
+ module.exports.getStageCursorKey = getStageCursorKey;
216
+
217
+ module.exports.isAggregationExplain = isAggregationExplain;
218
+ module.exports.isShardedAggregationExplain = isShardedAggregationExplain;
219
+ module.exports.isShardedFindExplain = isShardedFindExplain;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mongodb-explain-compat",
3
- "version": "2.8.0",
3
+ "version": "2.10.1",
4
4
  "description": "Convert mongodb SBE explain output to 4.4 explain output",
5
5
  "keywords": [
6
6
  "mongodb",
@@ -10,8 +10,8 @@
10
10
  "main": "lib/index.js",
11
11
  "compass:main": "lib/index.js",
12
12
  "exports": {
13
- "require": "./lib/index.js",
14
- "import": "./.esm-wrapper.mjs"
13
+ "import": "./.esm-wrapper.mjs",
14
+ "require": "./lib/index.js"
15
15
  },
16
16
  "compass:exports": {
17
17
  ".": "./lib/index.js"
@@ -27,7 +27,7 @@
27
27
  "lint": "eslint **/*.js",
28
28
  "test": "npm run lint && npm run compile && nyc mocha --colors test/*.js",
29
29
  "compile": "gen-esm-wrapper . ./.esm-wrapper.mjs",
30
- "prepublishOnly": "npm run compile",
30
+ "prepublishOnly": "npm run compile && compass-scripts check-exports-exist",
31
31
  "test-ci": "npm run test",
32
32
  "depcheck": "depcheck",
33
33
  "bootstrap": "npm run compile"
@@ -54,5 +54,5 @@
54
54
  "mocha": "^8.1.3",
55
55
  "nyc": "^15.1.0"
56
56
  },
57
- "gitHead": "2c4149184e87b8340851590663287b914d7ee63e"
57
+ "gitHead": "94eb44eba276874d7a3bfa44e394d87a8d9b88be"
58
58
  }