refactor: add generic helpers and type-safe TypedMap wrapper
- add util.TypedMap, a generic wrapper over sync.Map to drop call-site type assertions - add generic util.MapSlice and reuse it in ConvertZSetMembersToFloat64 - make GetKeysFromSet/SliceToSet/RemoveTargetsFromSliceSimple/DeduplicateAndReportDuplicates generic - migrate graphOverview to util.TypedMap[int64, *Graph] - build redis suggestions via util.MapSlice in measurement group recommend
This commit is contained in:
parent
908c713565
commit
82622d0d85
|
|
@ -2,32 +2,28 @@
|
|||
package diagram
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"modelRT/util"
|
||||
)
|
||||
|
||||
// graphOverview define struct of storage all circuit diagram topologic data
|
||||
var graphOverview sync.Map
|
||||
// graphOverview define struct of storage all circuit diagram topologic data keyed by pageID
|
||||
var graphOverview util.TypedMap[int64, *Graph]
|
||||
|
||||
// PrintGrapMap define func of print circuit diagram topologic info data
|
||||
func PrintGrapMap() {
|
||||
graphOverview.Range(func(key, value any) bool {
|
||||
fmt.Println(key, value)
|
||||
graphOverview.Range(func(pageID int64, graph *Graph) bool {
|
||||
fmt.Println(pageID, graph)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// GetGraphMap define func of get circuit diagram topologic data by pageID
|
||||
func GetGraphMap(pageID int64) (*Graph, error) {
|
||||
value, ok := graphOverview.Load(pageID)
|
||||
graph, ok := graphOverview.Load(pageID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can not find graph by pageID:%d", pageID)
|
||||
}
|
||||
graph, ok := value.(*Graph)
|
||||
if !ok {
|
||||
return nil, errors.New("convert to graph struct failed")
|
||||
}
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"modelRT/diagram"
|
||||
"modelRT/logger"
|
||||
"modelRT/orm"
|
||||
"modelRT/util"
|
||||
|
||||
"github.com/RediSearch/redisearch-go/v2/redisearch"
|
||||
)
|
||||
|
|
@ -63,10 +64,9 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
|||
}
|
||||
|
||||
safeSAdd(constants.RedisAllGridSetKey, measSet.AllGridTags)
|
||||
gridSug := make([]redisearch.Suggestion, 0, len(measSet.AllGridTags))
|
||||
for _, gridTag := range measSet.AllGridTags {
|
||||
gridSug = append(gridSug, redisearch.Suggestion{Term: gridTag, Score: constants.DefaultScore})
|
||||
}
|
||||
gridSug := util.MapSlice(measSet.AllGridTags, func(gridTag string) redisearch.Suggestion {
|
||||
return redisearch.Suggestion{Term: gridTag, Score: constants.DefaultScore}
|
||||
})
|
||||
ac.AddTerms(gridSug...)
|
||||
|
||||
safeSAdd(constants.RedisAllZoneSetKey, measSet.AllZoneTags)
|
||||
|
|
@ -78,19 +78,16 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
|||
|
||||
// building the grid -> zones hierarchy
|
||||
for gridTag, zoneTags := range measSet.GridToZoneTags {
|
||||
sug := make([]redisearch.Suggestion, 0, len(zoneTags))
|
||||
for _, zoneTag := range zoneTags {
|
||||
term := fmt.Sprintf("%s.%s", gridTag, zoneTag)
|
||||
// add redis fuzzy search suggestion for token1-token7 type
|
||||
sug = append(sug, redisearch.Suggestion{Term: term, Score: constants.DefaultScore})
|
||||
}
|
||||
// add redis fuzzy search suggestion for token1-token7 type
|
||||
sug := util.MapSlice(zoneTags, func(zoneTag string) redisearch.Suggestion {
|
||||
return redisearch.Suggestion{Term: fmt.Sprintf("%s.%s", gridTag, zoneTag), Score: constants.DefaultScore}
|
||||
})
|
||||
safeSAdd(fmt.Sprintf(constants.RedisSpecGridZoneSetKey, gridTag), zoneTags)
|
||||
ac.AddTerms(sug...)
|
||||
}
|
||||
|
||||
// building the zone -> stations hierarchy
|
||||
for zoneTag, stationTags := range measSet.ZoneToStationTags {
|
||||
sug := make([]redisearch.Suggestion, 0, len(stationTags))
|
||||
gridTag, exists := zoneToGridPath[zoneTag]
|
||||
if !exists {
|
||||
err := fmt.Errorf("zone tag to grid tag mapping not found for zoneTag: %s", zoneTag)
|
||||
|
|
@ -98,11 +95,10 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, stationTag := range stationTags {
|
||||
// add redis fuzzy search suggestion for token1-token7 type
|
||||
term := fmt.Sprintf("%s.%s.%s", gridTag, zoneTag, stationTag)
|
||||
sug = append(sug, redisearch.Suggestion{Term: term, Score: constants.DefaultScore})
|
||||
}
|
||||
// add redis fuzzy search suggestion for token1-token7 type
|
||||
sug := util.MapSlice(stationTags, func(stationTag string) redisearch.Suggestion {
|
||||
return redisearch.Suggestion{Term: fmt.Sprintf("%s.%s.%s", gridTag, zoneTag, stationTag), Score: constants.DefaultScore}
|
||||
})
|
||||
|
||||
safeSAdd(fmt.Sprintf(constants.RedisSpecZoneStationSetKey, zoneTag), stationTags)
|
||||
ac.AddTerms(sug...)
|
||||
|
|
|
|||
|
|
@ -7,15 +7,22 @@ import (
|
|||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// MapSlice define func to build a new slice by applying f to every element of s.
|
||||
func MapSlice[T, U any](s []T, f func(T) U) []U {
|
||||
result := make([]U, 0, len(s))
|
||||
for _, item := range s {
|
||||
result = append(result, f(item))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ConvertZSetMembersToFloat64 define func to conver zset member type to float64
|
||||
func ConvertZSetMembersToFloat64(members []redis.Z) []float64 {
|
||||
dataFloats := make([]float64, 0, len(members))
|
||||
// recovery time sorted in ascending order
|
||||
sortRedisZByTimeMemberAscending(members)
|
||||
for _, member := range members {
|
||||
dataFloats = append(dataFloats, member.Score)
|
||||
}
|
||||
return dataFloats
|
||||
return MapSlice(members, func(member redis.Z) float64 {
|
||||
return member.Score
|
||||
})
|
||||
}
|
||||
|
||||
func sortRedisZByTimeMemberAscending(data []redis.Z) {
|
||||
|
|
|
|||
16
util/map.go
16
util/map.go
|
|
@ -1,11 +1,13 @@
|
|||
// Package util provide some utility functions
|
||||
package util
|
||||
|
||||
// GetKeysFromSet define func to get all keys from a map[string]struct{}
|
||||
func GetKeysFromSet(set map[string]struct{}) []string {
|
||||
keys := make([]string, 0, len(set))
|
||||
for key := range set {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// GetKeysFromSet define func to get all keys from a set-like map.
|
||||
// It delegates to the standard library maps/slices helpers.
|
||||
func GetKeysFromSet[K comparable, V any](set map[K]V) []K {
|
||||
return slices.Collect(maps.Keys(set))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
// Package util provide some utility functions
|
||||
package util
|
||||
|
||||
// RemoveTargetsFromSliceSimple define func to remove targets from a slice of strings
|
||||
func RemoveTargetsFromSliceSimple(targetsSlice []string, targetsToRemove []string) []string {
|
||||
targetsToRemoveSet := make(map[string]struct{}, len(targetsToRemove))
|
||||
for _, target := range targetsToRemove {
|
||||
targetsToRemoveSet[target] = struct{}{}
|
||||
}
|
||||
// RemoveTargetsFromSliceSimple define func to remove targets from a slice
|
||||
func RemoveTargetsFromSliceSimple[T comparable](targetsSlice []T, targetsToRemove []T) []T {
|
||||
targetsToRemoveSet := SliceToSet(targetsToRemove)
|
||||
|
||||
for i := len(targetsSlice) - 1; i >= 0; i-- {
|
||||
if _, found := targetsToRemoveSet[targetsSlice[i]]; found {
|
||||
|
|
@ -17,21 +14,21 @@ func RemoveTargetsFromSliceSimple(targetsSlice []string, targetsToRemove []strin
|
|||
return targetsSlice
|
||||
}
|
||||
|
||||
// SliceToSet define func to convert string slice to set
|
||||
func SliceToSet(targetsSlice []string) map[string]struct{} {
|
||||
set := make(map[string]struct{}, len(targetsSlice))
|
||||
// SliceToSet define func to convert a slice to a set
|
||||
func SliceToSet[T comparable](targetsSlice []T) map[T]struct{} {
|
||||
set := make(map[T]struct{}, len(targetsSlice))
|
||||
for _, target := range targetsSlice {
|
||||
set[target] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// DeduplicateAndReportDuplicates define func to deduplicate a slice of strings and report duplicates
|
||||
func DeduplicateAndReportDuplicates(targetsSlice []string, sourceSlice []string) (deduplicated []string, duplicates []string) {
|
||||
// DeduplicateAndReportDuplicates define func to deduplicate a slice and report duplicates
|
||||
func DeduplicateAndReportDuplicates[T comparable](targetsSlice []T, sourceSlice []T) (deduplicated []T, duplicates []T) {
|
||||
targetSet := SliceToSet(targetsSlice)
|
||||
deduplicated = make([]string, 0, len(sourceSlice))
|
||||
deduplicated = make([]T, 0, len(sourceSlice))
|
||||
// duplicate items slice
|
||||
duplicates = make([]string, 0, len(sourceSlice))
|
||||
duplicates = make([]T, 0, len(sourceSlice))
|
||||
|
||||
for _, source := range sourceSlice {
|
||||
if _, found := targetSet[source]; found {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
// Package util provide some utility functions
|
||||
package util
|
||||
|
||||
import "sync"
|
||||
|
||||
// TypedMap define a type-safe generic wrapper around sync.Map.
|
||||
// It keeps the concurrency guarantees of sync.Map while removing the
|
||||
// per-call-site type assertions previously required for every Load.
|
||||
type TypedMap[K comparable, V any] struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
// Load define func of return the value stored for key, and whether it was found.
|
||||
func (t *TypedMap[K, V]) Load(key K) (V, bool) {
|
||||
value, ok := t.m.Load(key)
|
||||
if !ok {
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
// safe: only values of type V are ever stored through this wrapper
|
||||
return value.(V), true
|
||||
}
|
||||
|
||||
// Store define func of set the value for key.
|
||||
func (t *TypedMap[K, V]) Store(key K, value V) {
|
||||
t.m.Store(key, value)
|
||||
}
|
||||
|
||||
// Swap define func of store value for key and return the previous value (if any).
|
||||
// loaded reports whether a value was already present for key.
|
||||
func (t *TypedMap[K, V]) Swap(key K, value V) (previous V, loaded bool) {
|
||||
prev, loaded := t.m.Swap(key, value)
|
||||
if !loaded {
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
return prev.(V), true
|
||||
}
|
||||
|
||||
// Delete define func of remove the value for key.
|
||||
func (t *TypedMap[K, V]) Delete(key K) {
|
||||
t.m.Delete(key)
|
||||
}
|
||||
|
||||
// Range define func of iterate over all key/value pairs.
|
||||
// Iteration stops early if f returns false.
|
||||
func (t *TypedMap[K, V]) Range(f func(key K, value V) bool) {
|
||||
t.m.Range(func(key, value any) bool {
|
||||
return f(key.(K), value.(V))
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue