2022-01-26 16:40:50 +08:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/1340691923/xwl_bi/engine/logs"
|
|
|
|
"github.com/1340691923/xwl_bi/platform-basic-libs/util"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/valyala/fastjson"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FastjsonParser struct {
|
|
|
|
fjp fastjson.Parser
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *FastjsonParser) Parse(bs []byte) (metric *FastjsonMetric, err error) {
|
|
|
|
var value *fastjson.Value
|
|
|
|
if value, err = p.fjp.ParseBytes(bs); err != nil {
|
|
|
|
err = errors.Wrapf(err, "")
|
|
|
|
return
|
|
|
|
}
|
2022-03-07 17:15:08 +08:00
|
|
|
metric = &FastjsonMetric{ value: value}
|
2022-01-26 16:40:50 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type FastjsonMetric struct {
|
|
|
|
value *fastjson.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetString(key string, nullable bool) (val interface{}) {
|
|
|
|
v := c.value.Get(key)
|
|
|
|
if v == nil || v.Type() == fastjson.TypeNull {
|
|
|
|
if nullable {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val = ""
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeString:
|
|
|
|
b, _ := v.StringBytes()
|
|
|
|
val = util.Bytes2str(b)
|
|
|
|
default:
|
|
|
|
val = v.String()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetFloat(key string, nullable bool) (val interface{}) {
|
|
|
|
v := c.value.Get(key)
|
|
|
|
if !fjCompatibleFloat(v) {
|
|
|
|
val = getDefaultFloat(nullable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if val2, err := v.Float64(); err != nil {
|
|
|
|
val = getDefaultFloat(nullable)
|
|
|
|
} else {
|
|
|
|
val = val2
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetInt(key string, nullable bool) (val interface{}) {
|
|
|
|
v := c.value.Get(key)
|
|
|
|
if !fjCompatibleInt(v) {
|
|
|
|
val = getDefaultInt(nullable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeTrue:
|
|
|
|
val = int64(1)
|
|
|
|
case fastjson.TypeFalse:
|
|
|
|
val = int64(0)
|
|
|
|
default:
|
|
|
|
if val2, err := v.Int64(); err != nil {
|
|
|
|
val = getDefaultInt(nullable)
|
|
|
|
} else {
|
|
|
|
val = val2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetDateTime(key string, nullable bool) (val interface{}) {
|
|
|
|
v := c.value.Get(key)
|
|
|
|
if !fjCompatibleDateTime(v) {
|
|
|
|
val = getDefaultDateTime(nullable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeNumber:
|
|
|
|
var f float64
|
|
|
|
if f, err = v.Float64(); err != nil {
|
|
|
|
val = getDefaultDateTime(nullable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val = UnixFloat(f)
|
|
|
|
case fastjson.TypeString:
|
|
|
|
var b []byte
|
|
|
|
if b, err = v.StringBytes(); err != nil || len(b) == 0 {
|
|
|
|
val = getDefaultDateTime(nullable)
|
|
|
|
return
|
|
|
|
}
|
2022-03-07 17:15:08 +08:00
|
|
|
if val, err = c.ParseDateTime(util.Bytes2str(b)); err != nil {
|
2022-01-26 16:40:50 +08:00
|
|
|
val = getDefaultDateTime(nullable)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
val = getDefaultDateTime(nullable)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-03-07 17:15:08 +08:00
|
|
|
|
|
|
|
func (c *FastjsonMetric) ParseDateTime(val string) (t time.Time, err error) {
|
|
|
|
|
|
|
|
var t2 time.Time
|
|
|
|
if val == "" {
|
|
|
|
err = ErrParseDateTime
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if t2, err = time.ParseInLocation(util.TimeFormat, val, time.Local); err != nil {
|
|
|
|
err = ErrParseDateTime
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t = t2.UTC()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-26 16:40:50 +08:00
|
|
|
func (c *FastjsonMetric) GetElasticDateTime(key string, nullable bool) (val interface{}) {
|
|
|
|
t := c.GetDateTime(key, nullable)
|
|
|
|
if t != nil {
|
|
|
|
val = t.(time.Time).Unix()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetArray(key string, typ int) (val interface{}) {
|
|
|
|
v := c.value.Get(key)
|
|
|
|
val = makeArray(typ)
|
|
|
|
if v == nil || v.Type() != fastjson.TypeArray {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
array, _ := v.Array()
|
|
|
|
switch typ {
|
|
|
|
case Int:
|
|
|
|
for _, e := range array {
|
|
|
|
var v int64
|
|
|
|
if e.Type() == fastjson.TypeTrue {
|
|
|
|
v = 1
|
|
|
|
} else {
|
|
|
|
v, _ = e.Int64()
|
|
|
|
}
|
|
|
|
val = append(val.([]int64), v)
|
|
|
|
}
|
|
|
|
case Float:
|
|
|
|
for _, e := range array {
|
|
|
|
v, _ := e.Float64()
|
|
|
|
val = append(val.([]float64), v)
|
|
|
|
}
|
|
|
|
case String:
|
|
|
|
for _, e := range array {
|
|
|
|
var s string
|
|
|
|
switch e.Type() {
|
|
|
|
case fastjson.TypeNull:
|
|
|
|
s = ""
|
|
|
|
case fastjson.TypeString:
|
|
|
|
b, _ := e.StringBytes()
|
|
|
|
s = util.Bytes2str(b)
|
|
|
|
default:
|
|
|
|
s = e.String()
|
|
|
|
}
|
|
|
|
val = append(val.([]string), s)
|
|
|
|
}
|
|
|
|
case DateTime:
|
|
|
|
for _, e := range array {
|
|
|
|
var t time.Time
|
|
|
|
switch e.Type() {
|
|
|
|
case fastjson.TypeNumber:
|
|
|
|
if f, err := e.Float64(); err != nil {
|
|
|
|
t = Epoch
|
|
|
|
} else {
|
|
|
|
t = UnixFloat(f)
|
|
|
|
}
|
|
|
|
case fastjson.TypeString:
|
|
|
|
if b, err := e.StringBytes(); err != nil || len(b) == 0 {
|
|
|
|
t = Epoch
|
|
|
|
} else {
|
|
|
|
var err error
|
2022-03-07 17:15:08 +08:00
|
|
|
if t, err = c.ParseDateTime(util.Bytes2str(b)); err != nil {
|
2022-01-26 16:40:50 +08:00
|
|
|
t = Epoch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
t = Epoch
|
|
|
|
}
|
|
|
|
val = append(val.([]time.Time), t)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
logs.Logger.Error("LOGIC ERROR: unsupported array", zap.Int("typ", typ))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetNewKeys(knownKeys *sync.Map, newKeys *sync.Map) (foundNew bool) {
|
|
|
|
var obj *fastjson.Object
|
|
|
|
var err error
|
|
|
|
if obj, err = c.value.Object(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
obj.Visit(func(key []byte, v *fastjson.Value) {
|
|
|
|
strKey := util.Bytes2str(key)
|
|
|
|
if _, loaded := knownKeys.LoadOrStore(strKey, nil); !loaded {
|
|
|
|
if typ := FjDetectType(v); typ != TypeUnknown {
|
|
|
|
newKeys.Store(strKey, typ)
|
|
|
|
foundNew = true
|
|
|
|
} else {
|
|
|
|
logs.Logger.Error("FastjsonMetric.GetNewKeys failed to detect field type", zap.String("key", strKey), zap.Reflect("value", v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FastjsonMetric) GetParseObject() (v *fastjson.Value) {
|
|
|
|
return c.value
|
|
|
|
}
|
|
|
|
|
|
|
|
func fjCompatibleInt(v *fastjson.Value) (ok bool) {
|
|
|
|
if v == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeTrue:
|
|
|
|
ok = true
|
|
|
|
case fastjson.TypeFalse:
|
|
|
|
ok = true
|
|
|
|
case fastjson.TypeNumber:
|
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func fjCompatibleFloat(v *fastjson.Value) (ok bool) {
|
|
|
|
if v == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeNumber:
|
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func fjCompatibleDateTime(v *fastjson.Value) (ok bool) {
|
|
|
|
if v == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeNumber:
|
|
|
|
ok = true
|
|
|
|
case fastjson.TypeString:
|
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDefaultInt(nullable bool) (val interface{}) {
|
|
|
|
if nullable {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val = int64(0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDefaultFloat(nullable bool) (val interface{}) {
|
|
|
|
if nullable {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val = float64(0.0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDefaultDateTime(nullable bool) (val interface{}) {
|
|
|
|
if nullable {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val = Epoch
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func FjDetectType(v *fastjson.Value) (typ int) {
|
|
|
|
switch v.Type() {
|
|
|
|
case fastjson.TypeNull:
|
|
|
|
case fastjson.TypeTrue:
|
|
|
|
typ = Int
|
|
|
|
case fastjson.TypeFalse:
|
|
|
|
typ = Int
|
|
|
|
case fastjson.TypeNumber:
|
|
|
|
typ = Float
|
|
|
|
if _, err := v.Int64(); err == nil {
|
|
|
|
typ = Int
|
|
|
|
}
|
|
|
|
case fastjson.TypeString:
|
|
|
|
typ = String
|
|
|
|
if val, err := v.StringBytes(); err == nil {
|
2022-03-07 17:15:08 +08:00
|
|
|
if _, err := parseInLocation(util.Bytes2str(val), time.Local); err == nil {
|
2022-01-26 16:40:50 +08:00
|
|
|
typ = DateTime
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case fastjson.TypeArray:
|
|
|
|
if arr, err := v.Array(); err == nil && len(arr) > 0 {
|
|
|
|
typ2 := FjDetectType(arr[0])
|
|
|
|
switch typ2 {
|
|
|
|
case Int:
|
|
|
|
typ = IntArray
|
|
|
|
case Float:
|
|
|
|
typ = FloatArray
|
|
|
|
case String:
|
|
|
|
typ = StringArray
|
|
|
|
case DateTime:
|
|
|
|
typ = DateTimeArray
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
typ = String
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|