feat(mongodb): support find explain in the SQL editor

This commit is contained in:
Loc Nguyen authored and GitHub committed 2026-09-18 01:45:37 +08:00
1 parent 2e85c8b55d
commit a78fc48a7b
15 files changed
+290 -9

No files matched your search

@@ -6,6 +6,8 @@ const mocks = vi.hoisted(() => ({
ensureConnected: vi.fn(),
getConnectionConfig: vi.fn(),
mongoFindDocuments: vi.fn(),
mongoExplainFind: vi.fn(),
cancelQuery: vi.fn(),
mongoParseShellCommand: vi.fn(),
}));
@@ -14,6 +16,8 @@ vi.mock("@/lib/backend/api", async (importOriginal) => {
return {
...actual,
mongoFindDocuments: mocks.mongoFindDocuments,
mongoExplainFind: mocks.mongoExplainFind,
cancelQuery: mocks.cancelQuery,
mongoParseShellCommand: mocks.mongoParseShellCommand,
};
});
@@ -118,4 +122,35 @@ describe("queryStore MongoDB execution summary", () => {
],
});
});
it("forwards the active execution ID to a pending explain and its cancellation", async () => {
let rejectExplain!: (reason: Error) => void;
mocks.mongoExplainFind.mockImplementation(
() =>
new Promise((_resolve, reject) => {
rejectExplain = reject;
}),
);
mocks.cancelQuery.mockImplementation(async () => {
rejectExplain(new Error("Query canceled"));
return true;
});
const { useQueryStore } = await import("@/stores/queryStore");
const store = useQueryStore();
const tabId = store.createTab("mongo-1", "app", "Query");
const execution = store.executeTabSql(tabId, 'db.users.find({}).explain("executionStats")');
await vi.waitFor(() => expect(mocks.mongoExplainFind).toHaveBeenCalledOnce());
const executionId = store.tabs.find((tab) => tab.id === tabId)!.executionId;
try {
expect(executionId).toEqual(expect.any(String));
expect(mocks.mongoExplainFind).toHaveBeenCalledWith("mongo-1", "app", "users", expect.objectContaining({ verbosity: "executionStats" }), executionId);
await expect(store.cancelTabExecution(tabId)).resolves.toBe(true);
expect(mocks.cancelQuery).toHaveBeenCalledWith(executionId);
} finally {
rejectExplain(new Error("Query canceled"));
await execution;
}
expect(store.tabs.find((tab) => tab.id === tabId)).toMatchObject({ isExecuting: false, executionId: undefined });
});
});
+22
View File
@@ -6595,6 +6595,28 @@ export const useQueryStore = defineStore("query", () => {
});
break;
}
case "findExplain": {
queryExecutionLog("info", "mongo-explain:start", { traceId, collection: mongoCommand.collection, database: currentDatabase });
const plan = await api.mongoExplainFind(
executionConnectionId,
currentDatabase,
mongoCommand.collection,
{
skip: mongoCommand.skip,
limit: mongoCommand.limit,
filter: mongoCommand.filter,
projection: mongoCommand.projection,
sort: mongoCommand.sort,
collation: mongoCommand.collation,
verbosity: mongoCommand.verbosity,
},
executionId,
);
allResults.push(markQueryResultRowsRaw(annotateMongoResult(mongoDocumentsToQueryResult([plan], performance.now() - commandStartedAt, 1))));
mongoEditTarget = undefined;
queryExecutionLog("info", "mongo-explain:done", { traceId, collection: mongoCommand.collection, database: currentDatabase, elapsed: elapsed() });
break;
}
case "aggregate": {
if (options?.mongoSafety) {
const safety = evaluateMongoAggregateSafety(mongoCommand, options.mongoSafety);