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
|
package diagram
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
"modelRT/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// graphOverview define struct of storage all circuit diagram topologic data
|
// graphOverview define struct of storage all circuit diagram topologic data keyed by pageID
|
||||||
var graphOverview sync.Map
|
var graphOverview util.TypedMap[int64, *Graph]
|
||||||
|
|
||||||
// PrintGrapMap define func of print circuit diagram topologic info data
|
// PrintGrapMap define func of print circuit diagram topologic info data
|
||||||
func PrintGrapMap() {
|
func PrintGrapMap() {
|
||||||
graphOverview.Range(func(key, value any) bool {
|
graphOverview.Range(func(pageID int64, graph *Graph) bool {
|
||||||
fmt.Println(key, value)
|
fmt.Println(pageID, graph)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGraphMap define func of get circuit diagram topologic data by pageID
|
// GetGraphMap define func of get circuit diagram topologic data by pageID
|
||||||
func GetGraphMap(pageID int64) (*Graph, error) {
|
func GetGraphMap(pageID int64) (*Graph, error) {
|
||||||
value, ok := graphOverview.Load(pageID)
|
graph, ok := graphOverview.Load(pageID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("can not find graph by pageID:%d", pageID)
|
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
|
return graph, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"modelRT/diagram"
|
"modelRT/diagram"
|
||||||
"modelRT/logger"
|
"modelRT/logger"
|
||||||
"modelRT/orm"
|
"modelRT/orm"
|
||||||
|
"modelRT/util"
|
||||||
|
|
||||||
"github.com/RediSearch/redisearch-go/v2/redisearch"
|
"github.com/RediSearch/redisearch-go/v2/redisearch"
|
||||||
)
|
)
|
||||||
|
|
@ -63,10 +64,9 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
||||||
}
|
}
|
||||||
|
|
||||||
safeSAdd(constants.RedisAllGridSetKey, measSet.AllGridTags)
|
safeSAdd(constants.RedisAllGridSetKey, measSet.AllGridTags)
|
||||||
gridSug := make([]redisearch.Suggestion, 0, len(measSet.AllGridTags))
|
gridSug := util.MapSlice(measSet.AllGridTags, func(gridTag string) redisearch.Suggestion {
|
||||||
for _, gridTag := range measSet.AllGridTags {
|
return redisearch.Suggestion{Term: gridTag, Score: constants.DefaultScore}
|
||||||
gridSug = append(gridSug, redisearch.Suggestion{Term: gridTag, Score: constants.DefaultScore})
|
})
|
||||||
}
|
|
||||||
ac.AddTerms(gridSug...)
|
ac.AddTerms(gridSug...)
|
||||||
|
|
||||||
safeSAdd(constants.RedisAllZoneSetKey, measSet.AllZoneTags)
|
safeSAdd(constants.RedisAllZoneSetKey, measSet.AllZoneTags)
|
||||||
|
|
@ -78,19 +78,16 @@ func TraverseMeasurementGroupTables(ctx context.Context, measSet orm.Measurement
|
||||||
|
|
||||||
// building the grid -> zones hierarchy
|
// building the grid -> zones hierarchy
|
||||||
for gridTag, zoneTags := range measSet.GridToZoneTags {
|
for gridTag, zoneTags := range measSet.GridToZoneTags {
|
||||||
sug := make([]redisearch.Suggestion, 0, len(zoneTags))
|
// add redis fuzzy search suggestion for token1-token7 type
|
||||||
for _, zoneTag := range zoneTags {
|
sug := util.MapSlice(zoneTags, func(zoneTag string) redisearch.Suggestion {
|
||||||
term := fmt.Sprintf("%s.%s", gridTag, zoneTag)
|
return redisearch.Suggestion{Term: fmt.Sprintf("%s.%s", gridTag, zoneTag), Score: constants.DefaultScore}
|
||||||
// add redis fuzzy search suggestion for token1-token7 type
|
})
|
||||||
sug = append(sug, redisearch.Suggestion{Term: term, Score: constants.DefaultScore})
|
|
||||||
}
|
|
||||||
safeSAdd(fmt.Sprintf(constants.RedisSpecGridZoneSetKey, gridTag), zoneTags)
|
safeSAdd(fmt.Sprintf(constants.RedisSpecGridZoneSetKey, gridTag), zoneTags)
|
||||||
ac.AddTerms(sug...)
|
ac.AddTerms(sug...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// building the zone -> stations hierarchy
|
// building the zone -> stations hierarchy
|
||||||
for zoneTag, stationTags := range measSet.ZoneToStationTags {
|
for zoneTag, stationTags := range measSet.ZoneToStationTags {
|
||||||
sug := make([]redisearch.Suggestion, 0, len(stationTags))
|
|
||||||
gridTag, exists := zoneToGridPath[zoneTag]
|
gridTag, exists := zoneToGridPath[zoneTag]
|
||||||
if !exists {
|
if !exists {
|
||||||
err := fmt.Errorf("zone tag to grid tag mapping not found for zoneTag: %s", zoneTag)
|
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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, stationTag := range stationTags {
|
// add redis fuzzy search suggestion for token1-token7 type
|
||||||
// add redis fuzzy search suggestion for token1-token7 type
|
sug := util.MapSlice(stationTags, func(stationTag string) redisearch.Suggestion {
|
||||||
term := fmt.Sprintf("%s.%s.%s", gridTag, zoneTag, stationTag)
|
return redisearch.Suggestion{Term: fmt.Sprintf("%s.%s.%s", gridTag, zoneTag, stationTag), Score: constants.DefaultScore}
|
||||||
sug = append(sug, redisearch.Suggestion{Term: term, Score: constants.DefaultScore})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
safeSAdd(fmt.Sprintf(constants.RedisSpecZoneStationSetKey, zoneTag), stationTags)
|
safeSAdd(fmt.Sprintf(constants.RedisSpecZoneStationSetKey, zoneTag), stationTags)
|
||||||
ac.AddTerms(sug...)
|
ac.AddTerms(sug...)
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,22 @@ import (
|
||||||
"github.com/redis/go-redis/v9"
|
"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
|
// ConvertZSetMembersToFloat64 define func to conver zset member type to float64
|
||||||
func ConvertZSetMembersToFloat64(members []redis.Z) []float64 {
|
func ConvertZSetMembersToFloat64(members []redis.Z) []float64 {
|
||||||
dataFloats := make([]float64, 0, len(members))
|
|
||||||
// recovery time sorted in ascending order
|
// recovery time sorted in ascending order
|
||||||
sortRedisZByTimeMemberAscending(members)
|
sortRedisZByTimeMemberAscending(members)
|
||||||
for _, member := range members {
|
return MapSlice(members, func(member redis.Z) float64 {
|
||||||
dataFloats = append(dataFloats, member.Score)
|
return member.Score
|
||||||
}
|
})
|
||||||
return dataFloats
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortRedisZByTimeMemberAscending(data []redis.Z) {
|
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 provide some utility functions
|
||||||
package util
|
package util
|
||||||
|
|
||||||
// GetKeysFromSet define func to get all keys from a map[string]struct{}
|
import (
|
||||||
func GetKeysFromSet(set map[string]struct{}) []string {
|
"maps"
|
||||||
keys := make([]string, 0, len(set))
|
"slices"
|
||||||
for key := range set {
|
)
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
// GetKeysFromSet define func to get all keys from a set-like map.
|
||||||
return keys
|
// 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 provide some utility functions
|
||||||
package util
|
package util
|
||||||
|
|
||||||
// RemoveTargetsFromSliceSimple define func to remove targets from a slice of strings
|
// RemoveTargetsFromSliceSimple define func to remove targets from a slice
|
||||||
func RemoveTargetsFromSliceSimple(targetsSlice []string, targetsToRemove []string) []string {
|
func RemoveTargetsFromSliceSimple[T comparable](targetsSlice []T, targetsToRemove []T) []T {
|
||||||
targetsToRemoveSet := make(map[string]struct{}, len(targetsToRemove))
|
targetsToRemoveSet := SliceToSet(targetsToRemove)
|
||||||
for _, target := range targetsToRemove {
|
|
||||||
targetsToRemoveSet[target] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(targetsSlice) - 1; i >= 0; i-- {
|
for i := len(targetsSlice) - 1; i >= 0; i-- {
|
||||||
if _, found := targetsToRemoveSet[targetsSlice[i]]; found {
|
if _, found := targetsToRemoveSet[targetsSlice[i]]; found {
|
||||||
|
|
@ -17,21 +14,21 @@ func RemoveTargetsFromSliceSimple(targetsSlice []string, targetsToRemove []strin
|
||||||
return targetsSlice
|
return targetsSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
// SliceToSet define func to convert string slice to set
|
// SliceToSet define func to convert a slice to a set
|
||||||
func SliceToSet(targetsSlice []string) map[string]struct{} {
|
func SliceToSet[T comparable](targetsSlice []T) map[T]struct{} {
|
||||||
set := make(map[string]struct{}, len(targetsSlice))
|
set := make(map[T]struct{}, len(targetsSlice))
|
||||||
for _, target := range targetsSlice {
|
for _, target := range targetsSlice {
|
||||||
set[target] = struct{}{}
|
set[target] = struct{}{}
|
||||||
}
|
}
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeduplicateAndReportDuplicates define func to deduplicate a slice of strings and report duplicates
|
// DeduplicateAndReportDuplicates define func to deduplicate a slice and report duplicates
|
||||||
func DeduplicateAndReportDuplicates(targetsSlice []string, sourceSlice []string) (deduplicated []string, duplicates []string) {
|
func DeduplicateAndReportDuplicates[T comparable](targetsSlice []T, sourceSlice []T) (deduplicated []T, duplicates []T) {
|
||||||
targetSet := SliceToSet(targetsSlice)
|
targetSet := SliceToSet(targetsSlice)
|
||||||
deduplicated = make([]string, 0, len(sourceSlice))
|
deduplicated = make([]T, 0, len(sourceSlice))
|
||||||
// duplicate items slice
|
// duplicate items slice
|
||||||
duplicates = make([]string, 0, len(sourceSlice))
|
duplicates = make([]T, 0, len(sourceSlice))
|
||||||
|
|
||||||
for _, source := range sourceSlice {
|
for _, source := range sourceSlice {
|
||||||
if _, found := targetSet[source]; found {
|
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