feat: support nspath measurement shorthand recommendations
- add nspath-to-measurement recommend set keys and cleanup pattern - collect nspath measurement tags when component nspath matches bay tag - store token4.token7 autocomplete terms during measurement recommend init - add token4.token7 recommendations alongside existing two-token paths
This commit is contained in:
parent
491c10e8c5
commit
33eb2d9be8
|
|
@ -44,6 +44,9 @@ const (
|
|||
|
||||
// RedisSpecCompTagMeasSetKey define redis set key which store all measurement tag keys under specific component tag
|
||||
RedisSpecCompTagMeasSetKey = "%s_measurement_tag_keys"
|
||||
|
||||
// RedisSpecCompNSPathMeasSetKey define redis set key which store all measurement tag keys under specific component nspath
|
||||
RedisSpecCompNSPathMeasSetKey = "%s_nspath_measurement_tag_keys"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSe
|
|||
StationToCompNSPaths: make(map[string][]string),
|
||||
CompNSPathToCompTags: make(map[string][]string),
|
||||
CompTagToMeasTags: make(map[string][]string),
|
||||
CompNSPathToMeasTags: make(map[string][]string),
|
||||
}
|
||||
|
||||
g, gctx := errgroup.WithContext(ctx)
|
||||
|
|
@ -113,11 +114,14 @@ func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSe
|
|||
g.Go(func() error {
|
||||
var measurements []struct {
|
||||
orm.Measurement
|
||||
CompTag string `gorm:"column:comp_tag"`
|
||||
CompTag string `gorm:"column:comp_tag"`
|
||||
CompNSPath string `gorm:"column:comp_nspath"`
|
||||
BayTag string `gorm:"column:bay_tag"`
|
||||
}
|
||||
if err := db.Table("measurement").
|
||||
Select("measurement.*, component.tag as comp_tag").
|
||||
Select("measurement.*, component.tag as comp_tag, component.nspath as comp_nspath, bay.tag as bay_tag").
|
||||
Joins("left join component on measurement.component_uuid = component.global_uuid").
|
||||
Joins("left join bay on measurement.bay_uuid = bay.bay_uuid").
|
||||
Scan(&measurements).Error; err != nil {
|
||||
return fmt.Errorf("query measurements: %w", err)
|
||||
}
|
||||
|
|
@ -126,6 +130,9 @@ func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSe
|
|||
if m.CompTag != "" {
|
||||
mSet.CompTagToMeasTags[m.CompTag] = append(mSet.CompTagToMeasTags[m.CompTag], m.Tag)
|
||||
}
|
||||
if m.CompNSPath != "" && m.CompNSPath == m.BayTag {
|
||||
mSet.CompNSPathToMeasTags[m.CompNSPath] = append(mSet.CompNSPathToMeasTags[m.CompNSPath], m.Tag)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
|
|||
|
|
@ -187,6 +187,19 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
|||
ac.AddTerms(sug...)
|
||||
}
|
||||
|
||||
// building the component nspath -> measurement tags hierarchy for token4-token7 shorthand
|
||||
for compNSPath, measTags := range measSet.CompNSPathToMeasTags {
|
||||
sug := make([]redisearch.Suggestion, 0, len(measTags))
|
||||
for _, measTag := range measTags {
|
||||
sug = append(sug, redisearch.Suggestion{
|
||||
Term: fmt.Sprintf("%s.%s", compNSPath, measTag),
|
||||
Score: constants.DefaultScore,
|
||||
})
|
||||
}
|
||||
safeSAdd(fmt.Sprintf(constants.RedisSpecCompNSPathMeasSetKey, compNSPath), measTags)
|
||||
ac.AddTerms(sug...)
|
||||
}
|
||||
|
||||
var allErrs []error
|
||||
cmders, execErr := pipe.Exec(ctx)
|
||||
if execErr != nil {
|
||||
|
|
|
|||
|
|
@ -48,11 +48,12 @@ func CleanupRecommendRedisCache(ctx context.Context) error {
|
|||
}
|
||||
|
||||
patterns := []string{
|
||||
"*_zone_tag_keys", // correspond RedisSpecGridZoneSetKey
|
||||
"*_station_tag_keys", // correspond RedisSpecZoneStationSetKey
|
||||
"*_component_nspath_keys", // correspond RedisSpecStationCompNSPATHSetKey
|
||||
"*_component_tag_keys", // correspond RedisSpecCompNSPathCompTagSetKey
|
||||
"*_measurement_tag_keys", // correspond RedisSpecCompTagMeasSetKey
|
||||
"*_zone_tag_keys", // correspond RedisSpecGridZoneSetKey
|
||||
"*_station_tag_keys", // correspond RedisSpecZoneStationSetKey
|
||||
"*_component_nspath_keys", // correspond RedisSpecStationCompNSPATHSetKey
|
||||
"*_component_tag_keys", // correspond RedisSpecCompNSPathCompTagSetKey
|
||||
"*_measurement_tag_keys", // correspond RedisSpecCompTagMeasSetKey
|
||||
"*_nspath_measurement_tag_keys", // correspond RedisSpecCompNSPathMeasSetKey
|
||||
}
|
||||
|
||||
for _, pattern := range patterns {
|
||||
|
|
|
|||
|
|
@ -183,8 +183,10 @@ func RedisSearchRecommend(ctx context.Context, input string) map[string]SearchRe
|
|||
go handleLevelFuzzySearch(ctx, rdb, constants.ZoneRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllZoneSetKey, inputSlice, fanInChan)
|
||||
// component tagname search
|
||||
go handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllCompTagSetKey, inputSlice, fanInChan)
|
||||
// measurement tagname search by token4-token7 shorthand
|
||||
go handleNSPathMeasSearch(ctx, rdb, inputSlice, fanInChan)
|
||||
results := make(map[string]SearchResult)
|
||||
for range 2 {
|
||||
for range 3 {
|
||||
result := <-fanInChan
|
||||
if result.Err != nil {
|
||||
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
|
||||
|
|
@ -518,6 +520,151 @@ func getSpecificKeyByLength(hierarchy constants.RecommendHierarchyType, keyPrefi
|
|||
}
|
||||
}
|
||||
|
||||
func handleNSPathMeasSearch(ctx context.Context, rdb *redis.Client, inputSlice []string, fanInChan chan SearchResult) {
|
||||
hierarchy := constants.MeasTagRecommendHierarchyType
|
||||
nsPath := inputSlice[0]
|
||||
searchInput := inputSlice[1]
|
||||
|
||||
nsPathExists, err := rdb.SIsMember(ctx, constants.RedisAllCompNSPathSetKey, nsPath).Result()
|
||||
if err != nil {
|
||||
logger.Error(ctx, "check nspath exist from redis set failed", "key", constants.RedisAllCompNSPathSetKey, "nspath", nsPath, "error", err)
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: nil,
|
||||
IsFuzzy: false,
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
if !nsPathExists {
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: []string{},
|
||||
IsFuzzy: false,
|
||||
Err: nil,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
queryKey := fmt.Sprintf(constants.RedisSpecCompNSPathMeasSetKey, nsPath)
|
||||
if searchInput == "" {
|
||||
members, err := rdb.SMembers(ctx, queryKey).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
logger.Error(ctx, "query measurement tags by nspath failed", "key", queryKey, "nspath", nsPath, "error", err)
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: nil,
|
||||
IsFuzzy: false,
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: combineNSPathMeasResults(nsPath, members),
|
||||
IsFuzzy: false,
|
||||
Err: nil,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
measTagExists, err := rdb.SIsMember(ctx, queryKey, searchInput).Result()
|
||||
if err != nil {
|
||||
logger.Error(ctx, "check measurement tag exist from nspath set failed", "key", queryKey, "meas_tag", searchInput, "error", err)
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: nil,
|
||||
IsFuzzy: false,
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
if measTagExists {
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: []string{""},
|
||||
IsFuzzy: false,
|
||||
Err: nil,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
recommends, offset, err := runNSPathMeasFuzzySearch(ctx, nsPath, searchInput)
|
||||
if err != nil {
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: nil,
|
||||
IsFuzzy: true,
|
||||
Err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fanInChan <- SearchResult{
|
||||
RecommendType: hierarchy,
|
||||
QueryDatas: recommends,
|
||||
IsFuzzy: true,
|
||||
Offset: offset,
|
||||
Err: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func combineNSPathMeasResults(nsPath string, measTags []string) []string {
|
||||
results := make([]string, 0, len(measTags))
|
||||
for _, measTag := range measTags {
|
||||
results = append(results, fmt.Sprintf("%s.%s", nsPath, measTag))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func runNSPathMeasFuzzySearch(ctx context.Context, nsPath string, searchInput string) ([]string, int, error) {
|
||||
searchInputLen := len([]rune(searchInput))
|
||||
for searchInputLen != 0 {
|
||||
fuzzyInput := fmt.Sprintf("%s.%s", nsPath, searchInput)
|
||||
results, err := ac.SuggestOpts(fuzzyInput, redisearch.SuggestOptions{
|
||||
Num: math.MaxInt16,
|
||||
Fuzzy: true,
|
||||
WithScores: false,
|
||||
WithPayloads: false,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error(ctx, "query measurement tag by nspath fuzzy search failed", "query_key", fuzzyInput, "error", err)
|
||||
return nil, 0, fmt.Errorf("redisearch suggest failed: %w", err)
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
searchInput = trimLastRune(searchInput)
|
||||
searchInputLen = len([]rune(searchInput))
|
||||
continue
|
||||
}
|
||||
|
||||
recommends := make([]string, 0, len(results))
|
||||
for _, result := range results {
|
||||
termPrefix, termLastPart := splitLastToken(result.Term)
|
||||
if termPrefix == nsPath && strings.HasPrefix(termLastPart, searchInput) {
|
||||
recommends = append(recommends, result.Term)
|
||||
}
|
||||
}
|
||||
if len(recommends) == 0 {
|
||||
searchInput = trimLastRune(searchInput)
|
||||
searchInputLen = len([]rune(searchInput))
|
||||
continue
|
||||
}
|
||||
|
||||
return recommends, len([]rune(fmt.Sprintf("%s.%s", nsPath, searchInput))), nil
|
||||
}
|
||||
return []string{}, 0, nil
|
||||
}
|
||||
|
||||
func splitLastToken(term string) (string, string) {
|
||||
lastDotIndex := strings.LastIndex(term, ".")
|
||||
if lastDotIndex == -1 {
|
||||
return "", term
|
||||
}
|
||||
return term[:lastDotIndex], term[lastDotIndex+1:]
|
||||
}
|
||||
|
||||
// handleLevelFuzzySearch define func to process recommendation logic for specific levels(level >= 2)
|
||||
func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, redisSetKey string, inputSlice []string, fanInChan chan SearchResult) {
|
||||
inputSliceLen := len(inputSlice)
|
||||
|
|
|
|||
|
|
@ -16,4 +16,5 @@ type MeasurementSet struct {
|
|||
StationToCompNSPaths map[string][]string // Key: StationTag, Value: NSPaths
|
||||
CompNSPathToCompTags map[string][]string // Key: NSPaths, Value: CompTags
|
||||
CompTagToMeasTags map[string][]string // Key: CompTag, Value: MeasTags
|
||||
CompNSPathToMeasTags map[string][]string // Key: NSPaths, Value: MeasTags
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue