diff --git a/package.json b/package.json
index 6a807bd..8b50281 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,10 @@
"test:jest:update-snapshots": "yarn run test:jest -u",
"lint": "node ../../scripts/eslint ."
},
+ "dependencies": {
+ "object-hash": "^3.0.0",
+ "plotly.js-dist": "^2.34.0"
+ },
"resolutions": {
"@types/react": "^16.9.8",
"**/@types/jest": "^29.3.1",
diff --git a/public/pages/QueryDetails/Components/QuerySummary.tsx b/public/pages/QueryDetails/Components/QuerySummary.tsx
new file mode 100644
index 0000000..7d6d5fb
--- /dev/null
+++ b/public/pages/QueryDetails/Components/QuerySummary.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { EuiFlexGrid, EuiFlexItem, EuiHorizontalRule, EuiPanel, EuiText } from '@elastic/eui';
+
+const QuerySummary = ({ query }: { query: any }) => {
+ const convertTime = (unixTime: number) => {
+ const date = new Date(unixTime);
+ const loc = date.toDateString().split(' ');
+ return `${loc[1]} ${loc[2]}, ${loc[3]} @ ${date.toLocaleTimeString('en-US')}`;
+ };
+ return (
+
+
+ Summary
+
+
+
+
+
+ Timestamp
+
+ {convertTime(query.timestamp)}
+
+
+
+ Latency
+
+ {`${query.latency} ms`}
+
+
+
+ CPU Usage
+
+ {`${query.cpu} ns`}
+
+
+
+ Memory
+
+ {`${query.memory} B`}
+
+
+
+ Indexes
+
+ {query.indices.toString()}
+
+
+
+ Search type
+
+ {query.search_type.replaceAll('_', ' ')}
+
+
+
+ Coordinator node ID
+
+ {query.node_id}
+
+
+
+ Total shards
+
+ {query.total_shards}
+
+
+
+ );
+};
+
+// eslint-disable-next-line import/no-default-export
+export default QuerySummary;
diff --git a/public/pages/QueryDetails/QueryDetails.tsx b/public/pages/QueryDetails/QueryDetails.tsx
new file mode 100644
index 0000000..ce9e9d7
--- /dev/null
+++ b/public/pages/QueryDetails/QueryDetails.tsx
@@ -0,0 +1,148 @@
+import React, { useEffect } from 'react';
+import Plotly from 'plotly.js-dist';
+import {
+ EuiButton,
+ EuiCodeBlock,
+ EuiFlexGrid,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiHorizontalRule,
+ EuiPanel,
+ EuiSpacer,
+ EuiText,
+ EuiTitle,
+} from '@elastic/eui';
+import hash from 'object-hash';
+import { useParams, useHistory, useLocation } from 'react-router-dom';
+import { CoreStart } from '../../../../../src/core/public';
+import QuerySummary from './Components/QuerySummary';
+import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries';
+
+const QueryDetails = ({ queries, core }: { queries: any; core: CoreStart }) => {
+ const { hashedQuery } = useParams<{ hashedQuery: string }>();
+ const query = queries.find((q: any) => hash(q) === hashedQuery);
+
+ const convertTime = (unixTime: number) => {
+ const date = new Date(unixTime);
+ const loc = date.toDateString().split(' ');
+ return loc[1] + ' ' + loc[2] + ', ' + loc[3] + ' @ ' + date.toLocaleTimeString('en-US');
+ };
+
+ const history = useHistory();
+ const location = useLocation();
+
+ useEffect(() => {
+ core.chrome.setBreadcrumbs([
+ {
+ text: 'Query insights',
+ href: QUERY_INSIGHTS,
+ onClick: (e) => {
+ e.preventDefault();
+ history.push(QUERY_INSIGHTS);
+ },
+ },
+ { text: `Query details: ${convertTime(query.timestamp)}` },
+ ]);
+ }, [core.chrome, history, location, query.timestamp]);
+
+ useEffect(() => {
+ let x: number[] = Object.values(query.phase_latency_map);
+ if (x.length < 3) {
+ x = [0, 0, 0];
+ }
+ const data = [
+ {
+ x: x.reverse(),
+ y: ['Fetch ', 'Query ', 'Expand '],
+ type: 'bar',
+ orientation: 'h',
+ width: 0.5,
+ marker: { color: ['#F990C0', '#1BA9F5', '#7DE2D1'] },
+ base: [x[2] + x[1], x[2], 0],
+ text: x.map((value) => `${value}ms`),
+ textposition: 'outside',
+ cliponaxis: false,
+ },
+ ];
+ const layout = {
+ autosize: true,
+ margin: { l: 80, r: 80, t: 25, b: 15, pad: 0 },
+ autorange: true,
+ height: 120,
+ xaxis: {
+ side: 'top',
+ zeroline: false,
+ ticksuffix: 'ms',
+ autorangeoptions: { clipmin: 0 },
+ tickfont: { color: '#535966' },
+ linecolor: '#D4DAE5',
+ gridcolor: '#D4DAE5',
+ },
+ yaxis: { linecolor: '#D4DAE5' },
+ };
+ const config = { responsive: true };
+ Plotly.newPlot('latency', data, layout, config);
+ }, [query]);
+
+ const queryString = JSON.stringify(JSON.parse(JSON.stringify(query.source)), null, 2);
+ const queryDisplay = `{\n "query": ${queryString ? queryString.replace(/\n/g, '\n ') : ''}\n}`;
+
+ return (
+
+
+ Query details
+
+
+
+
+
+
+
+
+
+
+
+ Query
+
+
+
+
+ Open in search comparision
+
+
+
+
+
+
+ {queryDisplay}
+
+
+
+
+
+
+ Latency
+
+
+
+
+
+
+
+
+ );
+};
+
+// eslint-disable-next-line import/no-default-export
+export default QueryDetails;
diff --git a/public/pages/QueryInsights/QueryInsights.tsx b/public/pages/QueryInsights/QueryInsights.tsx
index 4cdde26..1d476dd 100644
--- a/public/pages/QueryInsights/QueryInsights.tsx
+++ b/public/pages/QueryInsights/QueryInsights.tsx
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
-import { EuiBasicTableColumn, EuiInMemoryTable, EuiSuperDatePicker } from '@elastic/eui';
+import { EuiBasicTableColumn, EuiInMemoryTable, EuiLink, EuiSuperDatePicker } from '@elastic/eui';
import { useHistory, useLocation } from 'react-router-dom';
+import hash from 'object-hash';
import { CoreStart } from '../../../../../src/core/public';
import { QUERY_INSIGHTS } from '../TopNQueries/TopNQueries';
@@ -59,7 +60,13 @@ const QueryInsights = ({
// Make into flyout instead?
name: 'Timestamp',
render: (query: any) => {
- return {convertTime(query.timestamp)};
+ return (
+
+ history.push(`/query-details/${hash(query)}`)}>
+ {convertTime(query.timestamp)}
+
+
+ );
},
sortable: (query) => query.timestamp,
truncateText: true,
diff --git a/public/pages/TopNQueries/TopNQueries.tsx b/public/pages/TopNQueries/TopNQueries.tsx
index 1a09ca3..b6a6af3 100644
--- a/public/pages/TopNQueries/TopNQueries.tsx
+++ b/public/pages/TopNQueries/TopNQueries.tsx
@@ -4,6 +4,7 @@ import { EuiTab, EuiTabs, EuiTitle, EuiSpacer } from '@elastic/eui';
import dateMath from '@elastic/datemath';
import QueryInsights from '../QueryInsights/QueryInsights';
import Configuration from '../Configuration/Configuration';
+import QueryDetails from '../QueryDetails/QueryDetails';
import { CoreStart } from '../../../../../src/core/public';
export const QUERY_INSIGHTS = '/queryInsights';
@@ -222,30 +223,6 @@ const TopNQueries = ({ core }: { core: CoreStart }) => {
[core]
);
- const retrieveConfigInfo = useCallback(
- async (
- get: boolean,
- enabled: boolean = false,
- metric: string = '',
- newTopN: string = '',
- newWindowSize: string = '',
- newTimeUnit: string = ''
- ) => {
- try {
- setMetricSettings(metric, {
- isEnabled: enabled,
- currTopN: newTopN,
- currWindowSize: newWindowSize,
- currTimeUnit: newTimeUnit,
- });
- } catch (error) {
- // eslint-disable-next-line no-console
- console.error('Failed to set settings:', error);
- }
- },
- []
- );
-
const onTimeChange = ({ start, end }: { start: string; end: string }) => {
const usedRange = recentlyUsedRanges.filter(
(range) => !(range.start === start && range.end === end)
@@ -270,6 +247,9 @@ const TopNQueries = ({ core }: { core: CoreStart }) => {
return (
+
+
+
Query insights - Top N queries