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  }