Skip to content

Commit 3e20c4f

Browse files
committed
Add ability to run a profile/explain right from the query card tab of
graph/search query.
1 parent fae5e99 commit 3e20c4f

File tree

7 files changed

+151
-2
lines changed

7 files changed

+151
-2
lines changed
Lines changed: 3 additions & 0 deletions
Loading

redisinsight/ui/src/components/query-card/QueryCard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import cx from 'classnames'
44
import { EuiLoadingContent, keys } from '@elastic/eui'
55
import { useParams } from 'react-router-dom'
66

7-
import { WBQueryType } from 'uiSrc/pages/workbench/constants'
7+
import { WBQueryType, ProfileQueryType } from 'uiSrc/pages/workbench/constants'
88
import { RunQueryMode, ResultsMode, ResultsSummary } from 'uiSrc/slices/interfaces/workbench'
99
import {
1010
getWBQueryType,
@@ -43,6 +43,7 @@ export interface Props {
4343
onQueryDelete: () => void
4444
onQueryReRun: () => void
4545
onQueryOpen: () => void
46+
onQueryProfile: (type: ProfileQueryType) => void
4647
}
4748

4849
const getDefaultPlugin = (views: IPluginVisualization[], query: string) =>
@@ -74,6 +75,7 @@ const QueryCard = (props: Props) => {
7475
createdAt,
7576
onQueryOpen,
7677
onQueryDelete,
78+
onQueryProfile,
7779
onQueryReRun,
7880
loading,
7981
emptyCommand,
@@ -183,6 +185,7 @@ const QueryCard = (props: Props) => {
183185
setSelectedValue={changeViewTypeSelected}
184186
onQueryDelete={onQueryDelete}
185187
onQueryReRun={onQueryReRun}
188+
onQueryProfile={onQueryProfile}
186189
/>
187190
{isOpen && (
188191
<>

redisinsight/ui/src/components/query-card/QueryCardHeader/QueryCardHeader.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { numberWithSpaces } from 'uiSrc/utils/numbers'
3232
import { ThemeContext } from 'uiSrc/contexts/themeContext'
3333
import { appPluginsSelector } from 'uiSrc/slices/app/plugins'
3434
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
35-
import { getViewTypeOptions, WBQueryType } from 'uiSrc/pages/workbench/constants'
35+
import { getViewTypeOptions, WBQueryType, getProfileViewTypeOptions, ProfileQueryType, isCommandAllowedForProfile } from 'uiSrc/pages/workbench/constants'
3636
import { IPluginVisualization } from 'uiSrc/slices/interfaces'
3737
import { RunQueryMode, ResultsMode, ResultsSummary } from 'uiSrc/slices/interfaces/workbench'
3838
import { appRedisCommandsSelector } from 'uiSrc/slices/app/redis-commands'
@@ -69,6 +69,7 @@ export interface Props {
6969
setSelectedValue: (type: WBQueryType, value: string) => void
7070
onQueryDelete: () => void
7171
onQueryReRun: () => void
72+
onQueryProfile: (type: ProfileQueryType) => void
7273
}
7374

7475
const getExecutionTimeString = (value: number): string => {
@@ -108,6 +109,7 @@ const QueryCardHeader = (props: Props) => {
108109
setSelectedValue,
109110
onQueryDelete,
110111
onQueryReRun,
112+
onQueryProfile,
111113
} = props
112114

113115
const { visualizations = [] } = useSelector(appPluginsSelector)
@@ -235,6 +237,30 @@ const QueryCardHeader = (props: Props) => {
235237
}
236238
})
237239

240+
const profileOptions: EuiSuperSelectOption<any>[] = (getProfileViewTypeOptions() as any[]).map((item) => {
241+
const { value, id, text } = item
242+
return {
243+
value: id ?? value,
244+
inputDisplay: (
245+
<div className={cx(styles.dropdownOption, styles.dropdownProfileOption)}>
246+
<EuiIcon
247+
className={styles.iconDropdownOption}
248+
type={'visTagCloud'}
249+
data-testid={`view-type-selected-${value}-${id}`}
250+
/>
251+
</div>
252+
),
253+
dropdownDisplay: (
254+
<div className={cx(styles.dropdownOption, styles.dropdownProfileOption)}>
255+
<span>{truncateText(text, 20)}</span>
256+
</div>
257+
),
258+
'data-test-subj': `profile-type-option-${value}-${id}`,
259+
}
260+
})
261+
262+
const canCommandProfile = isCommandAllowedForProfile(query.split(' ')[0].toLowerCase())
263+
238264
const indexForSeparator = findIndex(pluginsOptions, (option) => !option.internal)
239265
if (indexForSeparator > -1) {
240266
modifiedOptions.splice(indexForSeparator + 1, 0, {
@@ -318,6 +344,26 @@ const QueryCardHeader = (props: Props) => {
318344
</EuiToolTip>
319345
)}
320346
</EuiFlexItem>
347+
<EuiFlexItem
348+
grow={false}
349+
className={cx(styles.buttonIcon, styles.viewTypeIcon)}
350+
onClick={onDropDownViewClick}
351+
>
352+
{isOpen && profileOptions.length > 1 && canCommandProfile && !summaryText && (
353+
<div className={styles.dropdownWrapper}>
354+
<div className={styles.dropdown}>
355+
<EuiSuperSelect
356+
options={profileOptions}
357+
itemClassName={cx(styles.changeViewItem, styles.dropdownProfileItem)}
358+
className={cx(styles.changeView, styles.dropdownProfileIcon)}
359+
valueOfSelected={ProfileQueryType.Profile}
360+
onChange={(value: ProfileQueryType) => onQueryProfile(value)}
361+
data-testid="run-profile-type"
362+
/>
363+
</div>
364+
</div>
365+
)}
366+
</EuiFlexItem>
321367
<EuiFlexItem
322368
grow={false}
323369
className={cx(styles.buttonIcon, styles.viewTypeIcon)}

redisinsight/ui/src/components/query-card/QueryCardHeader/styles.module.scss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,27 @@ $marginIcon: 12px;
127127
height: 40px;
128128
}
129129

130+
.dropdownProfileIcon {
131+
padding: inherit !important;
132+
:global {
133+
.euiSuperSelectControl.euiFormControlLayoutIcons {
134+
display: none !important;
135+
}
136+
}
137+
}
138+
139+
.dropdownProfileOption {
140+
display: inherit !important;
141+
}
142+
143+
.dropdownProfileItem {
144+
:global {
145+
.euiContextMenu__icon {
146+
display: none !important;
147+
}
148+
}
149+
}
150+
130151
.dropdown {
131152
width: 168px;
132153
position: absolute;

redisinsight/ui/src/pages/workbench/components/wb-results/WBResults/WBResults.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import cx from 'classnames'
33
import { EuiIcon, EuiText } from '@elastic/eui'
44

55
import { Theme } from 'uiSrc/constants'
6+
import { ProfileQueryType } from 'uiSrc/pages/workbench/constants'
7+
import { generateProfileQueryForCommand } from 'uiSrc/pages/workbench/utils'
68
import { CodeButtonParams } from 'uiSrc/pages/workbench/components/enablement-area/interfaces'
79
import { Nullable } from 'uiSrc/utils'
810
import QueryCard from 'uiSrc/components/query-card'
@@ -84,6 +86,16 @@ const WBResults = (props: Props) => {
8486
activeResultsMode={activeResultsMode}
8587
resultsMode={resultsMode}
8688
onQueryOpen={() => onQueryOpen(id)}
89+
onQueryProfile={(profileType: ProfileQueryType) => {
90+
const profileQuery = generateProfileQueryForCommand(command, profileType)
91+
if (profileQuery) {
92+
return onQueryReRun(
93+
profileQuery,
94+
null,
95+
{ mode, results: resultsMode, clearEditor: false, },
96+
)
97+
}
98+
}}
8799
onQueryReRun={() => onQueryReRun(
88100
command,
89101
null,

redisinsight/ui/src/pages/workbench/constants.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,40 @@ export const VIEW_TYPE_OPTIONS = [
2727
export const getViewTypeOptions = () =>
2828
[...VIEW_TYPE_OPTIONS]
2929

30+
31+
export const SEARCH_COMMANDS = ['ft.search', 'ft.aggregate']
32+
export const GRAPH_COMMANDS = ['graph.query']
33+
34+
const ALLOWED_PROFILE_COMMANDS = [...SEARCH_COMMANDS, ...GRAPH_COMMANDS]
35+
36+
export function isCommandAllowedForProfile(cmd: string) {
37+
return ALLOWED_PROFILE_COMMANDS.includes(cmd)
38+
}
39+
40+
export enum ProfileQueryType {
41+
Profile = 'Profile',
42+
Explain = 'Explain'
43+
}
44+
45+
const PROFILE_VIEW_TYPE_OPTIONS = [
46+
{
47+
id: ProfileQueryType.Profile,
48+
text: 'Profile the command',
49+
name: 'Profile',
50+
value: WBQueryType.Text,
51+
},
52+
{
53+
id: ProfileQueryType.Explain,
54+
text: 'Explain the command',
55+
name: 'Explain',
56+
value: WBQueryType.Text,
57+
},
58+
]
59+
60+
export const getProfileViewTypeOptions = () =>
61+
[...PROFILE_VIEW_TYPE_OPTIONS]
62+
63+
3064
export enum ModuleCommandPrefix {
3165
RediSearch = 'FT.',
3266
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ProfileQueryType, SEARCH_COMMANDS, GRAPH_COMMANDS } from './constants'
2+
3+
function generateGraphProfileQuery(query: string, type: ProfileQueryType) {
4+
return [`graph.${type}`, ...query.split(' ').slice(1)].join(' ')
5+
}
6+
7+
function generateSearchProfileQuery(query: string, type: ProfileQueryType) {
8+
const commandSplit = query.split(' ')
9+
const key = commandSplit[0].toLowerCase()
10+
11+
if (type === ProfileQueryType.Explain) {
12+
return [`ft.${type}`, ...commandSplit.slice(1)].join(' ')
13+
} else {
14+
const index = commandSplit[1]
15+
const queryType = key.split('.')[1] // SEARCH / AGGREGATE
16+
return [`ft.${type}`, index, queryType, 'QUERY', ...commandSplit.slice(2)].join(' ')
17+
}
18+
}
19+
20+
export function generateProfileQueryForCommand(query: string, type: ProfileQueryType) {
21+
const cmd = query.split(' ')[0].toLowerCase()
22+
23+
if (GRAPH_COMMANDS.includes(cmd)) {
24+
return generateGraphProfileQuery(query, type)
25+
} else if (SEARCH_COMMANDS.includes(cmd)) {
26+
return generateSearchProfileQuery(query, type)
27+
}
28+
29+
return null
30+
}

0 commit comments

Comments
 (0)