Dgraph使用总结

纲要

  • 1.背景
  • 2.概念
  •   2.1 Schema
  •   2.2 数据写入
  • 3.查询
  •   3.1 Function/Filter
  •   3.2 查询变量
  •   3.3 Facets
  •   3.4 Recurse Query
  •   3.5 K-Shortest Path Queries
  • 4.部署
  •   4.1 单机部署
  •   4.2 集群部署

1.背景

  Dgraph是一个分布式的开源图数据库, 官方文档地址https://docs.dgraph.io/, 建议英文水平好的朋友可以直接去访问了解. 本文仅是对自己使用Dgraph的过程做一个总结, 着重介绍它的查询语法和一些有用而有重要的查询.

2.概念

2.1 Schema

  Dgraph和其它的大多数数据库一样, 也用schema来描述数据的类型(本质上都是图的边), 支持的类型如下

类型

描述

int

64位有符号整数

float

双精度浮点数

string

字符串

bool

布尔类型

dateTime

RFC3339格式时间戳

geo

地理位置类型

password

string (encrypted)

uid

图的边类型,用于由一个节点指向另一个节点,且带有方向

Dgraph中有节点和值两种类型, 每个节点都有一个内部分配的64位整数的uid属性, 用于唯一标记该节点. 节点与节点之间由 uid类型的边连接, 节点与值之间由标量类型的边连接.

2.2 数据写入

  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"
  }
}

3. 查询

  Dgraph的查询语法是以GraphQL为基础, 在此基础上针对自身的需要会对该语法规则有所改变.

3.1 Function/Filter

  Dgraph使用functionfilter对查询数据进行过滤. 在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
    }
  }
}

3.2 查询变量

  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
      }
    }
  }
}

3.3 Facets

  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的格式展示

3.4 Recurse Query

  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"
      }
    ]
  }
}

3.5 K-Shortest Path Queries

  也即最短路径查询, 用于查询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"
      }
    ]
  }
}

4.部署

  Dgraph支持单机部署和集群部署两种方式. 在Dgraph中有zero, alpha,ratel三类服务(也叫节点), 其中zero服务充当协调alpha的角色; alpha则是对外提供数据写入和查询的服务, 外部的客户端主要都是和alpha交互; ratel则是一个提供UI界面的服务, 方便用户写入/查询数据,查看/修改schema.

4.1 单机部署

  • 下载和安装
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

4.2 集群部署

  假设集群机器数为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节点存活才能提供服务.