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-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-09-24 16:43:11 +08:00
|
|
|
|
if input == "" {
|
|
|
|
|
|
// 返回所有 grid 名
|
|
|
|
|
|
return getAllGridKeys(ctx, constants.RedisAllGridSetKey)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-20 15:06:23 +08:00
|
|
|
|
inputSlice := strings.Split(input, ".")
|
|
|
|
|
|
inputSliceLen := len(inputSlice)
|
|
|
|
|
|
originInputLen := len(inputSlice)
|
|
|
|
|
|
|
|
|
|
|
|
switch inputSliceLen {
|
2025-09-25 16:39:45 +08:00
|
|
|
|
case 1:
|
2025-10-16 17:18:57 +08:00
|
|
|
|
// TODO 优化成NewSet的形式
|
2025-09-25 16:39:45 +08:00
|
|
|
|
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)
|
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
|
|
|
|
|
|
|
|
|
|
searchInput := input
|
2025-10-20 15:06:23 +08:00
|
|
|
|
inputLen := inputSliceLen
|
2025-10-16 17:18:57 +08:00
|
|
|
|
for inputLen != 0 && !gridExist {
|
|
|
|
|
|
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-09-26 16:47:40 +08:00
|
|
|
|
logger.Error(ctx, "query info by fuzzy failed", "query_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-09-24 16:43:11 +08:00
|
|
|
|
|
2025-10-15 17:08:32 +08:00
|
|
|
|
if len(results) == 0 {
|
|
|
|
|
|
// TODO 构建 for 循环返回所有可能的补全
|
2025-10-20 15:06:23 +08:00
|
|
|
|
searchInput = searchInput[:len(searchInput)-1]
|
2025-10-16 17:18:57 +08:00
|
|
|
|
inputLen = len(searchInput)
|
|
|
|
|
|
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, ".")
|
|
|
|
|
|
if len(termSlice) <= originInputLen {
|
|
|
|
|
|
recommends = append(recommends, result.Term)
|
|
|
|
|
|
}
|
2025-09-25 16:39:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 返回模糊查询结果
|
2025-10-20 15:06:23 +08:00
|
|
|
|
return recommends, true, nil
|
2025-09-25 16:39:45 +08:00
|
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
|
2025-09-25 16:39:45 +08:00
|
|
|
|
// 处理 input 不为空、不含有.并且 input 是一个完整的 grid key 的情况
|
|
|
|
|
|
if strings.HasSuffix(input, ".") == false {
|
2025-10-20 15:06:23 +08:00
|
|
|
|
recommend := input + "."
|
|
|
|
|
|
return []string{recommend}, false, nil
|
2025-09-25 16:39:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
default:
|
2025-10-20 15:06:23 +08:00
|
|
|
|
lastInput := inputSlice[inputSliceLen-1]
|
2025-09-26 16:47:40 +08:00
|
|
|
|
// 判断 queryKey 是否是空值,空值则返回上一级别下的所有key
|
2025-10-16 17:18:57 +08:00
|
|
|
|
if lastInput == "" {
|
2025-10-20 15:06:23 +08:00
|
|
|
|
setKey := getCombinedConstantsKeyByLength(inputSlice[inputSliceLen-2], inputSliceLen)
|
2025-09-25 16:39:45 +08:00
|
|
|
|
targetSet := diagram.NewRedisSet(ctx, setKey, 10, true)
|
2025-10-20 15:06:23 +08:00
|
|
|
|
keys, err := targetSet.SMembers(setKey)
|
2025-09-26 16:47:40 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error(ctx, "get all recommend key by setKey failed", "set_key", setKey, "error", err)
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return []string{}, false, fmt.Errorf("get all recommend key by setKey failed,%w", err)
|
2025-09-26 16:47:40 +08:00
|
|
|
|
}
|
2025-10-20 15:06:23 +08:00
|
|
|
|
|
|
|
|
|
|
var results []string
|
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
|
result := input + key
|
|
|
|
|
|
results = append(results, result)
|
|
|
|
|
|
}
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return results, false, nil
|
2025-09-25 16:39:45 +08:00
|
|
|
|
}
|
2025-09-24 16:43:11 +08:00
|
|
|
|
|
2025-10-20 15:06:23 +08:00
|
|
|
|
setKey := getCombinedConstantsKeyByLength(inputSlice[inputSliceLen-2], inputSliceLen)
|
2025-09-25 16:39:45 +08:00
|
|
|
|
targetSet := diagram.NewRedisSet(ctx, setKey, 10, true)
|
2025-10-16 17:18:57 +08:00
|
|
|
|
exist, err := targetSet.SIsMember(setKey, lastInput)
|
2025-09-25 16:39:45 +08:00
|
|
|
|
if err != nil {
|
2025-10-16 17:18:57 +08:00
|
|
|
|
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)
|
2025-09-25 16:39:45 +08:00
|
|
|
|
}
|
2025-09-26 16:47:40 +08:00
|
|
|
|
|
2025-10-16 17:18:57 +08:00
|
|
|
|
searchInput := input
|
|
|
|
|
|
inputLen := len(searchInput)
|
|
|
|
|
|
for inputLen != 0 && !exist {
|
2025-10-15 17:08:32 +08:00
|
|
|
|
logger.Info(ctx, "use fuzzy query", "input", input)
|
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 {
|
|
|
|
|
|
logger.Error(ctx, "query info by fuzzy failed", "query_key", input, "error", err)
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return []string{}, false, err
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(results) == 0 {
|
|
|
|
|
|
// TODO 构建 for 循环返回所有可能的补全
|
|
|
|
|
|
searchInput = input[:inputLen-1]
|
|
|
|
|
|
inputLen = len(searchInput)
|
|
|
|
|
|
continue
|
2025-09-26 16:47:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var terms []string
|
|
|
|
|
|
for _, result := range results {
|
|
|
|
|
|
terms = append(terms, result.Term)
|
|
|
|
|
|
}
|
|
|
|
|
|
// 返回模糊查询结果
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return terms, true, nil
|
2025-09-25 16:39:45 +08:00
|
|
|
|
}
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return []string{input}, false, nil
|
2025-09-24 16:43:11 +08:00
|
|
|
|
}
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return []string{}, false, nil
|
2025-09-24 16:43:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-16 17:18:57 +08:00
|
|
|
|
func getAllGridKeys(ctx context.Context, setKey string) ([]string, bool, error) {
|
2025-09-25 16:39:45 +08:00
|
|
|
|
// 从redis set 中获取所有的 grid key
|
2025-09-24 16:43:11 +08:00
|
|
|
|
gridSets := diagram.NewRedisSet(ctx, setKey, 10, true)
|
|
|
|
|
|
keys, err := gridSets.SMembers("grid_keys")
|
|
|
|
|
|
if err != nil {
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return []string{}, false, fmt.Errorf("get all root keys failed, error: %v", err)
|
2025-09-24 16:43:11 +08:00
|
|
|
|
}
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return keys, false, nil
|
2025-09-24 16:43:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-20 15:06:23 +08:00
|
|
|
|
func getSpecificZoneKeys(ctx context.Context, input string) ([]string, bool, error) {
|
|
|
|
|
|
setKey := fmt.Sprintf(constants.RedisSpecGridZoneSetKey, input)
|
2025-09-25 16:39:45 +08:00
|
|
|
|
// TODO 从redis set 中获取指定 grid 下的 zone key
|
2025-09-24 16:43:11 +08:00
|
|
|
|
zoneSets := diagram.NewRedisSet(ctx, setKey, 10, true)
|
2025-10-16 17:18:57 +08:00
|
|
|
|
keys, err := zoneSets.SMembers(setKey)
|
2025-09-24 16:43:11 +08:00
|
|
|
|
if err != nil {
|
2025-10-16 17:18:57 +08:00
|
|
|
|
return []string{}, false, fmt.Errorf("get all root keys failed, error: %v", err)
|
2025-09-24 16:43:11 +08:00
|
|
|
|
}
|
2025-10-20 15:06:23 +08:00
|
|
|
|
var results []string
|
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
|
result := input + "." + key
|
|
|
|
|
|
results = append(results, result)
|
|
|
|
|
|
}
|
|
|
|
|
|
return results, false, nil
|
2025-09-24 16:43:11 +08:00
|
|
|
|
}
|
2025-09-25 16:39:45 +08:00
|
|
|
|
|
|
|
|
|
|
func getConstantsKeyByLength(inputLen int) string {
|
|
|
|
|
|
switch inputLen {
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
return constants.RedisAllGridSetKey
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
return constants.RedisAllZoneSetKey
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
return constants.RedisAllStationSetKey
|
|
|
|
|
|
case 4:
|
|
|
|
|
|
return constants.RedisAllComponentSetKey
|
|
|
|
|
|
default:
|
|
|
|
|
|
return constants.RedisAllGridSetKey
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-27 15:56:46 +08:00
|
|
|
|
|
2025-10-16 17:18:57 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-27 15:56:46 +08:00
|
|
|
|
// GetLongestCommonPrefixLength define func of get longest common prefix length between two strings
|
2025-09-29 16:37:38 +08:00
|
|
|
|
func GetLongestCommonPrefixLength(input string, recommendResult string) int {
|
|
|
|
|
|
if input == "" {
|
|
|
|
|
|
return 0
|
2025-09-27 15:56:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 16:37:38 +08:00
|
|
|
|
minLen := min(len(input), len(recommendResult))
|
|
|
|
|
|
|
|
|
|
|
|
for i := range minLen {
|
|
|
|
|
|
if input[i] != recommendResult[i] {
|
2025-09-27 15:56:46 +08:00
|
|
|
|
return i
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return minLen
|
|
|
|
|
|
}
|