refactor: optimized refresh logic after update fields for set/zset type

This commit is contained in:
Lykin 2023-11-17 18:41:15 +08:00
parent 4db3909c6a
commit ddc3868f22
9 changed files with 211 additions and 95 deletions

View File

@ -1132,9 +1132,10 @@ func (b *browserService) SetHashValue(param types.SetHashParam) (resp types.JSRe
resp.Msg = err.Error() resp.Msg = err.Error()
return return
} }
affect, err = client.HSet(ctx, key, param.NewField, saveStr).Result() affect, err = client.HSet(ctx, key, param.NewField, saveStr).Result()
if affect <= 0 { if affect <= 0 {
// no new filed added, just replace exists item // no new filed added, just update exists item
removed = append(removed, types.HashEntryItem{ removed = append(removed, types.HashEntryItem{
Key: param.Field, Key: param.Field,
}) })
@ -1404,9 +1405,15 @@ func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSRe
client, ctx := item.client, item.ctx client, ctx := item.client, item.ctx
key := strutil.DecodeRedisKey(param.Key) key := strutil.DecodeRedisKey(param.Key)
var added, removed []types.SetEntryItem
var affect int64
// remove old value // remove old value
str := strutil.DecodeRedisKey(param.Value) str := strutil.DecodeRedisKey(param.Value)
_, _ = client.SRem(ctx, key, str).Result() if affect, _ = client.SRem(ctx, key, str).Result(); affect > 0 {
removed = append(removed, types.SetEntryItem{
Value: str,
})
}
// insert new value // insert new value
str = strutil.DecodeRedisKey(param.NewValue) str = strutil.DecodeRedisKey(param.NewValue)
@ -1415,15 +1422,29 @@ func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSRe
resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error()) resp.Msg = fmt.Sprintf(`save to type "%s" fail: %s`, param.Format, err.Error())
return return
} }
_, err = client.SAdd(ctx, key, saveStr).Result() if affect, _ = client.SAdd(ctx, key, saveStr).Result(); affect > 0 {
// add new item
var displayStr string
if len(param.RetDecode) > 0 && len(param.RetFormat) > 0 {
displayStr, _, _ = strutil.ConvertTo(saveStr, param.RetDecode, param.RetFormat)
}
added = append(added, types.SetEntryItem{
Value: saveStr,
DisplayValue: displayStr,
})
}
if err != nil { if err != nil {
resp.Msg = err.Error() resp.Msg = err.Error()
return return
} }
resp.Success = true resp.Success = true
resp.Data = map[string]any{ resp.Data = struct {
"added": saveStr, Added []types.SetEntryItem `json:"added,omitempty"`
Removed []types.SetEntryItem `json:"removed,omitempty"`
}{
Added: added,
Removed: removed,
} }
return return
} }
@ -1439,13 +1460,16 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
client, ctx := item.client, item.ctx client, ctx := item.client, item.ctx
key := strutil.DecodeRedisKey(param.Key) key := strutil.DecodeRedisKey(param.Key)
val, newVal := strutil.DecodeRedisKey(param.Value), strutil.DecodeRedisKey(param.NewValue) val, newVal := strutil.DecodeRedisKey(param.Value), strutil.DecodeRedisKey(param.NewValue)
updated := map[string]float64{} var added, updated, removed []types.ZSetEntryItem
var removed []string var replaced []types.ZSetReplaceItem
var affect int64
if len(newVal) <= 0 { if len(newVal) <= 0 {
// no new value, delete value // no new value, delete value
_, err = client.ZRem(ctx, key, val).Result() if affect, err = client.ZRem(ctx, key, val).Result(); affect > 0 {
if err == nil { //removed = append(removed, val)
removed = append(removed, val) removed = append(removed, types.ZSetEntryItem{
Value: val,
})
} }
} else { } else {
var saveVal string var saveVal string
@ -1455,27 +1479,57 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
} }
if saveVal == val { if saveVal == val {
// update score only affect, err = client.ZAdd(ctx, key, redis.Z{
_, err = client.ZAdd(ctx, key, redis.Z{
Score: param.Score, Score: param.Score,
Member: saveVal, Member: saveVal,
}).Result() }).Result()
if err == nil { displayValue, _, _ := strutil.ConvertTo(val, param.RetDecode, param.RetFormat)
updated[saveVal] = param.Score if affect > 0 {
// add new item
added = append(added, types.ZSetEntryItem{
Score: param.Score,
Value: val,
DisplayValue: displayValue,
})
} else {
// update score only
updated = append(updated, types.ZSetEntryItem{
Score: param.Score,
Value: val,
DisplayValue: displayValue,
})
} }
} else { } else {
// remove old value and add new one // remove old value and add new one
_, err = client.ZRem(ctx, key, val).Result() _, err = client.ZRem(ctx, key, val).Result()
if err == nil { if err != nil {
removed = append(removed, val) resp.Msg = err.Error()
return
} }
_, err = client.ZAdd(ctx, key, redis.Z{ affect, err = client.ZAdd(ctx, key, redis.Z{
Score: param.Score, Score: param.Score,
Member: saveVal, Member: saveVal,
}).Result() }).Result()
if err == nil { displayValue, _, _ := strutil.ConvertTo(saveVal, param.RetDecode, param.RetFormat)
updated[saveVal] = param.Score if affect <= 0 {
// no new value added, just update exists item
removed = append(removed, types.ZSetEntryItem{
Value: val,
})
updated = append(updated, types.ZSetEntryItem{
Score: param.Score,
Value: saveVal,
DisplayValue: displayValue,
})
} else {
// add new field
replaced = append(replaced, types.ZSetReplaceItem{
Score: param.Score,
Value: val,
NewValue: saveVal,
DisplayValue: displayValue,
})
} }
} }
} }
@ -1485,9 +1539,16 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J
} }
resp.Success = true resp.Success = true
resp.Data = map[string]any{ resp.Data = struct {
"updated": updated, Added []types.ZSetEntryItem `json:"added,omitempty"`
"removed": removed, Updated []types.ZSetEntryItem `json:"updated,omitempty"`
Replaced []types.ZSetReplaceItem `json:"replaced,omitempty"`
Removed []types.ZSetEntryItem `json:"removed,omitempty"`
}{
Added: added,
Updated: updated,
Replaced: replaced,
Removed: removed,
} }
return return
} }

View File

@ -75,22 +75,26 @@ type SetHashParam struct {
} }
type SetSetParam struct { type SetSetParam struct {
Server string `json:"server"` Server string `json:"server"`
DB int `json:"db"` DB int `json:"db"`
Key any `json:"key"` Key any `json:"key"`
Value any `json:"value"` Value any `json:"value"`
NewValue any `json:"newValue"` NewValue any `json:"newValue"`
Format string `json:"format,omitempty"` Format string `json:"format,omitempty"`
Decode string `json:"decode,omitempty"` Decode string `json:"decode,omitempty"`
RetFormat string `json:"retFormat,omitempty"`
RetDecode string `json:"retDecode,omitempty"`
} }
type SetZSetParam struct { type SetZSetParam struct {
Server string `json:"server"` Server string `json:"server"`
DB int `json:"db"` DB int `json:"db"`
Key any `json:"key"` Key any `json:"key"`
Value any `json:"value"` Value any `json:"value"`
NewValue any `json:"newValue"` NewValue any `json:"newValue"`
Score float64 `json:"score"` Score float64 `json:"score"`
Format string `json:"format,omitempty"` Format string `json:"format,omitempty"`
Decode string `json:"decode,omitempty"` Decode string `json:"decode,omitempty"`
RetFormat string `json:"retFormat,omitempty"`
RetDecode string `json:"retDecode,omitempty"`
} }

View File

@ -160,11 +160,11 @@ const onSave = () => {
<div class="editor-content-item flex-box-v flex-item-expand"> <div class="editor-content-item flex-box-v flex-item-expand">
<div class="editor-content-item-label">{{ props.valueLabel }}</div> <div class="editor-content-item-label">{{ props.valueLabel }}</div>
<n-input <n-input
:resizable="false"
:value="displayValue" :value="displayValue"
autofocus autofocus
class="flex-item-expand" class="flex-item-expand"
type="textarea" type="textarea"
:resizable="false"
@update:value="onUpdateValue" /> @update:value="onUpdateValue" />
<format-selector <format-selector
:decode="viewAs.decode" :decode="viewAs.decode"

View File

@ -389,14 +389,14 @@ defineExpose({
<div id="content-table" class="value-wrapper value-item-part flex-box-h flex-item-expand"> <div id="content-table" class="value-wrapper value-item-part flex-box-h flex-item-expand">
<!-- table --> <!-- table -->
<n-data-table <n-data-table
ref="tableRef"
v-show="!inFullEdit" v-show="!inFullEdit"
:row-key="(row) => row.k" ref="tableRef"
:bordered="false" :bordered="false"
:bottom-bordered="false" :bottom-bordered="false"
:columns="columns" :columns="columns"
:data="props.value" :data="props.value"
:loading="props.loading" :loading="props.loading"
:row-key="(row) => row.k"
:row-props="rowProps" :row-props="rowProps"
:single-column="true" :single-column="true"
:single-line="false" :single-line="false"

View File

@ -129,7 +129,7 @@ const saveEdit = async (pos, value, decode, format) => {
throw new Error('row not exists') throw new Error('row not exists')
} }
const { added, success, msg } = await browserStore.updateSetItem({ const { success, msg } = await browserStore.updateSetItem({
server: props.name, server: props.name,
db: props.db, db: props.db,
key: keyName.value, key: keyName.value,
@ -137,15 +137,10 @@ const saveEdit = async (pos, value, decode, format) => {
newValue: value, newValue: value,
decode, decode,
format, format,
retDecode: props.decode,
retFormat: props.format,
}) })
if (success) { if (success) {
row.v = added
const { value: displayVal } = await browserStore.convertValue({
value: row.v,
decode: props.decode,
format: props.format,
})
row.dv = displayVal
$message.success(i18n.t('dialogue.save_value_succ')) $message.success(i18n.t('dialogue.save_value_succ'))
} else { } else {
$message.error(msg) $message.error(msg)
@ -183,7 +178,7 @@ const actionColumn = {
row.v, row.v,
) )
if (success) { if (success) {
props.value.splice(index, 1) // props.value.splice(index, 1)
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v }))
} else { } else {
$message.error(msg) $message.error(msg)
@ -331,12 +326,12 @@ defineExpose({
<!-- table --> <!-- table -->
<n-data-table <n-data-table
v-show="!inFullEdit" v-show="!inFullEdit"
:row-key="(row) => row.v"
:bordered="false" :bordered="false"
:bottom-bordered="false" :bottom-bordered="false"
:columns="columns" :columns="columns"
:data="props.value" :data="props.value"
:loading="props.loading" :loading="props.loading"
:row-key="(row) => row.v"
:row-props="rowProps" :row-props="rowProps"
:single-column="true" :single-column="true"
:single-line="false" :single-line="false"

View File

@ -237,12 +237,12 @@ defineExpose({
</div> </div>
<div class="value-wrapper value-item-part flex-box-v flex-item-expand"> <div class="value-wrapper value-item-part flex-box-v flex-item-expand">
<n-data-table <n-data-table
:row-key="(row) => row.id"
:bordered="false" :bordered="false"
:bottom-bordered="false" :bottom-bordered="false"
:columns="columns" :columns="columns"
:data="props.value" :data="props.value"
:loading="props.loading" :loading="props.loading"
:row-key="(row) => row.id"
:single-column="true" :single-column="true"
:single-line="false" :single-line="false"
class="flex-item-expand" class="flex-item-expand"

View File

@ -6,7 +6,7 @@ import AddLink from '@/components/icons/AddLink.vue'
import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui' import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui'
import { types, types as redisTypes } from '@/consts/support_redis_type.js' import { types, types as redisTypes } from '@/consts/support_redis_type.js'
import EditableTableColumn from '@/components/common/EditableTableColumn.vue' import EditableTableColumn from '@/components/common/EditableTableColumn.vue'
import { head, isEmpty, keys, size } from 'lodash' import { isEmpty, size } from 'lodash'
import useDialogStore from 'stores/dialog.js' import useDialogStore from 'stores/dialog.js'
import bytes from 'bytes' import bytes from 'bytes'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js' import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
@ -183,7 +183,7 @@ const saveEdit = async (field, value, decode, format) => {
throw new Error('row not exists') throw new Error('row not exists')
} }
const { updated, success, msg } = await browserStore.updateZSetItem({ const { success, msg } = await browserStore.updateZSetItem({
server: props.name, server: props.name,
db: props.db, db: props.db,
key: keyName.value, key: keyName.value,
@ -194,14 +194,6 @@ const saveEdit = async (field, value, decode, format) => {
format, format,
}) })
if (success) { if (success) {
row.v = head(keys(updated))
row.s = updated[row.v]
const { value: displayVal } = await browserStore.convertValue({
value: row.v,
decode: props.decode,
format: props.format,
})
row.dv = displayVal
$message.success(i18n.t('dialogue.save_value_succ')) $message.success(i18n.t('dialogue.save_value_succ'))
} else { } else {
$message.error(msg) $message.error(msg)
@ -240,7 +232,7 @@ const actionColumn = {
row.v, row.v,
) )
if (success) { if (success) {
props.value.splice(index, 1) // props.value.splice(index, 1)
$message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v }))
} else { } else {
$message.error(msg) $message.error(msg)
@ -416,12 +408,12 @@ defineExpose({
<!-- table --> <!-- table -->
<n-data-table <n-data-table
v-show="!inFullEdit" v-show="!inFullEdit"
:row-key="(row) => row.v"
:bordered="false" :bordered="false"
:bottom-bordered="false" :bottom-bordered="false"
:columns="columns" :columns="columns"
:data="props.value" :data="props.value"
:loading="props.loading" :loading="props.loading"
:row-key="(row) => row.v"
:single-column="true" :single-column="true"
:single-line="false" :single-line="false"
class="flex-item-expand" class="flex-item-expand"

View File

@ -1040,6 +1040,8 @@ const useBrowserStore = defineStore('browser', {
value, value,
decode, decode,
format, format,
retDecode,
retFormat,
}) })
if (success) { if (success) {
/** /**
@ -1048,7 +1050,7 @@ const useBrowserStore = defineStore('browser', {
const { updated = [], removed = [], added = [], replaced = [] } = data const { updated = [], removed = [], added = [], replaced = [] } = data
const tab = useTabStore() const tab = useTabStore()
if (!isEmpty(removed)) { if (!isEmpty(removed)) {
const removedKeys = map(removed, (e) => e.k) const removedKeys = map(removed, 'k')
tab.removeValueEntries({ server, db, key, type: 'hash', entries: removedKeys }) tab.removeValueEntries({ server, db, key, type: 'hash', entries: removedKeys })
} }
if (!isEmpty(updated)) { if (!isEmpty(updated)) {
@ -1353,20 +1355,48 @@ const useBrowserStore = defineStore('browser', {
* @param {string|number[]} key * @param {string|number[]} key
* @param {string|number[]} value * @param {string|number[]} value
* @param {string|number[]} newValue * @param {string|number[]} newValue
* @param {string} [decode] * @param {decodeTypes} [decode]
* @param {string} [format] * @param {formatTypes} [format]
* @returns {Promise<{[msg]: string, success: boolean, [added]: string|number[]}>} * @param {decodeTypes} [retDecode]
* @param {formatTypes} [retFormat]
* @returns {Promise<{[msg]: string, success: boolean}>}
*/ */
async updateSetItem({ server, db, key, value, newValue, decode = decodeTypes.NONE, format = formatTypes.RAW }) { async updateSetItem({
server,
db,
key,
value,
newValue,
decode = decodeTypes.NONE,
format = formatTypes.RAW,
retDecode,
retFormat,
}) {
try { try {
const { data, success, msg } = await UpdateSetItem({ server, db, key, value, newValue, decode, format }) const { data, success, msg } = await UpdateSetItem({
server,
db,
key,
value,
newValue,
decode,
format,
retDecode,
retFormat,
})
if (success) { if (success) {
const { added } = data const { added, removed } = data
// const tab = useTabStore() const tab = useTabStore()
// tab.upsertValueEntries({ server, db, key, type: 'set', entries: { [value]: newValue } }) if (!isEmpty(removed)) {
return { success: true, added } const removedValues = map(removed, 'v')
tab.removeValueEntries({ server, db, key, type: 'set', entries: removedValues })
}
if (!isEmpty(added)) {
tab.insertValueEntries({ server, db, key, type: 'set', entries: added })
}
return { success }
} else { } else {
return { success, msg } return { success: false, msg }
} }
} catch (e) { } catch (e) {
return { success: false, msg: e.message } return { success: false, msg: e.message }
@ -1383,10 +1413,14 @@ const useBrowserStore = defineStore('browser', {
*/ */
async removeSetItem(server, db, key, value) { async removeSetItem(server, db, key, value) {
try { try {
const { success, msg } = await SetSetItem(server, db, key, true, [value]) const { data, success, msg } = await SetSetItem(server, db, key, true, [value])
if (success) { if (success) {
// const tab = useTabStore() const { removed } = data
// tab.removeValueEntries({ server: connName, db, key, type: 'set', entries: [value] }) const tab = useTabStore()
if (!isEmpty(removed)) {
const removedValues = map(removed, 'v')
tab.removeValueEntries({ server, db, key, type: 'set', entries: removedValues })
}
return { success } return { success }
} else { } else {
return { success, msg } return { success, msg }
@ -1436,7 +1470,10 @@ const useBrowserStore = defineStore('browser', {
* @param {number} score * @param {number} score
* @param {decodeTypes} decode * @param {decodeTypes} decode
* @param {formatTypes} format * @param {formatTypes} format
* @returns {Promise<{[msg]: string, success: boolean, [updated]: {}, [removed]: []}>} * @param {decodeTypes} [retDecode]
* @param {formatTypes} [retFormat]
* @param {number} [index] index for retrieve affect entries quickly
* @returns {Promise<{[msg]: string, success: boolean}>}
*/ */
async updateZSetItem({ async updateZSetItem({
server, server,
@ -1447,6 +1484,9 @@ const useBrowserStore = defineStore('browser', {
score, score,
decode = decodeTypes.NONE, decode = decodeTypes.NONE,
format = formatTypes.RAW, format = formatTypes.RAW,
retDecode,
retFormat,
index,
}) { }) {
try { try {
const { data, success, msg } = await UpdateZSetValue({ const { data, success, msg } = await UpdateZSetValue({
@ -1458,16 +1498,25 @@ const useBrowserStore = defineStore('browser', {
score, score,
decode, decode,
format, format,
retDecode,
retFormat,
}) })
if (success) { if (success) {
const { updated, removed } = data const { updated = [], added = [], removed = [], replaced = [] } = data
// const tab = useTabStore() const tab = useTabStore()
// if (!isEmpty(updated)) { if (!isEmpty(removed)) {
// tab.upsertValueEntries({ server, db, key, type: 'zset', entries: updated }) const removedValues = map(removed, 'v')
// } tab.removeValueEntries({ server, db, key, type: 'zset', entries: removedValues })
// if (!isEmpty(removed)) { }
// tab.removeValueEntries({ server, db, key, type: 'zset', entries: removed }) if (!isEmpty(updated)) {
// } tab.updateValueEntries({ server, db, key, type: 'zset', entries: updated })
}
if (!isEmpty(added)) {
tab.insertValueEntries({ server, db, key, type: 'zset', entries: added })
}
if (!isEmpty(replaced)) {
tab.replaceValueEntries({ server, db, key, type: 'zset', entries: replaced, index: [index] })
}
return { success, updated, removed } return { success, updated, removed }
} else { } else {
return { success, msg } return { success, msg }
@ -1490,10 +1539,11 @@ const useBrowserStore = defineStore('browser', {
const { data, success, msg } = await UpdateZSetValue({ server, db, key, value, newValue: '', score: 0 }) const { data, success, msg } = await UpdateZSetValue({ server, db, key, value, newValue: '', score: 0 })
if (success) { if (success) {
const { removed } = data const { removed } = data
// if (!isEmpty(removed)) { const tab = useTabStore()
// const tab = useTabStore() if (!isEmpty(removed)) {
// tab.removeValueEntries({ server: server, db, key, type: 'zset', entries: removed }) const removeValues = map(removed, 'v')
// } tab.removeValueEntries({ server, db, key, type: 'zset', entries: removeValues })
}
return { success, removed } return { success, removed }
} else { } else {
return { success, msg } return { success, msg }

View File

@ -403,8 +403,22 @@ const useTabStore = defineStore('tab', {
} }
break break
case 'zset': case 'zset': // ZSetReplaceItem[]
tab.value = tab.value || [] tab.value = tab.value || []
for (const idx of index) {
const entry = get(tab.value, idx)
if (entry != null) {
/** @type ZSetReplaceItem[] **/
const replaceEntry = remove(entries, ({ v }) => v === entry.k)
if (!isEmpty(replaceEntry)) {
entry.s = replaceEntry[0].s
entry.v = replaceEntry[0].nv
entry.dv = replaceEntry[0].dv
}
}
}
// the left entries do not included in index list, try to retrieve the whole list
for (const entry of entries) { for (const entry of entries) {
let updated = false let updated = false
for (const val of tab.value) { for (const val of tab.value) {
@ -464,23 +478,23 @@ const useTabStore = defineStore('tab', {
case 'hash': // string[] case 'hash': // string[]
tab.value = tab.value || {} tab.value = tab.value || {}
const removedElems = remove(tab.value, (e) => includes(entries, e.k)) const removedElems = remove(tab.value, ({ k }) => includes(entries, k))
tab.length -= size(removedElems) tab.length -= size(removedElems)
break break
case 'set': // []string case 'set': // []string
tab.value = tab.value || [] tab.value = tab.value || []
tab.length -= size(remove(tab.value, (v) => entries.indexOf(v) >= 0)) tab.length -= size(remove(tab.value, ({ v }) => includes(entries, v)))
break break
case 'zset': // string[] case 'zset': // string[]
tab.value = tab.value || [] tab.value = tab.value || []
tab.length -= size(remove(tab.value, (v) => entries.indexOf(v.value) >= 0)) tab.length -= size(remove(tab.value, ({ v }) => includes(entries, v)))
break break
case 'stream': // string[] case 'stream': // string[]
tab.value = tab.value || [] tab.value = tab.value || []
tab.length -= size(remove(tab.value, (v) => entries.indexOf(v.id) >= 0)) tab.length -= size(remove(tab.value, ({ v }) => includes(entries, v)))
break break
} }
}, },