optimize measurement recommend api
This commit is contained in:
parent
62e897190d
commit
f0a66263a3
|
|
@ -21,4 +21,6 @@
|
|||
# Go workspace file
|
||||
go.work
|
||||
|
||||
.vscode
|
||||
.vscode
|
||||
# Shield all log files in the log folder
|
||||
/log/
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ kafka:
|
|||
logger:
|
||||
mode: "development"
|
||||
level: "debug"
|
||||
filepath: "/home/douxu/log/modelRT-%s.log"
|
||||
filepath: "/Users/douxu/Workspace/coslight/modelRT/modelRT-%s.log"
|
||||
maxsize: 1
|
||||
maxbackups: 5
|
||||
maxage: 30
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"modelRT/logger"
|
||||
|
|
@ -24,34 +25,69 @@ func MeasurementRecommendHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
recommends, err := model.RedisSearchRecommend(c, request.Input)
|
||||
recommends, isFuzzy, err := model.RedisSearchRecommend(c, request.Input)
|
||||
// TODO delete debug info
|
||||
fmt.Printf("recommends in handler:%+v\n", recommends)
|
||||
if err != nil {
|
||||
logger.Error(c, "failed to get recommend data from redis", "input", request.Input, "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
Msg: err.Error(),
|
||||
PayLoad: map[string]interface{}{
|
||||
PayLoad: map[string]any{
|
||||
"input": request.Input,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var minOffset int
|
||||
for index, recommend := range recommends {
|
||||
offset := model.GetLongestCommonPrefixLength(request.Input, recommend)
|
||||
if index == 0 || offset < minOffset {
|
||||
minOffset = offset
|
||||
fmt.Printf("isFuzzy:%v\n", isFuzzy)
|
||||
|
||||
var finalOffset int
|
||||
if isFuzzy {
|
||||
var maxOffset int
|
||||
for index, recommend := range recommends {
|
||||
offset := model.GetLongestCommonPrefixLength(request.Input, recommend)
|
||||
if index == 0 || offset > maxOffset {
|
||||
maxOffset = offset
|
||||
}
|
||||
}
|
||||
finalOffset = maxOffset
|
||||
} else {
|
||||
var minOffset int
|
||||
for index, recommend := range recommends {
|
||||
offset := model.GetLongestCommonPrefixLength(request.Input, recommend)
|
||||
if index == 0 || offset < minOffset {
|
||||
minOffset = offset
|
||||
}
|
||||
}
|
||||
finalOffset = minOffset
|
||||
}
|
||||
|
||||
fmt.Printf("finalOffset:%v\n", finalOffset)
|
||||
|
||||
resultRecommends := make([]string, 0, len(recommends))
|
||||
seen := make(map[string]struct{}) // 使用空结构体节省内存
|
||||
|
||||
for _, recommend := range recommends {
|
||||
recommendation := recommend[finalOffset:]
|
||||
fmt.Printf("resultRecommend:%s\n", recommendation)
|
||||
fmt.Printf("len of resultRecommend:%d\n", len(recommendation))
|
||||
|
||||
if len(recommendation) != 0 {
|
||||
if _, exists := seen[recommendation]; !exists {
|
||||
seen[recommendation] = struct{}{}
|
||||
resultRecommends = append(resultRecommends, recommendation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
PayLoad: map[string]interface{}{
|
||||
PayLoad: map[string]any{
|
||||
"input": request.Input,
|
||||
"offset": minOffset,
|
||||
"recommended_list": recommends,
|
||||
"offset": finalOffset,
|
||||
"recommended_list": resultRecommends,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func InitAutocompleterWithPool(pool *redigo.Pool) {
|
|||
}
|
||||
|
||||
// RedisSearchRecommend define func of redis search by input string and return recommend results
|
||||
func RedisSearchRecommend(ctx context.Context, input string) ([]string, error) {
|
||||
func RedisSearchRecommend(ctx context.Context, input string) ([]string, bool, error) {
|
||||
rdb := diagram.GetRedisClientInstance()
|
||||
|
||||
if input == "" {
|
||||
|
|
@ -33,19 +33,23 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, error) {
|
|||
|
||||
inputs := strings.Split(input, ".")
|
||||
inputLen := len(inputs)
|
||||
|
||||
fmt.Printf("inputLen:%d\n", inputLen)
|
||||
switch inputLen {
|
||||
case 1:
|
||||
// TODO 优化成NewSet的形式
|
||||
gridExist, err := rdb.SIsMember(ctx, constants.RedisAllGridSetKey, input).Result()
|
||||
if err != nil {
|
||||
logger.Error(ctx, "check grid key exist failed ", "grid_key", input, "error", err)
|
||||
return []string{}, err
|
||||
return []string{}, false, err
|
||||
}
|
||||
|
||||
// TODO delete debug info
|
||||
fmt.Println("gridExist", gridExist)
|
||||
if !gridExist {
|
||||
results, err := ac.SuggestOpts(input, redisearch.SuggestOptions{
|
||||
// TODO 测试如何返回全部的可能模糊结果
|
||||
|
||||
searchInput := input
|
||||
inputLen := len(searchInput)
|
||||
for inputLen != 0 && !gridExist {
|
||||
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
||||
Num: math.MaxInt16,
|
||||
Fuzzy: true,
|
||||
WithScores: false,
|
||||
|
|
@ -56,12 +60,16 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, error) {
|
|||
fmt.Println("err", err)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "query info by fuzzy failed", "query_key", input, "error", err)
|
||||
return []string{}, err
|
||||
return []string{}, false, err
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
// TODO 构建 for 循环返回所有可能的补全
|
||||
// continue
|
||||
searchInput = input[:inputLen-1]
|
||||
inputLen = len(searchInput)
|
||||
fmt.Printf("next search input:%s\n", searchInput)
|
||||
fmt.Printf("next search input len:%d\n", inputLen)
|
||||
continue
|
||||
}
|
||||
|
||||
var grids []string
|
||||
|
|
@ -69,12 +77,12 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, error) {
|
|||
grids = append(grids, result.Term)
|
||||
}
|
||||
// 返回模糊查询结果
|
||||
return grids, nil
|
||||
return grids, true, nil
|
||||
}
|
||||
|
||||
// 处理 input 不为空、不含有.并且 input 是一个完整的 grid key 的情况
|
||||
if strings.HasSuffix(input, ".") == false {
|
||||
return []string{"."}, nil
|
||||
return []string{"."}, false, nil
|
||||
}
|
||||
|
||||
// 处理 input 不为空并且以.结尾的情况
|
||||
|
|
@ -83,73 +91,85 @@ func RedisSearchRecommend(ctx context.Context, input string) ([]string, error) {
|
|||
return getSpecificZoneKeys(ctx, setKey)
|
||||
}
|
||||
default:
|
||||
lastToken := inputs[inputLen-1]
|
||||
lastInput := inputs[inputLen-1]
|
||||
// 判断 queryKey 是否是空值,空值则返回上一级别下的所有key
|
||||
if lastToken == "" {
|
||||
setKey := getConstantsKeyByLength(inputLen - 1)
|
||||
if lastInput == "" {
|
||||
fmt.Println("last token is empty")
|
||||
// TODO 根据所在层级拼接相关setKey
|
||||
setKey := getCombinedConstantsKeyByLength(inputs[inputLen-2], inputLen)
|
||||
fmt.Printf("default case set key:%s\n", setKey)
|
||||
targetSet := diagram.NewRedisSet(ctx, setKey, 10, true)
|
||||
|
||||
results, err := targetSet.SMembers(setKey)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "get all recommend key by setKey failed", "set_key", setKey, "error", err)
|
||||
return []string{}, fmt.Errorf("get all recommend key by setKey failed,%w", err)
|
||||
return []string{}, false, fmt.Errorf("get all recommend key by setKey failed,%w", err)
|
||||
}
|
||||
return results, nil
|
||||
return results, false, nil
|
||||
}
|
||||
|
||||
querykey := lastToken
|
||||
setKey := getConstantsKeyByLength(inputLen)
|
||||
setKey := getCombinedConstantsKeyByLength(inputs[inputLen-2], inputLen)
|
||||
fmt.Printf("default case set key:%s\n", setKey)
|
||||
targetSet := diagram.NewRedisSet(ctx, setKey, 10, true)
|
||||
exist, err := targetSet.SIsMember(setKey, querykey)
|
||||
exist, err := targetSet.SIsMember(setKey, lastInput)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "check keys exist failed", "set_key", setKey, "query_key", querykey, "error", err)
|
||||
return []string{}, fmt.Errorf("check keys failed,%w", err)
|
||||
logger.Error(ctx, "check keys exist failed", "set_key", setKey, "query_key", lastInput, "error", err)
|
||||
return []string{}, false, fmt.Errorf("check keys failed,%w", err)
|
||||
}
|
||||
|
||||
if !exist {
|
||||
searchInput := input
|
||||
inputLen := len(searchInput)
|
||||
for inputLen != 0 && !exist {
|
||||
logger.Info(ctx, "use fuzzy query", "input", input)
|
||||
results, err := ac.SuggestOpts(input, redisearch.SuggestOptions{
|
||||
// TODO 测试如何返回全部的可能模糊结果
|
||||
results, err := ac.SuggestOpts(searchInput, redisearch.SuggestOptions{
|
||||
Num: math.MaxInt16,
|
||||
Fuzzy: true,
|
||||
WithScores: true,
|
||||
WithPayloads: true,
|
||||
WithScores: false,
|
||||
WithPayloads: false,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error(ctx, "query info by fuzzy failed", "query_key", input, "error", err)
|
||||
return []string{}, err
|
||||
return []string{}, false, err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
// TODO 构建 for 循环返回所有可能的补全
|
||||
searchInput = input[:inputLen-1]
|
||||
inputLen = len(searchInput)
|
||||
fmt.Printf("next search input:%s\n", searchInput)
|
||||
fmt.Printf("next search input len:%d\n", inputLen)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("default fuzzy search results:%v\n", results)
|
||||
var terms []string
|
||||
for _, result := range results {
|
||||
terms = append(terms, result.Term)
|
||||
}
|
||||
// 返回模糊查询结果
|
||||
return terms, nil
|
||||
return terms, true, nil
|
||||
}
|
||||
return []string{}, nil
|
||||
return []string{input}, false, nil
|
||||
}
|
||||
return []string{}, nil
|
||||
return []string{}, false, nil
|
||||
}
|
||||
|
||||
func getAllGridKeys(ctx context.Context, setKey string) ([]string, error) {
|
||||
func getAllGridKeys(ctx context.Context, setKey string) ([]string, bool, error) {
|
||||
// 从redis set 中获取所有的 grid key
|
||||
gridSets := diagram.NewRedisSet(ctx, setKey, 10, true)
|
||||
keys, err := gridSets.SMembers("grid_keys")
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("get all root keys failed, error: %v", err)
|
||||
return []string{}, false, fmt.Errorf("get all root keys failed, error: %v", err)
|
||||
}
|
||||
return keys, nil
|
||||
return keys, false, nil
|
||||
}
|
||||
|
||||
func getSpecificZoneKeys(ctx context.Context, setKey string) ([]string, error) {
|
||||
func getSpecificZoneKeys(ctx context.Context, setKey string) ([]string, bool, error) {
|
||||
// TODO 从redis set 中获取指定 grid 下的 zone key
|
||||
zoneSets := diagram.NewRedisSet(ctx, setKey, 10, true)
|
||||
keys, err := zoneSets.SMembers("grid_keys")
|
||||
keys, err := zoneSets.SMembers(setKey)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("get all root keys failed, error: %v", err)
|
||||
return []string{}, false, fmt.Errorf("get all root keys failed, error: %v", err)
|
||||
}
|
||||
return keys, nil
|
||||
return keys, false, nil
|
||||
}
|
||||
|
||||
func getConstantsKeyByLength(inputLen int) string {
|
||||
|
|
@ -167,6 +187,19 @@ func getConstantsKeyByLength(inputLen int) string {
|
|||
}
|
||||
}
|
||||
|
||||
func getCombinedConstantsKeyByLength(key string, inputLen int) string {
|
||||
switch inputLen {
|
||||
case 2:
|
||||
return fmt.Sprintf(constants.RedisSpecGridZoneSetKey, key)
|
||||
case 3:
|
||||
return fmt.Sprintf(constants.RedisSpecZoneStationSetKey, key)
|
||||
case 4:
|
||||
return fmt.Sprintf(constants.RedisSpecStationComponentSetKey, key)
|
||||
default:
|
||||
return constants.RedisAllGridSetKey
|
||||
}
|
||||
}
|
||||
|
||||
// GetLongestCommonPrefixLength define func of get longest common prefix length between two strings
|
||||
func GetLongestCommonPrefixLength(input string, recommendResult string) int {
|
||||
if input == "" {
|
||||
|
|
|
|||
Loading…
Reference in New Issue