Skip to content

Commit 58efc46

Browse files
authored
feat: new stat chart,editing table name,quick query (#461)
* feat: new stat chart,editing table name,quick query * fix: reset chart when table change * fix: active color * fix: sql code * style: checkbox * fix: page query * fix: editing table name * feat: fit popup
1 parent 3eb011d commit 58efc46

File tree

12 files changed

+343
-91
lines changed

12 files changed

+343
-91
lines changed

src/store/modules/logquery/index.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ const useLogQueryStore = defineStore('logQuery', () => {
4343

4444
const rangeTime = ref<Array<string>>([])
4545
const time = ref(10)
46-
const inputTableName = ref('')
46+
const inputTableName = ref('') // table after query
47+
const editingTableName = ref('') // table in editing
4748

4849
const columns = computed(() => {
4950
if (!inputTableName.value) {
@@ -82,8 +83,10 @@ const useLogQueryStore = defineStore('logQuery', () => {
8283
type Multiple = 1000 | 1000000 | 1000000000
8384
const multipleRe = /timestamp\((\d)\)/
8485
const dataLoadFlag = ref(0)
85-
const tsColumn = computed<TSColumn>(() => {
86-
const fields = tableMap.value[inputTableName.value] || []
86+
const tsColumn = shallowRef<TSColumn>()
87+
88+
const getTsColumn = (tableName: string) => {
89+
const fields = tableMap.value[tableName] || []
8790
const field = fields.filter((column) => column.data_type.toLowerCase().indexOf('timestamp') > -1)[0]
8891
if (!field) {
8992
return null
@@ -94,7 +97,7 @@ const useLogQueryStore = defineStore('logQuery', () => {
9497
multiple: (1000 ** (Number(timescale[1]) / 3)) as Multiple,
9598
...field,
9699
}
97-
})
100+
}
98101

99102
const query = () => {
100103
queryLoading.value = true
@@ -204,12 +207,12 @@ const useLogQueryStore = defineStore('logQuery', () => {
204207
let val = escapeSqlString(condition.value)
205208
if (condition.op === 'not contains') {
206209
val = `-"${val}"`
207-
} else if (condition.op === 'match sequence') {
210+
} else if (condition.op === 'contains') {
208211
val = `"${val}"`
209212
}
210-
return `MATCHES(${condition.field.name},'"${val}"')`
213+
return `MATCHES(${condition.field.name},'${val}')`
211214
}
212-
return `${condition.field.name} ${condition.op} '"${escapeSqlString(condition.value)}"'`
215+
return `${condition.field.name} ${condition.op} '${escapeSqlString(condition.value)}'`
213216
}
214217

215218
function buildCondition() {
@@ -242,15 +245,16 @@ const useLogQueryStore = defineStore('logQuery', () => {
242245
}
243246

244247
watch(
245-
[queryForm, unifiedRange, limit],
248+
[queryForm, unifiedRange, limit, editingTableName],
246249
() => {
247-
if (!inputTableName.value) {
250+
if (!editingTableName.value) {
248251
return
249252
}
250253
if (editorType.value !== 'builder') {
251254
return
252255
}
253-
let str = `SELECT * FROM ${inputTableName.value}`
256+
tsColumn.value = getTsColumn(editingTableName.value)
257+
let str = `SELECT * FROM ${editingTableName.value}`
254258
const where = buildCondition()
255259
if (where.length) {
256260
str += ` WHERE ${where.join('')}`
@@ -273,13 +277,34 @@ const useLogQueryStore = defineStore('logQuery', () => {
273277
}
274278
})
275279

280+
type TypeKey = keyof typeof typeMap
281+
const opMap = {
282+
String: ['=', 'contains', 'not contains', '!=', 'like'],
283+
Number: ['=', '!=', '>', '>=', '<', '<='],
284+
Time: ['>', '>=', '<', '<='],
285+
}
286+
type OpKey = keyof typeof opMap
287+
288+
function getOpByField(field: string): string[] {
289+
const fields = tableMap.value[editingTableName.value]
290+
const index = fields.findIndex((f) => f.name === field)
291+
if (index === -1) {
292+
return []
293+
}
294+
const type = fields[index].data_type as TypeKey
295+
const opKey = typeMap[type] as OpKey
296+
return opMap[opKey] || []
297+
}
298+
276299
function reset() {
300+
editingTableName.value = ''
277301
inputTableName.value = ''
278302
sql.value = ''
279303
editingSql.value = ''
280304
queryForm.conditions = []
281305
rows.value = []
282306
}
307+
283308
return {
284309
sql,
285310
query,
@@ -290,6 +315,7 @@ const useLogQueryStore = defineStore('logQuery', () => {
290315
selectedRowKey,
291316
rangeTime,
292317
inputTableName,
318+
editingTableName,
293319
tsColumn,
294320
time,
295321
unifiedRange,
@@ -311,7 +337,10 @@ const useLogQueryStore = defineStore('logQuery', () => {
311337
dataLoadFlag,
312338
showKeys,
313339
queryColumns,
340+
getOpByField,
314341
reset,
342+
getTsColumn,
315343
}
316344
})
345+
317346
export default useLogQueryStore
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<template lang="pug">
2+
a-card
3+
template(#extra)
4+
a-dropdown-button(size="small" type="text" @select="select")
5+
| {{ menuStr }}
6+
template(#icon)
7+
icon-down
8+
template(#content)
9+
a-doption(value="count") Write Count
10+
a-dsubmenu(trigger="hover" position="lt")
11+
template(#default)
12+
| Frequency Distribution
13+
template(#content)
14+
a-doption(v-for="field in filterFields" :value="`frequency_${field}`") {{ field }}
15+
16+
CountChart(v-if="currChart == 'count'")
17+
FunnelChart(v-if="currChart == 'frequency'" :key="frequencyField" :column="frequencyField")
18+
</template>
19+
20+
<script setup name="ChartContainer" lang="ts">
21+
import useLogQueryStore from '@/store/modules/logquery'
22+
import CountChart from './CountChart.vue'
23+
import FunnelChart from './FunnelChart.vue'
24+
25+
const currChart = ref('count')
26+
const { columns, inputTableName } = storeToRefs(useLogQueryStore())
27+
const frequencyField = ref('')
28+
const filterFields = computed(() =>
29+
columns.value.filter((column) => column.data_type === 'string').map((column) => column.name)
30+
)
31+
function select(action) {
32+
currChart.value = action.split('_')[0]
33+
if (currChart.value === 'frequency') {
34+
frequencyField.value = action.substring('frequency'.length + 1)
35+
}
36+
}
37+
const menuStr = computed(() => {
38+
if (currChart.value === 'count') {
39+
return 'Write Count '
40+
}
41+
return 'Frequency Distribution'
42+
})
43+
44+
watch(inputTableName, () => {
45+
currChart.value = 'count'
46+
})
47+
</script>
48+
49+
<style scoped lang="less">
50+
.arco-card :deep(.arco-card-header) {
51+
height: 18px;
52+
padding: 0 10px;
53+
}
54+
</style>

src/views/dashboard/logs/query/CountChart.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ VCharts(
114114
})
115115
116116
const countSql = computed(() => {
117-
if (!tsColumn.value) {
117+
if (!tsColumn.value || !inputTableName.value) {
118118
return ''
119119
}
120120

src/views/dashboard/logs/query/ExportLog.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
a-button(size="small" type="text" @click="exportSql")
33
| Export as csv
44
</template>
5-
65
<script setup name="ExportLog" lang="ts">
76
import fileDownload from 'js-file-download'
87
import editorAPI from '@/api/editor'
@@ -34,5 +33,4 @@ a-button(size="small" type="text" @click="exportSql")
3433
})
3534
}
3635
</script>
37-
3836
<style scoped lang="less"></style>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<template lang="pug">
2+
VCharts(
3+
ref="chart"
4+
style="width: 100%; height: 120px"
5+
:option="chartOptions"
6+
:autoresize="true"
7+
)
8+
</template>
9+
10+
<script setup name="LogCountChart" lang="ts">
11+
import VCharts from 'vue-echarts'
12+
import { useI18n } from 'vue-i18n'
13+
import * as echarts from 'echarts'
14+
import { watchOnce } from '@vueuse/core'
15+
import editorAPI from '@/api/editor'
16+
import useLogQueryStore from '@/store/modules/logquery'
17+
import { calculateInterval, generateTimeRange, toMs, TimeTypes, getWhereClause, addTsCondition, toObj } from './until'
18+
import type { TimeType } from './until'
19+
20+
const props = defineProps(['column'])
21+
const data = shallowRef<Array<any>>([])
22+
const { t } = useI18n()
23+
const chart = ref()
24+
const chartOptions = computed(() => ({
25+
grid: {
26+
left: '55px',
27+
bottom: '20px',
28+
right: '55px',
29+
top: '10px',
30+
},
31+
tooltip: {
32+
trigger: 'axis',
33+
axisPointer: { type: 'shadow' },
34+
},
35+
xAxis: {
36+
type: 'category',
37+
position: 'bottom',
38+
show: !!data.value.length,
39+
},
40+
yAxis: {
41+
type: 'value',
42+
},
43+
series: [
44+
{
45+
type: 'bar',
46+
data: data.value,
47+
itemStyle: {
48+
color: '#bdc4cd',
49+
},
50+
barMaxWidth: 50,
51+
},
52+
],
53+
graphic: {
54+
elements: [
55+
{
56+
type: 'text',
57+
left: 'center',
58+
top: 'middle',
59+
style: {
60+
text: data.value.length ? '' : 'No Chart data',
61+
fill: '#999',
62+
fontSize: 18,
63+
},
64+
},
65+
],
66+
},
67+
}))
68+
69+
const { inputTableName, unifiedRange, tsColumn, queryNum, sql, editorType, rows, tableIndex } = storeToRefs(
70+
useLogQueryStore()
71+
)
72+
const { getRelativeRange, buildCondition, query } = useLogQueryStore()
73+
74+
const chartSql = computed(() => {
75+
if (!tsColumn.value) {
76+
return ''
77+
}
78+
79+
let condition = ''
80+
if (editorType.value === 'text') {
81+
condition += getWhereClause(sql.value)
82+
}
83+
if (editorType.value === 'builder') {
84+
condition += buildCondition().join('')
85+
}
86+
if (condition !== '') {
87+
condition = `Where ${condition}`
88+
}
89+
90+
return `SELECT ${props.column} ,count(*) AS c FROM ${inputTableName.value} ${condition} GROUP BY ${props.column} ORDER BY c DESC`
91+
})
92+
93+
function chartQuery() {
94+
if (!chartSql.value) {
95+
return
96+
}
97+
editorAPI.runSQL(chartSql.value).then((result) => {
98+
const {
99+
rows: countRows,
100+
schema: { column_schemas: columnSchemas },
101+
} = result.output[0].records
102+
data.value = countRows
103+
})
104+
}
105+
106+
if (chartSql.value) {
107+
chartQuery()
108+
}
109+
110+
watch(queryNum, () => {
111+
chartQuery()
112+
})
113+
</script>
114+
115+
<style scoped lang="less"></style>

src/views/dashboard/logs/query/InputEditor.vue

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,6 @@
6161
})
6262
})
6363
64-
// parse table name
65-
watch(
66-
editingSql,
67-
() => {
68-
inputTableName.value = parseTable(editingSql.value)
69-
limit.value = parseLimit(editingSql.value)
70-
},
71-
{
72-
immediate: true,
73-
}
74-
)
75-
7664
const customSelectionTheme = EditorView.theme({
7765
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
7866
{

src/views/dashboard/logs/query/Pagination.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ a-space(v-if="pages.length")
109109
function loadPage(start: number, end: number, pageIndex: number) {
110110
pages.value[pageIndex].loading = true
111111
const tsName = tsColumn.value?.name as string
112-
const pageSql = addTsCondition(sql.value, tsName, start, end)
112+
const pageSql = addTsCondition(sql.value, tsName, start, end + 1)
113113
queryPage(pageSql)
114114
.then(() => {
115115
const index = pages.value.findIndex((page) => page.start === start && page.end === end)

0 commit comments

Comments
 (0)