github.com/GuanceCloud/cliutils@v1.1.21/point/kvs.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 package point 7 8 import ( 9 "fmt" 10 "sort" 11 "strings" 12 13 influxm "github.com/influxdata/influxdb1-client/models" 14 "golang.org/x/exp/slices" 15 ) 16 17 type KVs []*Field 18 19 // Raw return underlying raw data. 20 func (kv *Field) Raw() any { 21 switch kv.Val.(type) { 22 case *Field_I: 23 return kv.GetI() 24 case *Field_U: 25 return kv.GetU() 26 case *Field_F: 27 return kv.GetF() 28 case *Field_B: 29 return kv.GetB() 30 case *Field_D: 31 return kv.GetD() 32 case *Field_S: 33 return kv.GetS() 34 35 case *Field_A: 36 37 if v, err := AnyRaw(kv.GetA()); err != nil { 38 return nil 39 } else { 40 return v 41 } 42 default: 43 return nil 44 } 45 } 46 47 func (x KVs) Len() int { 48 return len(x) 49 } 50 51 func (x KVs) Swap(i, j int) { 52 x[i], x[j] = x[j], x[i] 53 } 54 55 func (x KVs) Less(i, j int) bool { 56 return strings.Compare(x[i].Key, x[j].Key) < 0 // stable sort 57 } 58 59 func (x KVs) Pretty() string { 60 var arr []string 61 for idx, kv := range x { 62 if kv == nil { 63 arr = append(arr, fmt.Sprintf("[%d] <nil>", idx)) 64 } else { 65 arr = append(arr, fmt.Sprintf("[%d] %s", idx, kv.String())) 66 } 67 } 68 69 // For key-values are not sorted while building the point, we 70 // think they are equal, so sort the string array to remove the 71 // ordering difference between points. 72 sort.Strings(arr) 73 74 return strings.Join(arr, "\n") 75 } 76 77 func (x KVs) PrettySorted() string { 78 var arr []string 79 for _, kv := range x { 80 if kv == nil { 81 arr = append(arr, "<nil>") 82 } else { 83 arr = append(arr, kv.String()) 84 } 85 } 86 87 sort.Strings(arr) 88 89 return strings.Join(arr, "\n") 90 } 91 92 // InfluxFields convert KVs to map structure. 93 func (x KVs) InfluxFields() map[string]any { 94 res := map[string]any{} 95 96 for _, kv := range x { 97 if kv.IsTag { 98 continue 99 } 100 101 switch x := kv.Val.(type) { 102 case *Field_I: 103 res[kv.Key] = x.I 104 case *Field_U: 105 res[kv.Key] = x.U 106 case *Field_F: 107 res[kv.Key] = x.F 108 case *Field_B: 109 res[kv.Key] = x.B 110 case *Field_D: 111 res[kv.Key] = x.D 112 case *Field_S: 113 res[kv.Key] = x.S 114 case *Field_A: 115 if v, err := AnyRaw(kv.GetA()); err != nil { 116 // pass 117 } else { 118 res[kv.Key] = v 119 } 120 default: 121 continue 122 } 123 } 124 125 return res 126 } 127 128 // InfluxTags convert tag KVs to map structure. 129 func (x KVs) InfluxTags() (res influxm.Tags) { 130 for _, kv := range x { 131 if !kv.IsTag { 132 continue 133 } 134 135 res = append(res, influxm.Tag{ 136 Key: []byte(kv.Key), 137 Value: []byte(kv.GetS()), 138 }) 139 } 140 141 // keep tags sorted used to build lineprotocol text 142 sort.Sort(res) 143 144 return 145 } 146 147 func clearKV(kv *Field) *Field { 148 kv.Key = "" 149 kv.IsTag = false 150 kv.Type = UNSPECIFIED 151 kv.Unit = "" 152 return kv 153 } 154 155 func resetKV(kv *Field) *Field { 156 switch v := kv.Val.(type) { 157 case *Field_I: 158 v.I = 0 159 case *Field_U: 160 v.U = 0 161 case *Field_F: 162 v.F = 0.0 163 case *Field_D: 164 v.D = v.D[:0] 165 case *Field_B: 166 v.B = false 167 case *Field_S: 168 v.S = "" 169 case *Field_A: 170 v.A.TypeUrl = "" 171 v.A.Value = v.A.Value[:0] 172 } 173 174 return kv 175 } 176 177 // ResetFull reset and reuse key-value. 178 func (x KVs) ResetFull() { 179 for i, kv := range x { 180 x[i] = resetKV(clearKV(kv)) 181 } 182 } 183 184 // Reset reset but drop value. 185 func (x KVs) Reset() { 186 for i, kv := range x { 187 kv = clearKV(kv) 188 kv.Val = nil // drop Val 189 x[i] = kv 190 } 191 } 192 193 // Has test if k exist. 194 func (x KVs) Has(k string) bool { 195 for _, f := range x { 196 if f.Key == k { 197 return true 198 } 199 } 200 201 return false 202 } 203 204 // Get get k's value, if k not exist, return nil. 205 func (x KVs) Get(k string) *Field { 206 for _, f := range x { 207 if f.Key == k { 208 return f 209 } 210 } 211 212 return nil 213 } 214 215 // GetTag get tag k's value, if the tag not exist, return nil. 216 func (x KVs) GetTag(k string) string { 217 for _, f := range x { 218 if !f.IsTag { 219 continue 220 } 221 222 if f.Key == k { 223 return f.GetS() 224 } 225 } 226 227 return "" 228 } 229 230 func (x KVs) Tags() (arr KVs) { 231 for _, kv := range x { 232 if !kv.IsTag { 233 continue 234 } 235 236 arr = append(arr, kv) 237 } 238 239 // should we buffer point's tags like this? 240 // p.tags = arr 241 return arr 242 } 243 244 func (x KVs) Fields() (arr KVs) { 245 for _, kv := range x { 246 if kv.IsTag { 247 continue 248 } 249 250 arr = append(arr, kv) 251 } 252 253 // should we buffer point's tags like this? 254 // p.tags = arr 255 return arr 256 } 257 258 // TrimFields keep max-n field kvs and drop the rest. 259 func (x KVs) TrimFields(n int) (arr KVs) { 260 cnt := 0 261 262 if len(x) <= n { 263 return x 264 } 265 266 for _, kv := range x { 267 if kv.IsTag { 268 arr = append(arr, kv) 269 continue 270 } else { 271 if cnt < n { 272 arr = append(arr, kv) 273 cnt++ 274 } else if defaultPTPool != nil { // drop the kv 275 defaultPTPool.PutKV(kv) 276 } 277 } 278 } 279 280 return arr 281 } 282 283 // TrimTags keep max-n tag kvs. 284 func (x KVs) TrimTags(n int) (arr KVs) { 285 cnt := 0 286 287 for _, kv := range x { 288 if !kv.IsTag { 289 arr = append(arr, kv) 290 continue 291 } else { 292 if cnt < n { 293 arr = append(arr, kv) 294 cnt++ 295 } else if defaultPTPool != nil { 296 defaultPTPool.PutKV(kv) 297 } 298 } 299 } 300 301 return arr 302 } 303 304 func (x KVs) TagCount() (i int) { 305 for _, kv := range x { 306 if kv.IsTag { 307 i++ 308 } 309 } 310 return 311 } 312 313 func (x KVs) FieldCount() (i int) { 314 for _, kv := range x { 315 if !kv.IsTag { 316 i++ 317 } 318 } 319 return 320 } 321 322 // Del delete field from x with Key == k. 323 func (x KVs) Del(k string) KVs { 324 for i, f := range x { 325 if f.Key == k { 326 x = slices.Delete(x, i, i+1) 327 if defaultPTPool != nil { 328 defaultPTPool.PutKV(f) 329 } 330 } 331 } 332 333 return x 334 } 335 336 // AddV2 add new field with opts. 337 // If force enabled, overwrite exist key. 338 func (x KVs) AddV2(k string, v any, force bool, opts ...KVOption) KVs { 339 kv := NewKV(k, v, opts...) 340 341 for i := range x { 342 if x[i].Key == k { // k exist 343 if force { 344 x[i] = kv // override exist tag/field 345 } 346 347 goto out // ignore the key 348 } 349 } 350 351 x = append(x, kv) 352 353 out: 354 return x 355 } 356 357 // Add add new field. 358 // Deprecated: use AddV2 359 // If force enabled, overwrite exist key. 360 func (x KVs) Add(k string, v any, isTag, force bool) KVs { 361 kv := NewKV(k, v) 362 363 if isTag { 364 switch v.(type) { 365 case string: 366 kv.IsTag = isTag 367 default: 368 // ignore isTag 369 } 370 } 371 372 for i := range x { 373 if x[i].Key == k { // k exist 374 if force { 375 x[i] = kv // override exist tag/field 376 } 377 378 goto out // ignore the key 379 } 380 } 381 382 x = append(x, kv) 383 384 out: 385 return x 386 } 387 388 func (x KVs) AddTag(k, v string) KVs { 389 x = x.Add(k, v, true, false) 390 return x 391 } 392 393 func (x KVs) MustAddTag(k, v string) KVs { 394 return x.Add(k, v, true, true) 395 } 396 397 func (x KVs) AddKV(kv *Field, force bool) KVs { 398 if kv == nil { 399 return x 400 } 401 402 for i := range x { 403 if x[i].Key == kv.Key { 404 if force { 405 x[i] = kv 406 } 407 goto out 408 } 409 } 410 411 x = append(x, kv) 412 413 out: 414 return x 415 } 416 417 func (x KVs) MustAddKV(kv *Field) KVs { 418 x = x.AddKV(kv, true) 419 return x 420 } 421 422 func PBType(v isField_Val) KeyType { 423 switch v.(type) { 424 case *Field_I: 425 return I 426 case *Field_U: 427 return U 428 case *Field_F: 429 return F 430 case *Field_B: 431 return B 432 case *Field_D: 433 return D 434 case *Field_S: 435 return S 436 case *Field_A: 437 return A 438 default: // nil or other types 439 return X 440 } 441 } 442 443 // Keys get k's value, if k not exist, return nil. 444 func (x KVs) Keys() *Keys { 445 arr := []*Key{KeyMeasurement, KeyTime} 446 447 for _, f := range x { 448 t := PBType(f.Val) 449 if t == X { 450 continue // ignore 451 } 452 453 arr = append(arr, NewKey(f.Key, t)) 454 } 455 456 return &Keys{arr: arr} 457 } 458 459 func KVKey(kv *Field) *Key { 460 t := PBType(kv.Val) 461 462 return NewKey(kv.Key, t) 463 } 464 465 type KVOption func(kv *Field) 466 467 // WithKVUnit set value's unit. 468 func WithKVUnit(u string) KVOption { 469 return func(kv *Field) { 470 kv.Unit = u 471 } 472 } 473 474 // WithKVType set field type(count/gauge/rate). 475 func WithKVType(t MetricType) KVOption { 476 return func(kv *Field) { 477 kv.Type = t 478 } 479 } 480 481 func WithKVTagSet(on bool) KVOption { 482 return func(kv *Field) { 483 switch kv.Val.(type) { 484 case *Field_S: 485 kv.IsTag = on 486 default: 487 // ignored 488 } 489 } 490 } 491 492 func doNewKV(k string, v any, opts ...KVOption) *Field { 493 return &Field{ 494 Key: k, 495 Val: newVal(v), 496 } 497 } 498 499 // NewKV get kv on specified key and value. 500 func NewKV(k string, v any, opts ...KVOption) *Field { 501 var kv *Field 502 if defaultPTPool != nil { 503 kv = defaultPTPool.GetKV(k, v) 504 } else { 505 kv = doNewKV(k, v, opts...) 506 } 507 508 for _, opt := range opts { 509 if opt != nil { 510 opt(kv) 511 } 512 } 513 514 return kv 515 } 516 517 // NewKVs create kvs slice from map structure. 518 func NewKVs(kvs map[string]interface{}) (res KVs) { 519 for k, v := range kvs { 520 res = append(res, NewKV(k, v)) 521 } 522 523 return res 524 } 525 526 // NewTags create tag kvs from map structure. 527 func NewTags(tags map[string]string) (arr KVs) { 528 for k, v := range tags { 529 arr = append(arr, NewKV(k, v, WithKVTagSet(true))) 530 } 531 532 return arr 533 }