refactor: simplify redis recommendation concurrency

- replace fan-in channel recommendation searches with errgroup-based execution
- return SearchResult directly from Redis recommendation helper functions
- fallback leading-dot recommendation input to initial token suggestions
- remove unused recommendLenType parameter from fuzzy member set validation
- clean up small model selection and naming issues
This commit is contained in:
douxu 2026-07-08 14:41:13 +08:00
parent f9824e2b24
commit d8668afa46
5 changed files with 157 additions and 202 deletions

View File

@ -2,6 +2,8 @@
package handler package handler
import ( import (
"fmt"
"modelRT/constants" "modelRT/constants"
"modelRT/database" "modelRT/database"
"modelRT/logger" "modelRT/logger"
@ -152,6 +154,8 @@ func validateBatchImportParams(params map[string]any) bool {
func validateTestTaskParams(params map[string]any) bool { func validateTestTaskParams(params map[string]any) bool {
// Test task has optional parameters, all are valid // Test task has optional parameters, all are valid
// sleep_duration defaults to 60 seconds if not provided // sleep_duration defaults to 60 seconds if not provided
// TODO Add more validation logic for test task parameters if needed
fmt.Println(params)
return true return true
} }

View File

@ -90,6 +90,6 @@ func (s *ShortAttrInfo) IsLocal() bool {
} }
// GetAttrValue define return the attribute value // GetAttrValue define return the attribute value
func (l *ShortAttrInfo) GetAttrValue() any { func (s *ShortAttrInfo) GetAttrValue() any {
return l.AttrValue return s.AttrValue
} }

View File

@ -1,3 +1,4 @@
// Package model define model struct of model runtime service
package model package model
import ( import (
@ -7,11 +8,12 @@ import (
// SelectModelByType define select the data structure for parsing based on the input model type // SelectModelByType define select the data structure for parsing based on the input model type
func SelectModelByType(modelType int) BasicModelInterface { func SelectModelByType(modelType int) BasicModelInterface {
if modelType == constants.BusbarType { switch modelType {
case constants.BusbarType:
return &orm.BusbarSection{} return &orm.BusbarSection{}
} else if modelType == constants.AsyncMotorType { case constants.AsyncMotorType:
return &orm.AsyncMotor{} return &orm.AsyncMotor{}
} else if modelType == constants.DemoType { case constants.DemoType:
return &orm.Demo{} return &orm.Demo{}
} }
return nil return nil

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"math" "math"
"strings" "strings"
"sync"
"modelRT/constants" "modelRT/constants"
"modelRT/diagram" "modelRT/diagram"
@ -16,6 +17,7 @@ import (
"github.com/RediSearch/redisearch-go/v2/redisearch" "github.com/RediSearch/redisearch-go/v2/redisearch"
redigo "github.com/gomodule/redigo/redis" redigo "github.com/gomodule/redigo/redis"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"golang.org/x/sync/errgroup"
) )
// SearchResult define struct to store redis query recommend search result // SearchResult define struct to store redis query recommend search result
@ -36,12 +38,44 @@ func InitAutocompleterWithPool(pool *redigo.Pool) {
ac = redisearch.NewAutocompleterFromPool(pool, constants.RedisSearchDictName) ac = redisearch.NewAutocompleterFromPool(pool, constants.RedisSearchDictName)
} }
type recommendSearchJob func() SearchResult
func runRecommendSearches(ctx context.Context, logMsg string, filterCompNSPath bool, jobs ...recommendSearchJob) map[string]SearchResult {
results := make(map[string]SearchResult)
var mu sync.Mutex
var group errgroup.Group
for _, job := range jobs {
job := job
group.Go(func() error {
result := job()
if result.Err != nil {
return fmt.Errorf("%s: %w", result.RecommendType, result.Err)
}
if filterCompNSPath && result.RecommendType == constants.CompNSPathRecommendHierarchyType {
result.QueryDatas = filterLocalNSPathResults(result.QueryDatas)
}
mu.Lock()
results[result.RecommendType.String()] = result
mu.Unlock()
return nil
})
}
if err := group.Wait(); err != nil {
logger.Error(ctx, logMsg, "error", err)
}
return results
}
// levelOneRedisSearch 处理第一段输入的搜索, 例如 grid 或本地 nspath。 // levelOneRedisSearch 处理第一段输入的搜索, 例如 grid 或本地 nspath。
func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, searchInput string, searchRedisKey string, fanInChan chan SearchResult) { func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, searchInput string, searchRedisKey string) (result SearchResult) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
logger.Error(ctx, "searchFunc panicked", "panic", r) logger.Error(ctx, "searchFunc panicked", "panic", r)
fanInChan <- SearchResult{ result = SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
@ -54,64 +88,59 @@ func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy const
if err != nil { if err != nil {
logger.Error(ctx, "redis membership check failed", "key", searchRedisKey, "member", searchInput, "op", "SIsMember", "error", err) logger.Error(ctx, "redis membership check failed", "key", searchRedisKey, "member", searchInput, "op", "SIsMember", "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
// the input key is the complete hierarchical value // the input key is the complete hierarchical value
if exists { if exists {
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: []string{"."}, QueryDatas: []string{"."},
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
// process fuzzy search result // process fuzzy search result
recommends, offset, err := runFuzzySearch(ctx, rdb, searchInput, "", hierarchy, recommendLenType) recommends, offset, err := runFuzzySearch(ctx, rdb, searchInput, "", hierarchy, recommendLenType)
if err != nil { if err != nil {
logger.Error(ctx, fmt.Sprintf("fuzzy search failed for %s hierarchical", util.GetLevelStrByRdsKey(searchRedisKey)), "search_input", searchInput, "error", err) logger.Error(ctx, fmt.Sprintf("fuzzy search failed for %s hierarchical", util.GetLevelStrByRdsKey(searchRedisKey)), "search_input", searchInput, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
if len(recommends) > 0 { if len(recommends) > 0 {
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: recommends, QueryDatas: recommends,
IsFuzzy: true, IsFuzzy: true,
Offset: offset, Offset: offset,
Err: nil, Err: nil,
} }
return
} }
recommends, err = fallbackAllCurrentLevel(ctx, rdb, hierarchy) recommends, err = fallbackAllCurrentLevel(ctx, rdb, hierarchy)
if err != nil { if err != nil {
logger.Error(ctx, "fallback all keys at current level failed", "hierarchy", hierarchy, "search_input", searchInput, "error", err) logger.Error(ctx, "fallback all keys at current level failed", "hierarchy", hierarchy, "search_input", searchInput, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: true, IsFuzzy: true,
IsFallback: true, IsFallback: true,
Err: err, Err: err,
} }
return
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: recommends, QueryDatas: recommends,
IsFuzzy: true, IsFuzzy: true,
@ -124,165 +153,102 @@ func levelOneRedisSearch(ctx context.Context, rdb *redis.Client, hierarchy const
// RedisSearchRecommend define func of redis search by input string and return recommend results // RedisSearchRecommend define func of redis search by input string and return recommend results
func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchResult { func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchResult {
rdb := diagram.GetRedisClientInstance() rdb := diagram.GetRedisClientInstance()
finalizeResults := func(results map[string]SearchResult) map[string]SearchResult { finalizeResults := func(normalizeInput string, results map[string]SearchResult) map[string]SearchResult {
results = suppressFallbackResultsWhenConcreteHitExists(results) results = suppressFallbackResultsWhenConcreteHitExists(results)
return normalizeRecommendResults(input, results) return normalizeRecommendResults(normalizeInput, results)
} }
if input == "" { if input == "" || shouldFallbackToInitialRecommend(input) {
fanInChan := make(chan SearchResult, 2) results := runRecommendSearches(ctx, "return all keys at the special level from redis failed", true,
// return all grid tagname func() SearchResult {
go getAllKeyByGridLevel(ctx, rdb, fanInChan) return getAllKeyByGridLevel(ctx, rdb)
// return all component nspath },
go getAllKeyByNSPathLevel(ctx, rdb, fanInChan) func() SearchResult {
results := make(map[string]SearchResult) return getAllKeyByNSPathLevel(ctx, rdb)
for range 2 { },
result := <-fanInChan )
if result.Err != nil { return finalizeResults("", results)
logger.Error(ctx, "return all keys at the special level from redis failed", "recommend_type", result.RecommendType, "error", result.Err)
continue
}
if result.RecommendType == constants.CompNSPathRecommendHierarchyType {
result.QueryDatas = filterLocalNSPathResults(result.QueryDatas)
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
} }
inputSlice := strings.Split(input, ".") inputSlice := strings.Split(input, ".")
inputSliceLen := len(inputSlice) inputSliceLen := len(inputSlice)
fanInChan := make(chan SearchResult, 4)
switch inputSliceLen { switch inputSliceLen {
case 1: case 1:
searchInput := inputSlice[0] searchInput := inputSlice[0]
// grid tagname search results := runRecommendSearches(ctx, "exec redis fuzzy search by key failed", true,
go levelOneRedisSearch(ctx, rdb, constants.GridRecommendHierarchyType, constants.FullRecommendLength, searchInput, constants.RedisAllGridSetKey, fanInChan) func() SearchResult {
// component nspath search return levelOneRedisSearch(ctx, rdb, constants.GridRecommendHierarchyType, constants.FullRecommendLength, searchInput, constants.RedisAllGridSetKey)
go levelOneRedisSearch(ctx, rdb, constants.CompNSPathRecommendHierarchyType, constants.IsLocalRecommendLength, searchInput, constants.RedisAllCompNSPathSetKey, fanInChan) },
func() SearchResult {
results := make(map[string]SearchResult) return levelOneRedisSearch(ctx, rdb, constants.CompNSPathRecommendHierarchyType, constants.IsLocalRecommendLength, searchInput, constants.RedisAllCompNSPathSetKey)
// TODO 后续根据支持的数据标识语法长度,进行值的变更 },
for range 2 { )
result := <-fanInChan return finalizeResults(input, results)
if result.Err != nil {
logger.Error(ctx, "exec redis fuzzy search by key failed", "recommend_type", result.RecommendType, "error", result.Err)
continue
}
if result.RecommendType == constants.CompNSPathRecommendHierarchyType {
result.QueryDatas = filterLocalNSPathResults(result.QueryDatas)
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
case 2: case 2:
// zone tagname search results := runRecommendSearches(ctx, "query all keys at the special level from redis failed", false,
go handleLevelFuzzySearch(ctx, rdb, constants.ZoneRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllZoneSetKey, inputSlice, fanInChan) func() SearchResult {
// component tagname search return handleLevelFuzzySearch(ctx, rdb, constants.ZoneRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllZoneSetKey, inputSlice)
go handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllCompTagSetKey, inputSlice, fanInChan) },
// measurement tagname search by token4-token7 shorthand func() SearchResult {
go handleNSPathMeasSearch(ctx, rdb, inputSlice, fanInChan) return handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllCompTagSetKey, inputSlice)
results := make(map[string]SearchResult) },
for range 3 { func() SearchResult {
result := <-fanInChan return handleNSPathMeasSearch(ctx, rdb, inputSlice)
if result.Err != nil { },
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err) )
continue return finalizeResults(input, results)
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
case 3: case 3:
// station tanname search results := runRecommendSearches(ctx, "query all keys at the special level from redis failed", false,
go handleLevelFuzzySearch(ctx, rdb, constants.StationRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllStationSetKey, inputSlice, fanInChan) func() SearchResult {
// config search return handleLevelFuzzySearch(ctx, rdb, constants.StationRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllStationSetKey, inputSlice)
go handleLevelFuzzySearch(ctx, rdb, constants.ConfigRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllConfigSetKey, inputSlice, fanInChan) },
func() SearchResult {
results := make(map[string]SearchResult) return handleLevelFuzzySearch(ctx, rdb, constants.ConfigRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllConfigSetKey, inputSlice)
for range 2 { },
result := <-fanInChan )
if result.Err != nil { return finalizeResults(input, results)
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
continue
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
case 4: case 4:
// component nspath search results := runRecommendSearches(ctx, "query all keys at the special level from redis failed", false,
go handleLevelFuzzySearch(ctx, rdb, constants.CompNSPathRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllCompNSPathSetKey, inputSlice, fanInChan) func() SearchResult {
// measurement tagname search return handleLevelFuzzySearch(ctx, rdb, constants.CompNSPathRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllCompNSPathSetKey, inputSlice)
go handleLevelFuzzySearch(ctx, rdb, constants.MeasTagRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllConfigSetKey, inputSlice, fanInChan) },
func() SearchResult {
results := make(map[string]SearchResult) return handleLevelFuzzySearch(ctx, rdb, constants.MeasTagRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllConfigSetKey, inputSlice)
for range 2 { },
result := <-fanInChan )
if result.Err != nil { return finalizeResults(input, results)
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
continue
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
case 5: case 5:
// component tagname search results := runRecommendSearches(ctx, "query all keys at the special level from redis failed", false,
go handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllCompTagSetKey, inputSlice, fanInChan) func() SearchResult {
return handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllCompTagSetKey, inputSlice)
results := make(map[string]SearchResult) },
for range 1 { )
result := <-fanInChan return finalizeResults(input, results)
if result.Err != nil {
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
continue
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
case 6: case 6:
// config search results := runRecommendSearches(ctx, "query all keys at the special level from redis failed", false,
go handleLevelFuzzySearch(ctx, rdb, constants.ConfigRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllConfigSetKey, inputSlice, fanInChan) func() SearchResult {
return handleLevelFuzzySearch(ctx, rdb, constants.ConfigRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllConfigSetKey, inputSlice)
results := make(map[string]SearchResult) },
for range 1 { )
result := <-fanInChan return finalizeResults(input, results)
if result.Err != nil {
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
continue
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
case 7: case 7:
// measurement tagname search results := runRecommendSearches(ctx, "query all keys at the special level from redis failed", false,
go handleLevelFuzzySearch(ctx, rdb, constants.MeasTagRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllMeasTagSetKey, inputSlice, fanInChan) func() SearchResult {
return handleLevelFuzzySearch(ctx, rdb, constants.MeasTagRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllMeasTagSetKey, inputSlice)
results := make(map[string]SearchResult) },
for range 1 { )
result := <-fanInChan return finalizeResults(input, results)
if result.Err != nil {
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
continue
}
results[result.RecommendType.String()] = result
}
return finalizeResults(results)
default: default:
logger.Error(ctx, "unsupport length of search input", "input_len", inputSliceLen) logger.Error(ctx, "unsupport length of search input", "input_len", inputSliceLen)
return nil return nil
} }
} }
func shouldFallbackToInitialRecommend(input string) bool {
return strings.HasPrefix(input, ".")
}
// suppressFallbackResultsWhenConcreteHitExists 有明确命中时丢弃兜底结果, 避免返回过宽推荐。 // suppressFallbackResultsWhenConcreteHitExists 有明确命中时丢弃兜底结果, 避免返回过宽推荐。
func suppressFallbackResultsWhenConcreteHitExists(results map[string]SearchResult) map[string]SearchResult { func suppressFallbackResultsWhenConcreteHitExists(results map[string]SearchResult) map[string]SearchResult {
hasConcreteHit := false hasConcreteHit := false
@ -494,23 +460,22 @@ func getAllSetKeyByHierarchy(hierarchy constants.RecommendHierarchyType) (string
} }
// getAllKeyByGridLevel 返回所有 grid tag, 用于空输入时的初始推荐。 // getAllKeyByGridLevel 返回所有 grid tag, 用于空输入时的初始推荐。
func getAllKeyByGridLevel(ctx context.Context, rdb *redis.Client, fanInChan chan SearchResult) { func getAllKeyByGridLevel(ctx context.Context, rdb *redis.Client) SearchResult {
setKey := constants.RedisAllGridSetKey setKey := constants.RedisAllGridSetKey
hierarchy := constants.GridRecommendHierarchyType hierarchy := constants.GridRecommendHierarchyType
members, err := rdb.SMembers(ctx, setKey).Result() members, err := rdb.SMembers(ctx, setKey).Result()
if err != nil && err != redigo.ErrNil { if err != nil && err != redigo.ErrNil {
logger.Error(ctx, "get all members from redis by special key failed", "key", setKey, "op", "SMembers", "error", err) logger.Error(ctx, "get all members from redis by special key failed", "key", setKey, "op", "SMembers", "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: members, QueryDatas: members,
IsFuzzy: false, IsFuzzy: false,
@ -519,23 +484,22 @@ func getAllKeyByGridLevel(ctx context.Context, rdb *redis.Client, fanInChan chan
} }
// getAllKeyByNSPathLevel 返回所有组件 nspath, 用于空输入时的初始推荐。 // getAllKeyByNSPathLevel 返回所有组件 nspath, 用于空输入时的初始推荐。
func getAllKeyByNSPathLevel(ctx context.Context, rdb *redis.Client, fanInChan chan SearchResult) { func getAllKeyByNSPathLevel(ctx context.Context, rdb *redis.Client) SearchResult {
queryKey := constants.RedisAllCompNSPathSetKey queryKey := constants.RedisAllCompNSPathSetKey
hierarchy := constants.CompNSPathRecommendHierarchyType hierarchy := constants.CompNSPathRecommendHierarchyType
members, err := rdb.SMembers(ctx, queryKey).Result() members, err := rdb.SMembers(ctx, queryKey).Result()
if err != nil && err != redigo.ErrNil { if err != nil && err != redigo.ErrNil {
logger.Error(ctx, "get all members by special key failed", "key", queryKey, "op", "SMembers", "error", err) logger.Error(ctx, "get all members by special key failed", "key", queryKey, "op", "SMembers", "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: members, QueryDatas: members,
IsFuzzy: false, IsFuzzy: false,
@ -611,7 +575,7 @@ func getSpecificKeyByLength(hierarchy constants.RecommendHierarchyType, keyPrefi
} }
// handleNSPathMeasSearch 处理 nspath.measurement 的简写搜索路径。 // handleNSPathMeasSearch 处理 nspath.measurement 的简写搜索路径。
func handleNSPathMeasSearch(ctx context.Context, rdb *redis.Client, inputSlice []string, fanInChan chan SearchResult) { func handleNSPathMeasSearch(ctx context.Context, rdb *redis.Client, inputSlice []string) SearchResult {
hierarchy := constants.MeasTagRecommendHierarchyType hierarchy := constants.MeasTagRecommendHierarchyType
nsPath := inputSlice[0] nsPath := inputSlice[0]
searchInput := inputSlice[1] searchInput := inputSlice[1]
@ -619,22 +583,20 @@ func handleNSPathMeasSearch(ctx context.Context, rdb *redis.Client, inputSlice [
nsPathExists, err := rdb.SIsMember(ctx, constants.RedisAllCompNSPathSetKey, nsPath).Result() nsPathExists, err := rdb.SIsMember(ctx, constants.RedisAllCompNSPathSetKey, nsPath).Result()
if err != nil { if err != nil {
logger.Error(ctx, "check nspath exist from redis set failed", "key", constants.RedisAllCompNSPathSetKey, "nspath", nsPath, "error", err) logger.Error(ctx, "check nspath exist from redis set failed", "key", constants.RedisAllCompNSPathSetKey, "nspath", nsPath, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
if !nsPathExists { if !nsPathExists {
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: []string{}, QueryDatas: []string{},
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
queryKey := fmt.Sprintf(constants.RedisSpecCompNSPathMeasSetKey, nsPath) queryKey := fmt.Sprintf(constants.RedisSpecCompNSPathMeasSetKey, nsPath)
@ -642,57 +604,52 @@ func handleNSPathMeasSearch(ctx context.Context, rdb *redis.Client, inputSlice [
members, err := rdb.SMembers(ctx, queryKey).Result() members, err := rdb.SMembers(ctx, queryKey).Result()
if err != nil && err != redis.Nil { if err != nil && err != redis.Nil {
logger.Error(ctx, "query measurement tags by nspath failed", "key", queryKey, "nspath", nsPath, "error", err) logger.Error(ctx, "query measurement tags by nspath failed", "key", queryKey, "nspath", nsPath, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: combineNSPathMeasResults(nsPath, members), QueryDatas: combineNSPathMeasResults(nsPath, members),
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
measTagExists, err := rdb.SIsMember(ctx, queryKey, searchInput).Result() measTagExists, err := rdb.SIsMember(ctx, queryKey, searchInput).Result()
if err != nil { if err != nil {
logger.Error(ctx, "check measurement tag exist from nspath set failed", "key", queryKey, "meas_tag", searchInput, "error", err) logger.Error(ctx, "check measurement tag exist from nspath set failed", "key", queryKey, "meas_tag", searchInput, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
if measTagExists { if measTagExists {
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: []string{""}, QueryDatas: []string{""},
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
recommends, offset, err := runNSPathMeasFuzzySearch(ctx, nsPath, searchInput) recommends, offset, err := runNSPathMeasFuzzySearch(ctx, nsPath, searchInput)
if err != nil { if err != nil {
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: true, IsFuzzy: true,
Err: err, Err: err,
} }
return
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: recommends, QueryDatas: recommends,
IsFuzzy: true, IsFuzzy: true,
@ -760,7 +717,7 @@ func splitLastToken(term string) (string, string) {
} }
// handleLevelFuzzySearch 处理第二层及以后层级的精确、模糊和兜底推荐。 // handleLevelFuzzySearch 处理第二层及以后层级的精确、模糊和兜底推荐。
func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, redisSetKey string, inputSlice []string, fanInChan chan SearchResult) { func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, redisSetKey string, inputSlice []string) SearchResult {
inputSliceLen := len(inputSlice) inputSliceLen := len(inputSlice)
searchInputIndex := inputSliceLen - 1 searchInputIndex := inputSliceLen - 1
searchInput := inputSlice[searchInputIndex] searchInput := inputSlice[searchInputIndex]
@ -783,36 +740,33 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co
keyExists, err := rdb.SIsMember(ctx, redisSetKey, specificalKey).Result() keyExists, err := rdb.SIsMember(ctx, redisSetKey, specificalKey).Result()
if err != nil { if err != nil {
logger.Error(ctx, "check key exist from redis set failed ", "key", redisSetKey, "error", err) logger.Error(ctx, "check key exist from redis set failed ", "key", redisSetKey, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
if !keyExists { if !keyExists {
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: []string{}, QueryDatas: []string{},
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
} }
members, err := queryMemberFromSpecificsLevel(ctx, rdb, hierarchy, specificalKey) members, err := queryMemberFromSpecificsLevel(ctx, rdb, hierarchy, specificalKey)
if err != nil && err != redis.Nil { if err != nil && err != redis.Nil {
logger.Error(ctx, "query members from redis by special key failed", "key", specificalKey, "member", searchInput, "op", "SMember", "error", err) logger.Error(ctx, "query members from redis by special key failed", "key", specificalKey, "member", searchInput, "op", "SMember", "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
recommandResults := combineQueryResultByInput(hierarchy, recommendLenType, inputSlice, members) recommandResults := combineQueryResultByInput(hierarchy, recommendLenType, inputSlice, members)
@ -838,25 +792,23 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co
} }
recommandResults = secondConfirmResults recommandResults = secondConfirmResults
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: recommandResults, QueryDatas: recommandResults,
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
keyExists, err := rdb.SIsMember(ctx, redisSetKey, searchInput).Result() keyExists, err := rdb.SIsMember(ctx, redisSetKey, searchInput).Result()
if err != nil { if err != nil {
logger.Error(ctx, "check key exist from redis set failed ", "key", redisSetKey, "error", err) logger.Error(ctx, "check key exist from redis set failed ", "key", redisSetKey, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
if keyExists { if keyExists {
@ -867,26 +819,24 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co
QueryData = []string{"."} QueryData = []string{"."}
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: QueryData, QueryDatas: QueryData,
IsFuzzy: false, IsFuzzy: false,
Err: nil, Err: nil,
} }
return
} }
// start redis fuzzy search // start redis fuzzy search
recommends, offset, err := runFuzzySearch(ctx, rdb, searchInput, searchPrefix, hierarchy, recommendLenType) recommends, offset, err := runFuzzySearch(ctx, rdb, searchInput, searchPrefix, hierarchy, recommendLenType)
if err != nil { if err != nil {
logger.Error(ctx, "fuzzy search failed by hierarchy", "hierarchy", hierarchy, "search_input", searchInput, "error", err) logger.Error(ctx, "fuzzy search failed by hierarchy", "hierarchy", hierarchy, "search_input", searchInput, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: false, IsFuzzy: false,
Err: err, Err: err,
} }
return
} }
isFallback := false isFallback := false
@ -895,20 +845,19 @@ func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy co
recommends, err = fallbackCurrentLevelByInput(ctx, rdb, hierarchy, inputSlice) recommends, err = fallbackCurrentLevelByInput(ctx, rdb, hierarchy, inputSlice)
if err != nil { if err != nil {
logger.Error(ctx, "fallback all keys at current level failed", "hierarchy", hierarchy, "search_input", searchInput, "error", err) logger.Error(ctx, "fallback all keys at current level failed", "hierarchy", hierarchy, "search_input", searchInput, "error", err)
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: nil, QueryDatas: nil,
IsFuzzy: true, IsFuzzy: true,
IsFallback: true, IsFallback: true,
Err: err, Err: err,
} }
return
} }
isFallback = true isFallback = true
offset = 0 offset = 0
} }
fanInChan <- SearchResult{ return SearchResult{
RecommendType: hierarchy, RecommendType: hierarchy,
QueryDatas: recommends, QueryDatas: recommends,
IsFuzzy: true, IsFuzzy: true,
@ -992,7 +941,7 @@ func runFuzzySearch(ctx context.Context, rdb *redis.Client, searchInput string,
} }
if termHierarchyLen == compareHierarchyLen && termPrefix == comparePrefix && strings.HasPrefix(termLastPart, searchInput) { if termHierarchyLen == compareHierarchyLen && termPrefix == comparePrefix && strings.HasPrefix(termLastPart, searchInput) {
exists, err := fuzzyRecommendMemberExists(ctx, rdb, hierarchy, recommendLenType, comparePrefix, termLastPart) exists, err := fuzzyRecommendMemberExists(ctx, rdb, hierarchy, comparePrefix, termLastPart)
if err != nil || !exists { if err != nil || !exists {
logger.Info(ctx, "fuzzy recommend term not found in specific redis set", logger.Info(ctx, "fuzzy recommend term not found in specific redis set",
"hierarchy", hierarchy, "hierarchy", hierarchy,
@ -1034,8 +983,8 @@ func fuzzyRecommendOffset(searchPrefix string, searchInput string) int {
} }
// fuzzyRecommendMemberExists 校验模糊候选末级 token 是否存在于对应 Redis set。 // fuzzyRecommendMemberExists 校验模糊候选末级 token 是否存在于对应 Redis set。
func fuzzyRecommendMemberExists(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, comparePrefix string, termLastPart string) (bool, error) { func fuzzyRecommendMemberExists(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, comparePrefix string, termLastPart string) (bool, error) {
setKey, ok := fuzzyRecommendMemberSetKey(hierarchy, recommendLenType, comparePrefix) setKey, ok := fuzzyRecommendMemberSetKey(hierarchy, comparePrefix)
if !ok { if !ok {
return true, nil return true, nil
} }
@ -1043,7 +992,7 @@ func fuzzyRecommendMemberExists(ctx context.Context, rdb *redis.Client, hierarch
} }
// fuzzyRecommendMemberSetKey 返回模糊候选校验时应使用的 Redis set key。 // fuzzyRecommendMemberSetKey 返回模糊候选校验时应使用的 Redis set key。
func fuzzyRecommendMemberSetKey(hierarchy constants.RecommendHierarchyType, recommendLenType string, comparePrefix string) (string, bool) { func fuzzyRecommendMemberSetKey(hierarchy constants.RecommendHierarchyType, comparePrefix string) (string, bool) {
switch hierarchy { switch hierarchy {
case constants.GridRecommendHierarchyType: case constants.GridRecommendHierarchyType:
return constants.RedisAllGridSetKey, true return constants.RedisAllGridSetKey, true

View File

@ -6,8 +6,8 @@ import (
"time" "time"
) )
// GenNanoTsStr define func to generate nanosecond timestamp string by current time // GenNanoTSStr define func to generate nanosecond timestamp string by current time
func GenNanoTsStr() string { func GenNanoTSStr() string {
now := time.Now() now := time.Now()
nanoseconds := now.UnixNano() nanoseconds := now.UnixNano()
timestampStr := strconv.FormatInt(nanoseconds, 10) timestampStr := strconv.FormatInt(nanoseconds, 10)