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": "长度",