mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-09 08:13:29 +08:00
🐛 fix(redis): 将 Key 搜索调整为前缀匹配
- 移除搜索 pattern 的前导通配符,避免中段 Key 误命中 - 将搜索模式和六语言文案由模糊统一为前缀 - 更新模式生成与命名空间筛选回归测试
This commit is contained in:
@@ -336,10 +336,10 @@ describe('RedisViewer tree interactions', () => {
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
expect(searchInput?.props.value).toBe('app:user');
|
||||
expect(updatedSearchModeGroup.props.value).toBe('fuzzy');
|
||||
expect(updatedSearchModeGroup.props.value).toBe('prefix');
|
||||
expect(redisBackend.RedisScanKeys).toHaveBeenLastCalledWith(
|
||||
expect.any(Object),
|
||||
'*[aA][pP][pP]:[uU][sS][eE][rR]*',
|
||||
'[aA][pP][pP]:[uU][sS][eE][rR]*',
|
||||
'0',
|
||||
600,
|
||||
);
|
||||
|
||||
@@ -255,7 +255,7 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchInput, setSearchInput] = useState('');
|
||||
const [searchPattern, setSearchPattern] = useState('*');
|
||||
const [searchMode, setSearchMode] = useState<RedisSearchMode>('fuzzy');
|
||||
const [searchMode, setSearchMode] = useState<RedisSearchMode>('prefix');
|
||||
const [cursor, setCursor] = useState<string>('0');
|
||||
const [hasMore, setHasMore] = useState(false);
|
||||
const [loadingAllKeys, setLoadingAllKeys] = useState(false);
|
||||
@@ -1098,8 +1098,8 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
setSearchMode('fuzzy');
|
||||
executeSearch(groupPath, 'fuzzy');
|
||||
setSearchMode('prefix');
|
||||
executeSearch(groupPath, 'prefix');
|
||||
}, [executeSearch]);
|
||||
|
||||
const stopTreeTitleEvent = (event: React.SyntheticEvent<HTMLElement>) => {
|
||||
@@ -2279,13 +2279,13 @@ const RedisViewer: React.FC<RedisViewerProps> = ({ connectionId, redisDB }) => {
|
||||
buttonStyle="solid"
|
||||
style={{ flexShrink: 0 }}
|
||||
>
|
||||
<Radio.Button value="fuzzy">{tr('redis_viewer.search.fuzzy')}</Radio.Button>
|
||||
<Radio.Button value="prefix">{tr('redis_viewer.search.prefix')}</Radio.Button>
|
||||
<Radio.Button value="exact">{tr('redis_viewer.search.exact')}</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Search
|
||||
{...noAutoCapInputProps}
|
||||
style={{ flex: 1 }}
|
||||
placeholder={searchMode === 'exact' ? tr('redis_viewer.placeholder.search_exact') : tr('redis_viewer.placeholder.search_fuzzy')}
|
||||
placeholder={searchMode === 'exact' ? tr('redis_viewer.placeholder.search_exact') : tr('redis_viewer.placeholder.search_prefix')}
|
||||
value={searchInput}
|
||||
onChange={handleSearchInputChange}
|
||||
onSearch={handleSearch}
|
||||
|
||||
@@ -128,13 +128,13 @@ const RedisViewerKeyToolbar: React.FC<RedisViewerKeyToolbarProps> = ({
|
||||
buttonStyle="solid"
|
||||
style={{ flexShrink: 0 }}
|
||||
>
|
||||
<Radio.Button value="fuzzy">{tr('redis_viewer.search.fuzzy')}</Radio.Button>
|
||||
<Radio.Button value="prefix">{tr('redis_viewer.search.prefix')}</Radio.Button>
|
||||
<Radio.Button value="exact">{tr('redis_viewer.search.exact')}</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Search
|
||||
{...noAutoCapInputProps}
|
||||
style={{ flex: 1 }}
|
||||
placeholder={searchMode === 'exact' ? tr('redis_viewer.placeholder.search_exact') : tr('redis_viewer.placeholder.search_fuzzy')}
|
||||
placeholder={searchMode === 'exact' ? tr('redis_viewer.placeholder.search_exact') : tr('redis_viewer.placeholder.search_prefix')}
|
||||
value={searchInput}
|
||||
onChange={onSearchInputChange}
|
||||
onSearch={onSearch}
|
||||
|
||||
@@ -10,24 +10,24 @@ describe('normalizeRedisSearchInput', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('wraps plain keywords with wildcard for contains matching', () => {
|
||||
it('adds only a trailing wildcard for prefix matching', () => {
|
||||
expect(normalizeRedisSearchInput('order')).toEqual({
|
||||
keyword: 'order',
|
||||
pattern: '*[oO][rR][dD][eE][rR]*',
|
||||
pattern: '[oO][rR][dD][eE][rR]*',
|
||||
});
|
||||
});
|
||||
|
||||
it('builds ascii case-insensitive patterns for letter keywords', () => {
|
||||
expect(normalizeRedisSearchInput('agent')).toEqual({
|
||||
keyword: 'agent',
|
||||
pattern: '*[aA][gG][eE][nN][tT]*',
|
||||
pattern: '[aA][gG][eE][nN][tT]*',
|
||||
});
|
||||
});
|
||||
|
||||
it('escapes redis glob special characters as literals', () => {
|
||||
expect(normalizeRedisSearchInput('user:*:[id]?')).toEqual({
|
||||
keyword: 'user:*:[id]?',
|
||||
pattern: '*[uU][sS][eE][rR]:\\*:\\[[iI][dD]\\]\\?*',
|
||||
pattern: '[uU][sS][eE][rR]:\\*:\\[[iI][dD]\\]\\?*',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const REDIS_GLOB_SPECIAL_CHARS = /([*?\[\]\\])/g;
|
||||
const ASCII_LETTER = /^[A-Za-z]$/;
|
||||
|
||||
export type RedisSearchMode = 'fuzzy' | 'exact';
|
||||
export type RedisSearchMode = 'prefix' | 'exact';
|
||||
|
||||
const escapeRedisGlobLiteral = (value: string): string => {
|
||||
return value.replace(REDIS_GLOB_SPECIAL_CHARS, '\\$1');
|
||||
@@ -21,7 +21,7 @@ const toCaseInsensitiveRedisGlobLiteral = (value: string): string => {
|
||||
|
||||
export const normalizeRedisSearchInput = (
|
||||
rawValue: string,
|
||||
mode: RedisSearchMode = 'fuzzy',
|
||||
mode: RedisSearchMode = 'prefix',
|
||||
): { keyword: string; pattern: string } => {
|
||||
const keyword = String(rawValue || '').trim();
|
||||
if (!keyword) {
|
||||
@@ -35,11 +35,11 @@ export const normalizeRedisSearchInput = (
|
||||
}
|
||||
return {
|
||||
keyword,
|
||||
pattern: `*${toCaseInsensitiveRedisGlobLiteral(keyword)}*`,
|
||||
pattern: `${toCaseInsensitiveRedisGlobLiteral(keyword)}*`,
|
||||
};
|
||||
};
|
||||
|
||||
export const normalizeRedisSearchDraftChange = (rawValue: string, mode: RedisSearchMode = 'fuzzy'): {
|
||||
export const normalizeRedisSearchDraftChange = (rawValue: string, mode: RedisSearchMode = 'prefix'): {
|
||||
keyword: string;
|
||||
pattern: string;
|
||||
shouldSearchImmediately: boolean;
|
||||
|
||||
@@ -6972,11 +6972,11 @@
|
||||
"redis_viewer.placeholder.new_key_name": "new:key:name",
|
||||
"redis_viewer.placeholder.new_member_value": "Neuen Mitgliedswert eingeben",
|
||||
"redis_viewer.placeholder.search_exact": "Vollständigen Key oder Namespace für exakte Suche eingeben",
|
||||
"redis_viewer.placeholder.search_fuzzy": "Keys suchen (unscharfe Suche)",
|
||||
"redis_viewer.placeholder.search_prefix": "Keys nach Präfix suchen",
|
||||
"redis_viewer.placeholder.stream_id": "Zum Beispiel: * oder 1723110000000-0",
|
||||
"redis_viewer.placeholder.value": "Wert",
|
||||
"redis_viewer.search.exact": "Exakt",
|
||||
"redis_viewer.search.fuzzy": "Unscharf",
|
||||
"redis_viewer.search.prefix": "Präfix",
|
||||
"redis_viewer.state.connection_not_found": "Verbindung nicht gefunden",
|
||||
"redis_viewer.state.empty_selection": "Wählen Sie einen Key aus, um Details anzuzeigen",
|
||||
"redis_viewer.state.import_preview_empty": "Wählen Sie eine Redis-Exportdatei aus, prüfen Sie die Vorschau und wählen Sie die zu importierenden Keys aus",
|
||||
|
||||
@@ -6972,11 +6972,11 @@
|
||||
"redis_viewer.placeholder.new_key_name": "new:key:name",
|
||||
"redis_viewer.placeholder.new_member_value": "Enter new member value",
|
||||
"redis_viewer.placeholder.search_exact": "Enter full Key or namespace for exact search",
|
||||
"redis_viewer.placeholder.search_fuzzy": "Search Keys (fuzzy match)",
|
||||
"redis_viewer.placeholder.search_prefix": "Search Keys by prefix",
|
||||
"redis_viewer.placeholder.stream_id": "For example: * or 1723110000000-0",
|
||||
"redis_viewer.placeholder.value": "Value",
|
||||
"redis_viewer.search.exact": "Exact",
|
||||
"redis_viewer.search.fuzzy": "Fuzzy",
|
||||
"redis_viewer.search.prefix": "Prefix",
|
||||
"redis_viewer.state.connection_not_found": "Connection not found",
|
||||
"redis_viewer.state.empty_selection": "Select a Key to view details",
|
||||
"redis_viewer.state.import_preview_empty": "Select a Redis export file to preview and choose Keys to import",
|
||||
|
||||
@@ -6972,11 +6972,11 @@
|
||||
"redis_viewer.placeholder.new_key_name": "new:key:name",
|
||||
"redis_viewer.placeholder.new_member_value": "新しいメンバー値を入力",
|
||||
"redis_viewer.placeholder.search_exact": "完全一致検索する Key または名前空間を入力",
|
||||
"redis_viewer.placeholder.search_fuzzy": "Key を検索(あいまい一致)",
|
||||
"redis_viewer.placeholder.search_prefix": "Key を前方一致で検索",
|
||||
"redis_viewer.placeholder.stream_id": "例: * または 1723110000000-0",
|
||||
"redis_viewer.placeholder.value": "値",
|
||||
"redis_viewer.search.exact": "完全一致",
|
||||
"redis_viewer.search.fuzzy": "あいまい",
|
||||
"redis_viewer.search.prefix": "前方一致",
|
||||
"redis_viewer.state.connection_not_found": "接続が見つかりません",
|
||||
"redis_viewer.state.empty_selection": "詳細を表示する Key を選択してください",
|
||||
"redis_viewer.state.import_preview_empty": "Redis エクスポートファイルを選択し、プレビューしてインポートする Key を選択してください",
|
||||
|
||||
@@ -6972,11 +6972,11 @@
|
||||
"redis_viewer.placeholder.new_key_name": "new:key:name",
|
||||
"redis_viewer.placeholder.new_member_value": "Введите значение нового участника",
|
||||
"redis_viewer.placeholder.search_exact": "Введите полный Key или namespace для точного поиска",
|
||||
"redis_viewer.placeholder.search_fuzzy": "Поиск Keys (нечеткое совпадение)",
|
||||
"redis_viewer.placeholder.search_prefix": "Поиск Keys по префиксу",
|
||||
"redis_viewer.placeholder.stream_id": "Например: * или 1723110000000-0",
|
||||
"redis_viewer.placeholder.value": "Значение",
|
||||
"redis_viewer.search.exact": "Точно",
|
||||
"redis_viewer.search.fuzzy": "Нечетко",
|
||||
"redis_viewer.search.prefix": "Префикс",
|
||||
"redis_viewer.state.connection_not_found": "Подключение не найдено",
|
||||
"redis_viewer.state.empty_selection": "Выберите Key, чтобы просмотреть детали",
|
||||
"redis_viewer.state.import_preview_empty": "Выберите файл экспорта Redis, просмотрите его и отметьте Keys для импорта",
|
||||
|
||||
@@ -6972,11 +6972,11 @@
|
||||
"redis_viewer.placeholder.new_key_name": "new:key:name",
|
||||
"redis_viewer.placeholder.new_member_value": "输入新成员值",
|
||||
"redis_viewer.placeholder.search_exact": "输入完整 Key 或命名空间进行精确搜索",
|
||||
"redis_viewer.placeholder.search_fuzzy": "搜索 Key(模糊匹配)",
|
||||
"redis_viewer.placeholder.search_prefix": "按前缀搜索 Key",
|
||||
"redis_viewer.placeholder.stream_id": "例如:* 或 1723110000000-0",
|
||||
"redis_viewer.placeholder.value": "值",
|
||||
"redis_viewer.search.exact": "精确",
|
||||
"redis_viewer.search.fuzzy": "模糊",
|
||||
"redis_viewer.search.prefix": "前缀",
|
||||
"redis_viewer.state.connection_not_found": "未找到连接",
|
||||
"redis_viewer.state.empty_selection": "选择一个 Key 查看详情",
|
||||
"redis_viewer.state.import_preview_empty": "请选择 Redis 导出文件,预览后勾选要导入的 Key",
|
||||
|
||||
@@ -6972,11 +6972,11 @@
|
||||
"redis_viewer.placeholder.new_key_name": "new:key:name",
|
||||
"redis_viewer.placeholder.new_member_value": "輸入新成員值",
|
||||
"redis_viewer.placeholder.search_exact": "輸入完整 Key 或命名空間進行精確搜尋",
|
||||
"redis_viewer.placeholder.search_fuzzy": "搜尋 Key(模糊比對)",
|
||||
"redis_viewer.placeholder.search_prefix": "依前綴搜尋 Key",
|
||||
"redis_viewer.placeholder.stream_id": "例如:* 或 1723110000000-0",
|
||||
"redis_viewer.placeholder.value": "值",
|
||||
"redis_viewer.search.exact": "精確",
|
||||
"redis_viewer.search.fuzzy": "模糊",
|
||||
"redis_viewer.search.prefix": "前綴",
|
||||
"redis_viewer.state.connection_not_found": "找不到連線",
|
||||
"redis_viewer.state.empty_selection": "選取一個 Key 以查看詳細資料",
|
||||
"redis_viewer.state.import_preview_empty": "請選擇 Redis 匯出檔案,預覽後勾選要匯入的 Key",
|
||||
|
||||
Reference in New Issue
Block a user