Bi-directional Relations

Bi-directional Relations #

Problem #

If I have a graph: A <-1-> B <-2-> C where A, B, C are subjects and objects linked with predicates 1, 2 (bidirectional). How can I get C using a path?

Test data #

Fig 1.: Graph diagram of test data

Show the test data in nquads format
<A> <predicate-1> <B> .
<B> <predicate-1> <A> .
<B> <predicate-2> <C> .
<C> <predicate-2> <B> .

<A1> <predicate-1> <B1> .
<B1> <predicate-1> <A1> .
<B1> <predicate-2> <C1> .
<C1> <predicate-2> <B1> .

<A2> <predicate-1> <B2> .
<B2> <predicate-1> <A2> .
<B2> <predicate-2> <C2> .
<C2> <predicate-2> <B2> .

Gizmo Query #

Simple query #

g.V().Tag("source").Out("<predicate-1>").Out("<predicate-2>").Tag('target').Out("<predicate-2>").Out("<predicate-1>").All()

Results:

{"id":"A","source":"A","target":"C"}
{"id":"A1","source":"A1","target":"C1"}
{"id":"A2","source":"A2","target":"C2"}

Query with bidirectional predicate #

g.V().Tag("source").Both("<predicate-1>").Both("<predicate-2>").Tag('target').All()

Results:

{"id":"C","source":"A","target":"C"}
{"id":"C1","source":"A1","target":"C1"}
{"id":"C2","source":"A2","target":"C2"}
{"id":"C","source":"A","target":"C"}
{"id":"C1","source":"A1","target":"C1"}
{"id":"C2","source":"A2","target":"C2"}

Query with morphism #

// with morphism
forth = g.V().Tag("source").Out("<predicate-1>").Out("<predicate-2>").Tag("target");
back = g.M().Out("<predicate-2>").Out("<predicate-1>")
forth.Follow(back).All()

Results:

{"id":"A","source":"A","target":"C"}
{"id":"A1","source":"A1","target":"C1"}
{"id":"A2","source":"A2","target":"C2"}

Golang Implementation #

Show the source code
package main

import (
	"context"
	"fmt"
	"github.com/cayleygraph/cayley"
	"github.com/cayleygraph/quad"
)

func main() {
	// Create an in-memory store
	store := InitStore()
	// Create Quads and uploads to the store
	quads := makeQuads()
	AddQuadsToStore(store, quads)

	// Execute the query
	doQuery(store)
}

// Create a mem-store for testing
func InitStore() *cayley.Handle {
	store, err := cayley.NewMemoryGraph()
	if err != nil {
		panic(err)
	}

	return store
}

func makeQuads() []quad.Quad {
	quads := []quad.Quad{}

	quads = append(quads, quad.Make("A", "1", "B", ""))
	quads = append(quads, quad.Make("B", "1", "A", ""))
	quads = append(quads, quad.Make("B", "2", "C", ""))
	quads = append(quads, quad.Make("C", "2", "B", ""))

	quads = append(quads, quad.Make("A1", "1", "B1", ""))
	quads = append(quads, quad.Make("B1", "1", "A1", ""))
	quads = append(quads, quad.Make("B1", "2", "C1", ""))
	quads = append(quads, quad.Make("C1", "2", "B1", ""))

	quads = append(quads, quad.Make("A2", "1", "B2", ""))
	quads = append(quads, quad.Make("B2", "1", "A2", ""))
	quads = append(quads, quad.Make("B2", "2", "C2", ""))
	quads = append(quads, quad.Make("C2", "2", "B2", ""))

	return quads
}

func AddQuadsToStore(store *cayley.Handle, quads []quad.Quad) {
	for _, q := range quads {
		store.AddQuad(q)
	}
}

func doQuery(store *cayley.Handle) {
	p := cayley.StartPath(store).Tag("source").Out("1").Out("2").Tag("target").Out("2").Out("1")

	p.Iterate(context.Background()).EachValue(nil, func(value quad.Value) {
		fmt.Printf("%v\n", value)
	})
	p.Iterate(context.Background()).TagValues(store, func(t map[string]quad.Value) {
		fmt.Printf("%v\n", t)
	})
}

Results:

"A"
"A1"
"A2"
map[source:"A" target:"C"]
map[source:"A1" target:"C1"]
map[source:"A2" target:"C2"]