From 367de31247ccb5efa3b7659edac12b93ae7ec15e Mon Sep 17 00:00:00 2001 From: douxu Date: Mon, 29 Jun 2026 14:13:58 +0800 Subject: [PATCH] fix: flatten measurement recommend response and normalize offsets - return a single measurement recommend payload instead of grouped payloads - remove recommended_type from response schema and regenerated swagger docs - normalize Redis recommend results in model layer with stable type ordering - deduplicate trimmed recommend terms and preserve the maximum response offset --- docs/docs.go | 4 -- docs/swagger.json | 4 -- docs/swagger.yaml | 3 - handler/measurement_recommend.go | 81 ++++++++++++++------------- model/redis_recommend.go | 95 ++++++++++++++++++++++++++------ network/response.go | 1 - 6 files changed, 118 insertions(+), 70 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 8aa977c..a3451d5 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -524,10 +524,6 @@ const docTemplate = `{ " \"I_B_rms\"", "\"I_C_rms\"]" ] - }, - "recommended_type": { - "type": "string", - "example": "grid_tag" } } }, diff --git a/docs/swagger.json b/docs/swagger.json index 5c5dc57..14b2253 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -518,10 +518,6 @@ " \"I_B_rms\"", "\"I_C_rms\"]" ] - }, - "recommended_type": { - "type": "string", - "example": "grid_tag" } } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 05519e2..92cbdc8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -86,9 +86,6 @@ definitions: items: type: string type: array - recommended_type: - example: grid_tag - type: string type: object network.RealTimeDataPayload: properties: diff --git a/handler/measurement_recommend.go b/handler/measurement_recommend.go index ef79916..b3d0ab6 100644 --- a/handler/measurement_recommend.go +++ b/handler/measurement_recommend.go @@ -2,10 +2,10 @@ package handler import ( + "modelRT/constants" "modelRT/logger" "modelRT/model" "modelRT/network" - "modelRT/util" "net/http" "github.com/gin-gonic/gin" @@ -55,8 +55,14 @@ func MeasurementRecommendHandler(c *gin.Context) { } recommendResults := model.RedisSearchRecommend(c, request.Input) - payloads := make([]network.MeasurementRecommendPayload, 0, len(recommendResults)) - for _, recommendResult := range recommendResults { + payload := network.MeasurementRecommendPayload{ + Input: request.Input, + RecommendedList: make([]string, 0), + } + seen := make(map[string]struct{}) + orderedResults := orderedRecommendResults(recommendResults) + + for _, recommendResult := range orderedResults { if recommendResult.Err != nil { err := recommendResult.Err logger.Error(c, "failed to get recommend data from redis", "input", request.Input, "error", err) @@ -70,52 +76,45 @@ func MeasurementRecommendHandler(c *gin.Context) { return } - var finalOffset int - recommends := recommendResult.QueryDatas - if recommendResult.IsFuzzy { - var maxOffset int - for index, recommend := range recommends { - offset := util.GetLongestCommonPrefixLength(request.Input, recommend) - if index == 0 || offset > maxOffset { - maxOffset = offset - } - } - finalOffset = maxOffset - } else { - var minOffset int - for index, recommend := range recommends { - offset := util.GetLongestCommonPrefixLength(request.Input, recommend) - if index == 0 || offset < minOffset { - minOffset = offset - } - } - finalOffset = minOffset + if recommendResult.Offset > payload.Offset { + payload.Offset = recommendResult.Offset } + } - resultRecommends := make([]string, 0, len(recommends)) - seen := make(map[string]struct{}) - - for _, recommend := range recommends { - recommendTerm := recommend[finalOffset:] - if len(recommendTerm) != 0 { - if _, exists := seen[recommendTerm]; !exists { - seen[recommendTerm] = struct{}{} - resultRecommends = append(resultRecommends, recommendTerm) - } + for _, recommendResult := range orderedResults { + for _, recommend := range recommendResult.QueryDatas { + if _, exists := seen[recommend]; !exists { + seen[recommend] = struct{}{} + payload.RecommendedList = append(payload.RecommendedList, recommend) } } - - payloads = append(payloads, network.MeasurementRecommendPayload{ - Input: request.Input, - Offset: finalOffset, - RecommendType: recommendResult.RecommendType.String(), - RecommendedList: resultRecommends, - }) } c.JSON(http.StatusOK, network.SuccessResponse{ Code: http.StatusOK, Msg: "success", - Payload: &payloads, + Payload: payload, }) } + +func orderedRecommendResults(recommendResults map[string]model.SearchResult) []model.SearchResult { + orderedTypes := []string{ + constants.CompNSPathRecommendHierarchyType.String(), + constants.GridRecommendHierarchyType.String(), + constants.ZoneRecommendHierarchyType.String(), + constants.StationRecommendHierarchyType.String(), + constants.CompTagRecommendHierarchyType.String(), + constants.ConfigRecommendHierarchyType.String(), + constants.MeasTagRecommendHierarchyType.String(), + } + + results := make([]model.SearchResult, 0, len(recommendResults)) + for _, recommendType := range orderedTypes { + result, ok := recommendResults[recommendType] + if !ok { + continue + } + results = append(results, result) + } + return results +} diff --git a/model/redis_recommend.go b/model/redis_recommend.go index ddfef2a..809851e 100644 --- a/model/redis_recommend.go +++ b/model/redis_recommend.go @@ -24,6 +24,7 @@ type SearchResult struct { RecommendType constants.RecommendHierarchyType QueryDatas []string IsFuzzy bool + Offset int Err error } @@ -72,7 +73,7 @@ func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy const } // process fuzzy search result - recommends, err := runFuzzySearch(ctx, rdb, searchInput, "", hierarchy, recommendLenType) + recommends, offset, err := runFuzzySearch(ctx, rdb, searchInput, "", hierarchy, recommendLenType) if err != nil { logger.Error(ctx, fmt.Sprintf("fuzzy search failed for %s hierarchical", util.GetLevelStrByRdsKey(searchRedisKey)), "search_input", searchInput, "error", err) fanInChan <- SearchResult{ @@ -89,6 +90,7 @@ func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy const RecommendType: hierarchy, QueryDatas: recommends, IsFuzzy: true, + Offset: offset, Err: nil, } return @@ -98,6 +100,7 @@ func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy const RecommendType: hierarchy, QueryDatas: []string{}, IsFuzzy: true, + Offset: offset, Err: nil, } } @@ -105,6 +108,9 @@ func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy const // RedisSearchRecommend define func of redis search by input string and return recommend results func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchResult { rdb := diagram.GetRedisClientInstance() + finalizeResults := func(results map[string]SearchResult) map[string]SearchResult { + return normalizeRecommendResults(input, results) + } if input == "" { fanInChan := make(chan SearchResult, 2) @@ -140,7 +146,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) } inputSlice := strings.Split(input, ".") @@ -189,7 +195,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) case 2: // zone tagname search go handleLevelFuzzySearch(ctx, rdb, constants.ZoneRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllZoneSetKey, inputSlice, fanInChan) @@ -205,7 +211,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) case 3: // station tanname search go handleLevelFuzzySearch(ctx, rdb, constants.StationRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllStationSetKey, inputSlice, fanInChan) @@ -222,7 +228,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) case 4: // component nspath search go handleLevelFuzzySearch(ctx, rdb, constants.CompNSPathRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllCompNSPathSetKey, inputSlice, fanInChan) @@ -239,7 +245,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) case 5: // component tagname search go handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllCompTagSetKey, inputSlice, fanInChan) @@ -254,7 +260,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) case 6: // config search go handleLevelFuzzySearch(ctx, rdb, constants.ConfigRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllConfigSetKey, inputSlice, fanInChan) @@ -269,7 +275,7 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) case 7: // measurement tagname search go handleLevelFuzzySearch(ctx, rdb, constants.MeasTagRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllMeasTagSetKey, inputSlice, fanInChan) @@ -284,13 +290,61 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe results[result.RecommendType.String()] = result } - return results + return finalizeResults(results) default: logger.Error(ctx, "unsupport length of search input", "input_len", inputSliceLen) return nil } } +func normalizeRecommendResults(input string, results map[string]SearchResult) map[string]SearchResult { + for key, result := range results { + if result.Err != nil { + continue + } + if !result.IsFuzzy { + result.Offset = calcSearchResultOffset(input, result.QueryDatas) + } + result.QueryDatas = trimRecommendTerms(result.QueryDatas, result.Offset) + results[key] = result + } + return results +} + +func calcSearchResultOffset(input string, recommends []string) int { + var minOffset int + for index, recommend := range recommends { + offset := util.GetLongestCommonPrefixLength(input, recommend) + if index == 0 || offset < minOffset { + minOffset = offset + } + } + return minOffset +} + +func trimRecommendTerms(recommends []string, offset int) []string { + resultRecommends := make([]string, 0, len(recommends)) + seen := make(map[string]struct{}) + + for _, recommend := range recommends { + if offset > len(recommend) { + continue + } + + recommendTerm := recommend[offset:] + if recommendTerm == "" { + continue + } + if _, exists := seen[recommendTerm]; exists { + continue + } + + seen[recommendTerm] = struct{}{} + resultRecommends = append(resultRecommends, recommendTerm) + } + return resultRecommends +} + func queryMemberFromSpecificsLevel(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, keyPrefix string) ([]string, error) { queryKey := getSpecificKeyByLength(hierarchy, keyPrefix) return rdb.SMembers(ctx, queryKey).Result() @@ -486,7 +540,6 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co } secondConfirmResults = append(secondConfirmResults, res) } - recommandResults = secondConfirmResults } fanInChan <- SearchResult{ @@ -528,7 +581,7 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co } // start redis fuzzy search - recommends, err := runFuzzySearch(ctx, rdb, searchInput, searchPrefix, hierarchy, recommendLenType) + recommends, offset, err := runFuzzySearch(ctx, rdb, searchInput, searchPrefix, hierarchy, recommendLenType) if err != nil { logger.Error(ctx, "fuzzy search failed by hierarchy", "hierarchy", hierarchy, "search_input", searchInput, "error", err) fanInChan <- SearchResult{ @@ -548,12 +601,13 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co RecommendType: hierarchy, QueryDatas: recommends, IsFuzzy: true, + Offset: offset, Err: nil, } } // runFuzzySearch define func to process redis fuzzy search -func runFuzzySearch(ctx context.Context, rdb *redis.Client, searchInput string, searchPrefix string, hierarchy constants.RecommendHierarchyType, recommendLenType string) ([]string, error) { +func runFuzzySearch(ctx context.Context, rdb *redis.Client, searchInput string, searchPrefix string, hierarchy constants.RecommendHierarchyType, recommendLenType string) ([]string, int, error) { var configToken string var comparePrefix string searchInputLen := len(searchInput) @@ -583,7 +637,7 @@ func runFuzzySearch(ctx context.Context, rdb *redis.Client, searchInput string, }) if err != nil { logger.Error(ctx, "query key by redis fuzzy search failed", "query_key", fuzzyInput, "error", err) - return nil, fmt.Errorf("redisearch suggest failed: %w", err) + return nil, 0, fmt.Errorf("redisearch suggest failed: %w", err) } if len(results) == 0 { @@ -618,13 +672,14 @@ func runFuzzySearch(ctx context.Context, rdb *redis.Client, searchInput string, } } - if recommendLenType == constants.FullRecommendLength { + switch recommendLenType { + case constants.FullRecommendLength: if result.Term == "" { termHierarchyLen = 1 } else { termHierarchyLen = strings.Count(result.Term, ".") + 1 } - } else if recommendLenType == constants.IsLocalRecommendLength { + case constants.IsLocalRecommendLength: if result.Term == "" { termHierarchyLen = 4 } else { @@ -641,7 +696,13 @@ func runFuzzySearch(ctx context.Context, rdb *redis.Client, searchInput string, } } - return recommends, nil + if len(recommends) == 0 { + searchInput = searchInput[:len(searchInput)-1] + searchInputLen = len(searchInput) + continue + } + + return recommends, len(searchInput), nil } - return []string{}, nil + return []string{}, 0, nil } diff --git a/network/response.go b/network/response.go index b111df7..10e7589 100644 --- a/network/response.go +++ b/network/response.go @@ -26,7 +26,6 @@ type WSResponse struct { type MeasurementRecommendPayload struct { Input string `json:"input" example:"transformfeeder1_220."` Offset int `json:"offset" example:"21"` - RecommendType string `json:"recommended_type" example:"grid_tag"` RecommendedList []string `json:"recommended_list" example:"[\"I_A_rms\", \"I_B_rms\",\"I_C_rms\"]"` }