gdbclient/internal/graphsonv3/graphreader.go (336 lines of code) (raw):
/*
* (C) 2019-present Alibaba Group Holding Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/**
* @author : Liu Jianping
* @date : 2019/11/20
*/
package graphsonv3
import (
"encoding/json"
"errors"
"github.com/aliyun/alibabacloud-gdb-go-sdk/gdbclient/graph"
"github.com/aliyun/alibabacloud-gdb-go-sdk/gdbclient/internal"
"go.uber.org/zap"
)
type result struct {
Type string `json:"@type"`
Value json.RawMessage `json:"@value"`
}
type vertexPropertyV3 struct {
Id string `json:"id"`
Value json.RawMessage `json:"value"`
Label string `json:"label"`
}
type vertexV3 struct {
Id string `json:"id"`
Label string `json:"label"`
Properties map[string][]result `json:"properties,omitempty"`
}
type propertyV3 struct {
Key string `json:"key"`
Value json.RawMessage `json:"value"`
}
type edgeV3 struct {
Id string `json:"id"`
Label string `json:"label"`
InV string `json:"inV"`
InVLabel string `json:"inVLabel"`
OutV string `json:"outV"`
OutVLabel string `json:"outVLabel"`
Properties map[string]result `json:"properties,omitempty"`
}
type pathV3 struct {
Labels result `json:"labels"`
Objects result `json:"objects"`
}
type getResultHandler func(r *result) (interface{}, error)
const (
gTypeBool = "g:Bool" // no bool type in '@type'
gTypeInt8 = "gx:Byte"
gTypeInt16 = "g:Int16" // not support short in GDB
gTypeInt32 = "g:Int32"
gTypeInt64 = "g:Int64"
gTypeFloat = "g:Float"
gTypeDouble = "g:Double"
gTypeString = "g:String" // no string type in '@type'
gTypeList = "g:List"
gTypeMap = "g:Map"
gTypeSet = "g:Set"
gTypeBulkSet = "g:BulkSet"
gTypeT = "g:T" // gremlin graph element type string
gTypeVertex = "g:Vertex"
gTypeEdge = "g:Edge"
gTypeVertexProperty = "g:VertexProperty"
gTypeProperty = "g:Property"
gTypePath = "g:Path"
)
var resultRouterMap map[string]getResultHandler
func init() {
resultRouterMap = map[string]getResultHandler{
gTypeInt8: getInt8,
gTypeInt32: getInt32,
gTypeInt64: getInt64,
gTypeFloat: getFloat,
gTypeDouble: getDouble,
gTypeT: getT,
gTypeList: getList,
gTypeMap: getMap,
gTypeSet: getSet,
gTypeBulkSet: getBulkSet,
gTypeVertex: getVertex,
gTypeEdge: getEdge,
gTypeVertexProperty: getVertexProperty,
gTypeProperty: getProperty,
gTypePath: getPath,
}
}
// main route for all json string
func getResult(raw json.RawMessage) ([]interface{}, error) {
var r result
if err := jsonUnmarshal(raw, &r); err != nil {
return nil, err
}
// response start with 'g:List'
if r.Type != gTypeList {
internal.Logger.Error("graphSonV3 response start", zap.String("start type", r.Type))
return nil, errors.New("response starts with not 'List'")
}
return resultListRouter(r.Value)
}
// result list
func resultListRouter(raw json.RawMessage) ([]interface{}, error) {
results := make([]interface{}, 0)
var j []json.RawMessage
err := json.Unmarshal(raw, &j)
if err != nil {
internal.Logger.Error("graphSonV3 error", zap.String("raw", string(raw)), zap.Error(err))
return nil, err
}
for _, jj := range j {
if jj == nil {
continue
}
if n, err := resultRouter(jj); err == nil {
results = append(results, n)
}
}
return results, nil
}
// result single
func resultRouter(raw json.RawMessage) (interface{}, error) {
var j result
if err := json.Unmarshal(raw, &j); err == nil {
if router, ok := resultRouterMap[j.Type]; ok {
return router(&j)
} else {
internal.Logger.Error("graphSonV3 unknown type", zap.String("type", j.Type), zap.String("raw", string(raw)))
return nil, errors.New("un-support type :" + j.Type)
}
} else {
return getBoolOrString(raw)
}
internal.Logger.Error("graphSonV3 un-handle response", zap.String("raw", string(raw)))
return nil, internal.NewDeserializerError("single result", raw, nil)
}
func getBoolOrString(raw json.RawMessage) (interface{}, error) {
var vstr string
if err := json.Unmarshal(raw, &vstr); err == nil {
return vstr, nil
}
var vbool bool
if err := json.Unmarshal(raw, &vbool); err == nil {
return vbool, nil
}
internal.Logger.Error("graphSonV3 un-handle response", zap.String("raw", string(raw)))
return nil, internal.NewDeserializerError("single bool or string", raw, nil)
}
// T type should be string value
func getT(r *result) (interface{}, error) {
var vstr string
err := json.Unmarshal(r.Value, &vstr)
return vstr, err
}
func getListBoolOrString(raw json.RawMessage) ([]interface{}, error) {
var vstr []string
if err := json.Unmarshal(raw, &vstr); err == nil {
results := make([]interface{}, len(vstr))
for i, v := range vstr {
results[i] = v
}
return results, nil
}
var vbool []bool
if err := json.Unmarshal(raw, &vbool); err == nil {
results := make([]interface{}, len(vbool))
for i, v := range vbool {
results[i] = v
}
return results, nil
}
internal.Logger.Error("graphSonV3 un-handle response", zap.String("raw", string(raw)))
return nil, internal.NewDeserializerError("list bool or string", raw, nil)
}
func getNumber(r *result) (float64, error) {
v := 0.0
err := json.Unmarshal(r.Value, &v)
if err != nil {
internal.Logger.Error("graphSonV3 un-handle number", zap.String("raw", string(r.Value)))
return 0, internal.NewDeserializerError("number", r.Value, err)
}
return v, nil
}
func getInt8(r *result) (interface{}, error) {
v, err := getNumber(r)
return int8(v), err
}
func getInt32(r *result) (interface{}, error) {
v, err := getNumber(r)
return int32(v), err
}
func getInt64(r *result) (interface{}, error) {
v, err := getNumber(r)
return int64(v), err
}
func getFloat(r *result) (interface{}, error) {
v, err := getNumber(r)
return float32(v), err
}
func getDouble(r *result) (interface{}, error) {
return getNumber(r)
}
func getList(r *result) (interface{}, error) {
return resultListRouter(r.Value)
}
// ugly 'set' serializer
// and return list([]interface{}) due to no set in golang
func getSet(r *result) (interface{}, error) {
return resultListRouter(r.Value)
}
// ugly 'map' serializer
func getMap(r *result) (interface{}, error) {
result := make(map[interface{}]interface{})
v, err := resultListRouter(r.Value)
if err != nil {
return nil, err
}
if len(v)%2 != 0 {
// nu-pair map key-value
return nil, internal.NewDeserializerError("map", r.Value, errors.New("un-pair map"))
}
for i := 0; i < len(v); {
key := v[i]
i++
value := v[i]
i++
result[key] = value
}
return result, nil
}
func getBulkSet(r *result) (interface{}, error) {
result := graph.NewBulkSet()
v, err := resultListRouter(r.Value)
if err != nil {
return nil, err
}
if len(v)%2 != 0 {
// nu-pair bulkSet key-value
return nil, internal.NewDeserializerError("bulkSet", r.Value, errors.New("un-pair bulkSet"))
}
for i := 0; i < len(v); {
key := v[i]
i++
value := v[i]
i++
if vp, ok := value.(int64); ok {
result.Add(key, vp)
} else {
internal.Logger.Error("graphSonV3 bulkSet value type error", zap.Any("value", value))
}
}
return result, nil
}
func getVertexProperty(r *result) (interface{}, error) {
v := &vertexPropertyV3{}
err := json.Unmarshal(r.Value, v)
if err != nil {
return nil, internal.NewDeserializerError("vertexProperty", r.Value, err)
}
props, err := resultRouter(v.Value)
if err != nil {
return nil, err
}
vp := graph.NewDetachedVertexProperty(graph.NewDetachedElement(v.Id, v.Label), props)
return vp, nil
}
func getVertex(r *result) (interface{}, error) {
v := &vertexV3{}
err := json.Unmarshal(r.Value, v)
if err != nil {
return nil, internal.NewDeserializerError("vertex", r.Value, err)
}
vertex := graph.NewDetachedVertex(graph.NewDetachedElement(v.Id, v.Label))
for _, props := range v.Properties {
for _, prop := range props {
p, err := getVertexProperty(&prop)
if err != nil {
return nil, err
}
if vp, ok := p.(*graph.DetachedVertexProperty); ok {
// attach vertex to this prop
vp.SetVertex(vertex)
// add prop to vertex element
vertex.AddProperty(vp)
}
}
}
return vertex, nil
}
func getProperty(r *result) (interface{}, error) {
v := &propertyV3{}
err := json.Unmarshal(r.Value, v)
if err != nil {
return nil, internal.NewDeserializerError("property", r.Value, err)
}
n, err := resultRouter(v.Value)
if err != nil {
return nil, err
}
prop := graph.NewDetachedProperty(v.Key, n, nil)
return prop, nil
}
func getEdge(r *result) (interface{}, error) {
v := &edgeV3{}
err := json.Unmarshal(r.Value, v)
if err != nil {
return nil, internal.NewDeserializerError("edge", r.Value, err)
}
inVertex := graph.NewDetachedVertex(graph.NewDetachedElement(v.InV, v.InVLabel))
outVertex := graph.NewDetachedVertex(graph.NewDetachedElement(v.OutV, v.OutVLabel))
edge := graph.NewDetachedEdge(graph.NewDetachedElement(v.Id, v.Label))
edge.SetVertex(true, outVertex)
edge.SetVertex(false, inVertex)
for _, prop := range v.Properties {
p, err := getProperty(&prop)
if err != nil {
return nil, err
}
if pp, ok := p.(*graph.DetachedProperty); ok {
edge.AddProperty(pp)
}
}
return edge, nil
}
func getPath(r *result) (interface{}, error) {
v := &pathV3{}
err := json.Unmarshal(r.Value, v)
if err != nil {
return nil, internal.NewDeserializerError("path", r.Value, err)
}
if v.Labels.Type != gTypeList || v.Objects.Type != gTypeList {
return nil, internal.NewDeserializerError("path", r.Value, errors.New("inner type error"))
}
objects, err := resultListRouter(v.Objects.Value)
if err != nil {
return nil, err
}
labels, err := resultListRouter(v.Labels.Value)
if err != nil {
return nil, err
}
// check size
if len(objects) != len(labels) {
return nil, internal.NewDeserializerError("path", r.Value, errors.New("un-pair labels and objects"))
}
path := graph.NewDetachedPath()
for i := 0; i < len(objects); i++ {
var labels_str []string
label := labels[i].([]interface{})
labels_str = make([]string, len(label), len(label))
for i := 0; i < len(label); i++ {
labels_str[i] = label[i].(string)
}
path.Extend(objects[i], labels_str)
}
return path, nil
}