Skip to content

Commit 7085375

Browse files
committed
Add proper recursive redisgraph PROFILE/EXPLAIN level parser
1 parent fd4e151 commit 7085375

File tree

5 files changed

+119
-38
lines changed

5 files changed

+119
-38
lines changed

redisinsight/ui/src/packages/ri-explain/src/Explain.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
CoreType,
88
EntityInfo,
99
ParseExplain,
10-
ParseGraph,
10+
ParseGraphV2,
1111
ParseProfile,
1212
ParseProfileCluster,
1313
GetAncestors,
@@ -24,7 +24,9 @@ export default function Explain(props: IExplain): JSX.Element {
2424

2525
if (command.startsWith('graph')) {
2626
const info = props.data[0].response
27-
const resp = ParseGraph(info)
27+
28+
const resp = ParseGraphV2(info)
29+
2830
return (
2931
<ExplainDraw
3032
data={resp}
@@ -129,6 +131,8 @@ function ExplainDraw({data, type, profilingTime}: {data: any, type: CoreType, pr
129131
const ancestors = GetAncestors(data, id, {found: false, pairs: []})
130132
if (ancestors.found) {
131133
ancestors.pairs.forEach(p => {
134+
const parentNode = document.querySelector(`#node-${p[0]}`)
135+
parentNode?.classList.add('ProfileContainerHover')
132136
document.querySelector(`[data-cell-id='${p[0]}-${p[1]}']`)?.childNodes.forEach(k => {
133137
(k as any).setAttribute("style", "stroke: #85A2FE; stroke-linecap: butt; stroke-width: 2px")
134138
})
@@ -141,6 +145,8 @@ function ExplainDraw({data, type, profilingTime}: {data: any, type: CoreType, pr
141145
const ancestors = GetAncestors(data, id, {found: false, pairs: []})
142146
if (ancestors.found) {
143147
ancestors.pairs.forEach(p => {
148+
const parentNode = document.querySelector(`#node-${p[0]}`)
149+
parentNode?.classList.remove('ProfileContainerHover')
144150
document.querySelector(`[data-cell-id='${p[0]}-${p[1]}']`)?.childNodes.forEach(k => {
145151
(k as any).setAttribute("style", "")
146152
})
@@ -198,7 +204,7 @@ function ExplainDraw({data, type, profilingTime}: {data: any, type: CoreType, pr
198204
nodeProps = {
199205
shape: 'react-profile-node',
200206
width: 320,
201-
height: 84,
207+
height: (info.snippet ? 114 : 84),
202208
}
203209
}
204210

redisinsight/ui/src/packages/ri-explain/src/Node.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function NodeToolTipContent(props: INodeToolTip) {
6464

6565
export function ProfileNode(props: INodeProps) {
6666
const info: EntityInfo = (props as any).node.getData()
67-
const {data, type, snippet, time, counter, size} = info
67+
const {id, data, type, snippet, time, counter, size} = info
6868

6969
let items = {}
7070

@@ -76,13 +76,19 @@ export function ProfileNode(props: INodeProps) {
7676
items['Size'] = size
7777
}
7878

79-
8079
return (
81-
<div className="ProfileContainer">
80+
<div className="ProfileContainer" id={`node-${id}`}>
8281
<div className="Main">
8382
<div>{data ? data : type}</div>
8483
<div className="Type">{[EntityType.GEO, EntityType.NUMERIC, EntityType.TEXT, EntityType.TAG].includes(type) ? type : ''}</div>
8584
</div>
85+
{
86+
snippet && (
87+
<div className='Footer'>
88+
{snippet}
89+
</div>
90+
)
91+
}
8692
<div className="MetaData">
8793
<EuiToolTip content={<NodeToolTipContent content={"Execution Time"} />}>
8894
<div className="Time">
@@ -93,7 +99,7 @@ export function ProfileNode(props: INodeProps) {
9399
<EuiToolTip content={<NodeToolTipContent items={items} />}>
94100
<div className="Size">
95101
<div>{counter}</div>
96-
<div className="IconContainer"><EuiIcon className="NodeIcon" size="m" type="iInCircle" /></div>
102+
<div className="IconContainer"><EuiIcon className="NodeIcon" size="m" type="reportingApp" /></div>
97103
</div>
98104
</EuiToolTip>
99105
</div>

redisinsight/ui/src/packages/ri-explain/src/main.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ interface Props {
1010

1111
import { appendIconComponentCache } from '@elastic/eui/es/components/icon/icon'
1212
import { icon as EuiIconClock } from '@elastic/eui/es/components/icon/assets/clock'
13-
import { icon as EuiIconIInCircle } from '@elastic/eui/es/components/icon/assets/iInCircle'
13+
import { icon as EuiIconReportingApp } from '@elastic/eui/es/components/icon/assets/app_reporting'
1414

1515
appendIconComponentCache({
1616
clock: EuiIconClock,
17-
iInCircle: EuiIconIInCircle,
17+
reportingApp: EuiIconReportingApp,
1818
})
1919

2020
const renderApp = (element: JSX.Element) => render(

redisinsight/ui/src/packages/ri-explain/src/parser.ts

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ export interface EntityInfo {
221221
counter?: string
222222
size?: string
223223
parentId?: string
224+
level?: number
224225
}
225226

226227
interface IAncestors {
@@ -719,44 +720,80 @@ export enum CoreType {
719720
Explain,
720721
}
721722

722-
export function ParseGraph(output: string[]) : EntityInfo {
723+
export function getOutputLevel(output: string) {
724+
let i = 0
725+
while (output[i] == ' ' && i < output.length) {
726+
i++
727+
}
728+
return (i > 0 ? i / 4 : 0) + 1
729+
}
723730

724-
const entities = [...output].reverse()
731+
function ParseEntity(entity: string, children: EntityInfo[]): EntityInfo {
732+
const info = entity.trim().split('|')
725733

726-
const first = entities.pop() as string
734+
let time: string | undefined = '', size: string | undefined = ''
727735

728-
function ParseEntity(entity: string, children: EntityInfo[]): EntityInfo {
729-
const info = entity.trim().split('|')
736+
const metaData = info.slice(-1)[0].trim()
730737

731-
let time: string | undefined = '', size: string | undefined = ''
738+
// Is GRAPH.PROFILE output
739+
if (metaData.startsWith('Records produced')) {
732740

733-
const metaData = info.slice(-1)[0].trim()
741+
[size, time] = metaData.trim().split(',')
734742

735-
// Is GRAPH.PROFILE output
736-
if (metaData.startsWith('Records produced')) {
743+
size = size.split(': ')[1]
744+
time = time.split(': ')[1].split(' ')[0]
745+
info.pop()
746+
}
737747

738-
[size, time] = metaData.trim().split(',')
748+
const snippet = [...info.slice(1)].join('|').trim()
739749

740-
size = size.split(': ')[1]
741-
time = time.split(': ')[1].split(' ')[0]
742-
info.pop()
743-
}
750+
return {
751+
id: uuidv4(),
752+
type: info[0] as EntityType,
753+
snippet,
754+
children,
755+
time,
756+
size,
757+
counter: size,
758+
level: getOutputLevel(entity),
759+
}
760+
}
744761

745-
const snippet = [...info.slice(1)].join('|').trim()
746762

747-
return {
748-
id: uuidv4(),
749-
type: info[0] as EntityType,
750-
snippet,
751-
children,
752-
time,
753-
size,
754-
counter: size,
763+
export function ParseGraphV2(output: string[]) {
764+
765+
const level = getOutputLevel(output[0]) + 1
766+
767+
let entity = ParseEntity(output[0], [])
768+
let children: EntityInfo[] = []
769+
770+
let pairs: [number, number][] = []
771+
772+
let s: number | null = null, e: number | null = null
773+
let i = 1
774+
775+
while (i < output.length) {
776+
let l = getOutputLevel(output[i])
777+
if (l === level) {
778+
if (s == null) {
779+
s = i
780+
} else if (s != null) {
781+
pairs.push([s, i])
782+
s = i
783+
}
755784
}
785+
i++
786+
}
787+
788+
if (s !== null) {
789+
pairs.push([s, i])
790+
}
791+
792+
for (let k = 0; k < pairs.length; k++) {
793+
let p = pairs[k]
794+
children.push({...ParseGraphV2(output.slice(p[0], p[1])), parentId: entity.id})
756795
}
757796

758-
return entities.reduce(
759-
(a, c) => ParseEntity(c, [a]),
760-
ParseEntity(first, [])
761-
)
797+
entity.children = children
798+
return entity
762799
}

redisinsight/ui/src/packages/ri-explain/src/styles/styles.less

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,15 @@
8080
background-color: var(--tooltip-background) !important;
8181
}
8282

83+
.ProfileContainerHover {
84+
border: 1px solid #85A2FE !important;
85+
86+
width: 50px;
87+
}
88+
8389
.ProfileContainer {
8490
width: 320px;
85-
max-height: 84px;
91+
min-height: 84px;
8692

8793
padding-left: 18px !important;
8894
padding-right: 18px !important;
@@ -121,6 +127,14 @@
121127
}
122128
}
123129

130+
.Footer{
131+
font-size: 12px;
132+
padding-top: 4px;
133+
padding-bottom: 12px;
134+
height: auto;
135+
color: #B5B6C0;
136+
}
137+
124138
.MetaData {
125139
display: flex;
126140
justify-content: space-between;
@@ -137,16 +151,30 @@
137151
display: flex;
138152
align-items: center;
139153
font-size: 12px;
154+
155+
div:first-child {
156+
padding-right: 5px;
157+
}
158+
140159
}
141160

142161
.Size {
143162
display: flex;
144163
align-items: center;
145164
font-size: 13px;
165+
166+
div:first-child {
167+
padding-right: 5px;
168+
}
169+
170+
146171
}
147172

148173
.IconContainer {
149-
align-self: end;
174+
> svg {
175+
width: 12px !important;
176+
height: 13px !important;
177+
}
150178
}
151179
}
152180
}
@@ -168,6 +196,10 @@
168196

169197
background-color: var(--node-background);
170198

199+
&:hover {
200+
border: 1px solid #85A2FE !important;
201+
}
202+
171203
.Main {
172204
// height: 42px;
173205
font-size: 13px;

0 commit comments

Comments
 (0)