DQL 基础
Dgraph Query Language, DQL查询语言(旧称GraphQL+-),是一门基于GraphQL的查询语言。GraphQL不是专门为图数据库而设计的,但是它的解析规则和图查询非常相似(查询语法的解析,schema的校验,子图塑型返回等),所以我们就直接在GraphQL的基础上增删一些特性,创造了DQL。
DQL目前还在开发中,我们在未来可以会增加一些新特性,以及简化目前的版本。
一个小示例
这个小例子使用了一个包含2.1千万条元组的电影和戏子的数据库。负载这个数据库的Dgraph是跑在云https://play.dgraph.io/上面的。你也可以选择本地运行
定义数据库的数据结构
本例子使用下面的schema定义数据库的结构:
# Define Directives and index
director.film: [uid] @reverse .
actor.film: [uid] @count .
genre: [uid] @reverse .
initial_release_date: dateTime @index(year) .
name: string @index(exact, term) @lang .
starring: [uid] .
performance.film: [uid] .
performance.character_note: string .
performance.character: [uid] .
performance.actor: [uid] .
performance.special_performance_type: [uid] .
type: [uid] .
# Define Types
type Person {
name
director.film
actor.film
}
type Movie {
name
initial_release_date
genre
starring
}
type Genre {
name
}
type Performance {
performance.film
performance.character
performance.actor
}
开始查询
对一些node进行的查询是建立在图数据库的搜索规则,匹配模式上的,返回一个图作为查询结果。
一个查询是由几个查询块组成的:先由一个根的查询块查询到一系列符合查询规则的nodes,然后再在这些nodes上面应用图匹配和图过滤。
更多的查询列子可以参照查询设计原则
错误码
当你使用DQL查询的时候,服务端可能会返回一个查询错误。在服务端返回的错误对象JSON中有一个code属性,code通常有两个取值
ErrorInvalidRequest: 可能是错误的请求(400)或者是服务器内部的错误(500)Error: 服务器内部的错误(500)
例如,当你提交请求解析错误时,服务端返回:
{
"errors": [
{
"message": "while lexing {\nq(func: has(\"test)){\nuid\n}\n} at line 2 column 12: Unexpected end of input.",
"extensions": {
"code": "ErrorInvalidRequest"
}
}
],
"data": null
}
Error
这是一个很少见的错误,一般是服务器序列化GOstruct到JSON对象时发生错误导致
ErrorInvalidRequst
返回值
每一个查询都有一个名字,和你发送给后端的查询的位于根块的名字一致。
如果一条边是一个值类型,那么这条边将直接返回。
在本例子的数据库中,边(edges)连接电影movies到导演directors和戏子actors,movies拥有name,release date更新时间,还有它在出名的影视数据库的id
下面是一个名为bladerunner的请求:通过比对电影的名字Blade Runner获取电影信息
{
bladerunner(func: eq(name@en, "Blade Runner")) {
uid
name@en
initial_release_date
netflix_id
}
}
它将会返回
{
"data": {
"bladerunner": [
{
"uid": "0x394c",
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z",
"netflix_id": "70083726"
}
]
}
}
上面的请求首先通过索引找到name边等于Blade Runner的node,然后返回该node的出边属性。每一个node都有一个唯一的64位的id,上面的请求中,uid边返回了该node的id,如果一个node的id已知,可以通过uid函数直接查找该节点。
{
bladerunner(func: uid(0x394c)) {
uid
name@en
initial_release_date
netflix_id
}
}
上面的请求将返回
{
"data": {
"bladerunner": [
{
"uid": "0x394c",
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z",
"netflix_id": "70083726"
}
]
}
}
一个查询可能匹配到多个node,下面查询所有名字含有Blade或Runner的节点。
{
bladerunner(func: anyofterms(name@en, "Blade Runner")) {
uid
name@en
initial_release_date
netflix_id
}
}
{
"data": {
"bladerunner": [
{
"uid": "0xd5f",
"name@en": "The Runner"
},
{
"uid": "0x394c",
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z",
"netflix_id": "70083726"
},
....
]
}
}
uid函数也能同时指定多个id
{
movies(func: uid(0xb5849, 0x394c)) {
uid
name@en
initial_release_date
netflix_id
}
}
返回
{
"data": {
"movies": [
{
"uid": "0x394c",
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z",
"netflix_id": "70083726"
},
{
"uid": "0xb5849",
"name@en": "James Cameron's Explorers: From the Titanic to the Moon",
"initial_release_date": "2006-01-01T00:00:00Z",
"netflix_id": "70058064"
}
]
}
}
展开图节点的边
一个查询能通过嵌套查询块{}从一个节点扩展到另一个节点
下面查询Blade Runner中的演员和人物:该查询首先查询出拥有名字为Blade Runner这条边的节点,然后从这个节点沿着向外的主演边starring找到演员饰演的角色的节点。从这些节点里面再沿着perfromance.actor边和performance.character边展开,找到演员的名字和在剧中饰演的角色的名字。
{
brCharacters(func: eq(name@en, "Blade Runner")) {
name@en
initial_release_date
starring {
performance.actor {
name@en # actor name
}
performance.character {
name@en # character name
}
}
}
}
{
"data": {
"brCharacters": [
{
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z",
"starring": [
{
"performance.actor": [
{
"name@en": "John Edward Allen"
}
],
"performance.character": [
{
"name@en": "Kaiser"
}
]
},
{
"performance.actor": [
{
"name@en": "Joanna Cassidy"
}
],
"performance.character": [
{
"name@en": "Zhora"
}
]
}
...
]
}
]
}
}
注释
所有在 #后面的都是注释
使用过滤
查询根查找一组初始节点,查询通过返回值并沿着边继续查询————查询中到达的任何节点都是在根处搜索之后遍历找到的。找到的节点可以通过应用@filter过滤,可以在根节点之后或任何边进行过滤。
查询示例:《银翼杀手》导演雷德利·斯科特在2000年之前发行的电影:
{
scott(func: eq(name@en, "Ridley Scott")) {
name@en
initial_release_date
director.film @filter(le(initial_release_date, "2000")) {
name@en
initial_release_date
}
}
}
将返回:
{
"data": {
"scott": [
{
"name@en": "Ridley Scott",
"director.film": [
{
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z"
},
{
"name@en": "Alien",
"initial_release_date": "1979-05-25T00:00:00Z"
},
{
"name@en": "Black Rain",
"initial_release_date": "1989-09-22T00:00:00Z"
},
{
"name@en": "White Squall",
"initial_release_date": "1996-02-02T00:00:00Z"
},
{
"name@en": "Thelma & Louise",
"initial_release_date": "1991-05-24T00:00:00Z"
},
{
"name@en": "G.I. Jane",
"initial_release_date": "1997-08-22T00:00:00Z"
},
{
"name@en": "1492 Conquest of Paradise",
"initial_release_date": "1992-10-08T00:00:00Z"
},
{
"name@en": "The Duellists",
"initial_release_date": "1977-12-01T00:00:00Z"
},
{
"name@en": "Someone to Watch Over Me",
"initial_release_date": "1987-10-09T00:00:00Z"
},
{
"name@en": "Legend",
"initial_release_date": "1985-08-28T00:00:00Z"
},
{
"name@en": "Boy and Bicycle",
"initial_release_date": "1997-09-07T00:00:00Z"
}
]
},
{
"name@en": "Ridley Scott"
}
]
}
}
查询示例:2000年之前上映的片名为“刀锋战士”或“奔跑者”的电影:
{
bladerunner(func: anyofterms(name@en, "Blade Runner")) @filter(le(initial_release_date, "2000")) {
uid
name@en
initial_release_date
netflix_id
}
}
将返回:
{
"data": {
"bladerunner": [
{
"uid": "0x394c",
"name@en": "Blade Runner",
"initial_release_date": "1982-06-25T00:00:00Z",
"netflix_id": "70083726"
},
{
"uid": "0x7160",
"name@en": "Blade",
"initial_release_date": "1973-12-01T00:00:00Z"
},
{
"uid": "0x15adb",
"name@en": "Lone Runner",
"initial_release_date": "1986-01-01T00:00:00Z",
"netflix_id": "70146965"
},
{
"uid": "0x16f0c",
"name@en": "Revenge of the Bushido Blade",
"initial_release_date": "1980-01-01T00:00:00Z",
"netflix_id": "70100967"
}
...
]
}
}
多语言支持
注意:必须在
Schema中指定@lang指令来查询或修改带有语言标记的谓词。
Dgraph支持UTF-8
使用以下规则指定返回语言的优先顺序:
- 最多只会返回一个结果(除了语言列表被设置为*的情况)。
- 从左到右考虑首选项列表:如果没有找到给定语言的值,则考虑列表中的下一种语言。
- 如果在任何指定的语言中没有值,则不返回值。
- 最后一个
.表示返回没有指定语言的值,或者如果没有没有语言的值,则返回some语言的值。 - 将语言列表值设置为
*将返回该谓词的所有值及其语言。也会返回没有语言标记的值。 例如: name: 查找一个untagged的字符串;如果没有untagged的值存在,则不返回任何值。name@.: 查找一个未标记的字符串,然后是任何语言。name@en: 查找zh标记的字符串;如果不存在带en标记的字符串,则不返回任何内容。name@en:.: 查找en,然后untagged,然后是任何语言。name@en:pl: 查找en,然后是pl,否则什么都不返回。name@*: 查找该谓词的所有值,并返回它们及其语言。例如,如果有两个语言为en和hi的值,则该查询将返回两个名为name@en和name@hi的键。
注意,在函数中,语言列表(包括
@*符号)是不允许的。无标记谓词、单语言标记和.符号如上所述。 在全文搜索函数(alloftext、anyoftext)中,当没有指定语言(untagged或@.)时,将使用默认的(英文)全文标记器。这并不意味着在查询无标记值时将搜索带有en标记的值,而是将无标记值视为英语文本。如果您不希望出现这种情况,可以为所需的语言使用适当的标记,用于修改和查询值。
查询示例:宝莱坞导演和演员法尔汉·阿赫塔尔(Farhan Akhtar)的一些电影有俄语、印地语和英语的名字,其他的没有:
{
q(func: allofterms(name@en, "Farhan Akhtar")) {
name@.
director.film {
name@ru:hi:en
name@en
name@hi
name@ru
}
}
}
将返回:
{
"data": {
"q": [
{
"name@.": "Farhan Akhtar",
"director.film": [
{
"name@ru:hi:en": "दिल चाहता है",
"name@en": "Dil Chahta Hai",
"name@hi": "दिल चाहता है"
},
{
"name@ru:hi:en": "Дон. Главарь мафии 2",
"name@en": "Don 2",
"name@hi": "डॉन २",
"name@ru": "Дон. Главарь мафии 2"
},
{
"name@ru:hi:en": "पोज़िटिव",
"name@en": "Positive",
"name@hi": "पोज़िटिव"
},
{
"name@ru:hi:en": "लक्ष्य",
"name@en": "Lakshya",
"name@hi": "लक्ष्य"
},
{
"name@ru:hi:en": "Дон. Главарь мафии",
"name@en": "Don: The Chase Begins Again",
"name@hi": "डॉन",
"name@ru": "Дон. Главарь мафии"
}
]
}
]
}
}