From ddc3868f22400c15593b5ea9a8d132dffb52d38c Mon Sep 17 00:00:00 2001 From: Lykin <137850705+tiny-craft@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:41:15 +0800 Subject: [PATCH] refactor: optimized refresh logic after update fields for set/zset type --- backend/services/browser_service.go | 105 ++++++++++++++---- backend/types/js_resp.go | 34 +++--- .../content_value/ContentEntryEditor.vue | 2 +- .../content_value/ContentValueHash.vue | 4 +- .../content_value/ContentValueSet.vue | 15 +-- .../content_value/ContentValueStream.vue | 2 +- .../content_value/ContentValueZSet.vue | 16 +-- frontend/src/stores/browser.js | 104 ++++++++++++----- frontend/src/stores/tab.js | 24 +++- 9 files changed, 211 insertions(+), 95 deletions(-) diff --git a/backend/services/browser_service.go b/backend/services/browser_service.go index e76f49d..ded2c8a 100644 --- a/backend/services/browser_service.go +++ b/backend/services/browser_service.go @@ -1132,9 +1132,10 @@ func (b *browserService) SetHashValue(param types.SetHashParam) (resp types.JSRe resp.Msg = err.Error() return } + affect, err = client.HSet(ctx, key, param.NewField, saveStr).Result() if affect <= 0 { - // no new filed added, just replace exists item + // no new filed added, just update exists item removed = append(removed, types.HashEntryItem{ Key: param.Field, }) @@ -1404,9 +1405,15 @@ func (b *browserService) UpdateSetItem(param types.SetSetParam) (resp types.JSRe client, ctx := item.client, item.ctx key := strutil.DecodeRedisKey(param.Key) + var added, removed []types.SetEntryItem + var affect int64 // remove old 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 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()) 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 { resp.Msg = err.Error() return } resp.Success = true - resp.Data = map[string]any{ - "added": saveStr, + resp.Data = struct { + Added []types.SetEntryItem `json:"added,omitempty"` + Removed []types.SetEntryItem `json:"removed,omitempty"` + }{ + Added: added, + Removed: removed, } return } @@ -1439,13 +1460,16 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J client, ctx := item.client, item.ctx key := strutil.DecodeRedisKey(param.Key) val, newVal := strutil.DecodeRedisKey(param.Value), strutil.DecodeRedisKey(param.NewValue) - updated := map[string]float64{} - var removed []string + var added, updated, removed []types.ZSetEntryItem + var replaced []types.ZSetReplaceItem + var affect int64 if len(newVal) <= 0 { // no new value, delete value - _, err = client.ZRem(ctx, key, val).Result() - if err == nil { - removed = append(removed, val) + if affect, err = client.ZRem(ctx, key, val).Result(); affect > 0 { + //removed = append(removed, val) + removed = append(removed, types.ZSetEntryItem{ + Value: val, + }) } } else { var saveVal string @@ -1455,27 +1479,57 @@ func (b *browserService) UpdateZSetValue(param types.SetZSetParam) (resp types.J } if saveVal == val { - // update score only - _, err = client.ZAdd(ctx, key, redis.Z{ + affect, err = client.ZAdd(ctx, key, redis.Z{ Score: param.Score, Member: saveVal, }).Result() - if err == nil { - updated[saveVal] = param.Score + displayValue, _, _ := strutil.ConvertTo(val, param.RetDecode, param.RetFormat) + 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 { // remove old value and add new one _, err = client.ZRem(ctx, key, val).Result() - if err == nil { - removed = append(removed, val) + if err != nil { + resp.Msg = err.Error() + return } - _, err = client.ZAdd(ctx, key, redis.Z{ + affect, err = client.ZAdd(ctx, key, redis.Z{ Score: param.Score, Member: saveVal, }).Result() - if err == nil { - updated[saveVal] = param.Score + displayValue, _, _ := strutil.ConvertTo(saveVal, param.RetDecode, param.RetFormat) + 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.Data = map[string]any{ - "updated": updated, - "removed": removed, + resp.Data = struct { + Added []types.ZSetEntryItem `json:"added,omitempty"` + 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 } diff --git a/backend/types/js_resp.go b/backend/types/js_resp.go index ddee3bf..6e5aabf 100644 --- a/backend/types/js_resp.go +++ b/backend/types/js_resp.go @@ -75,22 +75,26 @@ type SetHashParam struct { } type SetSetParam struct { - Server string `json:"server"` - DB int `json:"db"` - Key any `json:"key"` - Value any `json:"value"` - NewValue any `json:"newValue"` - Format string `json:"format,omitempty"` - Decode string `json:"decode,omitempty"` + Server string `json:"server"` + DB int `json:"db"` + Key any `json:"key"` + Value any `json:"value"` + NewValue any `json:"newValue"` + Format string `json:"format,omitempty"` + Decode string `json:"decode,omitempty"` + RetFormat string `json:"retFormat,omitempty"` + RetDecode string `json:"retDecode,omitempty"` } type SetZSetParam struct { - Server string `json:"server"` - DB int `json:"db"` - Key any `json:"key"` - Value any `json:"value"` - NewValue any `json:"newValue"` - Score float64 `json:"score"` - Format string `json:"format,omitempty"` - Decode string `json:"decode,omitempty"` + Server string `json:"server"` + DB int `json:"db"` + Key any `json:"key"` + Value any `json:"value"` + NewValue any `json:"newValue"` + Score float64 `json:"score"` + Format string `json:"format,omitempty"` + Decode string `json:"decode,omitempty"` + RetFormat string `json:"retFormat,omitempty"` + RetDecode string `json:"retDecode,omitempty"` } diff --git a/frontend/src/components/content_value/ContentEntryEditor.vue b/frontend/src/components/content_value/ContentEntryEditor.vue index a1e29d8..789387c 100644 --- a/frontend/src/components/content_value/ContentEntryEditor.vue +++ b/frontend/src/components/content_value/ContentEntryEditor.vue @@ -160,11 +160,11 @@ const onSave = () => {
{{ props.valueLabel }}
{ throw new Error('row not exists') } - const { added, success, msg } = await browserStore.updateSetItem({ + const { success, msg } = await browserStore.updateSetItem({ server: props.name, db: props.db, key: keyName.value, @@ -137,15 +137,10 @@ const saveEdit = async (pos, value, decode, format) => { newValue: value, decode, format, + retDecode: props.decode, + retFormat: props.format, }) 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')) } else { $message.error(msg) @@ -183,7 +178,7 @@ const actionColumn = { row.v, ) if (success) { - props.value.splice(index, 1) + // props.value.splice(index, 1) $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) } else { $message.error(msg) @@ -331,12 +326,12 @@ defineExpose({
{ throw new Error('row not exists') } - const { updated, success, msg } = await browserStore.updateZSetItem({ + const { success, msg } = await browserStore.updateZSetItem({ server: props.name, db: props.db, key: keyName.value, @@ -194,14 +194,6 @@ const saveEdit = async (field, value, decode, format) => { format, }) 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')) } else { $message.error(msg) @@ -240,7 +232,7 @@ const actionColumn = { row.v, ) if (success) { - props.value.splice(index, 1) + // props.value.splice(index, 1) $message.success(i18n.t('dialogue.delete_key_succ', { key: row.v })) } else { $message.error(msg) @@ -416,12 +408,12 @@ defineExpose({ e.k) + const removedKeys = map(removed, 'k') tab.removeValueEntries({ server, db, key, type: 'hash', entries: removedKeys }) } if (!isEmpty(updated)) { @@ -1353,20 +1355,48 @@ const useBrowserStore = defineStore('browser', { * @param {string|number[]} key * @param {string|number[]} value * @param {string|number[]} newValue - * @param {string} [decode] - * @param {string} [format] - * @returns {Promise<{[msg]: string, success: boolean, [added]: string|number[]}>} + * @param {decodeTypes} [decode] + * @param {formatTypes} [format] + * @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 { - 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) { - const { added } = data - // const tab = useTabStore() - // tab.upsertValueEntries({ server, db, key, type: 'set', entries: { [value]: newValue } }) - return { success: true, added } + const { added, removed } = data + const tab = useTabStore() + if (!isEmpty(removed)) { + 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 { - return { success, msg } + return { success: false, msg } } } catch (e) { return { success: false, msg: e.message } @@ -1383,10 +1413,14 @@ const useBrowserStore = defineStore('browser', { */ async removeSetItem(server, db, key, value) { try { - const { success, msg } = await SetSetItem(server, db, key, true, [value]) + const { data, success, msg } = await SetSetItem(server, db, key, true, [value]) if (success) { - // const tab = useTabStore() - // tab.removeValueEntries({ server: connName, db, key, type: 'set', entries: [value] }) + const { removed } = data + const tab = useTabStore() + if (!isEmpty(removed)) { + const removedValues = map(removed, 'v') + tab.removeValueEntries({ server, db, key, type: 'set', entries: removedValues }) + } return { success } } else { return { success, msg } @@ -1436,7 +1470,10 @@ const useBrowserStore = defineStore('browser', { * @param {number} score * @param {decodeTypes} decode * @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({ server, @@ -1447,6 +1484,9 @@ const useBrowserStore = defineStore('browser', { score, decode = decodeTypes.NONE, format = formatTypes.RAW, + retDecode, + retFormat, + index, }) { try { const { data, success, msg } = await UpdateZSetValue({ @@ -1458,16 +1498,25 @@ const useBrowserStore = defineStore('browser', { score, decode, format, + retDecode, + retFormat, }) if (success) { - const { updated, removed } = data - // const tab = useTabStore() - // if (!isEmpty(updated)) { - // tab.upsertValueEntries({ server, db, key, type: 'zset', entries: updated }) - // } - // if (!isEmpty(removed)) { - // tab.removeValueEntries({ server, db, key, type: 'zset', entries: removed }) - // } + const { updated = [], added = [], removed = [], replaced = [] } = data + const tab = useTabStore() + if (!isEmpty(removed)) { + const removedValues = map(removed, 'v') + tab.removeValueEntries({ server, db, key, type: 'zset', entries: removedValues }) + } + 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 } } else { return { success, msg } @@ -1490,10 +1539,11 @@ const useBrowserStore = defineStore('browser', { const { data, success, msg } = await UpdateZSetValue({ server, db, key, value, newValue: '', score: 0 }) if (success) { const { removed } = data - // if (!isEmpty(removed)) { - // const tab = useTabStore() - // tab.removeValueEntries({ server: server, db, key, type: 'zset', entries: removed }) - // } + const tab = useTabStore() + if (!isEmpty(removed)) { + const removeValues = map(removed, 'v') + tab.removeValueEntries({ server, db, key, type: 'zset', entries: removeValues }) + } return { success, removed } } else { return { success, msg } diff --git a/frontend/src/stores/tab.js b/frontend/src/stores/tab.js index 4385b8c..3dd0c5f 100644 --- a/frontend/src/stores/tab.js +++ b/frontend/src/stores/tab.js @@ -403,8 +403,22 @@ const useTabStore = defineStore('tab', { } break - case 'zset': + case 'zset': // ZSetReplaceItem[] 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) { let updated = false for (const val of tab.value) { @@ -464,23 +478,23 @@ const useTabStore = defineStore('tab', { case 'hash': // string[] 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) break case 'set': // []string 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 case 'zset': // string[] 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 case 'stream': // string[] 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 } },