如图所示,整个图包含2类点 person 和 software,2类边 knows 和 created,和几类属性 id、name、age、lang、weight。
环境准备
1. 安装 Go 语言环境,参考 Go 语言官网。
2. 获取图数据库的连接参数。在 控制台 实例详情页中可以查看实例的 VIP 和 PORT,即内网地址和 Gremlin 端口。
示例程序
新建一个 demo 目录,并初始化 module
mkdir graph_democd graph_demogo mod init demo
示例项目目录结构
|- graph_demo|- go.mod|- go.sum|- main.go|- model|- meta.go|- property.go|- vertex.go|- edge.go
定义点、边及属性等模型
meta.go: 属性、点边等对应的元数据定义
package modeltype PropertyType stringconst (PropertyLong = "T_LONG"PropertyInt = "T_INT"PropertyInt64 = "T_INT64"PropertyString = "T_STRING"PropertyDouble = "T_DOUBLE")type PropertyMeta struct {Label stringType PropertyTypeDefault string}type VertexMeta struct {Label stringPrimary stringProperties []string}type EdgeMeta struct {Label stringSrcVertexLabel stringDstVertexLabel stringProperties []string}
property.go: 属性模型定义
package modeltype Property struct {Type string `json:"@type"`Value PropertyValue `json:"@Value"`}type PropertyValue struct {Key string `json:"key"`Value interface{} `json:"value"`}
vertex.go: 点模型定义
package modelimport "encoding/json"type VertexList struct {listOfVertices ListVertices []Vertex}type List struct {Type string `json:"@type"`Value []interface{} `json:"@value"`}type Vertex struct {Type string `json:"@type"`Value VertexValue `json:"@value"`}type VertexValue struct {ID interface{} `json:"id"`Label string `json:"label"`Properties map[string][]Property `json:"properties,omitempty"`}func (vl *VertexList) UnmarshalJSON(data []byte) error {if err := json.Unmarshal(data, &vl.listOfVertices); err == nil {if data, err = json.Marshal(vl.listOfVertices.Value); err != nil {return err}}return json.Unmarshal(data, &vl.Vertices)}
edge.go: 边模型定义
package modelimport "encoding/json"type EdgeList struct {listOfEdges ListEdges []Edge}type Edge struct {Type string `json:"@type"`Value EdgeValue `json:"@value"`}type EdgeValue struct {ID interface{} `json:"id"`Label string `json:"label"`InVLabel string `json:"inVLabel,omitempty"`OutVLabel string `json:"outVLabel,omitempty"`InV interface{} `json:"inV,omitempty"`OutV interface{} `json:"outV,omitempty"`Properties map[string][]Property `json:"properties,omitempty"`}func (el *EdgeList) UnmarshalJSON(data []byte) error {if err := json.Unmarshal(data, &el.listOfEdges); err == nil {if data, err = json.Marshal(el.listOfEdges.Value); err != nil {return err}}return json.Unmarshal(data, &el.Edges)}
数据库操作
package mainimport ("fmt""log""strings""tutorial/model""github.com/northwesternmutual/grammes")type Tutorial struct {*grammes.Client}func New(host string, port int, username, password string) (*Tutorial, error) {url := fmt.Sprintf("ws://%s:%d", host, port)c, err := grammes.DialWithWebSocket(url, grammes.WithAuthUserPass(username, password))if err != nil {return nil, err}return &Tutorial{Client: c}, nil}func (t *Tutorial) CreatePropertyMetas(metas ...model.PropertyMeta) error {for _, meta := range metas {expr := fmt.Sprintf(`s.addP("%s", "%s", "%s")`, meta.Label, meta.Type, meta.Default)_, err := t.ExecuteStringQuery(expr)if err != nil {return err}}return nil}func (t *Tutorial) CreateVertexMetas(metas ...model.VertexMeta) error {for _, meta := range metas {expr := fmt.Sprintf(`s.addV("%s", "%s", ["%s"])`, meta.Label, meta.Primary, strings.Join(meta.Properties, "\\",\\""))_, err := t.ExecuteStringQuery(expr)if err != nil {return err}}return nil}func (t *Tutorial) CreateEdgeMetas(metas ...model.EdgeMeta) error {for _, meta := range metas {expr := fmt.Sprintf(`s.addE("%s", "%s", "%s", ["%s"])`, meta.Label, meta.SrcVertexLabel, meta.DstVertexLabel,strings.Join(meta.Properties, "\\",\\""))_, err := t.ExecuteStringQuery(expr)if err != nil {return err}}return nil}func createMetas(tutorial *Tutorial) {propMetas := []model.PropertyMeta{{Label: "id", Type: model.PropertyLong, Default: "0"},{Label: "name", Type: model.PropertyString, Default: ""},{Label: "age", Type: model.PropertyInt, Default: "0"},{Label: "weight", Type: model.PropertyDouble, Default: "0.0"},{Label: "lang", Type: model.PropertyString, Default: ""},}if err := tutorial.CreatePropertyMetas(propMetas...); err != nil {log.Fatal("create property meta err ", err)}vertexMetas := []model.VertexMeta{{Label: "person", Primary: "id", Properties: []string{"name", "age"}},{Label: "software", Primary: "id", Properties: []string{"name", "lang"}},}if err := tutorial.CreateVertexMetas(vertexMetas...); err != nil {log.Fatal("create vertex meta err ", err)}edgeMetas := []model.EdgeMeta{{Label: "knows", SrcVertexLabel: "person", DstVertexLabel: "person", Properties: []string{"weight"}},{Label: "created", SrcVertexLabel: "person", DstVertexLabel: "software", Properties: []string{"weight"}},}if err := tutorial.CreateEdgeMetas(edgeMetas...); err != nil {log.Fatal("create edge meta err ", err)}}func addVertexAndEdge(tutorial *Tutorial) {//添加点//添加点 person,其中 id=1,属性 name=marko,age=29_, err := tutorial.ExecuteBoundStringQuery("g.addV(LABEL).property(id, ID).property('name', NAME).property('age', AGE) ", map[string]string{"ID": "1", "LABEL": "person", "NAME": "marko", "AGE": "29"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加点 person,其中 id=2,属性 name=vadas,age=27_, err = tutorial.ExecuteBoundStringQuery("g.addV(LABEL).property(id, ID).property('name', NAME).property('age', AGE) ", map[string]string{"ID": "2", "LABEL": "person", "NAME": "vadas", "AGE": "27"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加点 person,其中 id=4,属性 name=josh,age=32_, err = tutorial.ExecuteBoundStringQuery("g.addV(LABEL).property(id, ID).property('name', NAME).property('age', AGE) ", map[string]string{"ID": "4", "LABEL": "person", "NAME": "josh", "AGE": "32"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加点 person,其中 id=6,属性 name =peter,age=35_, err = tutorial.ExecuteBoundStringQuery("g.addV(LABEL).property(id, ID).property('name', NAME).property('age', AGE) ", map[string]string{"ID": "6", "LABEL": "person", "NAME": "peter", "AGE": "35"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加点 software,其中 id=3,属性 name=lop,lang=java_, err = tutorial.ExecuteBoundStringQuery("g.addV(LABEL).property(id, ID).property('name', NAME).property('lang', LANG) ", map[string]string{"ID": "3", "LABEL": "software", "NAME": "lop", "LANG": "java"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加点 software,其中 id=5,属性 name=ripple,lang=java_, err = tutorial.ExecuteBoundStringQuery("g.addV(LABEL).property(id, ID).property('name', NAME).property('lang', LANG) ", map[string]string{"ID": "5", "LABEL": "software", "NAME": "ripple", "LANG": "java"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加边//添加边 knows,其中起点 id 为1,终点 id 为2_, err = tutorial.ExecuteBoundStringQuery("g.V(SRC).addE(LABEL).to(V(DST)) ", map[string]string{"LABEL": "knows", "SRC": "1", "DST": "2"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加边 knows,其中起点 id 为1,终点 id 为4_, err = tutorial.ExecuteBoundStringQuery("g.V(SRC).addE(LABEL).to(V(DST)) ", map[string]string{"LABEL": "knows", "SRC": "1", "DST": "4"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加边 created,其中起点 id 为1,终点 id 为3,属性 weight=0.4_, err = tutorial.ExecuteBoundStringQuery("g.V(SRC).addE(LABEL).to(V(DST)).property('weight', WEIGHT) ", map[string]string{"LABEL": "created", "SRC": "1", "DST": "3", "WEIGHT": "0.4"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加边 created,其中起点 id 为6,终点 id 为3,属性 weight=0.2_, err = tutorial.ExecuteBoundStringQuery("g.V(SRC).addE(LABEL).to(V(DST)).property('weight', WEIGHT) ", map[string]string{"LABEL": "created", "SRC": "6", "DST": "3", "WEIGHT": "0.2"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加边 created,其中起点 id 为4,终点 id 为3,属性 weight=0.4_, err = tutorial.ExecuteBoundStringQuery("g.V(SRC).addE(LABEL).to(V(DST)).property('weight', WEIGHT) ", map[string]string{"LABEL": "created", "SRC": "4", "DST": "3", "WEIGHT": "0.4"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}//添加边 created,其中起点 id 为4,终点 id 为5,属性 weight=1.0_, err = tutorial.ExecuteBoundStringQuery("g.V(SRC).addE(LABEL).to(V(DST)).property('weight', WEIGHT) ", map[string]string{"LABEL": "created", "SRC": "4", "DST": "5", "WEIGHT": "1.0"}, map[string]string{})if err != nil {log.Fatal("ExecuteBoundStringQuery addV err ", err)}}func main() {tutorial, err := New("KONISGRAPH_VIP", KONISGRAPH_PORT, "your useranme", "your password")//KONISGRAPH_VIP 图数据库 KonisGraph 实例的内网地址 vip,如 10.xx.xx.107//KONISGRAPH_PORT 图数据库 KonisGraph 实例的 Gremlin 端口,如 8186if err != nil {log.Fatal("open gremlin connection err ", err)}// 创建属性、点、边等元数据createMetas(tutorial)// 添加点和边addVertexAndEdge(tutorial)// 做一些查询// 查看 marko 的信息data, _ := tutorial.ExecuteBoundStringQuery("g.V().hasLabel(LABEL).has('name', NAME).valueMap()", map[string]string{"LABEL": "person", "NAME": "marko"}, map[string]string{})for _, item := range data {log.Println(string(item))}// 查找 marko 都认识哪些人data, _ = tutorial.ExecuteBoundStringQuery("g.V().hasLabel(LABEL).has('name', NAME).out(OUT_LABEL)", map[string]string{"LABEL": "person", "NAME": "marko","OUT_LABEL":"knows"}, map[string]string{})for _, item := range data {var vertices model.VertexListif err := vertices.UnmarshalJSON(item); err != nil {log.Fatal("unmarshal resp err: ", err)}var names []interface{}for _, vertex := range vertices.Vertices {names = append(names, vertex.Value.Properties["name"][0].Value.Value)}log.Print("Who marko knows: ", names)}// 查找哪些人创建了软件 lopdata, _ = tutorial.ExecuteBoundStringQuery("g.V().hasLabel(LABEL).has('name', NAME).in(IN_LABEL)", map[string]string{"LABEL": "software", "NAME": "lop","IN_LABEL":"created"}, map[string]string{})for _, item := range data {var vertices model.VertexListif err := vertices.UnmarshalJSON(item); err != nil {log.Fatal("unmarshal resp err: ", err)}var names []interface{}for _, vertex := range vertices.Vertices {names = append(names, vertex.Value.Properties["name"][0].Value.Value)}log.Println("Who creates software lop: ", names)}// 统计点数量, 返回值为数组,转 string 后为 {"@type":"g:List","@value":[{"@type":"g:Int64","@value":9999999999}]},真正的返回值为 value 后的9999999999,需要自己处理,其他非点边返回数据处理方式类似data, _ = tutorial.ExecuteBoundStringQuery("g.V().count()", map[string]string{}, map[string]string{})for _, item := range data {log.Println("Who creates software lop: ", string(item))}//关闭客户端,客户端可复用tutorial.Close()}
运行示例程序
go run main.go