From 6a048037b039f5974f9f7c056d5fd9e1aa125872 Mon Sep 17 00:00:00 2001 From: Lykin <137850705+tiny-craft@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:44:33 +0800 Subject: [PATCH] feat: add full search for set/zset --- backend/services/browser_service.go | 24 ++- .../content_value/ContentSearchInput.vue | 7 +- .../content_value/ContentValueSet.vue | 24 +-- .../content_value/ContentValueWrapper.vue | 3 +- .../content_value/ContentValueZSet.vue | 146 ++++++------------ frontend/src/langs/en-us.json | 1 + frontend/src/langs/zh-cn.json | 3 +- 7 files changed, 87 insertions(+), 121 deletions(-) diff --git a/backend/services/browser_service.go b/backend/services/browser_service.go index b64ee4f..b63cf04 100644 --- a/backend/services/browser_service.go +++ b/backend/services/browser_service.go @@ -762,6 +762,12 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } case "set": + if !strings.HasPrefix(matchPattern, "*") { + matchPattern = "*" + matchPattern + } + if !strings.HasSuffix(matchPattern, "*") { + matchPattern = matchPattern + "*" + } loadSetHandle := func() ([]types.SetEntryItem, bool, bool, error) { var items []types.SetEntryItem var cursor uint64 @@ -769,11 +775,11 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS var subErr error var loadedKey []string scanSize := int64(Preferences().GetScanSize()) - if param.Full { + if param.Full || matchPattern != "*" { // load all cursor, reset = 0, true for { - loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() + loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, matchPattern, scanSize).Result() if subErr != nil { return items, reset, false, subErr } @@ -797,7 +803,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } else { cursor, _, reset = getEntryCursor() } - loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() + loadedKey, cursor, subErr = client.SScan(ctx, key, cursor, matchPattern, scanSize).Result() items = make([]types.SetEntryItem, len(loadedKey)) for i, val := range loadedKey { items[i].Value = val @@ -820,17 +826,23 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } case "zset": + if !strings.HasPrefix(matchPattern, "*") { + matchPattern = "*" + matchPattern + } + if !strings.HasSuffix(matchPattern, "*") { + matchPattern = matchPattern + "*" + } loadZSetHandle := func() ([]types.ZSetEntryItem, bool, bool, error) { var items []types.ZSetEntryItem var reset bool var cursor uint64 scanSize := int64(Preferences().GetScanSize()) var loadedVal []string - if param.Full { + if param.Full || matchPattern != "*" { // load all cursor, reset = 0, true for { - loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() + loadedVal, cursor, err = client.ZScan(ctx, key, cursor, matchPattern, scanSize).Result() if err != nil { return items, reset, false, err } @@ -858,7 +870,7 @@ func (b *browserService) GetKeyDetail(param types.KeyDetailParam) (resp types.JS } else { cursor, _, reset = getEntryCursor() } - loadedVal, cursor, err = client.ZScan(ctx, key, cursor, param.MatchPattern, scanSize).Result() + loadedVal, cursor, err = client.ZScan(ctx, key, cursor, matchPattern, scanSize).Result() loadedLen := len(loadedVal) items = make([]types.ZSetEntryItem, loadedLen/2) var score float64 diff --git a/frontend/src/components/content_value/ContentSearchInput.vue b/frontend/src/components/content_value/ContentSearchInput.vue index fa2a0db..d82a9c9 100644 --- a/frontend/src/components/content_value/ContentSearchInput.vue +++ b/frontend/src/components/content_value/ContentSearchInput.vue @@ -1,6 +1,6 @@ @@ -287,12 +289,10 @@ defineExpose({ @rename="emit('rename')" />
- +
diff --git a/frontend/src/components/content_value/ContentValueWrapper.vue b/frontend/src/components/content_value/ContentValueWrapper.vue index 1fa3ecb..1f3365b 100644 --- a/frontend/src/components/content_value/ContentValueWrapper.vue +++ b/frontend/src/components/content_value/ContentValueWrapper.vue @@ -100,13 +100,14 @@ const loadData = async (reset, full, selMatch) => { */ const onReload = async (selDecode, selFormat) => { try { - const { name, db, keyCode, keyPath, decode, format } = data.value + const { name, db, keyCode, keyPath, decode, format, matchPattern } = data.value await browserStore.reloadKey({ server: name, db, key: keyCode || keyPath, decode: selDecode || decode, format: selFormat || format, + matchPattern, }) } finally { } diff --git a/frontend/src/components/content_value/ContentValueZSet.vue b/frontend/src/components/content_value/ContentValueZSet.vue index d24c606..78ef795 100644 --- a/frontend/src/components/content_value/ContentValueZSet.vue +++ b/frontend/src/components/content_value/ContentValueZSet.vue @@ -3,7 +3,7 @@ import { computed, h, reactive, ref } from 'vue' import { useI18n } from 'vue-i18n' import ContentToolbar from './ContentToolbar.vue' import AddLink from '@/components/icons/AddLink.vue' -import { NButton, NCode, NIcon, NInput, useThemeVars } from 'naive-ui' +import { NButton, NCode, NIcon, useThemeVars } from 'naive-ui' import { types, types as redisTypes } from '@/consts/support_redis_type.js' import EditableTableColumn from '@/components/common/EditableTableColumn.vue' import { isEmpty, size } from 'lodash' @@ -17,6 +17,7 @@ import IconButton from '@/components/common/IconButton.vue' import ContentEntryEditor from '@/components/content_value/ContentEntryEditor.vue' import FormatSelector from '@/components/content_value/FormatSelector.vue' import Edit from '@/components/icons/Edit.vue' +import ContentSearchInput from '@/components/content_value/ContentSearchInput.vue' const i18n = useI18n() const themeVars = useThemeVars() @@ -51,7 +52,7 @@ const props = defineProps({ loading: Boolean, }) -const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete']) +const emit = defineEmits(['loadmore', 'loadall', 'reload', 'rename', 'delete', 'match']) /** * @@ -61,18 +62,6 @@ const keyName = computed(() => { return !isEmpty(props.keyCode) ? props.keyCode : props.keyPath }) -const filterOption = [ - { - value: 1, - label: i18n.t('common.value'), - }, - { - value: 2, - label: i18n.t('common.score'), - }, -] -const filterType = ref(1) - const browserStore = useBrowserStore() const dialogStore = useDialogStore() const keyType = redisTypes.ZSET @@ -89,46 +78,46 @@ const inEdit = computed(() => { }) const fullEdit = ref(false) -const scoreFilterOption = ref(null) +// const scoreFilterOption = ref(null) const scoreColumn = computed(() => ({ key: 'score', title: i18n.t('common.score'), align: 'center', titleAlign: 'center', resizable: true, - filterOptionValue: scoreFilterOption.value, - filter(value, row) { - const score = parseFloat(row.s) - if (isNaN(score)) { - return true - } - - const regex = /^(>=|<=|>|<|=|!=)?(\d+(\.\d*)?)?$/ - const matches = value.match(regex) - if (matches) { - const operator = matches[1] || '' - const filterScore = parseFloat(matches[2] || '') - if (!isNaN(filterScore)) { - switch (operator) { - case '>=': - return score >= filterScore - case '<=': - return score <= filterScore - case '>': - return score > filterScore - case '<': - return score < filterScore - case '=': - return score === filterScore - case '!=': - return score !== filterScore - } - } - } else { - return !!~row.v.indexOf(value.toString()) - } - return true - }, + // filterOptionValue: scoreFilterOption.value, + // filter(value, row) { + // const score = parseFloat(row.s) + // if (isNaN(score)) { + // return true + // } + // + // const regex = /^(>=|<=|>|<|=|!=)?(\d+(\.\d*)?)?$/ + // const matches = value.match(regex) + // if (matches) { + // const operator = matches[1] || '' + // const filterScore = parseFloat(matches[2] || '') + // if (!isNaN(filterScore)) { + // switch (operator) { + // case '>=': + // return score >= filterScore + // case '<=': + // return score <= filterScore + // case '>': + // return score > filterScore + // case '<': + // return score < filterScore + // case '=': + // return score === filterScore + // case '!=': + // return score !== filterScore + // } + // } + // } else { + // return !!~row.v.indexOf(value.toString()) + // } + // return true + // }, render: (row) => { return row.s }, @@ -294,52 +283,28 @@ const onAddRow = () => { dialogStore.openAddFieldsDialog(props.name, props.db, props.keyPath, props.keyCode, types.ZSET) } -const filterValue = ref('') const onFilterInput = (val) => { - switch (filterType.value) { - case filterOption[0].value: - // filter value - scoreFilterOption.value = null - valueFilterOption.value = val - break - case filterOption[1].value: - // filter score - valueFilterOption.value = null - scoreFilterOption.value = val - break - } + valueFilterOption.value = val } -const onChangeFilterType = (type) => { - onFilterInput(filterValue.value) -} - -const clearFilter = () => { - valueFilterOption.value = null - scoreFilterOption.value = null +const onMatchInput = (matchVal, filterVal) => { + valueFilterOption.value = filterVal + emit('match', matchVal) } const onUpdateFilter = (filters, sourceColumn) => { - switch (filterType.value) { - case filterOption[0].value: - // filter value - valueFilterOption.value = filters[sourceColumn.key] - break - case filterOption[1].value: - // filter score - scoreFilterOption.value = filters[sourceColumn.key] - break - } + valueFilterOption.value = filters[sourceColumn.key] } const onFormatChanged = (selDecode, selFormat) => { emit('reload', selDecode, selFormat) } +const searchInputRef = ref(null) defineExpose({ reset: () => { - clearFilter() resetEdit() + searchInputRef.value?.reset() }, }) @@ -360,25 +325,10 @@ defineExpose({ @rename="emit('rename')" />
- - - - -
{{ $t('interface.score_filter_tip') }}
-
-
+
diff --git a/frontend/src/langs/en-us.json b/frontend/src/langs/en-us.json index 3962571..e6b4490 100644 --- a/frontend/src/langs/en-us.json +++ b/frontend/src/langs/en-us.json @@ -82,6 +82,7 @@ "unpin_edit": "Cancel Pin", "search": "Search", "full_search": "Full Search", + "full_search_result": "The content has been matched as '*{pattern}*'", "filter_field": "Filter Field", "filter_value": "Filter Value", "length": "Length", diff --git a/frontend/src/langs/zh-cn.json b/frontend/src/langs/zh-cn.json index c19652b..6464dfc 100644 --- a/frontend/src/langs/zh-cn.json +++ b/frontend/src/langs/zh-cn.json @@ -81,7 +81,8 @@ "pin_edit": "固定编辑框(保存后不关闭)", "unpin_edit": "取消固定", "search": "搜索", - "full_search": "全文搜索", + "full_search": "全文匹配", + "full_search_result": "内容已匹配为 *{pattern}*", "filter_field": "筛选字段", "filter_value": "筛选值", "length": "长度",