Dgraph是一个分布式的开源图数据库, 官方文档地址https://docs.dgraph.io/
, 建议英文水平好的朋友可以直接去访问了解. 本文仅是对自己使用Dgraph的过程做一个总结, 着重介绍它的查询语法和一些有用而有重要的查询.
Dgraph和其它的大多数数据库一样, 也用schema来描述数据的类型(本质上都是图的边), 支持的类型如下
类型 | 描述 |
---|---|
int | 64位有符号整数 |
float | 双精度浮点数 |
string | 字符串 |
bool | 布尔类型 |
dateTime | RFC3339格式时间戳 |
geo | 地理位置类型 |
password | string (encrypted) |
uid | 图的边类型,用于由一个节点指向另一个节点,且带有方向 |
Dgraph中有节点和值两种类型, 每个节点都有一个内部分配的64位整数的uid属性, 用于唯一标记该节点. 节点与节点之间由
uid
类型的边连接, 节点与值之间由标量类型的边连接.
Dgraph的数据导入遵循W3C标准RDF格式
, 这个格式是一种三元组格式, 形式如下:
<subject> <predicate> <object> .
subject 代表图的一个节点, predicate代表节点的边名, object代表边的值(值的类型就是上述schema的类型)
写入例子1:
<0x01> <name> "Alice" .
这个三元组, 在图的场景下表示相当于: uid=0x01
的节点的name
边指向了字符串Alice
, 在关系数据库的场景相当于: 更新id=1的记录的name字段为Alice
写入例子2:
_:alice <name> "Alice" .
_:alice <friend> _:bob .
_:bob <name> "Betty" .
在不知道uid的场景下, 写入数据时每个节点由dgraph内部分配一个uid, 且可以用_:identifier
的形式代表该节点, 以方便后面继续使用. 比如上述例子表示, 名叫"Alice"的人(即节点)有个名叫"Betty"的朋友.
除了三元组的格式外, dgraph还提供了json格式的数据写入, 比如 写入例子2 的三元组可用下面json表示:
{
"uid": "_:alice",
"name": "Alice",
"friend": {
"uid": "_:bob",
"name": "Betty"
}
}
Dgraph的查询语法是以GraphQL为基础, 在此基础上针对自身的需要会对该语法规则有所改变.
Dgraph使用function
和filter
对查询数据进行过滤. 在dgraph中function和filter的区别仅在于放位置的不同.下面是支持的function和filter:
名称 | 有效类型 | 描述 |
---|---|---|
allofterms | string | 类似与like过滤, 如allofterms(predicate, "space-separated term list") 表示过滤某个边的值必须同是包含space-separated,term和list三个词, 但忽略顺序. |
anyofterms | string | 与allofterms 类似, 但anyofterms 只要求包含任一个词. |
alloftext | string | 全文检索 |
regexp | string | 正则匹配, 语法为regexp(name, /^Steven*/) |
eq,lt,le,gt,ge | int, float, string, dateTime | 分别代表=, <, <=, >, >= |
uid | / | 语法为uid(0x11,0x12) , 根据传进来的uid值, 过滤相应节点 |
has | / | 语法为has(name) , 过滤有某条边的节点 |
and,or,not | / | 逻辑过滤器, 用于联结上述的function/filter |
查询例子1:
{
#查询名字里同时包含Farhan Akhtar的人
q(func: allofterms(name, "Farhan Akhtar")) {
uid
name
}
}
查询例子2:
{
#查询名字里以Steven开头的导演和他导演的含有ryan字符串的电影
directors(func: regexp(name, /^Steven*$/)) {
name
director.film @filter(regexp(name, /ryan/i)) {
name
}
}
}
dgraph可以通过as
关键词将一个查询块的任意部分设为一个变量, 以供后面的子查询或者其它查询块使用. 这个变量本质上是一个uid列表, 因此要利用uid
函数进行引用.
通用的查询格式为:
{
A as not_a_real_query(...) {
B as some_edge @filter(...) { # can't use C or B in this filter
C as ... {
# A, B and C here
}
# A, B and C here
}
# A, B and C can be used in any blocks here
}
# A, B and C can be used in any other query block
}
查询例子1:
{
#查询名叫"Peter Jackson"的人的自导自演电影和扮演的角色
PJ as var(func:allofterms(name, "Peter Jackson")) {
F as director.film
}
peterJ(func: uid(PJ)) {
name
actor.film {
performance.film @filter(uid(F)) {
film_name: name
}
performance.character {
character: name
}
}
}
}
Dgraph 用facets描述边的自定义属性. 如两个用户间有一条叫friend的边, 可以利用facets在这friend边上设置一个close属性值表示他们的亲密度.
带facets的数据写入例子:
_:alice <name> "Alice" .
_:alice <mobile> "040123456" (since=2006-01-02T15:04:05) .
_:alice <car> "MA0123" (since=2006-02-02T13:01:09, first=true) .
facets数据写入在最后的
()
里设置
查询facets数据:
query
{
data(func: eq(name, "Alice")) {
name
mobile @facets(since)
car @facets(since)
}
}
result
{
"data": {
"data": [
{
"name": "Alice",
"mobile|since": "2006-01-02T15:04:05Z",
"mobile": "40123456",
"car|since": "2006-02-02T13:01:09Z",
"car": "MA0123"
}
]
}
}
facets数据默认用
predicate_name|facet_name
的格式展示
Recurse Query也叫递归查询. 此查询会一层层的展开一组边,直到我们到达所有叶节点或达到的最大深度参数depth
指定的深度.
查询例子:
query
{
#查询member_id=60002581用户的2级下线
event(func: allofterms(member_id, "60002581")) @recurse(loop: false, depth:3) {
member_id
~member_next_level_of
}
}
result
{
"data": {
"event": [
{
"member_id": "60002581",
"~member_next_level_of": [
{
"member_id": "70005199",
"uid": "0x459b"
}
],
"uid": "0x2382"
}
]
}
}
也即最短路径查询, 用于查询Dgraph中两个节点的最短路径. 常见的使用场景为想要分析某用户与某产品之间的关系路径.
查询例子:
query
{
path as shortest(from: 0x24a, to: 0x4) {
~purchase
purchase
}
path(func: uid(path)) {
uid
}
}
result
{
"data": {
"path": [
{
"uid": "0x24a"
},
{
"uid": "0x85"
},
{
"uid": "0x1ca6"
},
{
"uid": "0x4"
}
],
"_path_": [
{
"purchase": [
{
"~purchase": [
{
"purchase": [
{
"uid": "0x4"
}
],
"uid": "0x1ca6"
}
],
"uid": "0x85"
}
],
"uid": "0x24a"
}
]
}
}
Dgraph支持单机部署和集群部署两种方式. 在Dgraph中有zero
, alpha
,ratel
三类服务(也叫节点), 其中zero服务充当协调alpha的角色; alpha则是对外提供数据写入和查询的服务, 外部的客户端主要都是和alpha交互; ratel则是一个提供UI界面的服务, 方便用户写入/查询数据,查看/修改schema.
curl https://get.dgraph.io -sSf | bash //这条指令会自动下载最新版本的dgraph并自动安装
//启动zero服务, 默认使用5080端口与外部通讯
dgraph zero
//启动alpha服务, 默认使用9080端口与外部通讯
dgraph alpha --lru_mb 2048 --zero localhost:5080
//启动ratel, 默认使用8000端口对外提供访问
dgraph-ratel
假设集群机器数为3台
下载和安装
此步骤与单机部署的下载安装相同, 在3台机器上均执行上述指令.
启动服务
//选择某一台机器上启动zero服务, 用my参数指定自身的host, 以便和集群的alpha服务通讯, 用replicas指定副本数
dgraph zero --my dev210.sugo.net:5080 --replicas 3
//在三台机器上启动alpha服务, 用my参数指定自身的host
dgraph alpha --lru_mb 2048 --my dev210.sugo.net:7080 --zero dev210.sugo.net:5080 //210机器执行
dgraph alpha --lru_mb 2048 --my dev211.sugo.net:7080 --zero dev210.sugo.net:5080 //211机器执行
dgraph alpha --lru_mb 2048 --my dev212.sugo.net:7080 --zero dev210.sugo.net:5080 //212机器执行
//选择某一台机器启动ratel服务
dgraph-ratel
注意:
集群模式下, 若zero设置了replicas参数, 则集群必须有半数以上的alpha节点存活才可对外提供服务. 在本例下, 即需要2个及以上数量的alpha节点存活才能提供服务.