From 82622d0d858e8ea717d51e76a89f41e4d04131c9 Mon Sep 17 00:00:00 2001 From: douxu Date: Tue, 16 Jun 2026 16:15:28 +0800 Subject: [PATCH] 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 --- diagram/topologic_set.go | 18 +++----- model/measurement_group_recommend_model.go | 28 +++++------- util/convert.go | 17 +++++--- util/map.go | 16 ++++--- util/string.go | 23 +++++----- util/typed_map.go | 51 ++++++++++++++++++++++ 6 files changed, 101 insertions(+), 52 deletions(-) create mode 100644 util/typed_map.go diff --git a/diagram/topologic_set.go b/diagram/topologic_set.go index ccc17a9..4c96a4a 100644 --- a/diagram/topologic_set.go +++ b/diagram/topologic_set.go @@ -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 } diff --git a/model/measurement_group_recommend_model.go b/model/measurement_group_recommend_model.go index bcb9f52..02babc8 100644 --- a/model/measurement_group_recommend_model.go +++ b/model/measurement_group_recommend_model.go @@ -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...) diff --git a/util/convert.go b/util/convert.go index 6e4f04f..c4d378b 100644 --- a/util/convert.go +++ b/util/convert.go @@ -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) { diff --git a/util/map.go b/util/map.go index e6f3116..8cd7303 100644 --- a/util/map.go +++ b/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)) } diff --git a/util/string.go b/util/string.go index 47e2e43..d01c098 100644 --- a/util/string.go +++ b/util/string.go @@ -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 { diff --git a/util/typed_map.go b/util/typed_map.go new file mode 100644 index 0000000..6693668 --- /dev/null +++ b/util/typed_map.go @@ -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)) + }) +}