facet和Edge属性
Dgraph支持facet————edge上的键值对————是RDF三元组的扩展。也就是说,facet向边添加属性,而不是向节点添加属性。例如,两个节点之间的朋友边可能具有亲密友谊的bool属性。facet也可以用作边(edge)的权重。
虽然你可能会发现自己很多时候倾向于一些方面,但它们不应该被滥用。例如,给friend这条边(edge)添加一个date_of_birth属性可能不是一个正经的建模。对于friend这条边,你更应该添加比如start_of_friendship这个属性。然而,facet在Dgraph中并不像谓词(predicate)那样是一等公民。
Facet键是字符串,值可以是string、bool、int、float和dateTime。对于int和float,只接受32位有符号整数和64位浮点数。
下面这些对于facet的变更将会贯穿整章。该变更(mutation)为一些人添加了数据,例如,在mobile和car中记录了since facet,以记录Alice购买汽车并开始使用手机号码的时间。
首先,我们添加一些Schema:
curl localhost:8080/alter -XPOST -d $'
name: string @index(exact, term) .
rated: [uid] @reverse @count .
' | python -m json.tool | less
curl -H "Content-Type: application/rdf" localhost:8080/mutate?commitNow=true -XPOST -d $'
{
set {
# -- Facets on scalar predicates
_:alice <name> "Alice" .
_:alice <dgraph.type> "Person" .
_:alice <mobile> "040123456" (since=2006-01-02T15:04:05) .
_:alice <car> "MA0123" (since=2006-02-02T13:01:09, first=true) .
_:bob <name> "Bob" .
_:bob <dgraph.type> "Person" .
_:bob <car> "MA0134" (since=2006-02-02T13:01:09) .
_:charlie <name> "Charlie" .
_:charlie <dgraph.type> "Person" .
_:dave <name> "Dave" .
_:dave <dgraph.type> "Person" .
# -- Facets on UID predicates
_:alice <friend> _:bob (close=true, relative=false) .
_:alice <friend> _:charlie (close=false, relative=true) .
_:alice <friend> _:dave (close=true, relative=true) .
# -- Facets for variable propagation
_:movie1 <name> "Movie 1" .
_:movie1 <dgraph.type> "Movie" .
_:movie2 <name> "Movie 2" .
_:movie2 <dgraph.type> "Movie" .
_:movie3 <name> "Movie 3" .
_:movie3 <dgraph.type> "Movie" .
_:alice <rated> _:movie1 (rating=3) .
_:alice <rated> _:movie2 (rating=2) .
_:alice <rated> _:movie3 (rating=5) .
_:bob <rated> _:movie1 (rating=5) .
_:bob <rated> _:movie2 (rating=5) .
_:bob <rated> _:movie3 (rating=5) .
_:charlie <rated> _:movie1 (rating=2) .
_:charlie <rated> _:movie2 (rating=5) .
_:charlie <rated> _:movie3 (rating=1) .
}
}' | python -m json.tool | less
标量(scalar)谓词(predicate)上的Facets
查询Alice的name、mobile和Car与不查询facet的结果相同:
{
data(func: eq(name, "Alice")) {
name
mobile
car
}
}
{
"data": {
"data": [
{
"name": "Alice",
"mobile": "040123456",
"car": "MA0123"
}
]
}
}
语法@facet (facet-name)用于查询facet数据。对于Alice, mobile和car的since facet被查询如下:
{
data(func: eq(name, "Alice")) {
name
mobile @facets(since)
car @facets(since)
}
}
{
"data": {
"data": [
{
"name": "Alice",
"mobile|since": "2006-01-02T15:04:05Z",
"mobile": "040123456",
"car|since": "2006-02-02T13:01:09Z",
"car": "MA0123"
}
]
}
}
facet在与对应的边(edge)相同的级别返回,并具有像edge|facet这样的键。
边(edge)上的所有facet都使用@facet查询:
{
data(func: eq(name, "Alice")) {
name
mobile @facets
car @facets
}
}
{
"data": {
"data": [
{
"name": "Alice",
"mobile|since": "2006-01-02T15:04:05Z",
"mobile": "040123456",
"car|first": true,
"car|since": "2006-02-02T13:01:09Z",
"car": "MA0123"
}
]
}
}
Facets 国际化
facet键和值在发生变化时可以直接使用特定于语言的字符。但是在查询时,facet键需要括在尖括号<>中。这类似于谓词。有关更多信息,请参阅谓词(predicate)i18n。
注意,在查询时,Dgraph支持面向键的国际化资源标识符(IRIs)。
{
set {
_:person1 <name> "Daniel" (वंश="स्पेनी", ancestry="Español") .
_:person1 <dgraph.type> "Person" .
_:person2 <name> "Raj" (वंश="हिंदी", ancestry="हिंदी") .
_:person2 <dgraph.type> "Person" .
_:person3 <name> "Zhang Wei" (वंश="चीनी", ancestry="中文") .
_:person3 <dgraph.type> "Person" .
}
}
查询,注意<>:
{
q(func: has(name)) {
name @facets(<वंश>)
}
}
在Facets上使用别名
可以在请求特定谓词(predicate)时指定别名。语法类似于为其他谓词请求别名的方式。orderasc和orderdesc不允许作为别名,因为它们有特殊的含义。除此之外,其他任何东西都可以设置为别名。
这里我们分别为since、close facet设置car_since、close_friend别名:
{
data(func: eq(name, "Alice")) {
name
mobile
car @facets(car_since: since)
friend @facets(close_friend: close) {
name
}
}
}
{
"data": {
"data": [
{
"name": "Alice",
"mobile": "040123456",
"car_since": "2006-02-02T13:01:09Z",
"car": "MA0123",
"friend": [
{
"name": "Bob",
"close_friend": true
},
{
"name": "Charlie",
"close_friend": false
},
{
"name": "Dave",
"close_friend": true
}
]
}
]
}
}
UID谓词(predicate)上的Facets
UID边(edge)上的facet与值边(value edge)上的facet的工作方式类似。
例如,friend这条边拥有一个close的edge。Alice和Bob的friend边(edge)的close``facet被设置为true,Alice和Charlie之间被设置为false。
现在查询Alice的朋友:
{
data(func: eq(name, "Alice")) {
name
friend {
name
}
}
}
{
"data": {
"data": [
{
"name": "Alice",
"friend": [
{
"name": "Bob"
},
{
"name": "Charlie"
},
{
"name": "Dave"
}
]
}
]
}
}
查询Alice的朋友这条边(edge)上的close``facet为false的朋友:
{
data(func: eq(name, "Alice")) {
name
friend @facets(close) {
name
}
}
}
{
"data": {
"data": [
{
"name": "Alice",
"friend": [
{
"name": "Bob",
"friend|close": true
},
{
"name": "Charlie",
"friend|close": false
},
{
"name": "Dave",
"friend|close": true
}
]
}
]
}
}
对于像friend这样的UID边(edge),facet会转到相应子元素下。在上面的示例中,您可以看到Alice和Bob之间的边(edge)上的close``facet。
{
data(func: eq(name, "Alice")) {
name
friend @facets {
name
car @facets
}
}
}
{
"data": {
"data": [
{
"name": "Alice",
"friend": [
{
"name": "Bob",
"car|since": "2006-02-02T13:01:09Z",
"car": "MA0134",
"friend|close": true,
"friend|relative": false
},
{
"name": "Charlie",
"friend|close": false,
"friend|relative": true
},
{
"name": "Dave",
"friend|close": true,
"friend|relative": true
}
]
}
]
}
}
Bob有一辆车,它有一个facet since,在结果中,它是key car|since下Bob的同一个对象的一部分。此外,Bob和Alice之间的密切关系也是Bob的输出对象的一部分。Charlie没有Car边(edge),因此只有UID facets。
在facets上使用过滤
Dgraph支持基于facet的边(edge)过滤。过滤的工作原理类似于它在没有facet的边(edge)上的工作原理,并且具有相同的可用函数。
找到爱丽丝的好朋友:
{
data(func: eq(name, "Alice")) {
friend @facets(eq(close, true)) {
name
}
}
}
{
"data": {
"data": [
{
"friend": [
{
"name": "Bob"
},
{
"name": "Dave"
}
]
}
]
}
}
要返回facet和过滤器,请向查询添加另一个@facet (<facetname>):
{
data(func: eq(name, "Alice")) {
friend @facets(eq(close, true)) @facets(relative) { # filter close friends and give relative status
name
}
}
}
{
"data": {
"data": [
{
"friend": [
{
"name": "Bob",
"friend|relative": false
},
{
"name": "Dave",
"friend|relative": true
}
]
}
]
}
}
Facet查询能够和AND, OR, NOT 组合使用:
{
data(func: eq(name, "Alice")) {
friend @facets(eq(close, true) AND eq(relative, true)) @facets(relative) { # filter close friends in my relation
name
}
}
}
{
"data": {
"data": [
{
"friend": [
{
"name": "Dave",
"friend|relative": true
}
]
}
]
}
}
使用Facet排序
可以对uid边(edge)上的facet进行排序。在这里,我们将爱丽丝、鲍勃和查理对电影的评级进行分类,这是一个facet:
{
me(func: anyofterms(name, "Alice Bob Charlie")) {
name
rated @facets(orderdesc: rating) {
name
}
}
}
{
"data": {
"me": [
{
"name": "Bob",
"rated": [
{
"name": "Movie 1",
"rated|rating": 5
},
{
"name": "Movie 2",
"rated|rating": 5
},
{
"name": "Movie 3",
"rated|rating": 5
}
]
},
{
"name": "Alice",
"rated": [
{
"name": "Movie 3",
"rated|rating": 5
},
{
"name": "Movie 1",
"rated|rating": 3
},
{
"name": "Movie 2",
"rated|rating": 2
}
]
},
{
"name": "Charlie",
"rated": [
{
"name": "Movie 2",
"rated|rating": 5
},
{
"name": "Movie 1",
"rated|rating": 2
},
{
"name": "Movie 3",
"rated|rating": 1
}
]
}
]
}
}
将Facet值赋值给变量
UID边(edge)上的facet可以存储在值变量(variables)中。这个变量是从边(edge)目标到facet的映射。
找到Alice被标记为close和relative的朋友:
{
var(func: eq(name, "Alice")) {
friend @facets(a as close, b as relative)
}
friend(func: uid(a)) {
name
val(a)
}
relative(func: uid(b)) {
name
val(b)
}
}
{
"data": {
"friend": [
{
"name": "Bob",
"val(a)": true
},
{
"name": "Charlie",
"val(a)": false
},
{
"name": "Dave",
"val(a)": true
}
],
"relative": [
{
"name": "Bob",
"val(b)": false
},
{
"name": "Charlie",
"val(b)": true
},
{
"name": "Dave",
"val(b)": true
}
]
}
}
Facet和值传播
可以将int和float的Facet值赋给变量,从而使这些值(variables)传播。
爱丽丝、鲍勃和查理都给每部电影打分。facet评级上的值变量将电影映射到评级。通过多条路径到达电影的查询将对每条路径的评级进行求和。下面总结了爱丽丝、鲍勃和查理对这三部电影的评分:
{
var(func: anyofterms(name, "Alice Bob Charlie")) {
num_raters as math(1)
rated @facets(r as rating) {
total_rating as math(r) # sum of the 3 ratings
average_rating as math(total_rating / num_raters)
}
}
data(func: uid(total_rating)) {
name
val(total_rating)
val(average_rating)
}
}
{
"data": {
"data": [
{
"name": "Movie 1",
"val(total_rating)": 10,
"val(average_rating)": 3
},
{
"name": "Movie 2",
"val(total_rating)": 12,
"val(average_rating)": 4
},
{
"name": "Movie 3",
"val(total_rating)": 11,
"val(average_rating)": 3
}
]
}
}
Facet 和聚合(aggregation)
可以聚合分配给值变量的Facet值。
{
data(func: eq(name, "Alice")) {
name
rated @facets(r as rating) {
name
}
avg(val(r))
}
}
{
"data": {
"data": [
{
"name": "Alice",
"rated": [
{
"name": "Movie 1",
"rated|rating": 3
},
{
"name": "Movie 2",
"rated|rating": 2
},
{
"name": "Movie 3",
"rated|rating": 5
}
],
"avg(val(r))": 3.333333
}
]
}
}
但是请注意,r是一个从电影到查询中到达电影的边缘上的评分总和的映射。因此,下面的代码不能正确地分别计算Alice和Bob的平均评级————它计算的是Alice和Bob评级平均值的2倍。
{
data(func: anyofterms(name, "Alice Bob")) {
name
rated @facets(r as rating) {
name
}
avg(val(r))
}
}
{
"data": {
"data": [
{
"name": "Bob",
"rated": [
{
"name": "Movie 1",
"rated|rating": 5
},
{
"name": "Movie 2",
"rated|rating": 5
},
{
"name": "Movie 3",
"rated|rating": 5
}
],
"avg(val(r))": 8.333333
},
{
"name": "Alice",
"rated": [
{
"name": "Movie 1",
"rated|rating": 3
},
{
"name": "Movie 2",
"rated|rating": 2
},
{
"name": "Movie 3",
"rated|rating": 5
}
],
"avg(val(r))": 8.333333
}
]
}
}
计算用户的平均评级需要一个将用户映射到其评级总和的变量:
{
var(func: has(rated)) {
num_rated as math(1)
rated @facets(r as rating) {
avg_rating as math(r / num_rated)
}
}
data(func: uid(avg_rating)) {
name
val(avg_rating)
}
}
{
"data": {
"data": [
{
"name": "Movie 1",
"val(avg_rating)": 3
},
{
"name": "Movie 2",
"val(avg_rating)": 4
},
{
"name": "Movie 3",
"val(avg_rating)": 3
}
]
}
}