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 define redis set key which store all measurement tag keys under specific component tag
|
||||||
RedisSpecCompTagMeasSetKey = "%s_measurement_tag_keys"
|
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 (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSe
|
||||||
StationToCompNSPaths: make(map[string][]string),
|
StationToCompNSPaths: make(map[string][]string),
|
||||||
CompNSPathToCompTags: make(map[string][]string),
|
CompNSPathToCompTags: make(map[string][]string),
|
||||||
CompTagToMeasTags: make(map[string][]string),
|
CompTagToMeasTags: make(map[string][]string),
|
||||||
|
CompNSPathToMeasTags: make(map[string][]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
g, gctx := errgroup.WithContext(ctx)
|
g, gctx := errgroup.WithContext(ctx)
|
||||||
|
|
@ -113,11 +114,14 @@ func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSe
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
var measurements []struct {
|
var measurements []struct {
|
||||||
orm.Measurement
|
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").
|
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 component on measurement.component_uuid = component.global_uuid").
|
||||||
|
Joins("left join bay on measurement.bay_uuid = bay.bay_uuid").
|
||||||
Scan(&measurements).Error; err != nil {
|
Scan(&measurements).Error; err != nil {
|
||||||
return fmt.Errorf("query measurements: %w", err)
|
return fmt.Errorf("query measurements: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -126,6 +130,9 @@ func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSe
|
||||||
if m.CompTag != "" {
|
if m.CompTag != "" {
|
||||||
mSet.CompTagToMeasTags[m.CompTag] = append(mSet.CompTagToMeasTags[m.CompTag], m.Tag)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,19 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
||||||
ac.AddTerms(sug...)
|
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
|
var allErrs []error
|
||||||
cmders, execErr := pipe.Exec(ctx)
|
cmders, execErr := pipe.Exec(ctx)
|
||||||
if execErr != nil {
|
if execErr != nil {
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,12 @@ func CleanupRecommendRedisCache(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
patterns := []string{
|
patterns := []string{
|
||||||
"*_zone_tag_keys", // correspond RedisSpecGridZoneSetKey
|
"*_zone_tag_keys", // correspond RedisSpecGridZoneSetKey
|
||||||
"*_station_tag_keys", // correspond RedisSpecZoneStationSetKey
|
"*_station_tag_keys", // correspond RedisSpecZoneStationSetKey
|
||||||
"*_component_nspath_keys", // correspond RedisSpecStationCompNSPATHSetKey
|
"*_component_nspath_keys", // correspond RedisSpecStationCompNSPATHSetKey
|
||||||
"*_component_tag_keys", // correspond RedisSpecCompNSPathCompTagSetKey
|
"*_component_tag_keys", // correspond RedisSpecCompNSPathCompTagSetKey
|
||||||
"*_measurement_tag_keys", // correspond RedisSpecCompTagMeasSetKey
|
"*_measurement_tag_keys", // correspond RedisSpecCompTagMeasSetKey
|
||||||
|
"*_nspath_measurement_tag_keys", // correspond RedisSpecCompNSPathMeasSetKey
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
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)
|
go handleLevelFuzzySearch(ctx, rdb, constants.ZoneRecommendHierarchyType, constants.FullRecommendLength, constants.RedisAllZoneSetKey, inputSlice, fanInChan)
|
||||||
// component tagname search
|
// component tagname search
|
||||||
go handleLevelFuzzySearch(ctx, rdb, constants.CompTagRecommendHierarchyType, constants.IsLocalRecommendLength, constants.RedisAllCompTagSetKey, inputSlice, fanInChan)
|
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)
|
results := make(map[string]SearchResult)
|
||||||
for range 2 {
|
for range 3 {
|
||||||
result := <-fanInChan
|
result := <-fanInChan
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
logger.Error(ctx, "query all keys at the special level from redis failed", "query_key", result.RecommendType, "error", result.Err)
|
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)
|
// 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) {
|
func handleLevelFuzzySearch(ctx context.Context, rdb *redis.Client, hierarchy constants.RecommendHierarchyType, recommendLenType string, redisSetKey string, inputSlice []string, fanInChan chan SearchResult) {
|
||||||
inputSliceLen := len(inputSlice)
|
inputSliceLen := len(inputSlice)
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,5 @@ type MeasurementSet struct {
|
||||||
StationToCompNSPaths map[string][]string // Key: StationTag, Value: NSPaths
|
StationToCompNSPaths map[string][]string // Key: StationTag, Value: NSPaths
|
||||||
CompNSPathToCompTags map[string][]string // Key: NSPaths, Value: CompTags
|
CompNSPathToCompTags map[string][]string // Key: NSPaths, Value: CompTags
|
||||||
CompTagToMeasTags map[string][]string // Key: CompTag, Value: MeasTags
|
CompTagToMeasTags map[string][]string // Key: CompTag, Value: MeasTags
|
||||||
|
CompNSPathToMeasTags map[string][]string // Key: NSPaths, Value: MeasTags
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue