2025-09-24 16:43:11 +08:00
|
|
|
// Package model define model struct of model runtime service
|
|
|
|
|
package model
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2025-10-15 17:08:32 +08:00
|
|
|
"math"
|
2025-09-24 16:43:11 +08:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"modelRT/constants"
|
|
|
|
|
"modelRT/diagram"
|
2025-09-25 16:39:45 +08:00
|
|
|
"modelRT/logger"
|
2025-09-24 16:43:11 +08:00
|
|
|
|
2025-10-15 17:08:32 +08:00
|
|
|
"github.com/RediSearch/redisearch-go/v2/redisearch"
|
|
|
|
|
redigo "github.com/gomodule/redigo/redis"
|
2025-12-03 16:55:14 +08:00
|
|
|
"github.com/redis/go-redis/v9"
|
2025-09-24 16:43:11 +08:00
|
|
|
)
|
|
|
|
|
|
2025-10-15 17:08:32 +08:00
|
|
|
var ac *redisearch.Autocompleter
|
|
|
|
|
|
|
|
|
|
// InitAutocompleterWithPool define func of initialize the Autocompleter with redigo pool
|
|
|
|
|
func InitAutocompleterWithPool(pool *redigo.Pool) {
|
|
|
|
|
ac = redisearch.NewAutocompleterFromPool(pool, constants.RedisSearchDictName)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-25 16:39:45 +08:00
|
|
|
// RedisSearchRecommend define func of redis search by input string and return recommend results
|
2025-10-16 17:18:57 +08:00
|
|
|
func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, error) {
|
2025-09-24 17:26:46 +08:00
|
|
|
rdb := diagram.GetRedisClientInstance()
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
if input == "" {
|
|
|
|
|
// return all grid tagname
|
|
|
|
|
return getKeyBySpecificsLevel(ctx, rdb, 1, input)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inputSlice := strings.Split(input, ".")
|
|
|
|
|
inputSliceLen := len(inputSlice)
|
|
|
|
|
|
|
|
|
|
switch inputSliceLen {
|
|
|
|
|
case 1:
|
|
|
|
|
// grid tagname search
|
|
|
|
|
gridSearchInput := inputSlice[0]
|
|
|
|
|
gridExists, err := rdb.SIsMember(ctx, constants.RedisAllGridSetKey, gridSearchInput).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check grid key exist failed ", "grid_key", input, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if gridExists {
|
|
|
|
|
return []string{"."}, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// start grid tagname fuzzy search
|
|
|
|
|
recommends, err := runFuzzySearch(ctx, gridSearchInput, inputSliceLen)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "fuzzy search failed for level 1", "search_input", gridSearchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(recommends) > 0 {
|
|
|
|
|
return recommends, true, nil
|
|
|
|
|
}
|
|
|
|
|
return []string{}, true, nil
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
return handleLevelFuzzySearch(ctx, rdb, 2, constants.RedisAllZoneSetKey, inputSlice)
|
|
|
|
|
case 3:
|
|
|
|
|
return handleLevelFuzzySearch(ctx, rdb, 3, constants.RedisAllStationSetKey, inputSlice)
|
|
|
|
|
case 4:
|
|
|
|
|
return handleLevelFuzzySearch(ctx, rdb, 4, constants.RedisAllCompNSPathSetKey, inputSlice)
|
|
|
|
|
case 5:
|
|
|
|
|
return handleLevelFuzzySearch(ctx, rdb, 5, constants.RedisAllCompTagSetKey, inputSlice)
|
|
|
|
|
case 6:
|
|
|
|
|
return handleLevelFuzzySearch(ctx, rdb, 6, constants.RedisAllConfigSetKey, inputSlice)
|
|
|
|
|
case 7:
|
|
|
|
|
return handleLevelFuzzySearch(ctx, rdb, 7, constants.RedisAllMeasTagSetKey, inputSlice)
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
logger.Error(ctx, "unsupport length of search input", "input_len", inputSliceLen)
|
|
|
|
|
return []string{}, false, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getKeyBySpecificsLevel(ctx context.Context, rdb *redis.Client, inputLen int, input string) ([]string, bool, error) {
|
|
|
|
|
queryKey := getSpecificKeyByLength(inputLen, input)
|
|
|
|
|
results, err := rdb.SMembers(ctx, queryKey).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return []string{}, false, fmt.Errorf("get all keys failed, error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return results, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func combineQueryResultByInput(inputSliceLen int, inputSlice []string, queryResults []string) []string {
|
|
|
|
|
prefixs := make([]string, 0, len(inputSlice))
|
|
|
|
|
recommandResults := make([]string, 0, len(queryResults))
|
|
|
|
|
switch inputSliceLen {
|
|
|
|
|
case 2:
|
|
|
|
|
prefixs = []string{inputSlice[0]}
|
|
|
|
|
case 3:
|
|
|
|
|
prefixs = inputSlice[0:2]
|
|
|
|
|
case 4:
|
|
|
|
|
prefixs = inputSlice[0:3]
|
|
|
|
|
case 5:
|
|
|
|
|
prefixs = inputSlice[0:4]
|
|
|
|
|
case 6:
|
|
|
|
|
prefixs = inputSlice[0:5]
|
|
|
|
|
case 7:
|
|
|
|
|
prefixs = inputSlice[0:6]
|
|
|
|
|
default:
|
|
|
|
|
return []string{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, queryResult := range queryResults {
|
|
|
|
|
combineStrs := make([]string, 0, len(inputSlice))
|
|
|
|
|
combineStrs = append(combineStrs, prefixs...)
|
|
|
|
|
combineStrs = append(combineStrs, queryResult)
|
|
|
|
|
recommandResult := strings.Join(combineStrs, ".")
|
|
|
|
|
recommandResults = append(recommandResults, recommandResult)
|
|
|
|
|
}
|
|
|
|
|
return recommandResults
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getSpecificKeyByLength(inputLen int, input string) string {
|
|
|
|
|
switch inputLen {
|
|
|
|
|
case 1:
|
|
|
|
|
return constants.RedisAllGridSetKey
|
|
|
|
|
case 2:
|
|
|
|
|
return fmt.Sprintf(constants.RedisSpecGridZoneSetKey, input)
|
|
|
|
|
case 3:
|
|
|
|
|
return fmt.Sprintf(constants.RedisSpecZoneStationSetKey, input)
|
|
|
|
|
case 4:
|
|
|
|
|
return fmt.Sprintf(constants.RedisSpecStationCompNSPATHSetKey, input)
|
|
|
|
|
case 5:
|
|
|
|
|
return fmt.Sprintf(constants.RedisSpecStationCompTagSetKey, input)
|
|
|
|
|
case 6:
|
|
|
|
|
return constants.RedisAllConfigSetKey
|
|
|
|
|
case 7:
|
|
|
|
|
return fmt.Sprintf(constants.RedisSpecCompTagMeasSetKey, input)
|
|
|
|
|
default:
|
|
|
|
|
return constants.RedisAllGridSetKey
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleLevelFuzzySearch define func to process recommendation logic for specific levels(level >= 2)
|
|
|
|
|
func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, level int, keySetKey string, inputSlice []string) ([]string, bool, error) {
|
|
|
|
|
searchInputIndex := level - 1
|
|
|
|
|
searchInput := inputSlice[searchInputIndex]
|
|
|
|
|
|
|
|
|
|
if searchInput == "" {
|
|
|
|
|
var specificalKey string
|
|
|
|
|
specificalKeyIndex := searchInputIndex - 1
|
|
|
|
|
if specificalKeyIndex >= 0 {
|
|
|
|
|
specificalKey = inputSlice[specificalKeyIndex]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allResults, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, level, specificalKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
recommandResults := combineQueryResultByInput(level, inputSlice, allResults)
|
|
|
|
|
return recommandResults, isFuzzy, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyExists, err := rdb.SIsMember(ctx, keySetKey, searchInput).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check key exist failed ", "key", searchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if keyExists {
|
|
|
|
|
return []string{"."}, false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// start redis fuzzy search
|
|
|
|
|
recommends, err := runFuzzySearch(ctx, searchInput, level)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "fuzzy search failed for level", "level", level, "search_input", searchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(recommends) == 0 {
|
|
|
|
|
logger.Error(ctx, "fuzzy search without result", "level", level, "search_input", searchInput, "error", err)
|
|
|
|
|
return []string{}, true, nil
|
|
|
|
|
}
|
|
|
|
|
return combineQueryResultByInput(level, inputSlice, recommends), true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// runFuzzySearch define func to process redis fuzzy search
|
|
|
|
|
func runFuzzySearch(ctx context.Context, searchInput string, inputSliceLen int) ([]string, error) {
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
|
|
|
|
|
for searchInputLen != 0 {
|
|
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
|
|
|
|
Num: math.MaxInt16,
|
|
|
|
|
Fuzzy: true,
|
|
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "query key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
|
|
|
|
return nil, fmt.Errorf("redisearch suggest failed: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(results) == 0 {
|
|
|
|
|
// 如果没有结果,退一步(删除最后一个字节)并继续循环
|
|
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var recommends []string
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return recommends, nil
|
|
|
|
|
}
|
|
|
|
|
return []string{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RedisSearchRecommend1 define func of redis search by input string and return recommend results
|
|
|
|
|
func RedisSearchRecommend1(ctx context.Context, input string) ([]string, bool, error) {
|
|
|
|
|
rdb := diagram.GetRedisClientInstance()
|
|
|
|
|
|
2025-09-24 16:43:11 +08:00
|
|
|
if input == "" {
|
|
|
|
|
// 返回所有 grid 名
|
2025-12-03 16:55:14 +08:00
|
|
|
return getKeyBySpecificsLevel(ctx, rdb, 1, input)
|
2025-09-24 16:43:11 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-20 15:06:23 +08:00
|
|
|
inputSlice := strings.Split(input, ".")
|
|
|
|
|
inputSliceLen := len(inputSlice)
|
|
|
|
|
|
|
|
|
|
switch inputSliceLen {
|
2025-09-25 16:39:45 +08:00
|
|
|
case 1:
|
2025-12-04 17:26:35 +08:00
|
|
|
// grid tagname search
|
2025-12-03 16:55:14 +08:00
|
|
|
gridSearchInput := inputSlice[0]
|
|
|
|
|
gridExists, err := rdb.SIsMember(ctx, constants.RedisAllGridSetKey, gridSearchInput).Result()
|
2025-09-25 16:39:45 +08:00
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check grid key exist failed ", "grid_key", input, "error", err)
|
2025-10-16 17:18:57 +08:00
|
|
|
return []string{}, false, err
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-10-16 17:18:57 +08:00
|
|
|
|
2025-12-03 16:55:14 +08:00
|
|
|
if gridExists {
|
|
|
|
|
return []string{"."}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
// start grid tagname fuzzy search
|
2025-12-03 16:55:14 +08:00
|
|
|
searchInput := gridSearchInput
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
for searchInputLen != 0 && !gridExists {
|
2025-10-16 17:18:57 +08:00
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
2025-10-15 17:08:32 +08:00
|
|
|
Num: math.MaxInt16,
|
2025-09-25 16:39:45 +08:00
|
|
|
Fuzzy: true,
|
2025-10-15 17:08:32 +08:00
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
2025-09-25 16:39:45 +08:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
2025-12-03 16:55:14 +08:00
|
|
|
logger.Error(ctx, "query grid key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
2025-10-16 17:18:57 +08:00
|
|
|
return []string{}, false, err
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
2025-10-15 17:08:32 +08:00
|
|
|
if len(results) == 0 {
|
2025-12-03 16:55:14 +08:00
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
2025-10-20 15:06:23 +08:00
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
2025-12-03 16:55:14 +08:00
|
|
|
searchInputLen = len(searchInput)
|
2025-10-16 17:18:57 +08:00
|
|
|
continue
|
2025-10-15 17:08:32 +08:00
|
|
|
}
|
|
|
|
|
|
2025-10-20 15:06:23 +08:00
|
|
|
var recommends []string
|
2025-09-25 16:39:45 +08:00
|
|
|
for _, result := range results {
|
2025-10-20 15:06:23 +08:00
|
|
|
termSlice := strings.Split(result.Term, ".")
|
2025-12-03 16:55:14 +08:00
|
|
|
if len(termSlice) <= inputSliceLen {
|
2025-10-20 15:06:23 +08:00
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-12-03 16:55:14 +08:00
|
|
|
// return fuzzy search results
|
2025-10-20 15:06:23 +08:00
|
|
|
return recommends, true, nil
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-12-03 16:55:14 +08:00
|
|
|
case 2:
|
2025-12-04 17:26:35 +08:00
|
|
|
// zone tagname search
|
2025-12-03 16:55:14 +08:00
|
|
|
zoneSearchInput := inputSlice[1]
|
|
|
|
|
if zoneSearchInput == "" {
|
|
|
|
|
specificalGrid := inputSlice[0]
|
|
|
|
|
allZones, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, inputSliceLen, specificalGrid)
|
|
|
|
|
recommandResults := combineQueryResultByInput(inputSliceLen, inputSlice, allZones)
|
|
|
|
|
return recommandResults, isFuzzy, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
zoneExists, err := rdb.SIsMember(ctx, constants.RedisAllZoneSetKey, zoneSearchInput).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check zone key exist failed ", "zone_key", zoneSearchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if zoneExists {
|
|
|
|
|
return []string{"."}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
// start zone tagname fuzzy search
|
2025-12-03 16:55:14 +08:00
|
|
|
searchInput := zoneSearchInput
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
for searchInputLen != 0 && !zoneExists {
|
|
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
|
|
|
|
Num: math.MaxInt16,
|
|
|
|
|
Fuzzy: true,
|
|
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "query zone key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(results) == 0 {
|
|
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var recommends []string
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return fuzzy search results
|
|
|
|
|
return combineQueryResultByInput(inputSliceLen, inputSlice, recommends), true, nil
|
|
|
|
|
}
|
|
|
|
|
case 3:
|
2025-12-04 17:26:35 +08:00
|
|
|
// station tagname search
|
2025-12-03 16:55:14 +08:00
|
|
|
stationSearchInput := inputSlice[2]
|
|
|
|
|
if stationSearchInput == "" {
|
|
|
|
|
specificalZone := inputSlice[1]
|
|
|
|
|
allStations, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, inputSliceLen, specificalZone)
|
|
|
|
|
recommandResults := combineQueryResultByInput(inputSliceLen, inputSlice, allStations)
|
|
|
|
|
return recommandResults, isFuzzy, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stationExists, err := rdb.SIsMember(ctx, constants.RedisAllStationSetKey, stationSearchInput).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check station key exist failed ", "station_key", stationSearchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if stationExists {
|
|
|
|
|
return []string{"."}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
// start station tagname fuzzy search
|
2025-12-03 16:55:14 +08:00
|
|
|
searchInput := stationSearchInput
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
for searchInputLen != 0 && !stationExists {
|
|
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
|
|
|
|
Num: math.MaxInt16,
|
|
|
|
|
Fuzzy: true,
|
|
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "query station key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(results) == 0 {
|
|
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var recommends []string
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return fuzzy search results
|
|
|
|
|
return combineQueryResultByInput(inputSliceLen, inputSlice, recommends), true, nil
|
|
|
|
|
}
|
|
|
|
|
case 4:
|
|
|
|
|
// component nspath search
|
2025-12-04 17:26:35 +08:00
|
|
|
compNSPSearchInput := inputSlice[3]
|
|
|
|
|
if compNSPSearchInput == "" {
|
|
|
|
|
specificalStation := inputSlice[2]
|
|
|
|
|
allCompNSPaths, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, inputSliceLen, specificalStation)
|
|
|
|
|
recommandResults := combineQueryResultByInput(inputSliceLen, inputSlice, allCompNSPaths)
|
2025-12-03 16:55:14 +08:00
|
|
|
return recommandResults, isFuzzy, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
compNSPathExists, err := rdb.SIsMember(ctx, constants.RedisAllCompNSPathSetKey, compNSPSearchInput).Result()
|
2025-12-03 16:55:14 +08:00
|
|
|
if err != nil {
|
2025-12-04 17:26:35 +08:00
|
|
|
logger.Error(ctx, "check component nspath key exist failed ", "component_nspath_key", compNSPSearchInput, "error", err)
|
2025-12-03 16:55:14 +08:00
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
if compNSPathExists {
|
2025-12-03 16:55:14 +08:00
|
|
|
return []string{"."}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// start grid fuzzy search
|
2025-12-04 17:26:35 +08:00
|
|
|
searchInput := compNSPSearchInput
|
2025-12-03 16:55:14 +08:00
|
|
|
searchInputLen := len(searchInput)
|
2025-12-04 17:26:35 +08:00
|
|
|
for searchInputLen != 0 && !compNSPathExists {
|
2025-12-03 16:55:14 +08:00
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
|
|
|
|
Num: math.MaxInt16,
|
|
|
|
|
Fuzzy: true,
|
|
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
2025-12-04 17:26:35 +08:00
|
|
|
logger.Error(ctx, "query component nspath key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
2025-12-03 16:55:14 +08:00
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(results) == 0 {
|
|
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
2025-12-03 16:55:14 +08:00
|
|
|
var recommends []string
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return fuzzy search results
|
|
|
|
|
return combineQueryResultByInput(inputSliceLen, inputSlice, recommends), true, nil
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-12-03 16:55:14 +08:00
|
|
|
case 5:
|
|
|
|
|
// component tag search
|
|
|
|
|
compTagSearchInput := inputSlice[4]
|
2025-12-04 17:26:35 +08:00
|
|
|
if compTagSearchInput == "" {
|
|
|
|
|
// TODO 优化考虑是否使用 station 作为 key 的一部分
|
|
|
|
|
specificalStation := inputSlice[2]
|
|
|
|
|
allCompNSPaths, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, inputSliceLen, specificalStation)
|
|
|
|
|
recommandResults := combineQueryResultByInput(inputSliceLen, inputSlice, allCompNSPaths)
|
|
|
|
|
return recommandResults, isFuzzy, err
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
compTagExists, err := rdb.SIsMember(ctx, constants.RedisAllCompTagSetKey, compTagSearchInput).Result()
|
2025-09-25 16:39:45 +08:00
|
|
|
if err != nil {
|
2025-12-04 17:26:35 +08:00
|
|
|
logger.Error(ctx, "check component tag key exist failed ", "component_tag_key", compTagSearchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if compTagExists {
|
|
|
|
|
return []string{"."}, false, err
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-09-26 16:47:40 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
// start grid fuzzy search
|
|
|
|
|
searchInput := compTagSearchInput
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
for searchInputLen != 0 && !compTagExists {
|
2025-10-16 17:18:57 +08:00
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
2025-10-15 17:08:32 +08:00
|
|
|
Num: math.MaxInt16,
|
2025-09-26 16:47:40 +08:00
|
|
|
Fuzzy: true,
|
2025-10-16 17:18:57 +08:00
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
2025-09-26 16:47:40 +08:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
2025-12-04 17:26:35 +08:00
|
|
|
logger.Error(ctx, "query component tag key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
2025-10-16 17:18:57 +08:00
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
2025-12-04 17:26:35 +08:00
|
|
|
|
2025-10-16 17:18:57 +08:00
|
|
|
if len(results) == 0 {
|
2025-12-04 17:26:35 +08:00
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
2025-10-16 17:18:57 +08:00
|
|
|
continue
|
2025-09-26 16:47:40 +08:00
|
|
|
}
|
|
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
var recommends []string
|
2025-09-26 16:47:40 +08:00
|
|
|
for _, result := range results {
|
2025-12-04 17:26:35 +08:00
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
2025-09-26 16:47:40 +08:00
|
|
|
}
|
2025-12-04 17:26:35 +08:00
|
|
|
// return fuzzy search results
|
|
|
|
|
return combineQueryResultByInput(inputSliceLen, inputSlice, recommends), true, nil
|
|
|
|
|
}
|
|
|
|
|
case 6:
|
|
|
|
|
// configuration search
|
|
|
|
|
// TODO 优化
|
|
|
|
|
configSearchInput := inputSlice[5]
|
|
|
|
|
if configSearchInput == "" {
|
|
|
|
|
allCompNSPaths, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, inputSliceLen, "")
|
|
|
|
|
recommandResults := combineQueryResultByInput(inputSliceLen, inputSlice, allCompNSPaths)
|
|
|
|
|
return recommandResults, isFuzzy, err
|
2025-09-25 16:39:45 +08:00
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
configExists, err := rdb.SIsMember(ctx, constants.RedisAllConfigSetKey, configSearchInput).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check config key exist failed ", "config_key", configSearchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
if configExists {
|
|
|
|
|
return []string{"."}, false, err
|
|
|
|
|
}
|
2025-12-03 16:55:14 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
// start grid fuzzy search
|
|
|
|
|
searchInput := configSearchInput
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
for searchInputLen != 0 && !configExists {
|
|
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
|
|
|
|
Num: math.MaxInt16,
|
|
|
|
|
Fuzzy: true,
|
|
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "query config key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
2025-09-25 16:39:45 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
if len(results) == 0 {
|
|
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-09-27 15:56:46 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
var recommends []string
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return fuzzy search results
|
|
|
|
|
return combineQueryResultByInput(inputSliceLen, inputSlice, recommends), true, nil
|
|
|
|
|
}
|
|
|
|
|
case 7:
|
|
|
|
|
// measurement search
|
|
|
|
|
measSearchInput := inputSlice[6]
|
|
|
|
|
if measSearchInput == "" {
|
|
|
|
|
// use compoent tag for redis unique key prefix
|
|
|
|
|
specificalCompTag := inputSlice[4]
|
|
|
|
|
allMeasTags, isFuzzy, err := getKeyBySpecificsLevel(ctx, rdb, inputSliceLen, specificalCompTag)
|
|
|
|
|
recommandResults := combineQueryResultByInput(inputSliceLen, inputSlice, allMeasTags)
|
|
|
|
|
return recommandResults, isFuzzy, err
|
|
|
|
|
}
|
2025-12-03 16:55:14 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
measTagExists, err := rdb.SIsMember(ctx, constants.RedisAllMeasTagSetKey, measSearchInput).Result()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "check component tag key exist failed ", "component_tag_key", measSearchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
2025-10-16 17:18:57 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
if measTagExists {
|
|
|
|
|
return []string{"."}, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// start measurement tag fuzzy search
|
|
|
|
|
searchInput := measSearchInput
|
|
|
|
|
searchInputLen := len(searchInput)
|
|
|
|
|
for searchInputLen != 0 && !measTagExists {
|
|
|
|
|
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
|
|
|
|
Num: math.MaxInt16,
|
|
|
|
|
Fuzzy: true,
|
|
|
|
|
WithScores: false,
|
|
|
|
|
WithPayloads: false,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "query measurement tag key by redis fuzzy search failed", "query_key", searchInput, "error", err)
|
|
|
|
|
return []string{}, false, err
|
|
|
|
|
}
|
2025-09-27 15:56:46 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
if len(results) == 0 {
|
|
|
|
|
// TODO 考虑使用其他方式代替 for 循环退一字节的查询方式
|
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
|
|
|
|
searchInputLen = len(searchInput)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-09-29 16:37:38 +08:00
|
|
|
|
2025-12-04 17:26:35 +08:00
|
|
|
var recommends []string
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
termSlice := strings.Split(result.Term, ".")
|
|
|
|
|
if len(termSlice) <= inputSliceLen {
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return fuzzy search results
|
|
|
|
|
return combineQueryResultByInput(inputSliceLen, inputSlice, recommends), true, nil
|
2025-09-27 15:56:46 +08:00
|
|
|
}
|
2025-12-04 17:26:35 +08:00
|
|
|
default:
|
|
|
|
|
logger.Error(ctx, "unsupport length of search input", "input_len", inputSliceLen)
|
|
|
|
|
return []string{}, false, nil
|
2025-09-27 15:56:46 +08:00
|
|
|
}
|
2025-12-04 17:26:35 +08:00
|
|
|
return []string{}, false, nil
|
2025-09-27 15:56:46 +08:00
|
|
|
}
|