diff --git a/packages/mongodb-explain-compat/lib/index.js b/packages/mongodb-explain-compat/lib/index.js index 46df600b3ad..f344e2bfa82 100644 --- a/packages/mongodb-explain-compat/lib/index.js +++ b/packages/mongodb-explain-compat/lib/index.js @@ -51,17 +51,15 @@ function mapStages(queryPlan, sbeExecutionStages) { } // Do the actual mapping here. Use the head SBE node, and only aggregate - // 'docsExamined' and 'executionTimeMillisEstimate' based on all sub-nodes - // here. - const initialResult = mapPlanTree(queryPlan, stage => { + // 'docsExamined' based on all child nodes and 'executionTimeMillisEstimate' + // based on all top-level child nodes here. + return mapPlanTree(queryPlan, stage => { const sbeNodes = stage[kSBENodes]; const headSBENode = sbeNodes[0] || {}; return { ...omitChildStages(headSBENode), ...omitChildStages(stage), - executionTimeMillisEstimate: sbeNodes - .map(sbe => sbe.executionTimeMillis || sbe.executionTimeMillisEstimate) - .reduce((a, b) => a + b, 0), + executionTimeMillisEstimate: headSBENode.executionTimeMillis || headSBENode.executionTimeMillisEstimate, docsExamined: sbeNodes .filter(sbe => sbe.stage === 'seek' || sbe.stage === 'scan') .map(sbe => sbe.numReads || 0) @@ -72,16 +70,6 @@ function mapStages(queryPlan, sbeExecutionStages) { .reduce((a, b) => a + b, 0) }; }); - - // Add the execution time stats to all parent stages. - return mapPlanTree(initialResult, stage => { - let time = 0; - mapPlanTree(stage, child => { time += child.executionTimeMillisEstimate; }); - return { - ...omitChildStages(stage), - executionTimeMillisEstimate: time - }; - }); } module.exports = function(explain) { diff --git a/packages/mongodb-explain-compat/test/fixtures/in2.json b/packages/mongodb-explain-compat/test/fixtures/in2.json new file mode 100644 index 00000000000..08734c3f12f --- /dev/null +++ b/packages/mongodb-explain-compat/test/fixtures/in2.json @@ -0,0 +1,225 @@ +{ + "explainVersion": "2", + "queryPlanner": { + "namespace": "test.test", + "indexFilterSet": false, + "parsedQuery": { + "i": { + "$lt": 1000 + } + }, + "maxIndexedOrSolutionsReached": false, + "maxIndexedAndSolutionsReached": false, + "maxScansToExplodeReached": false, + "winningPlan": { + "queryPlan": { + "stage": "PROJECTION_SIMPLE", + "planNodeId": 2, + "transformBy": { + "i": true, + "_id": true + }, + "inputStage": { + "stage": "COLLSCAN", + "planNodeId": 1, + "filter": { + "i": { + "$lt": 1000 + } + }, + "direction": "forward" + } + }, + "slotBasedPlan": { + "slots": "$$RESULT=s9 $$RID=s5 env: { s3 = 1626346276832 (NOW), s1 = TimeZoneDatabase(Africa/Mogadishu...Antarctica/Syowa) (timeZoneDB), s2 = Timestamp(1626346273, 1) (CLUSTER_TIME) }", + "stages": "[2] mkbson s9 s4 [_id, i] keep [] true false \n[1] filter {fillEmpty (s8, false)} \n[1] traverse s8 s7 s6 [s4, s5] {s8 || s7} {s8} \nfrom \n [1] project [s6 = getField (s4, \"i\")] \n [1] scan s4 s5 none none none none [] @\"78c5ce66-d871-4313-b4f0-39ad010a8742\" true false \nin \n [1] project [s7 = ! fillEmpty (isNaN (s6), false) && fillEmpty (s6 < 1000, false)] \n [1] limit 1 \n [1] coscan \n" + } + }, + "rejectedPlans": [] + }, + "executionStats": { + "executionSuccess": true, + "nReturned": 1000, + "executionTimeMillis": 61, + "totalKeysExamined": 0, + "totalDocsExamined": 100000, + "executionStages": { + "stage": "mkbson", + "planNodeId": 2, + "nReturned": 1000, + "executionTimeMillisEstimate": 61, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "objSlot": 9, + "rootSlot": 4, + "fieldBehavior": "keep", + "fields": [ + "_id", + "i" + ], + "projectFields": [], + "projectSlots": [], + "forceNewObject": true, + "returnOldObject": false, + "inputStage": { + "stage": "filter", + "planNodeId": 1, + "nReturned": 1000, + "executionTimeMillisEstimate": 61, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "numTested": 100000, + "filter": "fillEmpty (s8, false) ", + "inputStage": { + "stage": "traverse", + "planNodeId": 1, + "nReturned": 100000, + "executionTimeMillisEstimate": 61, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "innerOpens": 0, + "innerCloses": 0, + "inputSlot": 6, + "outputSlot": 8, + "outputSlotInner": 7, + "correlatedSlots": [ + { + "low": 4, + "high": 0, + "unsigned": false + }, + { + "low": 5, + "high": 0, + "unsigned": false + } + ], + "nestedArraysDepth": 1, + "fold": "s8 || s7 ", + "final": "s8 ", + "outerStage": { + "stage": "project", + "planNodeId": 1, + "nReturned": 100000, + "executionTimeMillisEstimate": 58, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "projections": { + "6": "getField (s4, \"i\") " + }, + "inputStage": { + "stage": "scan", + "planNodeId": 1, + "nReturned": 100000, + "executionTimeMillisEstimate": 57, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "numReads": 100000, + "recordSlot": 4, + "recordIdSlot": 5, + "fields": [], + "outputSlots": [] + } + }, + "innerStage": { + "stage": "project", + "planNodeId": 1, + "nReturned": 100000, + "executionTimeMillisEstimate": 2, + "opens": 100000, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 0, + "projections": { + "7": "! fillEmpty (isNaN (s6), false) && fillEmpty (s6 < 1000, false) " + }, + "inputStage": { + "stage": "limit", + "planNodeId": 1, + "nReturned": 100000, + "executionTimeMillisEstimate": 0, + "opens": 100000, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 0, + "limit": 1, + "inputStage": { + "stage": "coscan", + "planNodeId": 1, + "nReturned": 100000, + "executionTimeMillisEstimate": 0, + "opens": 100000, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 0 + } + } + } + } + } + } + }, + "command": { + "find": "test", + "filter": { + "i": { + "$lt": 1000 + } + }, + "projection": { + "i": 1 + }, + "$db": "test" + }, + "serverInfo": { + "host": "addaleax2", + "port": 27017, + "version": "5.0.0-rc7", + "gitVersion": "8c11a9d7c59a156619ba387be14eb584f979442a" + }, + "serverParameters": { + "internalQueryFacetBufferSizeBytes": 104857600, + "internalQueryFacetMaxOutputDocSizeBytes": 104857600, + "internalLookupStageIntermediateDocumentMaxSizeBytes": 104857600, + "internalDocumentSourceGroupMaxMemoryBytes": 104857600, + "internalQueryMaxBlockingSortMemoryUsageBytes": 104857600, + "internalQueryProhibitBlockingMergeOnMongoS": 0, + "internalQueryMaxAddToSetBytes": 104857600, + "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": 104857600 + }, + "ok": 1, + "$clusterTime": { + "clusterTime": { + "$timestamp": "6985104054506487809" + }, + "signature": { + "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "keyId": { + "low": 0, + "high": 0, + "unsigned": false + } + } + }, + "operationTime": { + "$timestamp": "6985104054506487809" + } +} diff --git a/packages/mongodb-explain-compat/test/fixtures/out1.json b/packages/mongodb-explain-compat/test/fixtures/out1.json index 977b89d4ef1..b2e98b3bada 100644 --- a/packages/mongodb-explain-compat/test/fixtures/out1.json +++ b/packages/mongodb-explain-compat/test/fixtures/out1.json @@ -49,7 +49,7 @@ "stage": "FETCH", "planNodeId": 2, "nReturned": 1432, - "executionTimeMillisEstimate": 9, + "executionTimeMillisEstimate": 3, "advances": 1432, "opens": 1, "closes": 1, @@ -66,7 +66,7 @@ "stage": "IXSCAN", "planNodeId": 1, "nReturned": 1432, - "executionTimeMillisEstimate": 6, + "executionTimeMillisEstimate": 3, "advances": 1432, "opens": 1, "closes": 1, diff --git a/packages/mongodb-explain-compat/test/fixtures/out2.json b/packages/mongodb-explain-compat/test/fixtures/out2.json new file mode 100644 index 00000000000..3cce3e9752d --- /dev/null +++ b/packages/mongodb-explain-compat/test/fixtures/out2.json @@ -0,0 +1,134 @@ +{ + "queryPlanner": { + "namespace": "test.test", + "indexFilterSet": false, + "parsedQuery": { + "i": { + "$lt": 1000 + } + }, + "maxIndexedOrSolutionsReached": false, + "maxIndexedAndSolutionsReached": false, + "maxScansToExplodeReached": false, + "winningPlan": { + "stage": "PROJECTION_SIMPLE", + "planNodeId": 2, + "transformBy": { + "i": true, + "_id": true + }, + "inputStage": { + "stage": "COLLSCAN", + "planNodeId": 1, + "filter": { + "i": { + "$lt": 1000 + } + }, + "direction": "forward" + } + }, + "rejectedPlans": [], + "plannerVersion": 1 + }, + "executionStats": { + "executionSuccess": true, + "nReturned": 1000, + "executionTimeMillis": 61, + "totalKeysExamined": 0, + "totalDocsExamined": 100000, + "executionStages": { + "stage": "PROJECTION_SIMPLE", + "planNodeId": 2, + "nReturned": 1000, + "executionTimeMillisEstimate": 61, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "objSlot": 9, + "rootSlot": 4, + "fieldBehavior": "keep", + "fields": [ + "_id", + "i" + ], + "projectFields": [], + "projectSlots": [], + "forceNewObject": true, + "returnOldObject": false, + "inputStage": { + "stage": "COLLSCAN", + "planNodeId": 1, + "nReturned": 1000, + "executionTimeMillisEstimate": 61, + "opens": 1, + "closes": 1, + "saveState": 100, + "restoreState": 100, + "isEOF": 1, + "numTested": 100000, + "filter": { + "i": { + "$lt": 1000 + } + }, + "direction": "forward", + "docsExamined": 100000, + "keysExamined": 0 + }, + "transformBy": { + "i": true, + "_id": true + }, + "docsExamined": 0, + "keysExamined": 0 + } + }, + "command": { + "find": "test", + "filter": { + "i": { + "$lt": 1000 + } + }, + "projection": { + "i": 1 + }, + "$db": "test" + }, + "serverInfo": { + "host": "addaleax2", + "port": 27017, + "version": "5.0.0-rc7", + "gitVersion": "8c11a9d7c59a156619ba387be14eb584f979442a" + }, + "serverParameters": { + "internalQueryFacetBufferSizeBytes": 104857600, + "internalQueryFacetMaxOutputDocSizeBytes": 104857600, + "internalLookupStageIntermediateDocumentMaxSizeBytes": 104857600, + "internalDocumentSourceGroupMaxMemoryBytes": 104857600, + "internalQueryMaxBlockingSortMemoryUsageBytes": 104857600, + "internalQueryProhibitBlockingMergeOnMongoS": 0, + "internalQueryMaxAddToSetBytes": 104857600, + "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": 104857600 + }, + "ok": 1, + "$clusterTime": { + "clusterTime": { + "$timestamp": "6985104054506487809" + }, + "signature": { + "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "keyId": { + "low": 0, + "high": 0, + "unsigned": false + } + } + }, + "operationTime": { + "$timestamp": "6985104054506487809" + } +} diff --git a/packages/mongodb-explain-compat/test/index.js b/packages/mongodb-explain-compat/test/index.js index 63f796ef034..6e5cbade930 100644 --- a/packages/mongodb-explain-compat/test/index.js +++ b/packages/mongodb-explain-compat/test/index.js @@ -10,9 +10,12 @@ function fixture(name) { } describe('convertExplainCompat', () => { - it('maps stuff from the SBE format to the pre-SBE format', () => { + it('maps stuff from the SBE format to the pre-SBE format (in1)', () => { assert.deepStrictEqual(convertExplainCompat(fixture('in1')), fixture('out1')); }); + it('maps stuff from the SBE format to the pre-SBE format (in2)', () => { + assert.deepStrictEqual(convertExplainCompat(fixture('in2')), fixture('out2')); + }); it('keeps the old SBE format as-is', () => { assert.deepStrictEqual(convertExplainCompat(fixture('out1')), fixture('out1')); });