Skip to content

Commit

Permalink
feat: log refine (#464)
Browse files Browse the repository at this point in the history
Feat/log refine (#304)

* feat: use icon for cell menu

* fix: typo

* feat: use icon rect

* fix: color

* feat: icon pos and size

* style: menu

* fix: add editing ts column

* refactor: store

* fix: no ts chart

* fix: number convert

* fix: null columns
  • Loading branch information
sunchanglong authored Dec 16, 2024
1 parent 544a7cf commit 5a58612
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 90 deletions.
155 changes: 89 additions & 66 deletions src/store/modules/logquery/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,50 @@ type ColumnsMap = {
}

const useLogQueryStore = defineStore('logQuery', () => {
/** sql state */
// current query result sql
const sql = ref(``)
// editing sql
const editingSql = ref('')
// const columns = shallowRef<Array<SchemaType>>([])
const displayedColumns = useStorage<ColumnsMap>('logquery-table-column-visible', {})
const rows = shallowRef<Array<any>>([])
const inputTableName = ref('') // table after query
const editingTableName = ref('') // table in editing
// table schema map
const tableMap = ref<TableMap>({})
// computed queried columns schema
const columns = computed(() => {
if (!inputTableName.value) {
return []
}
return tableMap.value[inputTableName.value] || []
})
const tsColumn = shallowRef<TSColumn>()
// multiple relative to s, one of 1000 1000 * 1000 1000 * 1000 * 1000
type Multiple = 1000 | 1000000 | 1000000000
const multipleRe = /timestamp\((\d)\)/
const getTsColumn = (tableName: string) => {
const fields = tableMap.value[tableName] || []
const field = fields.filter((column) => column.data_type.toLowerCase().indexOf('timestamp') > -1)[0]
if (!field) {
return null
}
const timescale = multipleRe.exec(field.data_type)
if (!timescale) return null
return {
multiple: (1000 ** (Number(timescale[1]) / 3)) as Multiple,
...field,
}
}
// editing ts column
const editingTsColumn = computed<TSColumn>(() => {
return getTsColumn(editingTableName.value)
})

/** for table */
// column visible
const displayedColumns = useStorage<ColumnsMap>('logquery-table-column-visible', {})
// table rows
const rows = shallowRef<Array<any>>([])
// selected row key for detail view
const selectedRowKey = ref(-1)
const currRow = computed(() => {
if (selectedRowKey.value > -1) {
Expand All @@ -41,64 +78,68 @@ const useLogQueryStore = defineStore('logQuery', () => {
return null
})

/** toolbar */
// time select range time
const rangeTime = ref<Array<string>>([])
// time select relative time
const time = ref(10)
const inputTableName = ref('') // table after query
const editingTableName = ref('') // table in editing

const columns = computed(() => {
if (!inputTableName.value) {
return []
}
return tableMap.value[inputTableName.value]
})
// for make all query
const queryNum = ref(0)
// for table query
const tableIndex = ref(0)
// editor type
const editorType = ref('builder')
// query Columns differ from columns, keep orders with query
const queryColumns = shallowRef<Array<SchemaType>>([])

const queryForm = reactive({
conditions: [] as Array<Condition>,
orderBy: 'DESC',
})

const limit = ref(1000)
const queryLoading = ref(false)
const refresh = ref(false)
// unix seconds
// always return two element array, because time select component use two variable to implement relative time and range time
const unifiedRange = computed(() => {
if (time.value && time.value > 0) {
return [dayjs().subtract(time.value, 'minute').unix(), dayjs().unix()]
}
return rangeTime.value.map((v) => Number(v))
})

// convert to time unit by query table, ts column may have different timestamp accuracy
const getRelativeRange = (multiple: number) => {
if (time.value && time.value > 0) {
return [`now() - Interval '${time.value}m'`, 'now()']
}
return rangeTime.value.map((v) => Number(v) * multiple)
}
// multiple relative to s, one of 1000 1000 * 1000 1000 * 1000 * 1000
type Multiple = 1000 | 1000000 | 1000000000
const multipleRe = /timestamp\((\d)\)/

const dataLoadFlag = ref(0)
const tsColumn = shallowRef<TSColumn>()

const getTsColumn = (tableName: string) => {
const fields = tableMap.value[tableName] || []
const field = fields.filter((column) => column.data_type.toLowerCase().indexOf('timestamp') > -1)[0]
if (!field) {
return null
}
const timescale = multipleRe.exec(field.data_type)
if (!timescale) return null
return {
multiple: (1000 ** (Number(timescale[1]) / 3)) as Multiple,
...field,
/** for sql builder */
const queryForm = reactive({
conditions: [] as Array<Condition>,
orderBy: 'DESC',
})
type TypeKey = keyof typeof typeMap
const opMap = {
String: ['=', 'contains', 'not contains', '!=', 'like'],
Number: ['=', '!=', '>', '>=', '<', '<='],
Time: ['>', '>=', '<', '<='],
}
type OpKey = keyof typeof opMap

// get Operator List by field
function getOpByField(field: string): string[] {
const fields = tableMap.value[editingTableName.value]
const index = fields.findIndex((f) => f.name === field)
if (index === -1) {
return []
}
const type = fields[index].data_type as TypeKey
const opKey = typeMap[type] as OpKey
return opMap[opKey] || []
}
const limit = ref(1000)

// query handler
const query = () => {
queryLoading.value = true
return editorAPI
Expand All @@ -121,11 +162,11 @@ const useLogQueryStore = defineStore('logQuery', () => {
return columns.value[index]
}

const getColumn = (name: string) => {
const allColumns = tableMap.value[inputTableName.value]
const index = allColumns.findIndex((column) => column.name === name)
return allColumns[index]
}
// const getColumn = (name: string) => {
// const allColumns = tableMap.value[inputTableName.value]
// const index = allColumns.findIndex((column) => column.name === name)
// return allColumns[index]
// }

const mergeColumn = useLocalStorage('logquery-merge-column', true)
const showKeys = useLocalStorage('logquery-show-keys', true)
Expand Down Expand Up @@ -231,36 +272,37 @@ const useLogQueryStore = defineStore('logQuery', () => {
}
}
if (unifiedRange.value.length === 2) {
if (tsColumn.value) {
const { multiple } = tsColumn.value
if (editingTsColumn.value) {
const { multiple } = editingTsColumn.value
const [start, end] = getRelativeRange(multiple)
let prefix = ' AND'
if (!where.length) {
prefix = ''
}
where.push(`${prefix} ${tsColumn.value.name} >= ${start} AND ${tsColumn.value.name} < ${end}`)
where.push(`${prefix} ${editingTsColumn.value.name} >= ${start} AND ${editingTsColumn.value.name} < ${end}`)
}
}
return where
}

// construct editing sql
watch(
[queryForm, unifiedRange, limit, editingTableName],
[queryForm, unifiedRange, limit, editingTableName, editingTsColumn],
() => {
if (!editingTableName.value) {
return
}
if (editorType.value !== 'builder') {
return
}
tsColumn.value = getTsColumn(editingTableName.value)
let str = `SELECT * FROM ${editingTableName.value}`
const where = buildCondition()
if (where.length) {
str += ` WHERE ${where.join('')}`
}
if (tsColumn.value) {
str += ` ORDER BY ${tsColumn.value?.name} ${queryForm.orderBy}`
const tmpTsColumn = editingTsColumn.value
if (tmpTsColumn) {
str += ` ORDER BY ${tmpTsColumn.name} ${queryForm.orderBy}`
}
str += ` LIMIT ${limit.value}`
editingSql.value = str
Expand All @@ -271,31 +313,13 @@ const useLogQueryStore = defineStore('logQuery', () => {
}
)

// reset displayedColumn when columns change
watch(columns, () => {
if (!displayedColumns.value[inputTableName.value]) {
displayedColumns.value[inputTableName.value] = columns.value.map((c) => c.name)
}
})

type TypeKey = keyof typeof typeMap
const opMap = {
String: ['=', 'contains', 'not contains', '!=', 'like'],
Number: ['=', '!=', '>', '>=', '<', '<='],
Time: ['>', '>=', '<', '<='],
}
type OpKey = keyof typeof opMap

function getOpByField(field: string): string[] {
const fields = tableMap.value[editingTableName.value]
const index = fields.findIndex((f) => f.name === field)
if (index === -1) {
return []
}
const type = fields[index].data_type as TypeKey
const opKey = typeMap[type] as OpKey
return opMap[opKey] || []
}

function reset() {
editingTableName.value = ''
inputTableName.value = ''
Expand All @@ -317,6 +341,7 @@ const useLogQueryStore = defineStore('logQuery', () => {
inputTableName,
editingTableName,
tsColumn,
editingTsColumn,
time,
unifiedRange,
queryNum,
Expand All @@ -326,7 +351,6 @@ const useLogQueryStore = defineStore('logQuery', () => {
editorType,
queryForm,
buildCondition,
getColumn,
editingSql,
displayedColumns,
limit,
Expand All @@ -339,7 +363,6 @@ const useLogQueryStore = defineStore('logQuery', () => {
queryColumns,
getOpByField,
reset,
getTsColumn,
}
})

Expand Down
4 changes: 2 additions & 2 deletions src/views/dashboard/logs/query/ChartContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ a-card
template(#icon)
icon-down
template(#content)
a-doption(value="count") Write Count
a-doption(value="count") Row Count
a-dsubmenu(trigger="hover" position="lt")
template(#default)
| Frequency Distribution
Expand Down Expand Up @@ -36,7 +36,7 @@ a-card
}
const menuStr = computed(() => {
if (currChart.value === 'count') {
return 'Write Count '
return 'Row Count'
}
return 'Frequency Distribution'
})
Expand Down
1 change: 1 addition & 0 deletions src/views/dashboard/logs/query/CountChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ VCharts(
function countQuery() {
if (!countSql.value) {
data.value = []
return
}
editorAPI.runSQL(countSql.value).then((result) => {
Expand Down
4 changes: 3 additions & 1 deletion src/views/dashboard/logs/query/ExportLog.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template lang="pug">
a-button(size="small" type="text" @click="exportSql")
| Export as csv
| Export as CSV
</template>

<script setup name="ExportLog" lang="ts">
import fileDownload from 'js-file-download'
import editorAPI from '@/api/editor'
Expand Down Expand Up @@ -33,4 +34,5 @@ a-button(size="small" type="text" @click="exportSql")
})
}
</script>

<style scoped lang="less"></style>
2 changes: 1 addition & 1 deletion src/views/dashboard/logs/query/Pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ a-space(v-if="pages.length")
function loadPage(start: number, end: number, pageIndex: number) {
pages.value[pageIndex].loading = true
const tsName = tsColumn.value?.name as string
const pageSql = addTsCondition(sql.value, tsName, start, end + 1)
const pageSql = addTsCondition(sql.value, tsName, start, Number(end) + 1)
queryPage(pageSql)
.then(() => {
const index = pages.value.findIndex((page) => page.start === start && page.end === end)
Expand Down
4 changes: 2 additions & 2 deletions src/views/dashboard/logs/query/SQLBuilder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ a-form(
icon-plus(style="cursor: pointer; font-size: 14px" @click="addCondition")
a-form-item(label="ORDER BY")
a-space
a-typography-text(v-if="tsColumn" code) {{ tsColumn.name }}
a-typography-text(v-if="editingTsColumn" code) {{ editingTsColumn.name }}
a-select(v-model="form.orderBy" style="width: auto" :options="['DESC', 'ASC']")
| LIMIT
a-input-number(
Expand All @@ -66,7 +66,7 @@ a-form(
const {
tableMap,
inputTableName,
tsColumn,
editingTsColumn,
queryForm: form,
limit,
editingTableName,
Expand Down
Loading

0 comments on commit 5a58612

Please sign in to comment.