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
This commit is contained in:
parent
8faba682b3
commit
367de31247
|
|
@ -524,10 +524,6 @@ const docTemplate = `{
|
|||
" \"I_B_rms\"",
|
||||
"\"I_C_rms\"]"
|
||||
]
|
||||
},
|
||||
"recommended_type": {
|
||||
"type": "string",
|
||||
"example": "grid_tag"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -518,10 +518,6 @@
|
|||
" \"I_B_rms\"",
|
||||
"\"I_C_rms\"]"
|
||||
]
|
||||
},
|
||||
"recommended_type": {
|
||||
"type": "string",
|
||||
"example": "grid_tag"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -86,9 +86,6 @@ definitions:
|
|||
items:
|
||||
type: string
|
||||
type: array
|
||||
recommended_type:
|
||||
example: grid_tag
|
||||
type: string
|
||||
type: object
|
||||
network.RealTimeDataPayload:
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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\"]"`
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue