From ccfbe2cccb6ff691ee8c855fc49eb8afe1f1264c Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 6 Mar 2022 20:59:01 +0100 Subject: [PATCH] key detail tab, API --- packages/web/src/tabs/DbKeyDetailTab.svelte | 17 ++++ packages/web/src/tabs/index.js | 2 + .../web/src/widgets/DbKeysTreeNode.svelte | 11 +++ .../dbgate-plugin-redis/src/backend/driver.js | 98 ++++++++++++++++--- 4 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 packages/web/src/tabs/DbKeyDetailTab.svelte diff --git a/packages/web/src/tabs/DbKeyDetailTab.svelte b/packages/web/src/tabs/DbKeyDetailTab.svelte new file mode 100644 index 00000000..e1cfa83f --- /dev/null +++ b/packages/web/src/tabs/DbKeyDetailTab.svelte @@ -0,0 +1,17 @@ + + + diff --git a/packages/web/src/tabs/index.js b/packages/web/src/tabs/index.js index fa5c843c..18d6fafc 100644 --- a/packages/web/src/tabs/index.js +++ b/packages/web/src/tabs/index.js @@ -21,6 +21,7 @@ import * as CompareModelTab from './CompareModelTab.svelte'; import * as JsonTab from './JsonTab.svelte'; import * as ChangelogTab from './ChangelogTab.svelte'; import * as DiagramTab from './DiagramTab.svelte'; +import * as DbKeyDetailTab from './DbKeyDetailTab.svelte'; export default { TableDataTab, @@ -46,4 +47,5 @@ export default { JsonTab, ChangelogTab, DiagramTab, + DbKeyDetailTab, }; diff --git a/packages/web/src/widgets/DbKeysTreeNode.svelte b/packages/web/src/widgets/DbKeysTreeNode.svelte index 07e6b049..2e188e18 100644 --- a/packages/web/src/widgets/DbKeysTreeNode.svelte +++ b/packages/web/src/widgets/DbKeysTreeNode.svelte @@ -2,6 +2,7 @@ import AppObjectCore from '../appobj/AppObjectCore.svelte'; import { plusExpandIcon } from '../icons/expandIcons'; import FontIcon from '../icons/FontIcon.svelte'; + import openNewTab from '../utility/openNewTab'; import DbKeysSubTree from './DbKeysSubTree.svelte'; @@ -55,6 +56,16 @@ on:click={() => { if (item.type == 'dir') { isExpanded = !isExpanded; + } else { + openNewTab({ + tabComponent: 'DbKeyDetailTab', + title: 'Key: ' + database, + props: { + isDefaultBrowser: true, + conid, + database, + }, + }); } }} extInfo={item.count ? `(${item.count})` : null} diff --git a/plugins/dbgate-plugin-redis/src/backend/driver.js b/plugins/dbgate-plugin-redis/src/backend/driver.js index adca9a7a..dbab004a 100644 --- a/plugins/dbgate-plugin-redis/src/backend/driver.js +++ b/plugins/dbgate-plugin-redis/src/backend/driver.js @@ -117,22 +117,24 @@ const driver = { return Object.values(res); }, + async getKeyCardinality(pool, key, type) { + switch (type) { + case 'list': + return pool.llen(key); + case 'set': + return pool.scard(key); + case 'zset': + return pool.zcard(key); + case 'stream': + return pool.xlen(key); + case 'hash': + return pool.hlen(key); + } + }, + async enrichOneKeyInfo(pool, item) { item.type = await pool.type(item.key); - switch (item.type) { - case 'list': - item.count = await pool.llen(item.key); - break; - case 'set': - item.count = await pool.scard(item.key); - break; - case 'zset': - item.count = await pool.zcard(item.key); - break; - case 'stream': - item.count = await pool.xlen(item.key); - break; - } + item.count = await this.getKeyCardinality(pool, item.key, item.type); }, async enrichKeyInfo(pool, levelInfo) { @@ -142,6 +144,74 @@ const driver = { async (item) => await this.enrichOneKeyInfo(pool, item) ); }, + + async loadKeyInfo(pool, key) { + const res = {}; + const type = await pool.type(key); + + res.key = key; + res.type = type; + res.ttl = await pool.ttl(key); + res.count = await this.getKeyCardinality(pool, key, type); + + switch (type) { + case 'string': + res.value = await pool.get(key); + break; + case 'list': + res.tableColumns = ['value']; + break; + case 'set': + res.tableColumns = ['value']; + res.keyColumn = 'value'; + break; + case 'zset': + res.tableColumns = ['value', 'score']; + res.keyColumn = 'value'; + break; + case 'hash': + res.tableColumns = ['key', 'value']; + res.keyColumn = 'key'; + break; + } + + return res; + }, + + async loadKeyTableRange(pool, key, cursor, count) { + const type = await pool.type(key); + switch (type) { + case 'list': { + const res = await pool.lrange(key, cursor, start + count); + return { + cursor: res.length > count ? cursor + count : 0, + items: res.map((value) => ({ value })).slice(0, count), + }; + } + case 'set': { + const res = await pool.sscan(key, cursor, 'COUNT', count); + return { + cursor: parseInt(res[0]), + items: res[1].map((value) => ({ value })), + }; + } + case 'zset': { + const res = await pool.zscan(key, cursor, 'COUNT', count); + return { + cursor: parseInt(res[0]), + items: _.chunk(res[1], 2).map((item) => ({ value: item[0], score: item[1] })), + }; + } + case 'hash': { + const res = await pool.hscan(key, cursor, 'COUNT', count); + return { + cursor: parseInt(res[0]), + items: _.chunk(res[1], 2).map((item) => ({ key: item[0], value: item[1] })), + }; + } + } + return null; + }, }; module.exports = driver;