feat: add key and prefix reload
refactor: separate update db tree node logic from open database
This commit is contained in:
parent
e906a91964
commit
a200b554de
|
@ -307,18 +307,27 @@ func (c *connectionService) parseDBItemInfo(info string) map[string]int {
|
|||
// OpenDatabase open select database, and list all keys
|
||||
// @param path contain connection name and db name
|
||||
func (c *connectionService) OpenDatabase(connName string, db int) (resp types.JSResp) {
|
||||
return c.ScanKeys(connName, db, "*")
|
||||
}
|
||||
|
||||
// ScanKeys scan all keys below prefix
|
||||
func (c *connectionService) ScanKeys(connName string, db int, prefix string) (resp types.JSResp) {
|
||||
rdb, ctx, err := c.getRedisClient(connName, db)
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(prefix, "*") {
|
||||
prefix += ":*"
|
||||
}
|
||||
|
||||
//var keys []string
|
||||
keys := map[string]keyItem{}
|
||||
var cursor uint64
|
||||
for {
|
||||
var loadedKey []string
|
||||
loadedKey, cursor, err = rdb.Scan(ctx, cursor, "*", 10000).Result()
|
||||
loadedKey, cursor, err = rdb.Scan(ctx, cursor, prefix, 10000).Result()
|
||||
if err != nil {
|
||||
resp.Msg = err.Error()
|
||||
return
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
"dependencies": {
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.3",
|
||||
"sass": "^1.62.1",
|
||||
"vue": "^3.2.37",
|
||||
"pinia": "^2.1.4",
|
||||
"sass": "^1.63.6",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -20,9 +20,9 @@
|
|||
"naive-ui": "^2.34.4",
|
||||
"prettier": "^2.8.8",
|
||||
"unplugin-auto-import": "^0.16.4",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "^0.25.0",
|
||||
"vite": "^4.3.0"
|
||||
"unplugin-icons": "^0.16.3",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"vite": "^4.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@antfu/install-pkg": {
|
||||
|
@ -1503,9 +1503,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz",
|
||||
"integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz",
|
||||
"integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"vue-demi": ">=0.14.5"
|
||||
|
@ -1655,9 +1655,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.63.2",
|
||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.2.tgz",
|
||||
"integrity": "sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww==",
|
||||
"version": "1.63.6",
|
||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.6.tgz",
|
||||
"integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
@ -3113,9 +3113,9 @@
|
|||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||
},
|
||||
"pinia": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz",
|
||||
"integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz",
|
||||
"integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
|
||||
"requires": {
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"vue-demi": ">=0.14.5"
|
||||
|
@ -3212,9 +3212,9 @@
|
|||
}
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.63.2",
|
||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.2.tgz",
|
||||
"integrity": "sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww==",
|
||||
"version": "1.63.6",
|
||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.6.tgz",
|
||||
"integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==",
|
||||
"requires": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
"dependencies": {
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pinia": "^2.1.3",
|
||||
"sass": "^1.62.1",
|
||||
"vue": "^3.2.37",
|
||||
"pinia": "^2.1.4",
|
||||
"sass": "^1.63.6",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -21,8 +21,8 @@
|
|||
"naive-ui": "^2.34.4",
|
||||
"prettier": "^2.8.8",
|
||||
"unplugin-auto-import": "^0.16.4",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "^0.25.0",
|
||||
"vite": "^4.3.0"
|
||||
"unplugin-icons": "^0.16.3",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"vite": "^4.3.9"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
da66eb9d13a7ace25f7f75d36c2510f9
|
||||
e8efe46ff15777b1af82225bac5f4626
|
|
@ -56,7 +56,7 @@ const onDeleteKey = () => {
|
|||
<redis-type-tag :type="props.keyType" size="large"></redis-type-tag>
|
||||
<n-input v-model:value="props.keyPath">
|
||||
<template #suffix>
|
||||
<icon-button :icon="Refresh" t-tooltip="reload_key" size="18" @click="onReloadKey" />
|
||||
<icon-button :icon="Refresh" t-tooltip="reload" size="18" @click="onReloadKey" />
|
||||
</template>
|
||||
</n-input>
|
||||
</n-input-group>
|
||||
|
|
|
@ -152,7 +152,7 @@ const onClose = () => {
|
|||
preset="dialog"
|
||||
transform-origin="center"
|
||||
>
|
||||
<n-tabs v-model:value="tab">
|
||||
<n-tabs v-model:value="tab" type="line">
|
||||
<n-tab-pane :tab="$t('general')" display-directive="show" name="general">
|
||||
<n-form
|
||||
ref="generalFormRef"
|
||||
|
|
|
@ -116,7 +116,7 @@ const onClose = () => {
|
|||
preset="dialog"
|
||||
transform-origin="center"
|
||||
>
|
||||
<n-tabs v-model:value="tab">
|
||||
<n-tabs v-model:value="tab" type="line">
|
||||
<n-tab-pane :tab="$t('general')" display-directive="show" name="general">
|
||||
<n-form
|
||||
:label-width="formLabelWidth"
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script setup>
|
||||
import { h, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { h, nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import { ConnectionType } from '../../consts/connection_type.js'
|
||||
import { NIcon, useDialog, useMessage } from 'naive-ui'
|
||||
import Key from '../icons/Key.vue'
|
||||
import ToggleDb from '../icons/ToggleDb.vue'
|
||||
import { get, indexOf, size } from 'lodash'
|
||||
import { indexOf, isEmpty } from 'lodash'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import Refresh from '../icons/Refresh.vue'
|
||||
import CopyLink from '../icons/CopyLink.vue'
|
||||
|
@ -158,7 +158,20 @@ const onUpdateExpanded = (value, option, meta) => {
|
|||
}
|
||||
}
|
||||
|
||||
const onUpdateSelectedKeys = (keys, option) => {
|
||||
const onUpdateSelectedKeys = (keys, options) => {
|
||||
if (!isEmpty(options)) {
|
||||
// prevent load duplicate key
|
||||
for (const node of options) {
|
||||
if (node.type === ConnectionType.RedisValue) {
|
||||
const { key, name, db, redisKey } = node
|
||||
if (indexOf(selectedKeys.value, key) === -1) {
|
||||
connectionStore.loadKeyValue(name, db, redisKey)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedKeys.value = keys
|
||||
}
|
||||
|
||||
|
@ -210,13 +223,6 @@ const renderSuffix = ({ option }) => {
|
|||
|
||||
const nodeProps = ({ option }) => {
|
||||
return {
|
||||
onClick() {
|
||||
const { key, name, db, type, redisKey } = option
|
||||
if (option.type === ConnectionType.RedisValue) {
|
||||
connectionStore.loadKeyValue(name, db, redisKey)
|
||||
}
|
||||
// console.log('[click]:' + JSON.stringify(option))
|
||||
},
|
||||
onDblclick: async () => {
|
||||
if (loading.value) {
|
||||
console.warn('TODO: alert to ignore double click when loading')
|
||||
|
@ -279,6 +285,12 @@ const handleSelectContextMenu = (key) => {
|
|||
case 'key_newkey':
|
||||
dialogStore.openNewKeyDialog(redisKey, name, db)
|
||||
break
|
||||
case 'key_reload':
|
||||
connectionStore.scanKeys(name, db, redisKey)
|
||||
break
|
||||
case 'value_reload':
|
||||
connectionStore.loadKeyValue(name, db, redisKey)
|
||||
break
|
||||
case 'key_remove':
|
||||
case 'value_remove':
|
||||
confirmDialog.warning(i18n.t('remove_tip', { name: redisKey }), () => {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"disconnect_all": "Disconnect all connections",
|
||||
"filter": "Filter",
|
||||
"sort_conn": "Resort Connections",
|
||||
"reload_key": "Reload Current Key",
|
||||
"close_confirm": "Confirm close this tab and connection",
|
||||
"opening_connection": "Opening Connection...",
|
||||
"remove_tip": "{type} \"{name}\" will be deleted",
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"disconnect_all": "断开所有连接",
|
||||
"filter": "筛选",
|
||||
"sort_conn": "调整连接顺序",
|
||||
"reload_key": "重新载入此键内容",
|
||||
"close_confirm": "是否关闭当前连接",
|
||||
"opening_connection": "正在打开连接...",
|
||||
"remove_tip": "{type} \"{name}\" 将会被删除",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { get, isEmpty, last, map, remove, size, sortedIndexBy, split, uniq } from 'lodash'
|
||||
import { findIndex, get, isEmpty, last, map, remove, size, sortedIndexBy, split, uniq } from 'lodash'
|
||||
import {
|
||||
AddHashField,
|
||||
AddListItem,
|
||||
|
@ -18,6 +18,7 @@ import {
|
|||
RenameKey,
|
||||
SaveConnection,
|
||||
SaveSortedConnection,
|
||||
ScanKeys,
|
||||
SetHashValue,
|
||||
SetKeyTTL,
|
||||
SetKeyValue,
|
||||
|
@ -389,78 +390,8 @@ const useConnectionStore = defineStore('connections', {
|
|||
return
|
||||
}
|
||||
|
||||
// insert child to children list by order
|
||||
const sortedInsertChild = (childrenList, item) => {
|
||||
const insertIdx = sortedIndexBy(childrenList, item, 'key')
|
||||
childrenList.splice(insertIdx, 0, item)
|
||||
// childrenList.push(item)
|
||||
}
|
||||
// update all node item's children num
|
||||
const updateChildrenNum = (node) => {
|
||||
let count = 0
|
||||
const totalChildren = size(node.children)
|
||||
if (totalChildren > 0) {
|
||||
for (const elem of node.children) {
|
||||
updateChildrenNum(elem)
|
||||
count += elem.keys
|
||||
}
|
||||
} else {
|
||||
count += 1
|
||||
}
|
||||
node.keys = count
|
||||
// node.children = sortBy(node.children, 'label')
|
||||
}
|
||||
|
||||
const keyStruct = []
|
||||
const mark = {}
|
||||
for (const key in keys) {
|
||||
const keyPart = split(key, separator)
|
||||
// const prefixLen = size(keyPart) - 1
|
||||
const len = size(keyPart)
|
||||
let handlePath = ''
|
||||
let ks = keyStruct
|
||||
for (let i = 0; i < len; i++) {
|
||||
handlePath += keyPart[i]
|
||||
if (i !== len - 1) {
|
||||
// layer
|
||||
const treeKey = `${handlePath}@${ConnectionType.RedisKey}`
|
||||
if (!mark.hasOwnProperty(treeKey)) {
|
||||
mark[treeKey] = {
|
||||
key: `${connName}/db${db}/${treeKey}`,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
type: ConnectionType.RedisKey,
|
||||
children: [],
|
||||
}
|
||||
sortedInsertChild(ks, mark[treeKey])
|
||||
}
|
||||
ks = mark[treeKey].children
|
||||
handlePath += separator
|
||||
} else {
|
||||
// key
|
||||
const treeKey = `${handlePath}@${ConnectionType.RedisValue}`
|
||||
mark[treeKey] = {
|
||||
key: `${connName}/db${db}/${treeKey}`,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
type: ConnectionType.RedisValue,
|
||||
}
|
||||
sortedInsertChild(ks, mark[treeKey])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append db node to current connection's children
|
||||
const dbs = this.databases[connName]
|
||||
dbs[db].children = keyStruct
|
||||
dbs[db].opened = true
|
||||
updateChildrenNum(dbs[db])
|
||||
this._updateNodeChildren(connName, db, keys)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -491,7 +422,127 @@ const useConnectionStore = defineStore('connections', {
|
|||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* scan keys with prefix
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} prefix
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async scanKeys(connName, db, prefix) {
|
||||
const { data, success, msg } = await ScanKeys(connName, db, prefix)
|
||||
if (!success) {
|
||||
throw new Error(msg)
|
||||
}
|
||||
// remove current keys below prefix
|
||||
const prefixPart = split(prefix, separator)
|
||||
const dbs = this.databases[connName]
|
||||
let node = dbs[db]
|
||||
for (const key of prefixPart) {
|
||||
const idx = findIndex(node.children, { label: key })
|
||||
if (idx === -1) {
|
||||
node = null
|
||||
break
|
||||
}
|
||||
node = node.children[idx]
|
||||
}
|
||||
if (node != null) {
|
||||
node.children = []
|
||||
}
|
||||
|
||||
const { keys = [] } = data
|
||||
this._updateNodeChildren(connName, db, keys)
|
||||
},
|
||||
|
||||
/**
|
||||
* remove keys in db
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {Object.<string, {}>[]} keys
|
||||
* @private
|
||||
*/
|
||||
_updateNodeChildren(connName, db, keys) {
|
||||
// find match key node in node list
|
||||
const findNodeByKey = (nodes, key) => {
|
||||
const idx = findIndex(nodes, { key })
|
||||
return idx !== -1 ? nodes[idx] : null
|
||||
}
|
||||
// insert child to children list by order
|
||||
const sortedInsertChild = (childrenList, item) => {
|
||||
const insertIdx = sortedIndexBy(childrenList, item, 'key')
|
||||
childrenList.splice(insertIdx, 0, item)
|
||||
// childrenList.push(item)
|
||||
}
|
||||
// update all node item's children num
|
||||
const updateChildrenNum = (node) => {
|
||||
let count = 0
|
||||
const totalChildren = size(node.children)
|
||||
if (totalChildren > 0) {
|
||||
for (const elem of node.children) {
|
||||
updateChildrenNum(elem)
|
||||
count += elem.keys
|
||||
}
|
||||
} else {
|
||||
count += 1
|
||||
}
|
||||
node.keys = count
|
||||
// node.children = sortBy(node.children, 'label')
|
||||
}
|
||||
|
||||
const dbs = this.databases[connName]
|
||||
if (dbs[db].children == null) {
|
||||
dbs[db].children = []
|
||||
}
|
||||
const keyStruct = dbs[db].children
|
||||
for (const key in keys) {
|
||||
const keyPart = split(key, separator)
|
||||
// const prefixLen = size(keyPart) - 1
|
||||
const len = size(keyPart)
|
||||
let handlePath = ''
|
||||
let ks = keyStruct
|
||||
for (let i = 0; i < len; i++) {
|
||||
handlePath += keyPart[i]
|
||||
if (i !== len - 1) {
|
||||
// layer
|
||||
const curKey = `${connName}/db${db}/${handlePath}@${ConnectionType.RedisKey}`
|
||||
let selectedNode = findNodeByKey(ks, curKey)
|
||||
if (selectedNode == null) {
|
||||
selectedNode = {
|
||||
key: curKey,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
type: ConnectionType.RedisKey,
|
||||
children: [],
|
||||
}
|
||||
sortedInsertChild(ks, selectedNode)
|
||||
}
|
||||
ks = selectedNode.children
|
||||
handlePath += separator
|
||||
} else {
|
||||
// key
|
||||
const curKey = `${connName}/db${db}/${handlePath}@${ConnectionType.RedisValue}`
|
||||
const selectedNode = {
|
||||
key: curKey,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
type: ConnectionType.RedisValue,
|
||||
}
|
||||
sortedInsertChild(ks, selectedNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbs[db].opened = true
|
||||
updateChildrenNum(dbs[db])
|
||||
},
|
||||
|
||||
/**
|
||||
* add key to db
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
|
@ -521,9 +572,8 @@ const useConnectionStore = defineStore('connections', {
|
|||
for (let j = 0; j < len + 1; j++) {
|
||||
const treeKey = get(nodeList[j], 'key')
|
||||
const isLast = j >= len - 1
|
||||
const currentKey = `${connName}/db${db}/${redisKey}@${
|
||||
isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
|
||||
}`
|
||||
const keyType = isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
|
||||
const currentKey = `${connName}/db${db}/${redisKey}@${keyType}`
|
||||
if (treeKey > currentKey || isLast) {
|
||||
// out of search range, add new item
|
||||
if (isLastKeyPart) {
|
||||
|
@ -981,9 +1031,8 @@ const useConnectionStore = defineStore('connections', {
|
|||
const isLastKeyPart = i === keyLen - 1
|
||||
for (let j = 0; j < len; j++) {
|
||||
const treeKey = get(nodeList[j], 'key')
|
||||
const currentKey = `${connName}/db${db}/${redisKey}@${
|
||||
isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
|
||||
}`
|
||||
const keyType = isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
|
||||
const currentKey = `${connName}/db${db}/${redisKey}@${keyType}`
|
||||
if (treeKey > currentKey) {
|
||||
// out of search range, target not exists
|
||||
forceBreak = true
|
||||
|
|
|
@ -1,857 +0,0 @@
|
|||
import { get, isEmpty, last, remove, size, sortedIndexBy, split } from 'lodash'
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
AddHashField,
|
||||
AddListItem,
|
||||
AddZSetValue,
|
||||
CloseConnection,
|
||||
GetKeyValue,
|
||||
OpenConnection,
|
||||
OpenDatabase,
|
||||
RemoveKey,
|
||||
RenameKey,
|
||||
SetHashValue,
|
||||
SetKeyTTL,
|
||||
SetKeyValue,
|
||||
SetListItem,
|
||||
SetSetItem,
|
||||
UpdateSetItem,
|
||||
UpdateZSetValue,
|
||||
} from '../../wailsjs/go/services/connectionService.js'
|
||||
import { ConnectionType } from '../consts/connection_type.js'
|
||||
import useTabStore from './tab.js'
|
||||
import useConnectionStore from './connections.js'
|
||||
|
||||
const separator = ':'
|
||||
|
||||
const useDatabaseStore = defineStore('database', {
|
||||
/**
|
||||
* @typedef {Object} DatabaseItem
|
||||
* @property {string} key
|
||||
* @property {string} label
|
||||
* @property {string} name - server name, type != ConnectionType.Group only
|
||||
* @property {number} type
|
||||
* @property {number} [db] - database index, type == ConnectionType.RedisDB only
|
||||
* @property {number} keys
|
||||
* @property {boolean} [connected] - redis server is connected, type == ConnectionType.Server only
|
||||
* @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only
|
||||
* @property {boolean} [expanded] - current node is expanded
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {{connections: DatabaseItem[]}}
|
||||
*/
|
||||
state: () => ({
|
||||
connections: [], // all connections list
|
||||
databases: {}, // all database group by opened connections
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
/**
|
||||
* Open connection
|
||||
* @param {string} connName
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async openConnection(connName) {
|
||||
const { data, success, msg } = await OpenConnection(connName)
|
||||
if (!success) {
|
||||
throw new Error(msg)
|
||||
}
|
||||
// append to db node to current connection
|
||||
const connStore = useConnectionStore()
|
||||
const connNode = connStore.getConnection(connName)
|
||||
if (connNode == null) {
|
||||
throw new Error('no such connection')
|
||||
}
|
||||
const { db } = data
|
||||
if (isEmpty(db)) {
|
||||
throw new Error('no db loaded')
|
||||
}
|
||||
const children = []
|
||||
for (let i = 0; i < db.length; i++) {
|
||||
children.push({
|
||||
key: `${connName}/${db[i].name}`,
|
||||
label: db[i].name,
|
||||
name: connName,
|
||||
keys: db[i].keys,
|
||||
db: i,
|
||||
type: ConnectionType.RedisDB,
|
||||
// isLeaf: false,
|
||||
})
|
||||
}
|
||||
connNode.children = children
|
||||
connNode.connected = true
|
||||
},
|
||||
|
||||
/**
|
||||
* Close connection
|
||||
* @param {string} connName
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async closeConnection(connName) {
|
||||
const { success, msg } = await CloseConnection(connName)
|
||||
if (!success) {
|
||||
// throw new Error(msg)
|
||||
return false
|
||||
}
|
||||
|
||||
// successful close connection, remove all children
|
||||
const connNode = this.getConnection(connName)
|
||||
if (connNode == null) {
|
||||
// throw new Error('no such connection')
|
||||
return false
|
||||
}
|
||||
connNode.children = undefined
|
||||
connNode.isLeaf = undefined
|
||||
connNode.connected = false
|
||||
connNode.expanded = false
|
||||
return true
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Connection by path name
|
||||
* @param {string} connName
|
||||
* @returns
|
||||
*/
|
||||
getConnection(connName) {
|
||||
const conn = this.connections
|
||||
for (let i = 0; i < conn.length; i++) {
|
||||
if (conn[i].type === ConnectionType.Server && conn[i].key === connName) {
|
||||
return conn[i]
|
||||
} else if (conn[i].type === ConnectionType.Group) {
|
||||
const children = conn[i].children
|
||||
for (let j = 0; j < children.length; j++) {
|
||||
if (children[j].type === ConnectionType.Server && conn[i].key === connName) {
|
||||
return children[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
/**
|
||||
* Open database and load all keys
|
||||
* @param connName
|
||||
* @param db
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async openDatabase(connName, db) {
|
||||
const { data, success, msg } = await OpenDatabase(connName, db)
|
||||
if (!success) {
|
||||
throw new Error(msg)
|
||||
}
|
||||
const { keys = [] } = data
|
||||
if (isEmpty(keys)) {
|
||||
const connNode = this.getConnection(connName)
|
||||
const { children = [] } = connNode
|
||||
children[db].children = []
|
||||
children[db].opened = true
|
||||
return
|
||||
}
|
||||
|
||||
// insert child to children list by order
|
||||
const sortedInsertChild = (childrenList, item) => {
|
||||
const insertIdx = sortedIndexBy(childrenList, item, 'key')
|
||||
childrenList.splice(insertIdx, 0, item)
|
||||
// childrenList.push(item)
|
||||
}
|
||||
// update all node item's children num
|
||||
const updateChildrenNum = (node) => {
|
||||
let count = 0
|
||||
const totalChildren = size(node.children)
|
||||
if (totalChildren > 0) {
|
||||
for (const elem of node.children) {
|
||||
updateChildrenNum(elem)
|
||||
count += elem.keys
|
||||
}
|
||||
} else {
|
||||
count += 1
|
||||
}
|
||||
node.keys = count
|
||||
// node.children = sortBy(node.children, 'label')
|
||||
}
|
||||
|
||||
const keyStruct = []
|
||||
const mark = {}
|
||||
for (const key in keys) {
|
||||
const keyPart = split(key, separator)
|
||||
// const prefixLen = size(keyPart) - 1
|
||||
const len = size(keyPart)
|
||||
let handlePath = ''
|
||||
let ks = keyStruct
|
||||
for (let i = 0; i < len; i++) {
|
||||
handlePath += keyPart[i]
|
||||
if (i !== len - 1) {
|
||||
// layer
|
||||
const treeKey = `${handlePath}@${ConnectionType.RedisKey}`
|
||||
if (!mark.hasOwnProperty(treeKey)) {
|
||||
mark[treeKey] = {
|
||||
key: `${connName}/db${db}/${treeKey}`,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
type: ConnectionType.RedisKey,
|
||||
children: [],
|
||||
}
|
||||
sortedInsertChild(ks, mark[treeKey])
|
||||
}
|
||||
ks = mark[treeKey].children
|
||||
handlePath += separator
|
||||
} else {
|
||||
// key
|
||||
const treeKey = `${handlePath}@${ConnectionType.RedisValue}`
|
||||
mark[treeKey] = {
|
||||
key: `${connName}/db${db}/${treeKey}`,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey: handlePath,
|
||||
type: ConnectionType.RedisValue,
|
||||
}
|
||||
sortedInsertChild(ks, mark[treeKey])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append db node to current connection's children
|
||||
const connNode = this.getConnection(connName)
|
||||
const { children = [] } = connNode
|
||||
children[db].children = keyStruct
|
||||
children[db].opened = true
|
||||
updateChildrenNum(children[db])
|
||||
},
|
||||
|
||||
/**
|
||||
* select node
|
||||
* @param key
|
||||
* @param name
|
||||
* @param db
|
||||
* @param type
|
||||
* @param redisKey
|
||||
*/
|
||||
select({ key, name, db, type, redisKey }) {
|
||||
if (type === ConnectionType.RedisValue) {
|
||||
console.log(`[click]key:${key} db: ${db} redis key: ${redisKey}`)
|
||||
|
||||
// async get value for key
|
||||
this.loadKeyValue(name, db, redisKey).then(() => {})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* load redis key
|
||||
* @param server
|
||||
* @param db
|
||||
* @param key
|
||||
*/
|
||||
async loadKeyValue(server, db, key) {
|
||||
try {
|
||||
const { data, success, msg } = await GetKeyValue(server, db, key)
|
||||
if (success) {
|
||||
const { type, ttl, value } = data
|
||||
const tab = useTabStore()
|
||||
tab.upsertTab({
|
||||
server,
|
||||
db,
|
||||
type,
|
||||
ttl,
|
||||
key,
|
||||
value,
|
||||
})
|
||||
} else {
|
||||
console.warn('TODO: handle get key fail')
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @private
|
||||
*/
|
||||
_addKey(connName, db, key) {
|
||||
const connNode = this.getConnection(connName)
|
||||
const { children: dbs = [] } = connNode
|
||||
const dbDetail = get(dbs, db, {})
|
||||
|
||||
if (dbDetail == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const descendantChain = [dbDetail]
|
||||
|
||||
const keyPart = split(key, separator)
|
||||
let redisKey = ''
|
||||
const keyLen = size(keyPart)
|
||||
let added = false
|
||||
for (let i = 0; i < keyLen; i++) {
|
||||
redisKey += keyPart[i]
|
||||
|
||||
const node = last(descendantChain)
|
||||
const nodeList = get(node, 'children', [])
|
||||
const len = size(nodeList)
|
||||
const isLastKeyPart = i === keyLen - 1
|
||||
for (let j = 0; j < len + 1; j++) {
|
||||
const treeKey = get(nodeList[j], 'key')
|
||||
const isLast = j >= len - 1
|
||||
const currentKey = `${connName}/db${db}/${redisKey}@${
|
||||
isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
|
||||
}`
|
||||
if (treeKey > currentKey || isLast) {
|
||||
// out of search range, add new item
|
||||
if (isLastKeyPart) {
|
||||
// key not exists, add new one
|
||||
const item = {
|
||||
key: currentKey,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 1,
|
||||
redisKey,
|
||||
type: ConnectionType.RedisValue,
|
||||
}
|
||||
if (isLast) {
|
||||
nodeList.push(item)
|
||||
} else {
|
||||
nodeList.splice(j, 0, item)
|
||||
}
|
||||
added = true
|
||||
} else {
|
||||
// layer not exists, add new one
|
||||
const item = {
|
||||
key: currentKey,
|
||||
label: keyPart[i],
|
||||
name: connName,
|
||||
db,
|
||||
keys: 0,
|
||||
redisKey,
|
||||
type: ConnectionType.RedisKey,
|
||||
children: [],
|
||||
}
|
||||
if (isLast) {
|
||||
nodeList.push(item)
|
||||
descendantChain.push(last(nodeList))
|
||||
} else {
|
||||
nodeList.splice(j, 0, item)
|
||||
descendantChain.push(nodeList[j])
|
||||
}
|
||||
redisKey += separator
|
||||
added = true
|
||||
}
|
||||
break
|
||||
} else if (treeKey === currentKey) {
|
||||
if (isLastKeyPart) {
|
||||
// same key exists, do nothing
|
||||
console.log('TODO: same key exist, do nothing now, should replace value later')
|
||||
} else {
|
||||
// same group exists, find into it's children
|
||||
descendantChain.push(nodeList[j])
|
||||
redisKey += separator
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update ancestor node's info
|
||||
if (added) {
|
||||
const desLen = size(descendantChain)
|
||||
for (let i = 0; i < desLen; i++) {
|
||||
const children = get(descendantChain[i], 'children', [])
|
||||
let keys = 0
|
||||
for (const child of children) {
|
||||
if (child.type === ConnectionType.RedisKey) {
|
||||
keys += get(child, 'keys', 1)
|
||||
} else if (child.type === ConnectionType.RedisValue) {
|
||||
keys += get(child, 'keys', 0)
|
||||
}
|
||||
}
|
||||
descendantChain[i].keys = keys
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* set redis key
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number} keyType
|
||||
* @param {any} value
|
||||
* @param {number} ttl
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async setKey(connName, db, key, keyType, value, ttl) {
|
||||
try {
|
||||
const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl)
|
||||
if (success) {
|
||||
// update tree view data
|
||||
this._addKey(connName, db, key)
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* update hash field
|
||||
* when field is set, newField is null, delete field
|
||||
* when field is null, newField is set, add new field
|
||||
* when both field and newField are set, and field === newField, update field
|
||||
* when both field and newField are set, and field !== newField, delete field and add newField
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} field
|
||||
* @param {string} newField
|
||||
* @param {string} value
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
||||
*/
|
||||
async setHash(connName, db, key, field, newField, value) {
|
||||
try {
|
||||
const { data, success, msg } = await SetHashValue(connName, db, key, field, newField || '', value || '')
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
return { success, updated }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* insert or update hash field item
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number }action 0:ignore duplicated fields 1:overwrite duplicated fields
|
||||
* @param {string[]} fieldItems field1, value1, filed2, value2...
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
||||
*/
|
||||
async addHashField(connName, db, key, action, fieldItems) {
|
||||
try {
|
||||
const { data, success, msg } = await AddHashField(connName, db, key, action, fieldItems)
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
return { success, updated }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* remove hash field
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} field
|
||||
* @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>}
|
||||
*/
|
||||
async removeHashField(connName, db, key, field) {
|
||||
try {
|
||||
const { data, success, msg } = await SetHashValue(connName, db, key, field, '', '')
|
||||
if (success) {
|
||||
const { removed = [] } = data
|
||||
return { success, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* insert list item
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {int} action 0: push to head, 1: push to tail
|
||||
* @param {string[]}values
|
||||
* @returns {Promise<*|{msg, success: boolean}>}
|
||||
*/
|
||||
async addListItem(connName, db, key, action, values) {
|
||||
try {
|
||||
return AddListItem(connName, db, key, action, values)
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* prepend item to head of list
|
||||
* @param connName
|
||||
* @param db
|
||||
* @param key
|
||||
* @param values
|
||||
* @returns {Promise<[msg]: string, success: boolean, [item]: []>}
|
||||
*/
|
||||
async prependListItem(connName, db, key, values) {
|
||||
try {
|
||||
const { data, success, msg } = await AddListItem(connName, db, key, 0, values)
|
||||
if (success) {
|
||||
const { left = [] } = data
|
||||
return { success, item: left }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* append item to tail of list
|
||||
* @param connName
|
||||
* @param db
|
||||
* @param key
|
||||
* @param values
|
||||
* @returns {Promise<[msg]: string, success: boolean, [item]: any[]>}
|
||||
*/
|
||||
async appendListItem(connName, db, key, values) {
|
||||
try {
|
||||
const { data, success, msg } = await AddListItem(connName, db, key, 1, values)
|
||||
if (success) {
|
||||
const { right = [] } = data
|
||||
return { success, item: right }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* update value of list item by index
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number} index
|
||||
* @param {string} value
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
|
||||
*/
|
||||
async updateListItem(connName, db, key, index, value) {
|
||||
try {
|
||||
const { data, success, msg } = await SetListItem(connName, db, key, index, value)
|
||||
if (success) {
|
||||
const { updated = {} } = data
|
||||
return { success, updated }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* remove list item
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number} index
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [removed]: string[]}>}
|
||||
*/
|
||||
async removeListItem(connName, db, key, index) {
|
||||
try {
|
||||
const { data, success, msg } = await SetListItem(connName, db, key, index, '')
|
||||
if (success) {
|
||||
const { removed = [] } = data
|
||||
return { success, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* add item to set
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async addSetItem(connName, db, key, value) {
|
||||
try {
|
||||
const { success, msg } = await SetSetItem(connName, db, key, false, [value])
|
||||
if (success) {
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* update value of set item
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @param {string} newValue
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async updateSetItem(connName, db, key, value, newValue) {
|
||||
try {
|
||||
const { success, msg } = await UpdateSetItem(connName, db, key, value, newValue)
|
||||
if (success) {
|
||||
return { success: true }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* remove item from set
|
||||
* @param connName
|
||||
* @param db
|
||||
* @param key
|
||||
* @param value
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async removeSetItem(connName, db, key, value) {
|
||||
try {
|
||||
const { success, msg } = await SetSetItem(connName, db, key, true, [value])
|
||||
if (success) {
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* add item to sorted set
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number} action
|
||||
* @param {Object.<string, number>} vs value: score
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async addZSetItem(connName, db, key, action, vs) {
|
||||
try {
|
||||
const { success, msg } = await AddZSetValue(connName, db, key, action, vs)
|
||||
if (success) {
|
||||
return { success }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* update item of sorted set
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @param {string} newValue
|
||||
* @param {number} score
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}, [removed]: []}>}
|
||||
*/
|
||||
async updateZSetItem(connName, db, key, value, newValue, score) {
|
||||
try {
|
||||
const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, newValue, score)
|
||||
if (success) {
|
||||
const { updated, removed } = data
|
||||
return { success, updated, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* remove item from sorted set
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param key
|
||||
* @param {string} value
|
||||
* @returns {Promise<{[msg]: string, success: boolean, [removed]: []}>}
|
||||
*/
|
||||
async removeZSetItem(connName, db, key, value) {
|
||||
try {
|
||||
const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, '', 0)
|
||||
if (success) {
|
||||
const { removed } = data
|
||||
return { success, removed }
|
||||
} else {
|
||||
return { success, msg }
|
||||
}
|
||||
} catch (e) {
|
||||
return { success: false, msg: e.message }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* reset key's ttl
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {number} ttl
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async setTTL(connName, db, key, ttl) {
|
||||
try {
|
||||
const { success, msg } = await SetKeyTTL(connName, db, key, ttl)
|
||||
return success === true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @private
|
||||
*/
|
||||
_removeKey(connName, db, key) {
|
||||
const connNode = this.getConnection(connName)
|
||||
const { children: dbs = [] } = connNode
|
||||
const dbDetail = get(dbs, db, {})
|
||||
|
||||
if (dbDetail == null) {
|
||||
return
|
||||
}
|
||||
|
||||
const descendantChain = [dbDetail]
|
||||
const keyPart = split(key, separator)
|
||||
let redisKey = ''
|
||||
const keyLen = size(keyPart)
|
||||
let deleted = false
|
||||
let forceBreak = false
|
||||
for (let i = 0; i < keyLen && !forceBreak; i++) {
|
||||
redisKey += keyPart[i]
|
||||
|
||||
const node = last(descendantChain)
|
||||
const nodeList = get(node, 'children', [])
|
||||
const len = size(nodeList)
|
||||
const isLastKeyPart = i === keyLen - 1
|
||||
for (let j = 0; j < len; j++) {
|
||||
const treeKey = get(nodeList[j], 'key')
|
||||
const currentKey = `${connName}/db${db}/${redisKey}@${
|
||||
isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
|
||||
}`
|
||||
if (treeKey > currentKey) {
|
||||
// out of search range, target not exists
|
||||
forceBreak = true
|
||||
break
|
||||
} else if (treeKey === currentKey) {
|
||||
if (isLastKeyPart) {
|
||||
// find target
|
||||
nodeList.splice(j, 1)
|
||||
node.keys -= 1
|
||||
deleted = true
|
||||
forceBreak = true
|
||||
} else {
|
||||
// find into it's children
|
||||
descendantChain.push(nodeList[j])
|
||||
redisKey += separator
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (forceBreak) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// console.log(JSON.stringify(descendantChain))
|
||||
|
||||
// update ancestor node's info
|
||||
if (deleted) {
|
||||
const desLen = size(descendantChain)
|
||||
for (let i = desLen - 1; i > 0; i--) {
|
||||
const children = get(descendantChain[i], 'children', [])
|
||||
const parent = descendantChain[i - 1]
|
||||
if (isEmpty(children)) {
|
||||
const parentChildren = get(parent, 'children', [])
|
||||
const k = get(descendantChain[i], 'key')
|
||||
remove(parentChildren, (item) => item.key === k)
|
||||
}
|
||||
parent.keys -= 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* remove redis key
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async removeKey(connName, db, key) {
|
||||
try {
|
||||
const { data, success, msg } = await RemoveKey(connName, db, key)
|
||||
if (success) {
|
||||
// update tree view data
|
||||
this._removeKey(connName, db, key)
|
||||
|
||||
// set tab content empty
|
||||
const tab = useTabStore()
|
||||
tab.emptyTab(connName)
|
||||
return true
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
/**
|
||||
* rename key
|
||||
* @param {string} connName
|
||||
* @param {number} db
|
||||
* @param {string} key
|
||||
* @param {string} newKey
|
||||
* @returns {Promise<{[msg]: string, success: boolean}>}
|
||||
*/
|
||||
async renameKey(connName, db, key, newKey) {
|
||||
const { success = false, msg } = await RenameKey(connName, db, key, newKey)
|
||||
if (success) {
|
||||
// delete old key and add new key struct
|
||||
this._removeKey(connName, db, key)
|
||||
this._addKey(connName, db, newKey)
|
||||
return { success: true }
|
||||
} else {
|
||||
return { success: false, msg }
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useDatabaseStore
|
14
go.mod
14
go.mod
|
@ -3,7 +3,7 @@ module tinyrdm
|
|||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1
|
||||
github.com/bytedance/sonic v1.9.2
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/redis/go-redis/v9 v9.0.5
|
||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
|
||||
|
@ -22,7 +22,7 @@ require (
|
|||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/labstack/echo/v4 v4.10.2 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
@ -38,11 +38,11 @@ require (
|
|||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/crypto v0.10.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||
golang.org/x/net v0.11.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
)
|
||||
|
||||
// replace github.com/wailsapp/wails/v2 v2.5.1 => /Users/lykin/go/pkg/mod
|
||||
|
|
28
go.sum
28
go.sum
|
@ -3,8 +3,8 @@ github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3IS
|
|||
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
|
||||
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
|
@ -32,8 +32,8 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8
|
|||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
|
@ -88,13 +88,13 @@ github.com/wailsapp/wails/v2 v2.5.1/go.mod h1:jbOZbcr/zm79PxXxAjP8UoVlDd9wLW3uDs
|
|||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -106,12 +106,12 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
Loading…
Reference in New Issue