webooru
webooru copied to clipboard
cayley图数据库 | gzm's blogs
郭柱明
图数据库背景
在此之前已经简单记录了一下图数据的背景和发展现状 图数据引擎那部分现在还是知之甚少 还没深入去了解
图数据库背景
cayley 优点
- go 语言实现
- 运行简单 (三四条命令)
- 支持 http 接口以及 REPL 交互式
- 支持多种语言进行查询
- Gizmo
- MQL
- GraphQL
- 支持多种后端存储
- KVs: Bolt, LevelDB
- NoSQL: MongoDB, ElasticSearch, CouchDB/PouchDB
- SQL: PostgreSQL, CockroachDB, MySQL
- n-memory, ephemeral
- 模块化编程 容易拓展
- 良好的测试覆盖
- 速度快
- 免费
运行和应用
这里真的要非常注意 因为 cayley 提供的官方文档相当有限 会导致很多新手搞不清到底怎么使用这个图数据
cayley 使用两种方式
- 作为应用运行使用
- 作为第三方库运行使用
作为应用运行使用
下面是官方文档中作为应用进行运行使用的部分
参考链接
- 下载二进制文件解压或者下载源码进行编译
- 配置
- 导入数据
- 运行
运行使用一下命令
./cayley http --config=cayley.yml --host=0.0.0.0:64210
或者在 Linux 系统下后台运行
./cayley http --config=cayley.yml --host=0.0.0.0:64210 &
可以看到已经在后台运行 监听 64210 端口
image_1ci3f3d0g1hg21bt411nh4f04r09.png-26.4kB
上面这种运行方式是使用一个 go 原生net/http架起一个 web 服务器 以方便开放人员作为管理员可以在 web 界面上使用 UI 进行数据库的增删查找 如下
image_1ci24pfveosv1hudifc10ivmcl9.png-149.3kB
但是 对于这个 http 接口 官方并没有提供进一步的封装 意味着如果你要使用这种方式运行 cayley 并且在你的项目中连接这个数据库 那么你是需要自己拼凑我们用来增删查找的 Gizmo 语句 (如果选择使用 Gizmo) 类似以下
|
1
2
3
|
g.V().Has("
.Out("</film/film/starring>").Out("</film/performance/actor>")
.Out("
|
然后 post 给服务端上的 cayley
这就意味着你需要进一步封装这个 http 接口 就跟写一个爬虫一样 虽然在 Python 上已经有人做好了这部分工作
pyley一个封装了 cayley http 接口的客户端
但是 go 语言我目前还没发现有类似的 官方亦然
作为第三方库运行使用
我个人推荐将 cayley 作为第三方库来进行运行 一个是 go get 安装 cayley 第三方库的时候就顺带安装了 cayley 因为 cayley 本身就是 go 编写的 但是这方面的使用官方文档上简直坑得不行 仅仅提供了几个 hello world 对刚接触的开发者来说真的非常不友好
官方提供的几个example
hello_world和transaction都是使用
cayley.NewMemoryGraph()
这个 api 使用内存作为临时的存储位置进行存储的
hello_bolt和hello_schema都是使用一个叫 bolt 的键值对数据库进行存储的 这个需要一个路径来存存储着数据的文件
除此之外 官方文档就没提供太多 实际在 go 语言中链接 cayley 的方式了
但是我们小组的领导决定使用 mongodb 作为 backend 进行存储 上面的例子都没有太大的实际应用意义
我参照了 bolt 作为 backend 的例子修改了一下 获得了使用 mongodb 作为 backend 的方法
注释已经将每一部分讲的很清楚了 所以就不说其他什么了
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package main
import (
"context"
"fmt"
"log"
"github.com/cayleygraph/cayley"
"github.com/cayleygraph/cayley/graph"
_ "github.com/cayleygraph/cayley/graph/nosql/mongo"
"github.com/cayleygraph/cayley/quad"
)
func main() {
err := graph.InitQuadStore("mongo", "mongodb://120.92.100.60:27017", nil)
if err != nil {
log.Fatal(err)
}
store, err := cayley.NewGraph("mongo", "mongodb://120.92.100.60:27017", nil)
if err != nil {
log.Fatalln(err)
}
store.AddQuad(quad.Make("phrase of the day", "is of course", "Hello BoltDB!", "demo graph"))
p := cayley.StartPath(store, quad.String("phrase of the day")).Out(quad.String("is of course"))
it, _ := p.BuildIterator().Optimize()
it, _ = store.OptimizeIterator(it)
defer it.Close()
ctx := context.TODO()
for it.Next(ctx) {
token := it.Result()
value := store.NameOf(token)
nativeValue := quad.NativeOf(value)
fmt.Println(nativeValue)
}
if err := it.Err(); err != nil {
log.Fatalln(err)
}
}
|
输出
image_1ci3gj9n4178dooh1a7g166fsbbm.png-24.1kB
事务
- 初始化数据库获取一个 handle
- 获取一个事务
- 添加多个四元组到事务中
- 将事务添加到 handle 中
- 查询跟之前没什么区别
image_1ci8s50oq9jqrrt82c1hqk19nq19.png-107.8kB
数据结构与 api
-
更新或者删除四元组
cayley 的设计中是没有更新四元组这个 api 如果想要更新一个四元组只能先删除这个四元组 然后再重新添加一条新的四元组
image_1ci9habhi1iob1b5094s14k518t52j.png-105kB
数据集
[
](http://static.zybuluo.com/gzm1997/clywp0fgdwenshe2fbbw8c4a/image_1ci3jdcegdal17q8tga180t8at13.png "image_1ci3jdcegdal17q8tga180t8at13.png-135.6kB")image_1ci3jdcegdal17q8tga180t8at13.png-135.6kB
cayley api
四元组 含有主谓宾标签
|
1
2
3
|
func Quad(subject, predicate, object, label interface{}) quad.Quad {
return quad.Make(subject, predicate, object, label)
}
|
三元组 含有主谓宾 没有标签 (标签为 nil)
|
1
2
3
|
func Triple(subject, predicate, object interface{}) quad.Quad {
return Quad(subject, predicate, object, nil)
}
|
处理器 handle
|
1
|
type Handle = graph.Handle
|
一个 handle 的结构是
|
1
2
3
4
|
type Handle struct {
QuadStore
QuadWriter
}
|
包含一个匿名的四元组存储成员 一个匿名的四元组写入成员
新建一个处理器
|
1
|
func NewGraph(name, dbpath string, opts graph.Options) (*Handle, error)
|
返回一个 handle
新建一个内存存储的处理器
|
1
|
func NewMemoryGraph() (*Handle, error)
|
从它的具体实现 可以知道这基于memstore内存进行存储的处理器
|
1
2
3
|
func NewMemoryGraph() (*Handle, error) {
return NewGraph("memstore", "", nil)
}
|
迭代器
|
1
|
type Iterator = graph.Iterator
|
query path
|
1
|
type Path = path.Path
|
Gizmo
因为我们上面都是使用 Gizmo 作为查询语言进行查询的 所以很有必要记录一下 Gizmo 的基本语法
基本语句太多了 所以只简要记录一下常用的语句
- graph.M()
是
graph.Morphism()
的缩写 Morphism 创建一条路径 这样允许 Gizmo 的路径可以被复用
类似如下
var shorterPath = graph.Morphism().Out("foo").Out("bar")
graph.V(*)
是
graph.Vertex([nodeId],[nodeId]...)
的缩写 这个语句返回一个根据给定的点作为始点的 query path 没有 nodeID 的话以为着所有的点
- Path object .Morphism() 和. Vertex() 和这个函数都可以创建一个 query path 对象
path.And(path)
是
path.Intersect(path)
的缩写 就是逻辑与的意思
例子
|
1
|
g.V("
|
path.As(tags)
是
path.Tag(tags)
的缩写
Tag 保存一个 list 的 nodes 存到给定的 tag 主要是为了保存我们的工作或者为了了解一个 path 是怎样到达终点的 我们需要这个 tag 来进行标志一下
path.Back(tag)
跟 tag 来后退到 tag 上面去
path.Both([predicatePath], [tags])
获取同时进来和出去的节点
例子
|
1
|
g.V("
|
path.Count()
返回结果的数量
path.Difference(path)
是
path.Except(path)
的别名
Except 移除当前 path 上匹配了这个 query path 删的所有 paths
path.Follow(path)
是一种使用. Morphism() 准备好的 query path 的方式
path.ForEach(callback) or (limit, callback)
遍历 query path
path.Has(predicate, object)
一般应用于所有节点或者一个堆节点按照某个谓语进行查询 例子
|
1
|
g.V().Has("
|
path.In([predicatePath], [tags])
in 就比较直观了 就是按照这个谓语进来到这个节点
path.Out([predicatePath], [tags])
类似 就是按照这个谓语出来这个节点
path.Or(path)
是
path.Union(path)
别名
这个也比较直观 就是逻辑或
path.Save(predicate, tag)
保存所有使用这个谓语进入这个 tag 的节点 并且不遍历
https://gzm1997.github.io/2018/08/06/cayley%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93/