33
44package entity
55
6- import "time"
6+ import (
7+ "strings"
8+ "time"
9+
10+ "github.com/coze-dev/coze-loop/backend/pkg/json"
11+ )
712
813type ColumnExtractRule struct {
914 Column string
@@ -22,3 +27,128 @@ type ColumnExtractConfig struct {
2227 UpdatedAt time.Time
2328 UpdatedBy string
2429}
30+
31+ // Extract extracts the value from content using the JSONPath rule for the given column.
32+ // Returns the extracted string, or empty string if extraction fails.
33+ func (c * ColumnExtractConfig ) Extract (content string , column string ) string {
34+ if c == nil || len (c .Columns ) == 0 {
35+ return ""
36+ }
37+ var rule * ColumnExtractRule
38+ for i := range c .Columns {
39+ if c .Columns [i ].Column == column {
40+ rule = & c .Columns [i ]
41+ break
42+ }
43+ }
44+ if rule == nil {
45+ return ""
46+ }
47+ return extractByJSONPath (content , rule .JSONPath )
48+ }
49+
50+ // ColumnExtractConfigs is a list of ColumnExtractConfig with selection logic.
51+ type ColumnExtractConfigs []* ColumnExtractConfig
52+
53+ // SelectBest selects the best matching config using a scoring strategy.
54+ //
55+ // Priority dimensions (highest to lowest weight):
56+ // 1. Workspace: target workspace > default (wsID=0). Cross-workspace configs are excluded.
57+ // 2. Agent: exact agent match > empty agent (wildcard).
58+ // 3. SpanListType: exact match > wildcard '*'.
59+ // 4. PlatformType: exact match > wildcard '*'.
60+ //
61+ // Returns nil if no config matches.
62+ func (configs ColumnExtractConfigs ) SelectBest (workspaceId int64 , agentName , platformType , spanListType string ) * ColumnExtractConfig {
63+ var (
64+ best * ColumnExtractConfig
65+ bestScore int
66+ )
67+
68+ for _ , cfg := range configs {
69+ score := configScore (cfg , workspaceId , agentName , platformType , spanListType )
70+ if score < 0 {
71+ continue // not a valid match
72+ }
73+ if best == nil || score > bestScore {
74+ best = cfg
75+ bestScore = score
76+ }
77+ }
78+
79+ return best
80+ }
81+
82+ // configScore computes a match score for a config. Returns -1 if the config doesn't match.
83+ // Higher score = better match. Score layout (4 bits):
84+ //
85+ // bit 3 (8): workspace match (target ws=1, default ws=0)
86+ // bit 2 (4): agent match (exact=1, empty=0)
87+ // bit 1 (2): spanListType match (exact=1, wildcard=0)
88+ // bit 0 (1): platformType match (exact=1, wildcard=0)
89+ func configScore (cfg * ColumnExtractConfig , workspaceId int64 , agentName , platformType , spanListType string ) int {
90+ // workspace: must be target or default(0), reject cross-workspace
91+ isTarget := cfg .WorkspaceID == workspaceId
92+ isDefault := cfg .WorkspaceID == 0
93+ if ! isTarget && ! isDefault {
94+ return - 1
95+ }
96+
97+ // agent: must be exact match or empty wildcard
98+ agentMatch := agentName != "" && cfg .AgentName == agentName
99+ agentEmpty := cfg .AgentName == ""
100+ if ! agentMatch && ! agentEmpty {
101+ return - 1
102+ }
103+
104+ // platformType: must be exact or wildcard '*'
105+ platformExact := cfg .PlatformType == platformType
106+ platformWild := cfg .PlatformType == "*"
107+ if ! platformExact && ! platformWild {
108+ return - 1
109+ }
110+
111+ // spanListType: must be exact or wildcard '*'
112+ spanExact := cfg .SpanListType == spanListType
113+ spanWild := cfg .SpanListType == "*"
114+ if ! spanExact && ! spanWild {
115+ return - 1
116+ }
117+
118+ score := 0
119+ if isTarget {
120+ score |= 8
121+ }
122+ if agentMatch {
123+ score |= 4
124+ }
125+ if platformExact {
126+ score |= 1
127+ }
128+ if spanExact {
129+ score |= 2
130+ }
131+ return score
132+ }
133+
134+ func extractByJSONPath (content , jsonPath string ) string {
135+ if content == "" || jsonPath == "" {
136+ return ""
137+ }
138+ if ! json .Valid ([]byte (content )) {
139+ return ""
140+ }
141+ // For recursive descent queries ($..field), take the last match
142+ if strings .Contains (jsonPath , ".." ) {
143+ result , err := json .GetLastStringByJSONPath (content , jsonPath )
144+ if err != nil {
145+ return ""
146+ }
147+ return result
148+ }
149+ result , err := json .GetStringByJSONPathRecursively (content , jsonPath )
150+ if err != nil {
151+ return ""
152+ }
153+ return result
154+ }
0 commit comments