Any outgoing edge in Dgraph can be reversed using the @reverse
directive in the schema and be queried using tilde as the prefix of the edge name. e.g. <~myEdge>
.
Dgraph serializes directed graphs. This means that all properties always point from an entity to another entity or value in a single direction. S P -> O
.
Reverse edges are automatically generated edges and are not part of your dataset. This means that you cannot run mutations directly on the reverse edges. Mutating the forward edge will automatically update the reverse edge.
Using Reverse Edges correctly
In RDF the arrangement of the triples already defines what can be reversed.
_:MyObject <myEdge> _:BlankNode . #That's the right syntax of a reverse edge.
_:BlankNode <dgraph.type> "Person" .
The easiest way to correct and apply reverse edges is using JSON. It is simply having the directive on schema at the desired edge. When building your mutations remember that there is no reverse syntax in JSON. So what you should do is similar to RDF: change the arrangement of the JSON objects.
Since MyObject
is above the Person
entity, MyObject
must come before when formatting the mutation.
{
"set": [
{
"uid": "_:MyObject",
"dgraph.type": "Object",
"myEdge": {
"uid": "_:BlankNode",
"dgraph.type": "Person"
}
}
]
}
Another way to do this is to separate into small chunks/batches and use blank nodes as references. This facilitates the organization and reuse of references.
{
"set": [
{
"uid": "_:MyObject",
"dgraph.type": "Object",
"myEdge": [{"uid": "_:BlankNode"}]
},
{
"uid": "_:BlankNode",
"dgraph.type": "Person"
}
]
}
More reverse examples
In RDF the correct way to apply reverse edges is very straight-forward.
name: String .
husband: uid @reverse .
wife: uid @reverse .
parent: [uid] @reverse .
{
set {
_:Megalosaurus <name> "Earl Sneed Sinclair" .
_:Megalosaurus <dgraph.type> "Dinosaur" .
_:Megalosaurus <wife> _:Allosaurus .
_:Allosaurus <name> "Francis Johanna Phillips Sinclair" (short="Fran") .
_:Allosaurus <dgraph.type> "Dinosaur" .
_:Allosaurus <husband> _:Megalosaurus .
_:Hypsilophodon <name> "Robert Mark Sinclair" (short="Robbie") .
_:Hypsilophodon <dgraph.type> "Dinosaur" .
_:Hypsilophodon <parent> _:Allosaurus (role="son") .
_:Hypsilophodon <parent> _:Megalosaurus (role="son") .
_:Protoceratops <name> "Charlene Fiona Sinclair" .
_:Protoceratops <dgraph.type> "Dinosaur" .
_:Protoceratops <parent> _:Allosaurus (role="daughter") .
_:Protoceratops <parent> _:Megalosaurus (role="daughter") .
_:MegalosaurusBaby <name> "Baby Sinclair" (short="Baby") .
_:MegalosaurusBaby <dgraph.type> "Dinosaur" .
_:MegalosaurusBaby <parent> _:Allosaurus (role="son") .
_:MegalosaurusBaby <parent> _:Megalosaurus (role="son") .
}
}
The directions are like:
Exchanged hierarchy:
Object -> Parent;
Object <~ Parent; #Reverse
Children to parents via "parent" edge.
wife and husband bidirectional using reverse.
Normal hierarchy:
Parent -> Object;
Parent <~ Object; #Reverse
This hierarchy is not part of the example, but is generally used in all graph models.
To make this hierarchy we need to bring the hierarchical relationship starting from the parents and not from the children. Instead of using the edges "wife" and "husband" we switch to single edge called "married" to simplify the model.
_:Megalosaurus <name> "Earl Sneed Sinclair" .
_:Megalosaurus <dgraph.type> "Dinosaur" .
_:Megalosaurus <married> _:Allosaurus .
_:Megalosaurus <parent> _:Hypsilophodon (role="son") .
_:Megalosaurus <parent> _:Protoceratops (role="daughter") .
_:Megalosaurus <parent> _:MegalosaurusBaby (role="son") .
_:Allosaurus <name> "Francis Johanna Phillips Sinclair" (short="Fran") .
_:Allosaurus <dgraph.type> "Dinosaur" .
_:Allosaurus <married> _:Megalosaurus .
_:Allosaurus <parent> _:Hypsilophodon (role="son") .
_:Allosaurus <parent> _:Protoceratops (role="daughter") .
_:Allosaurus <parent> _:MegalosaurusBaby (role="son") .
Queries
-
wife_husband
is the reversedwife
edge. -
husband
is an actual edge.
{
q(func: has(wife)) {
name
WF as wife {
name
}
}
reverseIt(func: uid(WF)) {
name
wife_husband : ~wife {
name
}
husband {
name
}
}
}
-
Children
is the reversedparent
edge.
{
q(func: has(name)) @filter(eq(name, "Earl Sneed Sinclair")){
name
Children : ~parent @facets {
name
}
}
}
Reverse Edges and Facets
Facets on reverse edges are the same as the forward edge. That is, if you set or update a facet on an edge, its reverse will have the same facets.
{
set {
_:Megalosaurus <name> "Earl Sneed Sinclair" .
_:Megalosaurus <dgraph.type> "Dinosaur" .
_:Megalosaurus <wife> _:Allosaurus .
_:Megalosaurus <parent> _:MegalosaurusBaby (role="parent -> child") .
_:MegalosaurusBaby <name> "Baby Sinclair" (short="Baby -> parent") .
_:MegalosaurusBaby <dgraph.type> "Dinosaur" .
_:MegalosaurusBaby <parent> _:Megalosaurus (role="child -> parent") .
}
}
Using a similar query from the previous example:
{
Parent(func: has(name)) @filter(eq(name, "Earl Sneed Sinclair")){
name
C as Children : parent @facets {
name
}
}
Child(func: uid(C)) {
name
parent @facets {
name
}
}
}
{
"data": {
"Parent": [
{
"name": "Earl Sneed Sinclair",
"Children": [
{
"name": "Baby Sinclair",
"Children|role": "parent -> child"
}
]
}
],
"Child": [
{
"name": "Baby Sinclair",
"parent": [
{
"name": "Earl Sneed Sinclair",
"parent|role": "child -> parent"
}
]
}
]
}
}
This is a companion discussion topic for the original entry at https://dgraph.io/docs/mutations/reverse-edges/