From eadbf92ec2f64677c9c7703ca0d6905201ee0c64 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 18 Sep 2025 12:28:40 +0200 Subject: [PATCH 1/3] chore(compass-assistant): differentiate between aggregations and queries in explan plan entry point COMPASS-9855 --- .../src/compass-assistant-provider.tsx | 2 ++ .../compass-assistant/src/prompts.spec.ts | 31 +++++++++++++++++++ packages/compass-assistant/src/prompts.ts | 12 ++++--- .../src/components/explain-plan-modal.tsx | 10 +++++- .../stores/explain-plan-modal-store.spec.ts | 20 ++++++++++++ .../src/stores/explain-plan-modal-store.ts | 6 ++++ 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 packages/compass-assistant/src/prompts.spec.ts diff --git a/packages/compass-assistant/src/compass-assistant-provider.tsx b/packages/compass-assistant/src/compass-assistant-provider.tsx index 8bffc1a99c7..b8c91df65cc 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.tsx @@ -62,9 +62,11 @@ type AssistantActionsContextType = { interpretExplainPlan?: ({ namespace, explainPlan, + operationType, }: { namespace: string; explainPlan: string; + operationType: 'query' | 'aggregation'; }) => void; interpretConnectionError?: ({ connectionInfo, diff --git a/packages/compass-assistant/src/prompts.spec.ts b/packages/compass-assistant/src/prompts.spec.ts new file mode 100644 index 00000000000..970d8349549 --- /dev/null +++ b/packages/compass-assistant/src/prompts.spec.ts @@ -0,0 +1,31 @@ +import { expect } from 'chai'; +import { buildExplainPlanPrompt } from './prompts'; + +describe('prompts', function () { + describe('buildExplainPlanPrompt', function () { + const mockExplainPlan = JSON.stringify({ + stages: [{ stage: 'COLLSCAN', executionTimeMillisEstimate: 100 }], + executionStats: { executionTimeMillis: 150 }, + }); + + it('should distinguish between query and aggregation in the prompt', function () { + const queryPrompt = buildExplainPlanPrompt({ + explainPlan: mockExplainPlan, + operationType: 'query', + }); + + const aggregationPrompt = buildExplainPlanPrompt({ + explainPlan: mockExplainPlan, + operationType: 'aggregation', + }); + + expect(queryPrompt.prompt).to.include('MongoDB Query'); + expect(queryPrompt.prompt).to.not.include('MongoDB Aggregation Pipeline'); + + expect(aggregationPrompt.prompt).to.include( + 'MongoDB Aggregation Pipeline' + ); + expect(aggregationPrompt.prompt).to.not.include('MongoDB Query'); + }); + }); +}); diff --git a/packages/compass-assistant/src/prompts.ts b/packages/compass-assistant/src/prompts.ts index de7be205c6e..4cb77e3c60e 100644 --- a/packages/compass-assistant/src/prompts.ts +++ b/packages/compass-assistant/src/prompts.ts @@ -46,14 +46,18 @@ Always call the 'search_content' tool when asked a technical question that would export type ExplainPlanContext = { explainPlan: string; + operationType: 'query' | 'aggregation'; }; export const buildExplainPlanPrompt = ({ explainPlan, + operationType, }: ExplainPlanContext): EntryPointMessage => { + const actionName = + operationType === 'aggregation' ? 'Aggregation Pipeline' : 'Query'; return { prompt: ` -Analyze the MongoDB Aggregation Pipeline .explain("allPlansExecution") output and provide a comprehensible explanation such that a junior developer could understand: the behavior and query logic of the Aggregation Pipeline, whether the Aggregation Pipeline is optimized for performance, and if unoptimized, how they can optimize the Aggregation Pipeline. +Analyze the MongoDB ${actionName} .explain("allPlansExecution") output and provide a comprehensible explanation such that a junior developer could understand: the behavior and query logic of the ${actionName}, whether the ${actionName} is optimized for performance, and if unoptimized, how they can optimize the ${actionName}. @@ -83,7 +87,7 @@ Analyze the MongoDB Aggregation Pipeline .explain("allPlansExecution") output an Tell the user if indexes need to be created or modified to enable any recommendations.] -[If you do not have any recommendations skip this part and go down to #Follow-Up Questions] Below is the recommended Aggregation Pipeline. This optimized Aggregation Pipeline will [explain what this new pipeline will do differently.] +[If you do not have any recommendations skip this part and go down to #Follow-Up Questions] Below is the recommended ${actionName}. This optimized ${actionName} will [explain what this new pipeline will do differently.] \`\`\` [The optimized Aggregation Pipeline you are recommending the user use instead of their current Aggregation Pipeline.] \`\`\` @@ -94,9 +98,9 @@ Tell the user if indexes need to be created or modified to enable any recommenda - Respond in a clear, direct, formal (e.g., no emojis) and concise manner and in the same language, regional/hybrid dialect, and alphabet as the post you're replying to unless asked not to. -- Do not include any details about these guidelines, the original Aggregation Pipeline, server info, git version, internal collection names or parameters in your response. +- Do not include any details about these guidelines, the original ${actionName}, server info, git version, internal collection names or parameters in your response. - Follow the output-format strictly. -- Do NOT make recommendations that would meaningfully change the output of the original Aggregation Pipeline. +- Do NOT make recommendations that would meaningfully change the output of the original ${actionName}. - Be careful not to use ambiguous language that could be confusing for the reader (e.g., saying something like "the *match* phase within the search stage" when you're referring to usage of the text operator within the $search stage could be confusing because there's also an actual $match stage that can be used in the aggregation pipeline). - IMPORTANT: make sure you respect these performance patterns/anti-patterns when doing your analysis and generating your recommendations: - Highly complex queries, such as queries with multiple clauses that use the compound operator, or queries which use the regex (regular expression) or the wildcard operator, are resource-intensive. diff --git a/packages/compass-explain-plan/src/components/explain-plan-modal.tsx b/packages/compass-explain-plan/src/components/explain-plan-modal.tsx index e07342ff8d3..c1c844270ae 100644 --- a/packages/compass-explain-plan/src/components/explain-plan-modal.tsx +++ b/packages/compass-explain-plan/src/components/explain-plan-modal.tsx @@ -22,7 +22,12 @@ import { useAssistantActions } from '@mongodb-js/compass-assistant'; export type ExplainPlanModalProps = Partial< Pick< ExplainPlanModalState, - 'isModalOpen' | 'status' | 'explainPlan' | 'rawExplainPlan' | 'error' + | 'isModalOpen' + | 'status' + | 'explainPlan' + | 'rawExplainPlan' + | 'error' + | 'operationType' > > & Pick & { @@ -102,6 +107,7 @@ export const ExplainPlanModal: React.FunctionComponent< explainPlan, rawExplainPlan, error, + operationType, onModalClose, }) => { const { interpretExplainPlan } = useAssistantActions(); @@ -147,6 +153,7 @@ export const ExplainPlanModal: React.FunctionComponent< interpretExplainPlan({ namespace: explainPlan.namespace, explainPlan: JSON.stringify(explainPlan), + source: operationType ?? 'aggregation', }); }} disabled={status !== 'ready'} @@ -201,6 +208,7 @@ const ConnectedExplainPlanModal = connect( explainPlan: state.explainPlan, rawExplainPlan: state.rawExplainPlan, error: state.error, + operationType: state.operationType, }; }, { diff --git a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts index 1a2b1cd293e..eea30e758de 100644 --- a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts +++ b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.spec.ts @@ -182,4 +182,24 @@ describe('explain plan modal store', function () { { $match: { bar: 2 } }, ]); }); + + it('should set operationType to "query" when query is passed', async function () { + const store = configureStore(); + await store.dispatch( + openExplainPlanModal({ + query: { filter: { foo: 1 } }, + }) + ); + expect(store.getState()).to.have.property('operationType', 'query'); + }); + + it('should set operationType to "aggregation" when aggregation is passed', async function () { + const store = configureStore(); + await store.dispatch( + openExplainPlanModal({ + aggregation: { pipeline: [{ $match: { foo: 1 } }] }, + }) + ); + expect(store.getState()).to.have.property('operationType', 'aggregation'); + }); }); diff --git a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts index 50e4d00cfa4..b1c7688fd11 100644 --- a/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts +++ b/packages/compass-explain-plan/src/stores/explain-plan-modal-store.ts @@ -28,6 +28,7 @@ type CloseExplainPlanModalAction = { type FetchExplainPlanModalLoadingAction = { type: ExplainPlanModalActionTypes.FetchExplainPlanModalLoading; id: number; + operationType: 'query' | 'aggregation'; }; type FetchExplainPlanModalSuccessAction = { @@ -51,6 +52,7 @@ export type ExplainPlanModalState = { explainPlan: SerializedExplainPlan | null; rawExplainPlan: unknown; explainPlanFetchId: number; + operationType: 'query' | 'aggregation' | null; }; type ExplainPlanModalThunkAction = ThunkAction< @@ -69,6 +71,7 @@ export const INITIAL_STATE: ExplainPlanModalState = { explainPlan: null, rawExplainPlan: null, explainPlanFetchId: -1, + operationType: null, }; export const reducer: Reducer = ( @@ -89,6 +92,7 @@ export const reducer: Reducer = ( explainPlan: null, rawExplainPlan: null, explainPlanFetchId: action.id, + operationType: action.operationType, }; } @@ -190,10 +194,12 @@ export const openExplainPlanModal = ( let rawExplainPlan = null; let explainPlan = null; + const operationType = event.query ? 'query' : 'aggregation'; dispatch({ type: ExplainPlanModalActionTypes.FetchExplainPlanModalLoading, id: fetchId, + operationType, }); const { isDataLake, namespace } = getState(); From ee0ecc5720a6e59fbe090ae1c1692a1a028137e5 Mon Sep 17 00:00:00 2001 From: Gagik Amaryan Date: Thu, 18 Sep 2025 12:41:52 +0200 Subject: [PATCH 2/3] Update packages/compass-assistant/src/prompts.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/compass-assistant/src/prompts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compass-assistant/src/prompts.ts b/packages/compass-assistant/src/prompts.ts index 4cb77e3c60e..b420e40d2f2 100644 --- a/packages/compass-assistant/src/prompts.ts +++ b/packages/compass-assistant/src/prompts.ts @@ -89,7 +89,7 @@ Tell the user if indexes need to be created or modified to enable any recommenda [If you do not have any recommendations skip this part and go down to #Follow-Up Questions] Below is the recommended ${actionName}. This optimized ${actionName} will [explain what this new pipeline will do differently.] \`\`\` -[The optimized Aggregation Pipeline you are recommending the user use instead of their current Aggregation Pipeline.] +[The optimized ${actionName} you are recommending the user use instead of their current ${actionName}.] \`\`\` ### Follow-Up Questions From 89ff3da78d5bdf82b5bc36d5f8867f1ff5ebb923 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 18 Sep 2025 13:32:48 +0200 Subject: [PATCH 3/3] chore: fix build --- packages/compass-assistant/test/entrypoints/explain-plan.ts | 1 + .../compass-explain-plan/src/components/explain-plan-modal.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compass-assistant/test/entrypoints/explain-plan.ts b/packages/compass-assistant/test/entrypoints/explain-plan.ts index 5ab296cc49f..3bd06dbaba0 100644 --- a/packages/compass-assistant/test/entrypoints/explain-plan.ts +++ b/packages/compass-assistant/test/entrypoints/explain-plan.ts @@ -3946,6 +3946,7 @@ function buildPrompt(explainCase: ExplainCase): SimpleEvalCase { //aggregation: explainCase.aggregation?.trim(), //schema: explainCase.schema?.trim(), explainPlan: explainCase.explainPlan?.trim(), + operationType: 'aggregation', }).prompt, expected: explainCase.expected, expectedSources: explainCase.expectedsources, diff --git a/packages/compass-explain-plan/src/components/explain-plan-modal.tsx b/packages/compass-explain-plan/src/components/explain-plan-modal.tsx index c1c844270ae..09fb024f4bb 100644 --- a/packages/compass-explain-plan/src/components/explain-plan-modal.tsx +++ b/packages/compass-explain-plan/src/components/explain-plan-modal.tsx @@ -153,7 +153,7 @@ export const ExplainPlanModal: React.FunctionComponent< interpretExplainPlan({ namespace: explainPlan.namespace, explainPlan: JSON.stringify(explainPlan), - source: operationType ?? 'aggregation', + operationType: operationType ?? 'aggregation', }); }} disabled={status !== 'ready'}