Thanks for following topic:
- Go-Gin 学习笔记 github-link 【from: 夜未央天未明】
博主用清晰的代码结构很好的展示了Gin 框架, 所以本文不再赘述有关Gin 框架的内容 - 用cytoscape.js展示neo4j网络关系图【from: 钟柱】
作者所用事例简单清晰,解释的很详细,有助于初学者上手
本文将focus 在:
- neo4j golang access api
- response jquery neo4j data with Gin
Neo4j API
在neo4j 的官方文档中,给出了golang api 的官方文档:
Using Neo4j from Go
并且在文末提供了 expample github project 的链接:
https://github.com/neo4j-examples/golang-bolt-movie-example
但是,该项目使用的api 在五年前已经不再维护了,所提供的API 也无法访问neo4j 2.x
所以,请不要再使用此driver去开发neo4j的项目了。这个项目名称叫:
- github.com/johnnadratowski/golang-neo4j-bolt-driver
如果你所借鉴的代码还在使用以下声明,请及时替换,并且根据官方新的driver 进行修改:
import (
"github.com/johnnadratowski/golang-neo4j-bolt-driver"
)
新的官方driver 在这里:
https://github.com/neo4j/neo4j-go-driver
可以这样引用:
import (
"github.com/neo4j/neo4j-go-driver/v4/neo4j"
)
安装的时候,最好在自己Go 的安装目录下建立v4子目录,然后download : neo4j v4.2.1 source code解压后copy 到 你的v4 目录下。
Btw, 我使用的是 VS code. 是在vs code 的terminal 窗口下安装的Gin 和 neo4j:
- go get -u -v github.com/gin-gonic/gin
- go get github.com/neo4j/neo4j-go-dirver/neo4j
write API可以参见driver 本身提供的例子:
utils_test.go
我自写了两个 read API: 读 node 和 读relationship
返回的是 slice: []neo4j.Node 和 []neo4j.Relationship
package database
import (
"log"
"github.com/neo4j/neo4j-go-driver/v4/neo4j"
)
func CreateDriver(uri, username, password string) (neo4j.Driver, error) {
return neo4j.NewDriver(uri, neo4j.BasicAuth(username, password, ""))
}
func CloseDriver(driver neo4j.Driver) error {
return driver.Close()
}
func NodeCreate(driver neo4j.Driver, Cypher string, DB string) error {
session := driver.NewSession(neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close()
_, err := session.WriteTransaction(func(tx neo4j.Transaction) (interface{}, error) {
result, err := tx.Run(Cypher, nil)
if err != nil {
log.Println("wirte to DB with error:", err)
return nil, err
}
return result.Consume()
})
return err
}
func NodeQuery(driver neo4j.Driver, Cypher string, DB string) ([]neo4j.Node, error ) {
var list []neo4j.Node
session := driver.NewSession(neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
defer session.Close()
_, err := session.ReadTransaction(func(tx neo4j.Transaction)(interface{}, error) {
result, err := tx.Run(Cypher, nil)
if err != nil {
return nil, err
}
for result.Next() {
record := result.Record()
if value, ok := record.Get("n"); ok {
node := value.(neo4j.Node)
list = append(list, node)
}
}
if err = result.Err(); err != nil {
return nil, err
}
return list, result.Err()
})
if err != nil {
log.Println("Read error:", err)
}
return list, err
}
func EdgeQuery(driver neo4j.Driver, Cypher string, DB string) ([]neo4j.Relationship, error ) {
var list []neo4j.Relationship
session := driver.NewSession(neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
defer session.Close()
_, err := session.ReadTransaction(func(tx neo4j.Transaction)(interface{}, error) {
result, err := tx.Run(Cypher, nil)
if err != nil {
log.Println("EdggeQuery Run failed: ", err)
return nil, err
}
for result.Next() {
record := result.Record()
if value, ok := record.Get("r"); ok {
relationship := value.(neo4j.Relationship)
list = append(list, relationship)
}
}
if err = result.Err(); err != nil {
return nil, err
}
return list, result.Err()
})
if err != nil {
log.Println("Read error:", err)
}
return list, err
}
我在application 里调用的这两个 read API 的example code:
(虽然预留了db name,但是neo4j community 版不支持 multiple database,所以暂时没用)
package models
import (
"fmt"
"log"
"strconv"
"../database"
)
type Aomobj struct {
NodeId string json:"id" form:"id"
ObjId string json:"objId" form:"objId"
ObjString string json:"name" form:"name"
}
type Aomedge struct {
EdgeId string json:"id" form:"id"
EdgeName string json:"relationship" form:"relationship"
StartId string json:"source" form:"source"
EndId string json:"target" form:"target"
}
type NodeData struct {
Data Aomobj json:"data" form:"data"
}
type EdgeData struct {
Data Aomedge json:"data" form:"data"
}
var (
neo4jURL = "bolt://localhost:7687"
)
func GetAomObjList( count int32) (nodes []NodeData) {
nodes = make([]NodeData, 0)
driver, err := database.CreateDriver(neo4jURL, "neo4j", "neo4j")
if err != nil {
log.Println("error connecting to neo4j:", err)
return nil
}
data, err := database.NodeQuery(driver, fmt.Sprintf("MATCH (n:AOM) RETURN n LIMIT %d", count ), "")
for i:=0; i < len(data); i++ {
var node NodeData
node.Data.NodeId = strconv.FormatInt(data[i].Id, 10)
node.Data.ObjId = data[i].Props["OID"].(string)
node.Data.ObjString = data[i].Props["Desc"].(string)
nodes = append(nodes, node)
}
database.CloseDriver(driver)
return nodes
}
func GetAomObjRelationship( count int32) (Edges []EdgeData) {
Edges = make([]EdgeData, 0)
driver, err := database.CreateDriver(neo4jURL, "neo4j", "neo4j")
if err != nil {
log.Println("error connecting to neo4j:", err)
return nil
}
data, err := database.EdgeQuery(driver, fmt.Sprintf("MATCH ()-[r]->() RETURN r LIMIT %d",count ), "")
for i:=0; i < len(data); i++ {
var edge EdgeData
edge.Data.EdgeId = "r" + strconv.FormatInt(data[i].Id,10)
edge.Data.EdgeName = data[i].Type
edge.Data.StartId = strconv.FormatInt(data[i].StartId, 10)
edge.Data.EndId = strconv.FormatInt(data[i].EndId,10)
Edges = append(Edges, edge)
}
database.CloseDriver(driver)
return Edges
}
之所以要在app 数据结构(Aomobj 和 Aomedge)外面套一层 “data” 封装,是为了后面返回给cytoscape.js 的时候方便些.
注意 !!!
edge.Data.EdgeId = "r" + strconv.FormatInt(data[i].Id,10)
我在生成edge ID 的时候,增加了字母 “r” (relationship)而不是直接使用了neo4j分配的ID,因为neo4j生成的edge id 和 node id 是有重叠(overlap)的,所以在此要和node 的id 区分开,因为:
*** cytoscape.js 不允许edge 和node 的id 有冲突!!!**
please reference:cytoscape.issue 779
每个node 的结构可以在neo4j 的brower 里查看, 这里给出一个例子,便于大家理解:
图片里的就是一个 aomObj node.
“identity” 字段是 neo4j 自动分配的,数据类型 int64, 存在neo4j.Node.Id 中。
我在 app 读取的语句:
node.Data.NodeId = strconv.FormatInt(data[i].Id, 10)
之所以转成string 类型,是因为在传递给cytoscape.js 的时候,所有字段都是string 类型的。
labels 也可以读取到,它对应着字段:neo4j.Node.Labels
* 注意 是labels,因为一个node 支持被mark 上多种label
其它的属性保存在neo4j.Node.Props 中,需要根据这个node 的已有属性去读取。比如我创建这个node 的时候,建立的”OID” 属性,那么读的时候 就这样:
data[i].Props["OID"].(string)
关于 “Edge”, 也就是relationship 和 node 一样,有id 和属性,但是没有label. it looks like this:
在Gin 里返回 jquery 的结果
在web browser 上的点击,触发里浏览器向server 发送请求,server 回应了http 200 和 一个html 后,在浏览器端运行了响应的js, 在此以 “钟柱” 的code.js 为例:
$(function(){
$.get('/home/graph', function(result) {
console.log('come into jquery GET')
var style = [
{
selector: 'node',
css: {
'content': 'data(name)',
'color': 'white',
'text-valign': 'center',
'text-outline-color': '#6699FF',
'background-color': '#6699FF',
'height': 60,
'width': 60,
'font-size': '8px',
'font-weight': 'bold',
}
},
{
selector: 'edge',
css: {
'content': 'data(relationship)',
'curve-style': 'bezier',
'target-arrow-shape': 'triangle',
'line-color': '#CCCCCC',
'width': 1,
'font-size': 8px',
}
}
];
var cy = cytoscape({
container: document.getElementById('cy'),
style: style,
layout: { name: 'cose'},
elements: result.elements
console.log('Get data in Jquery')
console.log(result.elements)
});
}, 'json');
});
code.js trigger GET 请求,server 端响应这个请求后,返回 result 参数:
elements: result.elements
这个result 参数是json 格式的,用于传递给cytoscape.js使用
大致的格式 looks like this:
{
"elements": {
"edges": [
{"data": {"relationship": "isNotFriend", "source": "16", "target": "318"}},
],
"nodes": [
{"data": {"id": "16", "label": "AOM", "OID": 100, "name": "Tom"}},
{"data": {"id": "318", "label": "AOM", "OID": "900", "name": "Jerry"}},
]
}
}
我们用读取API 从neo4j 中读取数据后,只要组织成以上结构,然后调用Gin.H{} 就可以了,不需要调用encoding.json 方法.
注意: 所有需要转成json 格式的变量名称,依旧需要遵循首字母大写的原则
package apis
import (
"log"
"net/http"
"../models"
"github.com/gin-gonic/gin"
)
type elements struct {
Nodes []models.NodeData json:"nodes" form:"nodes"
Edges []models.EdgeData json:"edges" form:"edges"
}
func GetAomObj(c *gin.Context) {
log.Println("call GetAomObj")
nodes := models.GetAomObjList(200)
edges := models.GetAomObjRelationship(200)
e :=elements{Nodes:nodes, Edges: edges}
c.JSON(http.StatusOK, gin.H{"elements": e})
}
打开浏览器的调试功能,可以看到js 的log和返回的数据:
Original: https://blog.csdn.net/weixin_44104670/article/details/113075483
Author: 带枷头陀
Title: Golang + Gin + cytocsape.js + neo4j
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/595850/
转载文章受原作者版权保护。转载请注明原作者出处!