From 00f045ea08cce9263bb8cf3e811a6cf6191b6bb3 Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Sun, 15 Sep 2019 22:25:29 +0300 Subject: [PATCH 01/13] Make gizmo methods lower case --- query/gizmo/gizmo.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/query/gizmo/gizmo.go b/query/gizmo/gizmo.go index 648bcd2b1..ce21ed993 100644 --- a/query/gizmo/gizmo.go +++ b/query/gizmo/gizmo.go @@ -18,6 +18,8 @@ import ( "context" "fmt" "sort" + "unicode" + "reflect" "github.com/dop251/goja" @@ -59,6 +61,23 @@ func NewSession(qs graph.QuadStore) *Session { return s } +func lcFirst(str string) string { + for i, v := range str { + return string(unicode.ToLower(v)) + str[i+1:] + } + return "" +} + +type fieldNameMapperToLower struct{} + +func (fieldNameMapperToLower) FieldName(t reflect.Type, f reflect.StructField) string { + return lcFirst(f.Name) +} + +func (fieldNameMapperToLower) MethodName(t reflect.Type, m reflect.Method) string { + return lcFirst(m.Name) +} + type Session struct { qs graph.QuadStore vm *goja.Runtime @@ -88,6 +107,7 @@ func (s *Session) buildEnv() error { return nil } s.vm = goja.New() + s.vm.SetFieldNameMapper(fieldNameMapperToLower{}) s.vm.Set("graph", &graphObject{s: s}) s.vm.Set("g", s.vm.Get("graph")) for name, val := range defaultEnv { From e83db42bfa709473278ae21f77195d03da287dfa Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Sun, 15 Sep 2019 23:03:23 +0300 Subject: [PATCH 02/13] Fix tests --- query/gizmo/gizmo_test.go | 232 +++++++++++++++++++------------------- 1 file changed, 117 insertions(+), 115 deletions(-) diff --git a/query/gizmo/gizmo_test.go b/query/gizmo/gizmo_test.go index ffe839909..494a2193a 100644 --- a/query/gizmo/gizmo_test.go +++ b/query/gizmo/gizmo_test.go @@ -77,142 +77,142 @@ var testQueries = []struct { { message: "get a single vertex", query: ` - g.V("").All() + g.v("").all() `, expect: []string{""}, }, { - message: "use .GetLimit", + message: "use .getLimit", query: ` - g.V().GetLimit(5) + g.v().getLimit(5) `, expect: []string{"", "", "", "", ""}, }, { message: "get a single vertex (IRI)", query: ` - g.V(iri("alice")).All() + g.v(iri("alice")).all() `, expect: []string{""}, }, { - message: "use .Out()", + message: "use .out()", query: ` - g.V("").Out("").All() + g.v("").out("").all() `, expect: []string{""}, }, { - message: "use .Out() (IRI)", + message: "use .out() (IRI)", query: ` - g.V(iri("alice")).Out(iri("follows")).All() + g.v(iri("alice")).out(iri("follows")).all() `, expect: []string{""}, }, { - message: "use .Out() (any)", + message: "use .out() (any)", query: ` - g.V("").Out().All() + g.v("").out().all() `, expect: []string{"", "cool_person"}, }, { - message: "use .In()", + message: "use .in()", query: ` - g.V("").In("").All() + g.v("").in("").all() `, expect: []string{"", "", ""}, }, { - message: "use .In() (any)", + message: "use .in() (any)", query: ` - g.V("").In().All() + g.v("").in().all() `, expect: []string{"", "", ""}, }, { - message: "use .In() with .Filter()", + message: "use .in() with .filter()", query: ` - g.V("").In("").Filter(gt(iri("c")),lt(iri("d"))).All() + g.v("").in("").filter(gt(iri("c")),lt(iri("d"))).all() `, expect: []string{""}, }, { - message: "use .In() with .Filter(regex)", + message: "use .in() with .filter(regex)", query: ` - g.V("").In("").Filter(regex("ar?li.*e")).All() + g.v("").in("").filter(regex("ar?li.*e")).all() `, expect: nil, }, { - message: "use .In() with .Filter(prefix)", + message: "use .in() with .filter(prefix)", query: ` - g.V("").In("").Filter(like("al%")).All() + g.v("").in("").filter(like("al%")).all() `, expect: []string{""}, }, { - message: "use .In() with .Filter(wildcard)", + message: "use .in() with .filter(wildcard)", query: ` - g.V("").In("").Filter(like("a?i%e")).All() + g.v("").in("").filter(like("a?i%e")).all() `, expect: []string{""}, }, { - message: "use .In() with .Filter(regex with IRIs)", + message: "use .in() with .filter(regex with IRIs)", query: ` - g.V("").In("").Filter(regex("ar?li.*e", true)).All() + g.v("").in("").filter(regex("ar?li.*e", true)).all() `, expect: []string{"", ""}, }, { - message: "use .In() with .Filter(regex with IRIs)", + message: "use .in() with .filter(regex with IRIs)", query: ` - g.V("").In("").Filter(regex(iri("ar?li.*e"))).All() + g.v("").in("").filter(regex(iri("ar?li.*e"))).all() `, err: true, }, { - message: "use .In() with .Filter(regex,gt)", + message: "use .in() with .filter(regex,gt)", query: ` - g.V("").In("").Filter(regex("ar?li.*e", true),gt(iri("c"))).All() + g.v("").in("").filter(regex("ar?li.*e", true),gt(iri("c"))).all() `, expect: []string{""}, }, { - message: "use .Both()", + message: "use .both()", query: ` - g.V("").Both("").All() + g.v("").both("").all() `, expect: []string{"", "", ""}, }, { - message: "use .Both() with tag", + message: "use .both() with tag", query: ` - g.V("").Both(null, "pred").All() + g.v("").both(null, "pred").all() `, tag: "pred", expect: []string{"", "", ""}, }, { - message: "use .Tag()-.Is()-.Back()", + message: "use .tag()-.is()-.back()", query: ` - g.V("").In("").Tag("foo").Out("").Is("cool_person").Back("foo").All() + g.v("").in("").tag("foo").out("").is("cool_person").back("foo").all() `, expect: []string{""}, }, { - message: "separate .Tag()-.Is()-.Back()", + message: "separate .tag()-.is()-.back()", query: ` - x = g.V("").Out("").Tag("foo").Out("").Is("cool_person").Back("foo") - x.In("").Is("").Back("foo").All() + x = g.v("").out("").tag("foo").out("").is("cool_person").back("foo") + x.in("").is("").back("foo").all() `, expect: []string{""}, }, { - message: "do multiple .Back()s", + message: "do multiple .back()", query: ` - g.V("").Out("").As("f").Out("").Out("").Is("cool_person").Back("f").In("").In("").As("acd").Out("").Is("cool_person").Back("f").All() + g.v("").out("").as("f").out("").out("").is("cool_person").back("f").in("").in("").as("acd").out("").is("cool_person").back("f").all() `, tag: "acd", expect: []string{""}, @@ -220,14 +220,14 @@ var testQueries = []struct { { message: "use Except to filter out a single vertex", query: ` - g.V("", "").Except(g.V("")).All() + g.v("", "").except(g.v("")).all() `, expect: []string{""}, }, { message: "use chained Except", query: ` - g.V("", "", "").Except(g.V("")).Except(g.V("")).All() + g.v("", "", "").except(g.v("")).except(g.v("")).all() `, expect: []string{""}, }, @@ -235,7 +235,7 @@ var testQueries = []struct { { message: "use Unique", query: ` - g.V("", "", "").Out("").Unique().All() + g.v("", "", "").out("").unique().all() `, expect: []string{"", "", ""}, }, @@ -244,16 +244,16 @@ var testQueries = []struct { { message: "show simple morphism", query: ` - grandfollows = g.M().Out("").Out("") - g.V("").Follow(grandfollows).All() + grandfollows = g.m().out("").out("") + g.v("").follow(grandfollows).all() `, expect: []string{"", "", ""}, }, { message: "show reverse morphism", query: ` - grandfollows = g.M().Out("").Out("") - g.V("").FollowR(grandfollows).All() + grandfollows = g.m().out("").out("") + g.v("").followR(grandfollows).all() `, expect: []string{"", "", ""}, }, @@ -262,49 +262,49 @@ var testQueries = []struct { { message: "show simple intersection", query: ` - function follows(x) { return g.V(x).Out("") } - follows("").And(follows("")).All() + function follows(x) { return g.v(x).out("") } + follows("").and(follows("")).all() `, expect: []string{""}, }, { message: "show simple morphism intersection", query: ` - grandfollows = g.M().Out("").Out("") - function gfollows(x) { return g.V(x).Follow(grandfollows) } - gfollows("").And(gfollows("")).All() + grandfollows = g.m().out("").out("") + function gfollows(x) { return g.v(x).follow(grandfollows) } + gfollows("").and(gfollows("")).all() `, expect: []string{""}, }, { message: "show double morphism intersection", query: ` - grandfollows = g.M().Out("").Out("") - function gfollows(x) { return g.V(x).Follow(grandfollows) } - gfollows("").And(gfollows("")).And(gfollows("")).All() + grandfollows = g.m().out("").out("") + function gfollows(x) { return g.v(x).follow(grandfollows) } + gfollows("").and(gfollows("")).and(gfollows("")).all() `, expect: []string{""}, }, { message: "show reverse intersection", query: ` - grandfollows = g.M().Out("").Out("") - g.V("").FollowR(grandfollows).Intersect(g.V("").FollowR(grandfollows)).All() + grandfollows = g.m().out("").out("") + g.v("").followR(grandfollows).intersect(g.v("").followR(grandfollows)).all() `, expect: []string{""}, }, { message: "show standard sort of morphism intersection, continue follow", - query: `gfollowers = g.M().In("").In("") - function cool(x) { return g.V(x).As("a").Out("").Is("cool_person").Back("a") } - cool("").Follow(gfollowers).Intersect(cool("").Follow(gfollowers)).All() + query: `gfollowers = g.m().in("").in("") + function cool(x) { return g.v(x).as("a").out("").is("cool_person").back("a") } + cool("").follow(gfollowers).intersect(cool("").follow(gfollowers)).all() `, expect: []string{""}, }, { message: "test Or()", query: ` - g.V("").Out("").Or(g.V().Has("", "cool_person")).All() + g.v("").out("").or(g.v().has("", "cool_person")).all() `, expect: []string{"", "", "", ""}, }, @@ -313,28 +313,28 @@ var testQueries = []struct { { message: "show a simple Has", query: ` - g.V().Has("", "cool_person").All() + g.v().has("", "cool_person").all() `, expect: []string{"", "", ""}, }, { message: "show a simple HasR", query: ` - g.V().HasR("", "").All() + g.v().hasR("", "").all() `, expect: []string{"cool_person"}, }, { message: "show a double Has", query: ` - g.V().Has("", "cool_person").Has("", "").All() + g.v().has("", "cool_person").has("", "").all() `, expect: []string{""}, }, { message: "show a Has with filter", query: ` - g.V().Has("", gt("")).All() + g.v().has("", gt("")).all() `, expect: []string{"", "", "", ""}, }, @@ -343,21 +343,21 @@ var testQueries = []struct { { message: "use Limit", query: ` - g.V().Has("", "cool_person").Limit(2).All() + g.v().has("", "cool_person").limit(2).all() `, expect: []string{"", ""}, }, { message: "use Skip", query: ` - g.V().Has("", "cool_person").Skip(2).All() + g.v().has("", "cool_person").skip(2).all() `, expect: []string{""}, }, { message: "use Skip and Limit", query: ` - g.V().Has("", "cool_person").Skip(1).Limit(1).All() + g.v().has("", "cool_person").skip(1).limit(1).all() `, expect: []string{""}, }, @@ -365,14 +365,14 @@ var testQueries = []struct { { message: "show Count", query: ` - g.V().Has("").Count() + g.v().has("").count() `, expect: []string{"5"}, }, { message: "use Count value", query: ` - g.Emit(g.V().Has("").Count()+1) + g.emit(g.v().has("").count()+1) `, expect: []string{"6"}, }, @@ -381,7 +381,7 @@ var testQueries = []struct { { message: "show a simple save", query: ` - g.V().Save("", "somecool").All() + g.v().save("", "somecool").all() `, tag: "somecool", expect: []string{"cool_person", "cool_person", "cool_person", "smart_person", "smart_person"}, @@ -389,7 +389,7 @@ var testQueries = []struct { { message: "show a simple save optional", query: ` - g.V("","").Out("").SaveOpt("", "somecool").All() + g.v("","").out("").saveOpt("", "somecool").all() `, tag: "somecool", expect: []string{"cool_person", "cool_person"}, @@ -397,7 +397,7 @@ var testQueries = []struct { { message: "show a simple saveR", query: ` - g.V("cool_person").SaveR("", "who").All() + g.v("cool_person").saveR("", "who").all() `, tag: "who", expect: []string{"", "", ""}, @@ -405,7 +405,7 @@ var testQueries = []struct { { message: "show an out save", query: ` - g.V("").Out(null, "pred").All() + g.v("").out(null, "pred").all() `, tag: "pred", expect: []string{"", "", ""}, @@ -413,7 +413,7 @@ var testQueries = []struct { { message: "show a tag list", query: ` - g.V("").Out(null, ["pred", "foo", "bar"]).All() + g.v("").out(null, ["pred", "foo", "bar"]).all() `, tag: "foo", expect: []string{"", "", ""}, @@ -421,28 +421,28 @@ var testQueries = []struct { { message: "show a pred list", query: ` - g.V("").Out(["", ""]).All() + g.v("").out(["", ""]).all() `, expect: []string{"", "", "cool_person"}, }, { message: "show a predicate path", query: ` - g.V("").Out(g.V(""), "pred").All() + g.v("").out(g.v(""), "pred").all() `, expect: []string{"", ""}, }, { message: "list all bob's incoming predicates", query: ` - g.V("").InPredicates().All() + g.v("").inPredicates().all() `, expect: []string{""}, }, { message: "save all bob's incoming predicates", query: ` - g.V("").SaveInPredicates("pred").All() + g.v("").saveInPredicates("pred").all() `, expect: []string{"", "", ""}, tag: "pred", @@ -450,129 +450,129 @@ var testQueries = []struct { { message: "list all labels", query: ` - g.V().Labels().All() + g.v().labels().all() `, expect: []string{""}, }, { message: "list all in predicates", query: ` - g.V().InPredicates().All() + g.v().inPredicates().all() `, expect: []string{"", "", ""}, }, { message: "list all out predicates", query: ` - g.V().OutPredicates().All() + g.v().outPredicates().all() `, expect: []string{"", "", ""}, }, { message: "traverse using LabelContext", query: ` - g.V("").LabelContext("").Out("").All() + g.v("").labelContext("").out("").all() `, expect: []string{"smart_person"}, }, { message: "open and close a LabelContext", query: ` - g.V().LabelContext("").In("").LabelContext(null).In("").All() + g.v().labelContext("").in("").labelContext(null).in("").all() `, expect: []string{"", ""}, }, { message: "issue #254", - query: `g.V({"id":""}).All()`, + query: `g.v({"id":""}).all()`, expect: nil, err: true, }, { message: "roundtrip values", query: ` - v = g.V("").ToValue() - s = g.V(v).Out("").ToValue() - g.V(s).All() + v = g.v("").toValue() + s = g.v(v).out("").toValue() + g.v(s).all() `, expect: []string{"cool_person"}, }, { message: "roundtrip values (tag map)", query: ` - v = g.V("").TagValue() - s = g.V(v.id).Out("").TagValue() - g.V(s.id).All() + v = g.v("").tagValue() + s = g.v(v.id).out("").tagValue() + g.v(s.id).all() `, expect: []string{"cool_person"}, }, { message: "show ToArray", query: ` - arr = g.V("").In("").ToArray() - for (i in arr) g.Emit(arr[i]); + arr = g.v("").in("").toArray() + for (i in arr) g.emit(arr[i]); `, expect: []string{"", "", ""}, }, { message: "show ToArray with limit", query: ` - arr = g.V("").In("").ToArray(2) - for (i in arr) g.Emit(arr[i]); + arr = g.v("").in("").toArray(2) + for (i in arr) g.emit(arr[i]); `, expect: []string{"", ""}, }, { message: "show ForEach", query: ` - g.V("").In("").ForEach(function(o){g.Emit(o.id)}); + g.v("").in("").forEach(function(o){g.emit(o.id)}); `, expect: []string{"", "", ""}, }, { message: "show ForEach with limit", query: ` - g.V("").In("").ForEach(2, function(o){g.Emit(o.id)}); + g.v("").in("").forEach(2, function(o){g.emit(o.id)}); `, expect: []string{"", ""}, }, { message: "clone paths", query: ` - var alice = g.V('') - g.Emit(alice.ToValue()) - var out = alice.Out('') - g.Emit(out.ToValue()) - g.Emit(alice.ToValue()) + var alice = g.v('') + g.emit(alice.toValue()) + var out = alice.out('') + g.emit(out.toValue()) + g.emit(alice.toValue()) `, expect: []string{"", "", ""}, }, { message: "default namespaces", query: ` - g.AddDefaultNamespaces() - g.Emit(g.Uri('rdf:type')) + g.addDefaultNamespaces() + g.emit(g.uri('rdf:type')) `, expect: []string{""}, }, { message: "add namespace", query: ` - g.AddNamespace('ex','http://example.net/') - g.Emit(g.Uri('ex:alice')) + g.addNamespace('ex','http://example.net/') + g.emit(g.uri('ex:alice')) `, expect: []string{""}, }, { message: "recursive follow", query: ` - g.V("").FollowRecursive("").All(); + g.v("").followRecursive("").all(); `, expect: []string{"", "", "", ""}, }, { message: "recursive follow tag", query: ` - g.V("").FollowRecursive("", "depth").All(); + g.v("").followRecursive("", "depth").all(); `, tag: "depth", expect: []string{intVal(1), intVal(1), intVal(2), intVal(2)}, @@ -580,21 +580,21 @@ var testQueries = []struct { { message: "recursive follow path", query: ` - g.V("").FollowRecursive(g.V().Out("")).All(); + g.v("").followRecursive(g.v().out("")).all(); `, expect: []string{"", "", "", ""}, }, { message: "find non-existent", query: ` - g.V('').ForEach(function(d){ g.Emit(d); }) + g.v('').forEach(function(d){ g.emit(d); }) `, expect: nil, }, { message: "default limit All", query: ` - g.V().All() + g.v().all() `, limit: issue718Limit, data: issue718Graph(), @@ -603,7 +603,7 @@ var testQueries = []struct { { message: "issue #758. Verify saveOpt respects label context", query: ` - g.V("").LabelContext("").SaveOpt("", "statusTag").All() + g.v("").labelContext("").saveOpt("", "statusTag").all() `, tag: "statusTag", file: multiGraphTestFile, @@ -612,7 +612,7 @@ var testQueries = []struct { { message: "issue #758. Verify saveR respects label context.", query: ` - g.V("smart_person").LabelContext("").SaveR("", "who").All() + g.v("smart_person").labelContext("").saveR("", "who").all() `, tag: "who", file: multiGraphTestFile, @@ -708,7 +708,9 @@ var issue160TestGraph = []quad.Quad{ } func TestIssue160(t *testing.T) { - qu := `g.V().Tag('query').Out(raw('follows')).Out(raw('follows')).ForEach(function (item) { if (item.id !== item.query) g.Emit({ id: item.id }); })` + qu := `g.v().tag('query').out(raw('follows')).out(raw('follows')).forEach(function (item) { + if (item.id !== item.query) g.emit({ id: item.id }); + })` expect := []string{ "****\nid : alice\n", "****\nid : bob\n", @@ -737,8 +739,8 @@ func TestIssue160(t *testing.T) { func TestShapeOf(t *testing.T) { ses := makeTestSession(nil) - const query = `g.V().ForEach(function(x){ -g.Emit({id: x.id}) + const query = `g.v().forEach(function(x){ +g.emit({id: x.id}) })` _, err := ses.ShapeOf(query) require.NoError(t, err) From 00d552a1b80169018e05bdeb0f316728ac499386 Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Sun, 15 Sep 2019 23:13:06 +0300 Subject: [PATCH 03/13] Fix integration tests --- graph/graphtest/integration.go | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/graph/graphtest/integration.go b/graph/graphtest/integration.go index 9bc98c893..25e22cf2c 100644 --- a/graph/graphtest/integration.go +++ b/graph/graphtest/integration.go @@ -92,7 +92,7 @@ var queries = []struct { { message: "name predicate", query: ` - g.V("Humphrey Bogart").In("").All() + g.v("Humphrey Bogart").in("").all() `, expect: []interface{}{ map[string]string{"id": ""}, @@ -105,11 +105,11 @@ var queries = []struct { { message: "two large sets with no intersection", query: ` - function getId(x) { return g.V(x).In("") } - var actor_to_film = g.M().In("").In("") + function getId(x) { return g.v(x).in("") } + var actor_to_film = g.m().in("").in("") - getId("Oliver Hardy").Follow(actor_to_film).Out("").Intersect( - getId("Mel Blanc").Follow(actor_to_film).Out("")).All() + getId("Oliver Hardy").follow(actor_to_film).out("").intersect( + getId("Mel Blanc").follow(actor_to_film).out("")).all() `, expect: nil, }, @@ -119,19 +119,19 @@ var queries = []struct { message: "three huge sets with small intersection", long: true, query: ` - function getId(x) { return g.V(x).In("") } - var actor_to_film = g.M().In("").In("") + function getId(x) { return g.v(x).in("") } + var actor_to_film = g.m().in("").in("") - var a = getId("Oliver Hardy").Follow(actor_to_film).FollowR(actor_to_film) - var b = getId("Mel Blanc").Follow(actor_to_film).FollowR(actor_to_film) - var c = getId("Billy Gilbert").Follow(actor_to_film).FollowR(actor_to_film) + var a = getId("Oliver Hardy").follow(actor_to_film).followR(actor_to_film) + var b = getId("Mel Blanc").follow(actor_to_film).followR(actor_to_film) + var c = getId("Billy Gilbert").follow(actor_to_film).followR(actor_to_film) seen = {} - a.Intersect(b).Intersect(c).ForEach(function (d) { + a.intersect(b).intersect(c).forEach(function (d) { if (!(d.id in seen)) { seen[d.id] = true; - g.Emit(d) + g.emit(d) } }) `, @@ -148,7 +148,7 @@ var queries = []struct { message: "the helpless checker", long: true, query: ` - g.V().As("person").In("").In().In().Out("").Is("Casablanca").All() + g.v().as("person").in("").in().in().out("").is("Casablanca").all() `, tag: "person", expect: []interface{}{ @@ -175,7 +175,7 @@ var queries = []struct { message: "the helpless checker, negated (films without Ingrid Bergman)", long: true, query: ` - g.V().As("person").In("").In().In().Out("").Except(g.V("Ingrid Bergman").In("").In().In().Out("")).Is("Casablanca").All() + g.v().as("person").in("").in().in().out("").except(g.v("Ingrid Bergman").in("").in().in().out("")).is("Casablanca").all() `, tag: "person", expect: nil, @@ -184,7 +184,7 @@ var queries = []struct { message: "the helpless checker, negated (without actors Ingrid Bergman)", long: true, query: ` - g.V().As("person").In("").Except(g.V("Ingrid Bergman").In("")).In().In().Out("").Is("Casablanca").All() + g.v().as("person").in("").except(g.v("Ingrid Bergman").in("")).in().in().out("").is("Casablanca").all() `, tag: "person", expect: []interface{}{ @@ -209,7 +209,7 @@ var queries = []struct { //A: "Sandra Bullock" { message: "Net and Speed", - query: common + `m1_actors.Intersect(m2_actors).Out("").All() + query: common + `m1_actors.intersect(m2_actors).out("").All() `, expect: []interface{}{ map[string]string{"id": SandraB, "movie1": "The Net", "movie2": nSpeed}, @@ -220,7 +220,7 @@ var queries = []struct { //A: No { message: "Keanu in The Net", - query: common + `actor2.Intersect(m1_actors).Out("").All() + query: common + `actor2.intersect(m1_actors).out("").All() `, expect: nil, }, @@ -229,7 +229,7 @@ var queries = []struct { //A: Yes { message: "Keanu in Speed", - query: common + `actor2.Intersect(m2_actors).Out("").All() + query: common + `actor2.intersect(m2_actors).out("").All() `, expect: []interface{}{ map[string]string{"id": KeanuR, "movie2": nSpeed}, @@ -242,7 +242,7 @@ var queries = []struct { { message: "Keanu with other in The Net", long: true, - query: common + `actor2.Follow(coStars1).Intersect(m1_actors).Out("").All() + query: common + `actor2.Follow(coStars1).intersect(m1_actors).out("").All() `, expect: []interface{}{ map[string]string{"id": SandraB, "movie1": "The Net", "costar1_movie": nSpeed}, @@ -256,7 +256,7 @@ var queries = []struct { { message: "Keanu and Bullock with other", long: true, - query: common + `actor1.Save("","costar1_actor").Follow(coStars1).Intersect(actor2.Save("","costar2_actor").Follow(coStars2)).Out("").All() + query: common + `actor1.save("","costar1_actor").follow(coStars1).intersect(actor2.save("","costar2_actor").follow(coStars2)).out("").All() `, expect: []interface{}{ costarTag(SandraB, SandraB, "The Proposal", KeanuR, nSpeed), @@ -430,7 +430,7 @@ var queries = []struct { { message: "Save a number of predicates around a set of nodes", query: ` - g.V("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").Save("", "char").Save("", "act").SaveR("", "film").All() + g.v("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").save("", "char").save("", "act").saveR("", "film").All() `, expect: []interface{}{ map[string]string{"act": "", "char": "Rick Blaine", "film": "", "id": "_:9037"}, @@ -443,21 +443,21 @@ var queries = []struct { } const common = ` -var movie1 = g.V().Has("", "The Net") -var movie2 = g.V().Has("", "Speed") -var actor1 = g.V().Has("", "Sandra Bullock") -var actor2 = g.V().Has("", "Keanu Reeves") +var movie1 = g.v().has("", "The Net") +var movie2 = g.v().has("", "Speed") +var actor1 = g.v().has("", "Sandra Bullock") +var actor2 = g.v().has("", "Keanu Reeves") // (film) -> starring -> (actor) -var filmToActor = g.Morphism().Out("").Out("") +var filmToActor = g.Morphism().out("").out("") // (actor) -> starring -> [film -> starring -> (actor)] -var coStars1 = g.Morphism().In("").In("").Save("","costar1_movie").Follow(filmToActor) -var coStars2 = g.Morphism().In("").In("").Save("","costar2_movie").Follow(filmToActor) +var coStars1 = g.Morphism().in("").in("").save("","costar1_movie").follow(filmToActor) +var coStars2 = g.Morphism().in("").in("").save("","costar2_movie").follow(filmToActor) // Stars for the movies "The Net" and "Speed" -var m1_actors = movie1.Save("","movie1").Follow(filmToActor) -var m2_actors = movie2.Save("","movie2").Follow(filmToActor) +var m1_actors = movie1.save("","movie1").follow(filmToActor) +var m2_actors = movie2.save("","movie2").follow(filmToActor) ` func prepare(t testing.TB, gen testutil.DatabaseFunc) (graph.QuadStore, func()) { From 709a9f64b82d1c41e2278c7e716977129f4eae2c Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Sun, 15 Sep 2019 23:13:54 +0300 Subject: [PATCH 04/13] Fixed integration tests --- graph/graphtest/integration.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/graph/graphtest/integration.go b/graph/graphtest/integration.go index 25e22cf2c..73f2d74c8 100644 --- a/graph/graphtest/integration.go +++ b/graph/graphtest/integration.go @@ -209,7 +209,7 @@ var queries = []struct { //A: "Sandra Bullock" { message: "Net and Speed", - query: common + `m1_actors.intersect(m2_actors).out("").All() + query: common + `m1_actors.intersect(m2_actors).out("").all() `, expect: []interface{}{ map[string]string{"id": SandraB, "movie1": "The Net", "movie2": nSpeed}, @@ -220,7 +220,7 @@ var queries = []struct { //A: No { message: "Keanu in The Net", - query: common + `actor2.intersect(m1_actors).out("").All() + query: common + `actor2.intersect(m1_actors).out("").all() `, expect: nil, }, @@ -229,7 +229,7 @@ var queries = []struct { //A: Yes { message: "Keanu in Speed", - query: common + `actor2.intersect(m2_actors).out("").All() + query: common + `actor2.intersect(m2_actors).out("").all() `, expect: []interface{}{ map[string]string{"id": KeanuR, "movie2": nSpeed}, @@ -242,7 +242,7 @@ var queries = []struct { { message: "Keanu with other in The Net", long: true, - query: common + `actor2.Follow(coStars1).intersect(m1_actors).out("").All() + query: common + `actor2.Follow(coStars1).intersect(m1_actors).out("").all() `, expect: []interface{}{ map[string]string{"id": SandraB, "movie1": "The Net", "costar1_movie": nSpeed}, @@ -256,7 +256,7 @@ var queries = []struct { { message: "Keanu and Bullock with other", long: true, - query: common + `actor1.save("","costar1_actor").follow(coStars1).intersect(actor2.save("","costar2_actor").follow(coStars2)).out("").All() + query: common + `actor1.save("","costar1_actor").follow(coStars1).intersect(actor2.save("","costar2_actor").follow(coStars2)).out("").all() `, expect: []interface{}{ costarTag(SandraB, SandraB, "The Proposal", KeanuR, nSpeed), @@ -430,7 +430,7 @@ var queries = []struct { { message: "Save a number of predicates around a set of nodes", query: ` - g.v("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").save("", "char").save("", "act").saveR("", "film").All() + g.v("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").save("", "char").save("", "act").saveR("", "film").all() `, expect: []interface{}{ map[string]string{"act": "", "char": "Rick Blaine", "film": "", "id": "_:9037"}, @@ -449,11 +449,11 @@ var actor1 = g.v().has("", "Sandra Bullock") var actor2 = g.v().has("", "Keanu Reeves") // (film) -> starring -> (actor) -var filmToActor = g.Morphism().out("").out("") +var filmToActor = g.morphism().out("").out("") // (actor) -> starring -> [film -> starring -> (actor)] -var coStars1 = g.Morphism().in("").in("").save("","costar1_movie").follow(filmToActor) -var coStars2 = g.Morphism().in("").in("").save("","costar2_movie").follow(filmToActor) +var coStars1 = g.morphism().in("").in("").save("","costar1_movie").follow(filmToActor) +var coStars2 = g.morphism().in("").in("").save("","costar2_movie").follow(filmToActor) // Stars for the movies "The Net" and "Speed" var m1_actors = movie1.save("","movie1").follow(filmToActor) From 41efe679dd4b328995f5258e1d795171878ce09b Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Sun, 15 Sep 2019 23:16:21 +0300 Subject: [PATCH 05/13] fixed integrations test --- graph/graphtest/integration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/graphtest/integration.go b/graph/graphtest/integration.go index 73f2d74c8..10b13fbc3 100644 --- a/graph/graphtest/integration.go +++ b/graph/graphtest/integration.go @@ -242,7 +242,7 @@ var queries = []struct { { message: "Keanu with other in The Net", long: true, - query: common + `actor2.Follow(coStars1).intersect(m1_actors).out("").all() + query: common + `actor2.follow(coStars1).intersect(m1_actors).out("").all() `, expect: []interface{}{ map[string]string{"id": SandraB, "movie1": "The Net", "costar1_movie": nSpeed}, From 1acc521bedf510f511b95e277fb2c96e40ac4e5d Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 00:54:55 +0300 Subject: [PATCH 06/13] Make constructors capitalised --- graph/graphtest/integration.go | 32 +++--- query/gizmo/environ.go | 15 +-- query/gizmo/gizmo.go | 24 +++-- query/gizmo/gizmo_test.go | 179 ++++++++++++++++----------------- 4 files changed, 127 insertions(+), 123 deletions(-) diff --git a/graph/graphtest/integration.go b/graph/graphtest/integration.go index 10b13fbc3..a3d1edd27 100644 --- a/graph/graphtest/integration.go +++ b/graph/graphtest/integration.go @@ -92,7 +92,7 @@ var queries = []struct { { message: "name predicate", query: ` - g.v("Humphrey Bogart").in("").all() + g.V("Humphrey Bogart").in("").all() `, expect: []interface{}{ map[string]string{"id": ""}, @@ -105,8 +105,8 @@ var queries = []struct { { message: "two large sets with no intersection", query: ` - function getId(x) { return g.v(x).in("") } - var actor_to_film = g.m().in("").in("") + function getId(x) { return g.V(x).in("") } + var actor_to_film = g.M().in("").in("") getId("Oliver Hardy").follow(actor_to_film).out("").intersect( getId("Mel Blanc").follow(actor_to_film).out("")).all() @@ -119,8 +119,8 @@ var queries = []struct { message: "three huge sets with small intersection", long: true, query: ` - function getId(x) { return g.v(x).in("") } - var actor_to_film = g.m().in("").in("") + function getId(x) { return g.V(x).in("") } + var actor_to_film = g.M().in("").in("") var a = getId("Oliver Hardy").follow(actor_to_film).followR(actor_to_film) var b = getId("Mel Blanc").follow(actor_to_film).followR(actor_to_film) @@ -148,7 +148,7 @@ var queries = []struct { message: "the helpless checker", long: true, query: ` - g.v().as("person").in("").in().in().out("").is("Casablanca").all() + g.V().as("person").in("").in().in().out("").is("Casablanca").all() `, tag: "person", expect: []interface{}{ @@ -175,7 +175,7 @@ var queries = []struct { message: "the helpless checker, negated (films without Ingrid Bergman)", long: true, query: ` - g.v().as("person").in("").in().in().out("").except(g.v("Ingrid Bergman").in("").in().in().out("")).is("Casablanca").all() + g.V().as("person").in("").in().in().out("").except(g.V("Ingrid Bergman").in("").in().in().out("")).is("Casablanca").all() `, tag: "person", expect: nil, @@ -184,7 +184,7 @@ var queries = []struct { message: "the helpless checker, negated (without actors Ingrid Bergman)", long: true, query: ` - g.v().as("person").in("").except(g.v("Ingrid Bergman").in("")).in().in().out("").is("Casablanca").all() + g.V().as("person").in("").except(g.V("Ingrid Bergman").in("")).in().in().out("").is("Casablanca").all() `, tag: "person", expect: []interface{}{ @@ -430,7 +430,7 @@ var queries = []struct { { message: "Save a number of predicates around a set of nodes", query: ` - g.v("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").save("", "char").save("", "act").saveR("", "film").all() + g.V("_:9037", "_:49278", "_:44112", "_:44709", "_:43382").save("", "char").save("", "act").saveR("", "film").all() `, expect: []interface{}{ map[string]string{"act": "", "char": "Rick Blaine", "film": "", "id": "_:9037"}, @@ -443,17 +443,17 @@ var queries = []struct { } const common = ` -var movie1 = g.v().has("", "The Net") -var movie2 = g.v().has("", "Speed") -var actor1 = g.v().has("", "Sandra Bullock") -var actor2 = g.v().has("", "Keanu Reeves") +var movie1 = g.V().has("", "The Net") +var movie2 = g.V().has("", "Speed") +var actor1 = g.V().has("", "Sandra Bullock") +var actor2 = g.V().has("", "Keanu Reeves") // (film) -> starring -> (actor) -var filmToActor = g.morphism().out("").out("") +var filmToActor = g.Morphism().out("").out("") // (actor) -> starring -> [film -> starring -> (actor)] -var coStars1 = g.morphism().in("").in("").save("","costar1_movie").follow(filmToActor) -var coStars2 = g.morphism().in("").in("").save("","costar2_movie").follow(filmToActor) +var coStars1 = g.Morphism().in("").in("").save("","costar1_movie").follow(filmToActor) +var coStars2 = g.Morphism().in("").in("").save("","costar2_movie").follow(filmToActor) // Stars for the movies "The Net" and "Speed" var m1_actors = movie1.save("","movie1").follow(filmToActor) diff --git a/query/gizmo/environ.go b/query/gizmo/environ.go index 04553c3bf..27008481b 100644 --- a/query/gizmo/environ.go +++ b/query/gizmo/environ.go @@ -36,12 +36,13 @@ import ( // // This is the only special object in the environment, generates the query objects. // Under the hood, they're simple objects that get compiled to a Go iterator tree when executed. +// Methods starting with "New" are accessible in JavaScript with a capital letter (e.g. NewV becomes V) type graphObject struct { s *Session } // Uri creates an IRI values from a given string. -func (g *graphObject) Uri(s string) quad.IRI { +func (g *graphObject) NewIRI(s string) quad.IRI { return quad.IRI(g.s.ns.FullIRI(s)) } @@ -61,8 +62,8 @@ func (g *graphObject) LoadNamespaces() error { } // V is a shorthand for Vertex. -func (g *graphObject) V(call goja.FunctionCall) goja.Value { - return g.Vertex(call) +func (g *graphObject) NewV(call goja.FunctionCall) goja.Value { + return g.NewVertex(call) } // Vertex starts a query path at the given vertex/vertices. No ids means "all vertices". @@ -73,7 +74,7 @@ func (g *graphObject) V(call goja.FunctionCall) goja.Value { // * `nodeId` (Optional): A string or list of strings representing the starting vertices. // // Returns: Path object -func (g *graphObject) Vertex(call goja.FunctionCall) goja.Value { +func (g *graphObject) NewVertex(call goja.FunctionCall) goja.Value { qv, err := toQuadValues(exportArgs(call.Arguments)) if err != nil { return throwErr(g.s.vm, err) @@ -86,8 +87,8 @@ func (g *graphObject) Vertex(call goja.FunctionCall) goja.Value { } // M is a shorthand for Morphism. -func (g *graphObject) M() *pathObject { - return g.Morphism() +func (g *graphObject) NewM() *pathObject { + return g.NewMorphism() } // Morphism creates a morphism path object. Unqueryable on it's own, defines one end of the path. @@ -97,7 +98,7 @@ func (g *graphObject) M() *pathObject { // var shorterPath = graph.Morphism().Out("foo").Out("bar") // // is the common use case. See also: path.Follow(), path.FollowR(). -func (g *graphObject) Morphism() *pathObject { +func (g *graphObject) NewMorphism() *pathObject { return &pathObject{ s: g.s, path: path.StartMorphism(), diff --git a/query/gizmo/gizmo.go b/query/gizmo/gizmo.go index ce21ed993..66cb23314 100644 --- a/query/gizmo/gizmo.go +++ b/query/gizmo/gizmo.go @@ -17,9 +17,10 @@ package gizmo import ( "context" "fmt" + "reflect" "sort" + "strings" "unicode" - "reflect" "github.com/dop251/goja" @@ -62,19 +63,24 @@ func NewSession(qs graph.QuadStore) *Session { } func lcFirst(str string) string { - for i, v := range str { - return string(unicode.ToLower(v)) + str[i+1:] - } - return "" + for i, v := range str { + return string(unicode.ToLower(v)) + str[i+1:] + } + return "" } -type fieldNameMapperToLower struct{} +type fieldNameMapper struct{} -func (fieldNameMapperToLower) FieldName(t reflect.Type, f reflect.StructField) string { +const constructMethodPrefix = "New" + +func (fieldNameMapper) FieldName(t reflect.Type, f reflect.StructField) string { return lcFirst(f.Name) } -func (fieldNameMapperToLower) MethodName(t reflect.Type, m reflect.Method) string { +func (fieldNameMapper) MethodName(t reflect.Type, m reflect.Method) string { + if strings.HasPrefix(m.Name, constructMethodPrefix) { + return strings.TrimPrefix(m.Name, constructMethodPrefix) + } return lcFirst(m.Name) } @@ -107,7 +113,7 @@ func (s *Session) buildEnv() error { return nil } s.vm = goja.New() - s.vm.SetFieldNameMapper(fieldNameMapperToLower{}) + s.vm.SetFieldNameMapper(fieldNameMapper{}) s.vm.Set("graph", &graphObject{s: s}) s.vm.Set("g", s.vm.Get("graph")) for name, val := range defaultEnv { diff --git a/query/gizmo/gizmo_test.go b/query/gizmo/gizmo_test.go index 494a2193a..9e67f2b79 100644 --- a/query/gizmo/gizmo_test.go +++ b/query/gizmo/gizmo_test.go @@ -69,7 +69,7 @@ var testQueries = []struct { query string limit int tag string - file string + file string expect []string err bool // TODO(dennwc): define error types for Gizmo and handle them }{ @@ -77,119 +77,119 @@ var testQueries = []struct { { message: "get a single vertex", query: ` - g.v("").all() + g.V("").all() `, expect: []string{""}, }, { message: "use .getLimit", query: ` - g.v().getLimit(5) + g.V().getLimit(5) `, expect: []string{"", "", "", "", ""}, }, { message: "get a single vertex (IRI)", query: ` - g.v(iri("alice")).all() + g.V(iri("alice")).all() `, expect: []string{""}, }, { message: "use .out()", query: ` - g.v("").out("").all() + g.V("").out("").all() `, expect: []string{""}, }, { message: "use .out() (IRI)", query: ` - g.v(iri("alice")).out(iri("follows")).all() + g.V(iri("alice")).out(iri("follows")).all() `, expect: []string{""}, }, { message: "use .out() (any)", query: ` - g.v("").out().all() + g.V("").out().all() `, expect: []string{"", "cool_person"}, }, { message: "use .in()", query: ` - g.v("").in("").all() + g.V("").in("").all() `, expect: []string{"", "", ""}, }, { message: "use .in() (any)", query: ` - g.v("").in().all() + g.V("").in().all() `, expect: []string{"", "", ""}, }, { message: "use .in() with .filter()", query: ` - g.v("").in("").filter(gt(iri("c")),lt(iri("d"))).all() + g.V("").in("").filter(gt(iri("c")),lt(iri("d"))).all() `, expect: []string{""}, }, { message: "use .in() with .filter(regex)", query: ` - g.v("").in("").filter(regex("ar?li.*e")).all() + g.V("").in("").filter(regex("ar?li.*e")).all() `, expect: nil, }, { message: "use .in() with .filter(prefix)", query: ` - g.v("").in("").filter(like("al%")).all() + g.V("").in("").filter(like("al%")).all() `, expect: []string{""}, }, { message: "use .in() with .filter(wildcard)", query: ` - g.v("").in("").filter(like("a?i%e")).all() + g.V("").in("").filter(like("a?i%e")).all() `, expect: []string{""}, }, { message: "use .in() with .filter(regex with IRIs)", query: ` - g.v("").in("").filter(regex("ar?li.*e", true)).all() + g.V("").in("").filter(regex("ar?li.*e", true)).all() `, expect: []string{"", ""}, }, { message: "use .in() with .filter(regex with IRIs)", query: ` - g.v("").in("").filter(regex(iri("ar?li.*e"))).all() + g.V("").in("").filter(regex(iri("ar?li.*e"))).all() `, err: true, }, { message: "use .in() with .filter(regex,gt)", query: ` - g.v("").in("").filter(regex("ar?li.*e", true),gt(iri("c"))).all() + g.V("").in("").filter(regex("ar?li.*e", true),gt(iri("c"))).all() `, expect: []string{""}, }, { message: "use .both()", query: ` - g.v("").both("").all() + g.V("").both("").all() `, expect: []string{"", "", ""}, }, { message: "use .both() with tag", query: ` - g.v("").both(null, "pred").all() + g.V("").both(null, "pred").all() `, tag: "pred", expect: []string{"", "", ""}, @@ -197,14 +197,14 @@ var testQueries = []struct { { message: "use .tag()-.is()-.back()", query: ` - g.v("").in("").tag("foo").out("").is("cool_person").back("foo").all() + g.V("").in("").tag("foo").out("").is("cool_person").back("foo").all() `, expect: []string{""}, }, { message: "separate .tag()-.is()-.back()", query: ` - x = g.v("").out("").tag("foo").out("").is("cool_person").back("foo") + x = g.V("").out("").tag("foo").out("").is("cool_person").back("foo") x.in("").is("").back("foo").all() `, expect: []string{""}, @@ -212,7 +212,7 @@ var testQueries = []struct { { message: "do multiple .back()", query: ` - g.v("").out("").as("f").out("").out("").is("cool_person").back("f").in("").in("").as("acd").out("").is("cool_person").back("f").all() + g.V("").out("").as("f").out("").out("").is("cool_person").back("f").in("").in("").as("acd").out("").is("cool_person").back("f").all() `, tag: "acd", expect: []string{""}, @@ -220,14 +220,14 @@ var testQueries = []struct { { message: "use Except to filter out a single vertex", query: ` - g.v("", "").except(g.v("")).all() + g.V("", "").except(g.V("")).all() `, expect: []string{""}, }, { message: "use chained Except", query: ` - g.v("", "", "").except(g.v("")).except(g.v("")).all() + g.V("", "", "").except(g.V("")).except(g.V("")).all() `, expect: []string{""}, }, @@ -235,7 +235,7 @@ var testQueries = []struct { { message: "use Unique", query: ` - g.v("", "", "").out("").unique().all() + g.V("", "", "").out("").unique().all() `, expect: []string{"", "", ""}, }, @@ -244,16 +244,16 @@ var testQueries = []struct { { message: "show simple morphism", query: ` - grandfollows = g.m().out("").out("") - g.v("").follow(grandfollows).all() + grandfollows = g.M().out("").out("") + g.V("").follow(grandfollows).all() `, expect: []string{"", "", ""}, }, { message: "show reverse morphism", query: ` - grandfollows = g.m().out("").out("") - g.v("").followR(grandfollows).all() + grandfollows = g.M().out("").out("") + g.V("").followR(grandfollows).all() `, expect: []string{"", "", ""}, }, @@ -262,7 +262,7 @@ var testQueries = []struct { { message: "show simple intersection", query: ` - function follows(x) { return g.v(x).out("") } + function follows(x) { return g.V(x).out("") } follows("").and(follows("")).all() `, expect: []string{""}, @@ -270,8 +270,8 @@ var testQueries = []struct { { message: "show simple morphism intersection", query: ` - grandfollows = g.m().out("").out("") - function gfollows(x) { return g.v(x).follow(grandfollows) } + grandfollows = g.M().out("").out("") + function gfollows(x) { return g.V(x).follow(grandfollows) } gfollows("").and(gfollows("")).all() `, expect: []string{""}, @@ -279,8 +279,8 @@ var testQueries = []struct { { message: "show double morphism intersection", query: ` - grandfollows = g.m().out("").out("") - function gfollows(x) { return g.v(x).follow(grandfollows) } + grandfollows = g.M().out("").out("") + function gfollows(x) { return g.V(x).follow(grandfollows) } gfollows("").and(gfollows("")).and(gfollows("")).all() `, expect: []string{""}, @@ -288,15 +288,15 @@ var testQueries = []struct { { message: "show reverse intersection", query: ` - grandfollows = g.m().out("").out("") - g.v("").followR(grandfollows).intersect(g.v("").followR(grandfollows)).all() + grandfollows = g.M().out("").out("") + g.V("").followR(grandfollows).intersect(g.V("").followR(grandfollows)).all() `, expect: []string{""}, }, { message: "show standard sort of morphism intersection, continue follow", - query: `gfollowers = g.m().in("").in("") - function cool(x) { return g.v(x).as("a").out("").is("cool_person").back("a") } + query: `gfollowers = g.M().in("").in("") + function cool(x) { return g.V(x).as("a").out("").is("cool_person").back("a") } cool("").follow(gfollowers).intersect(cool("").follow(gfollowers)).all() `, expect: []string{""}, @@ -304,7 +304,7 @@ var testQueries = []struct { { message: "test Or()", query: ` - g.v("").out("").or(g.v().has("", "cool_person")).all() + g.V("").out("").or(g.V().has("", "cool_person")).all() `, expect: []string{"", "", "", ""}, }, @@ -313,28 +313,28 @@ var testQueries = []struct { { message: "show a simple Has", query: ` - g.v().has("", "cool_person").all() + g.V().has("", "cool_person").all() `, expect: []string{"", "", ""}, }, { message: "show a simple HasR", query: ` - g.v().hasR("", "").all() + g.V().hasR("", "").all() `, expect: []string{"cool_person"}, }, { message: "show a double Has", query: ` - g.v().has("", "cool_person").has("", "").all() + g.V().has("", "cool_person").has("", "").all() `, expect: []string{""}, }, { message: "show a Has with filter", query: ` - g.v().has("", gt("")).all() + g.V().has("", gt("")).all() `, expect: []string{"", "", "", ""}, }, @@ -343,21 +343,21 @@ var testQueries = []struct { { message: "use Limit", query: ` - g.v().has("", "cool_person").limit(2).all() + g.V().has("", "cool_person").limit(2).all() `, expect: []string{"", ""}, }, { message: "use Skip", query: ` - g.v().has("", "cool_person").skip(2).all() + g.V().has("", "cool_person").skip(2).all() `, expect: []string{""}, }, { message: "use Skip and Limit", query: ` - g.v().has("", "cool_person").skip(1).limit(1).all() + g.V().has("", "cool_person").skip(1).limit(1).all() `, expect: []string{""}, }, @@ -365,14 +365,14 @@ var testQueries = []struct { { message: "show Count", query: ` - g.v().has("").count() + g.V().has("").count() `, expect: []string{"5"}, }, { message: "use Count value", query: ` - g.emit(g.v().has("").count()+1) + g.emit(g.V().has("").count()+1) `, expect: []string{"6"}, }, @@ -381,7 +381,7 @@ var testQueries = []struct { { message: "show a simple save", query: ` - g.v().save("", "somecool").all() + g.V().save("", "somecool").all() `, tag: "somecool", expect: []string{"cool_person", "cool_person", "cool_person", "smart_person", "smart_person"}, @@ -389,7 +389,7 @@ var testQueries = []struct { { message: "show a simple save optional", query: ` - g.v("","").out("").saveOpt("", "somecool").all() + g.V("","").out("").saveOpt("", "somecool").all() `, tag: "somecool", expect: []string{"cool_person", "cool_person"}, @@ -397,7 +397,7 @@ var testQueries = []struct { { message: "show a simple saveR", query: ` - g.v("cool_person").saveR("", "who").all() + g.V("cool_person").saveR("", "who").all() `, tag: "who", expect: []string{"", "", ""}, @@ -405,7 +405,7 @@ var testQueries = []struct { { message: "show an out save", query: ` - g.v("").out(null, "pred").all() + g.V("").out(null, "pred").all() `, tag: "pred", expect: []string{"", "", ""}, @@ -413,7 +413,7 @@ var testQueries = []struct { { message: "show a tag list", query: ` - g.v("").out(null, ["pred", "foo", "bar"]).all() + g.V("").out(null, ["pred", "foo", "bar"]).all() `, tag: "foo", expect: []string{"", "", ""}, @@ -421,28 +421,28 @@ var testQueries = []struct { { message: "show a pred list", query: ` - g.v("").out(["", ""]).all() + g.V("").out(["", ""]).all() `, expect: []string{"", "", "cool_person"}, }, { message: "show a predicate path", query: ` - g.v("").out(g.v(""), "pred").all() + g.V("").out(g.V(""), "pred").all() `, expect: []string{"", ""}, }, { message: "list all bob's incoming predicates", query: ` - g.v("").inPredicates().all() + g.V("").inPredicates().all() `, expect: []string{""}, }, { message: "save all bob's incoming predicates", query: ` - g.v("").saveInPredicates("pred").all() + g.V("").saveInPredicates("pred").all() `, expect: []string{"", "", ""}, tag: "pred", @@ -450,65 +450,65 @@ var testQueries = []struct { { message: "list all labels", query: ` - g.v().labels().all() + g.V().labels().all() `, expect: []string{""}, }, { message: "list all in predicates", query: ` - g.v().inPredicates().all() + g.V().inPredicates().all() `, expect: []string{"", "", ""}, }, { message: "list all out predicates", query: ` - g.v().outPredicates().all() + g.V().outPredicates().all() `, expect: []string{"", "", ""}, }, { message: "traverse using LabelContext", query: ` - g.v("").labelContext("").out("").all() + g.V("").labelContext("").out("").all() `, expect: []string{"smart_person"}, }, { message: "open and close a LabelContext", query: ` - g.v().labelContext("").in("").labelContext(null).in("").all() + g.V().labelContext("").in("").labelContext(null).in("").all() `, expect: []string{"", ""}, }, { message: "issue #254", - query: `g.v({"id":""}).all()`, + query: `g.V({"id":""}).all()`, expect: nil, err: true, }, { message: "roundtrip values", query: ` - v = g.v("").toValue() - s = g.v(v).out("").toValue() - g.v(s).all() + v = g.V("").toValue() + s = g.V(v).out("").toValue() + g.V(s).all() `, expect: []string{"cool_person"}, }, { message: "roundtrip values (tag map)", query: ` - v = g.v("").tagValue() - s = g.v(v.id).out("").tagValue() - g.v(s.id).all() + v = g.V("").tagValue() + s = g.V(v.id).out("").tagValue() + g.V(s.id).all() `, expect: []string{"cool_person"}, }, { message: "show ToArray", query: ` - arr = g.v("").in("").toArray() + arr = g.V("").in("").toArray() for (i in arr) g.emit(arr[i]); `, expect: []string{"", "", ""}, @@ -516,7 +516,7 @@ var testQueries = []struct { { message: "show ToArray with limit", query: ` - arr = g.v("").in("").toArray(2) + arr = g.V("").in("").toArray(2) for (i in arr) g.emit(arr[i]); `, expect: []string{"", ""}, @@ -524,21 +524,21 @@ var testQueries = []struct { { message: "show ForEach", query: ` - g.v("").in("").forEach(function(o){g.emit(o.id)}); + g.V("").in("").forEach(function(o){g.emit(o.id)}); `, expect: []string{"", "", ""}, }, { message: "show ForEach with limit", query: ` - g.v("").in("").forEach(2, function(o){g.emit(o.id)}); + g.V("").in("").forEach(2, function(o){g.emit(o.id)}); `, expect: []string{"", ""}, }, { message: "clone paths", query: ` - var alice = g.v('') + var alice = g.V('') g.emit(alice.toValue()) var out = alice.out('') g.emit(out.toValue()) @@ -550,7 +550,7 @@ var testQueries = []struct { message: "default namespaces", query: ` g.addDefaultNamespaces() - g.emit(g.uri('rdf:type')) + g.emit(g.IRI('rdf:type')) `, expect: []string{""}, }, @@ -558,21 +558,21 @@ var testQueries = []struct { message: "add namespace", query: ` g.addNamespace('ex','http://example.net/') - g.emit(g.uri('ex:alice')) + g.emit(g.IRI('ex:alice')) `, expect: []string{""}, }, { message: "recursive follow", query: ` - g.v("").followRecursive("").all(); + g.V("").followRecursive("").all(); `, expect: []string{"", "", "", ""}, }, { message: "recursive follow tag", query: ` - g.v("").followRecursive("", "depth").all(); + g.V("").followRecursive("", "depth").all(); `, tag: "depth", expect: []string{intVal(1), intVal(1), intVal(2), intVal(2)}, @@ -580,21 +580,21 @@ var testQueries = []struct { { message: "recursive follow path", query: ` - g.v("").followRecursive(g.v().out("")).all(); + g.V("").followRecursive(g.V().out("")).all(); `, expect: []string{"", "", "", ""}, }, { message: "find non-existent", query: ` - g.v('').forEach(function(d){ g.emit(d); }) + g.V('').forEach(function(d){ g.emit(d); }) `, expect: nil, }, { message: "default limit All", query: ` - g.v().all() + g.V().all() `, limit: issue718Limit, data: issue718Graph(), @@ -603,19 +603,19 @@ var testQueries = []struct { { message: "issue #758. Verify saveOpt respects label context", query: ` - g.v("").labelContext("").saveOpt("", "statusTag").all() + g.V("").labelContext("").saveOpt("", "statusTag").all() `, tag: "statusTag", - file: multiGraphTestFile, + file: multiGraphTestFile, expect: []string{"smart_person"}, }, { message: "issue #758. Verify saveR respects label context.", query: ` - g.v("smart_person").labelContext("").saveR("", "who").all() + g.V("smart_person").labelContext("").saveR("", "who").all() `, tag: "who", - file: multiGraphTestFile, + file: multiGraphTestFile, expect: []string{""}, }, } @@ -668,10 +668,10 @@ func TestGizmo(t *testing.T) { test.tag = TopResultTag } quads := simpleGraph - if (test.file == multiGraphTestFile){ + if test.file == multiGraphTestFile { quads = multiGraph } - + if test.data != nil { quads = test.data } @@ -708,7 +708,7 @@ var issue160TestGraph = []quad.Quad{ } func TestIssue160(t *testing.T) { - qu := `g.v().tag('query').out(raw('follows')).out(raw('follows')).forEach(function (item) { + qu := `g.V().tag('query').out(raw('follows')).out(raw('follows')).forEach(function (item) { if (item.id !== item.query) g.emit({ id: item.id }); })` expect := []string{ @@ -739,7 +739,7 @@ func TestIssue160(t *testing.T) { func TestShapeOf(t *testing.T) { ses := makeTestSession(nil) - const query = `g.v().forEach(function(x){ + const query = `g.V().forEach(function(x){ g.emit({id: x.id}) })` _, err := ses.ShapeOf(query) @@ -766,6 +766,3 @@ func issue718Nodes() []string { } return nodes } - - - From 664efb71cae925788f77d69a9443c51554cf63bd Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 01:00:08 +0300 Subject: [PATCH 07/13] Use DecodeRuneInString --- query/gizmo/gizmo.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/query/gizmo/gizmo.go b/query/gizmo/gizmo.go index 66cb23314..785178742 100644 --- a/query/gizmo/gizmo.go +++ b/query/gizmo/gizmo.go @@ -21,6 +21,7 @@ import ( "sort" "strings" "unicode" + "unicode/utf8" "github.com/dop251/goja" @@ -63,10 +64,8 @@ func NewSession(qs graph.QuadStore) *Session { } func lcFirst(str string) string { - for i, v := range str { - return string(unicode.ToLower(v)) + str[i+1:] - } - return "" + rune, size := utf8.DecodeRuneInString(str) + return string(unicode.ToLower(rune)) + str[size:] } type fieldNameMapper struct{} From 50aff30ba7956284404b1b3cc6110fb47b264aad Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 22:48:04 +0300 Subject: [PATCH 08/13] Backwards compatibility --- query/gizmo/environ.go | 29 ++++++++++++ query/gizmo/finals.go | 35 ++++++++++++++ query/gizmo/gizmo.go | 8 +++- query/gizmo/traversals.go | 98 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) diff --git a/query/gizmo/environ.go b/query/gizmo/environ.go index 27008481b..2c0be315b 100644 --- a/query/gizmo/environ.go +++ b/query/gizmo/environ.go @@ -120,6 +120,35 @@ func (g *graphObject) Emit(call goja.FunctionCall) goja.Value { return goja.Null() } +// Backwards compatibility +func (g *graphObject) CapitalizedUri(s string) quad.IRI { + return g.NewIRI(s) +} +func (g *graphObject) CapitalizedAddNamespace(pref, ns string) { + g.AddNamespace(pref, ns) +} +func (g *graphObject) CapitalizedAddDefaultNamespaces() { + g.AddDefaultNamespaces() +} +func (g *graphObject) CapitalizedLoadNamespaces() error { + return g.LoadNamespaces() +} +func (g *graphObject) CapitalizedNewV(call goja.FunctionCall) goja.Value { + return g.NewV(call) +} +func (g *graphObject) CapitalizedNewVertex(call goja.FunctionCall) goja.Value { + return g.NewVertex(call) +} +func (g *graphObject) CapitalizedNewM() *pathObject { + return g.NewM() +} +func (g *graphObject) CapitalizedNewMorphism() *pathObject { + return g.NewMorphism() +} +func (g *graphObject) CapitalizedEmit(call goja.FunctionCall) goja.Value { + return g.Emit(call) +} + func oneStringType(fnc func(s string) quad.Value) func(vm *goja.Runtime, call goja.FunctionCall) goja.Value { return func(vm *goja.Runtime, call goja.FunctionCall) goja.Value { args := toStrings(exportArgs(call.Arguments)) diff --git a/query/gizmo/finals.go b/query/gizmo/finals.go index 2f90e5f53..487bdfcde 100644 --- a/query/gizmo/finals.go +++ b/query/gizmo/finals.go @@ -168,6 +168,41 @@ func (p *pathObject) Count() (int64, error) { return p.s.countResults(it) } +// Backwards compatibility +func (p *pathObject) CaptializedGetLimit(limit int) error { + return p.GetLimit(limit) +} +func (p *pathObject) CaptializedAll() error { + return p.All() +} +func (p *pathObject) CaptializedtoArray(call goja.FunctionCall, withTags bool) goja.Value { + return p.toArray(call, withTags) +} +func (p *pathObject) CaptializedToArray(call goja.FunctionCall) goja.Value { + return p.ToArray(call) +} +func (p *pathObject) CaptializedTagArray(call goja.FunctionCall) goja.Value { + return p.TagArray(call) +} +func (p *pathObject) CaptializedtoValue(withTags bool) (interface{}, error) { + return p.toValue(withTags) +} +func (p *pathObject) CaptializedToValue() (interface{}, error) { + return p.ToValue() +} +func (p *pathObject) CaptializedTagValue() (interface{}, error) { + return p.TagValue() +} +func (p *pathObject) CaptializedMap(call goja.FunctionCall) goja.Value { + return p.Map(call) +} +func (p *pathObject) CaptializedForEach(call goja.FunctionCall) goja.Value { + return p.ForEach(call) +} +func (p *pathObject) CaptializedCount() (int64, error) { + return p.Count() +} + func quadValueToString(v quad.Value) string { if s, ok := v.(quad.String); ok { return string(s) diff --git a/query/gizmo/gizmo.go b/query/gizmo/gizmo.go index 785178742..ea841560d 100644 --- a/query/gizmo/gizmo.go +++ b/query/gizmo/gizmo.go @@ -70,13 +70,17 @@ func lcFirst(str string) string { type fieldNameMapper struct{} -const constructMethodPrefix = "New" - func (fieldNameMapper) FieldName(t reflect.Type, f reflect.StructField) string { return lcFirst(f.Name) } +const constructMethodPrefix = "New" +const backwardsCompatibilityPrefix = "Capitalized" + func (fieldNameMapper) MethodName(t reflect.Type, m reflect.Method) string { + if strings.HasPrefix(m.Name, backwardsCompatibilityPrefix) { + return strings.TrimPrefix(m.Name, backwardsCompatibilityPrefix) + } if strings.HasPrefix(m.Name, constructMethodPrefix) { return strings.TrimPrefix(m.Name, constructMethodPrefix) } diff --git a/query/gizmo/traversals.go b/query/gizmo/traversals.go index e8a010d07..78b5a9725 100644 --- a/query/gizmo/traversals.go +++ b/query/gizmo/traversals.go @@ -679,3 +679,101 @@ func (p *pathObject) Skip(offset int) *pathObject { np := p.clonePath().Skip(int64(offset)) return p.new(np) } + +// Backwards compatibility +func (p *pathObject) CapitalizedIs(call goja.FunctionCall) goja.Value { + return p.Is(call) +} +func (p *pathObject) CapitalizedIn(call goja.FunctionCall) goja.Value { + return p.In(call) +} +func (p *pathObject) CapitalizedOut(call goja.FunctionCall) goja.Value { + return p.Out(call) +} +func (p *pathObject) CapitalizedBoth(call goja.FunctionCall) goja.Value { + return p.Both(call) +} +func (p *pathObject) CapitalizedFollow(path *pathObject) *pathObject { + return p.Follow(path) +} +func (p *pathObject) CapitalizedFollowR(path *pathObject) *pathObject { + return p.FollowR(path) +} +func (p *pathObject) CapitalizedFollowRecursive(call goja.FunctionCall) goja.Value { + return p.FollowRecursive(call) +} +func (p *pathObject) CapitalizedAnd(path *pathObject) *pathObject { + return p.And(path) +} +func (p *pathObject) CapitalizedIntersect(path *pathObject) *pathObject { + return p.Intersect(path) +} +func (p *pathObject) CapitalizedUnion(path *pathObject) *pathObject { + return p.Union(path) +} +func (p *pathObject) CapitalizedOr(path *pathObject) *pathObject { + return p.Or(path) +} +func (p *pathObject) CapitalizedBack(tag string) *pathObject { + return p.Back(tag) +} +func (p *pathObject) CapitalizedTag(tags ...string) *pathObject { + return p.Tag(tags...) +} +func (p *pathObject) CapitalizedAs(tags ...string) *pathObject { + return p.As(tags...) +} +func (p *pathObject) CapitalizedHas(call goja.FunctionCall) goja.Value { + return p.Has(call) +} +func (p *pathObject) CapitalizedHasR(call goja.FunctionCall) goja.Value { + return p.HasR(call) +} +func (p *pathObject) CapitalizedSave(call goja.FunctionCall) goja.Value { + return p.Save(call) +} +func (p *pathObject) CapitalizedSaveR(call goja.FunctionCall) goja.Value { + return p.SaveR(call) +} +func (p *pathObject) CapitalizedSaveOpt(call goja.FunctionCall) goja.Value { + return p.SaveOpt(call) +} +func (p *pathObject) CapitalizedSaveOptR(call goja.FunctionCall) goja.Value { + return p.SaveOptR(call) +} +func (p *pathObject) CapitalizedExcept(path *pathObject) *pathObject { + return p.Except(path) +} +func (p *pathObject) CapitalizedUnique() *pathObject { + return p.Unique() +} +func (p *pathObject) CapitalizedDifference(path *pathObject) *pathObject { + return p.Difference(path) +} +func (p *pathObject) CapitalizedLabels() *pathObject { + return p.Labels() +} +func (p *pathObject) CapitalizedInPredicates() *pathObject { + return p.InPredicates() +} +func (p *pathObject) CapitalizedOutPredicates() *pathObject { + return p.OutPredicates() +} +func (p *pathObject) CapitalizedSaveInPredicates(tag string) *pathObject { + return p.SaveInPredicates(tag) +} +func (p *pathObject) CapitalizedSaveOutPredicates(tag string) *pathObject { + return p.SaveOutPredicates(tag) +} +func (p *pathObject) CapitalizedLabelContext(call goja.FunctionCall) goja.Value { + return p.LabelContext(call) +} +func (p *pathObject) CapitalizedFilter(args ...valFilter) (*pathObject, error) { + return p.Filter(args...) +} +func (p *pathObject) CapitalizedLimit(limit int) *pathObject { + return p.Limit(limit) +} +func (p *pathObject) CapitalizedSkip(offset int) *pathObject { + return p.Skip(offset) +} From 2b4f9b03ab474d1a27de7bfb081238cd17c2af52 Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 22:56:00 +0300 Subject: [PATCH 09/13] Update docs --- docs/GizmoAPI.md | 460 ++++++++++++++++++++++++++--------------------- 1 file changed, 257 insertions(+), 203 deletions(-) diff --git a/docs/GizmoAPI.md b/docs/GizmoAPI.md index 8ec612de1..e146b1fa9 100644 --- a/docs/GizmoAPI.md +++ b/docs/GizmoAPI.md @@ -1,4 +1,5 @@ # Gizmo API + ![Autogenerated file](https://img.shields.io/badge/file-generated-orange.svg) ## The `graph` object @@ -8,70 +9,62 @@ Name: `graph`, Alias: `g` This is the only special object in the environment, generates the query objects. Under the hood, they're simple objects that get compiled to a Go iterator tree when executed. - -### `graph.AddDefaultNamespaces()` +### `graph.addDefaultNamespaces()` AddDefaultNamespaces register all default namespaces for automatic IRI resolution. - -### `graph.AddNamespace(pref, ns)` +### `graph.addNamespace(pref, ns)` AddNamespace associates prefix with a given IRI namespace. - -### `graph.Emit(*)` +### `graph.emit(*)` Emit adds data programmatically to the JSON result list. Can be any JSON type. ```javascript -g.Emit({name:"bob"}) // push {"name":"bob"} as a result +g.emit({ name: "bob" }); // push {"name":"bob"} as a result ``` - -### `graph.LoadNamespaces()` +### `graph.loadNamespaces()` LoadNamespaces loads all namespaces saved to graph. - ### `graph.M()` M is a shorthand for Morphism. - ### `graph.Morphism()` Morphism creates a morphism path object. Unqueryable on it's own, defines one end of the path. Saving these to variables with ```javascript -var shorterPath = graph.Morphism().Out("foo").Out("bar") +var shorterPath = graph + .Morphism() + .out("foo") + .out("bar"); ``` -is the common use case. See also: path.Follow(), path.FollowR(). - +is the common use case. See also: path.follow(), path.followR(). -### `graph.Uri(s)` +### `graph.IRI(s)` Uri creates an IRI values from a given string. - ### `graph.V(*)` V is a shorthand for Vertex. - ### `graph.Vertex([nodeId],[nodeId]...)` Vertex starts a query path at the given vertex/vertices. No ids means "all vertices". - Arguments: -* `nodeId` (Optional): A string or list of strings representing the starting vertices. +- `nodeId` (Optional): A string or list of strings representing the starting vertices. Returns: Path object - ## Path object Both `.Morphism()` and `.Vertex()` create path objects, which provide the following traversal methods. @@ -105,23 +98,19 @@ according to the `` label, eg, the quad: "smart_person" . ``` - -### `path.All()` +### `path.all()` All executes the query and adds the results, with all tags, as a string-to-string (tag to node) map in the output set, one for each path that a traversal could take. - -### `path.And(path)` +### `path.and(path)` And is an alias for Intersect. - -### `path.As(tags)` +### `path.as(tags)` As is an alias for Tag. - -### `path.Back(tag)` +### `path.back([tag])` Back returns current path to a set of nodes on a given tag, preserving all constraints. @@ -131,9 +120,10 @@ Useful for traversing back in queries and taking another route for things that h Arguments: -* `tag`: A previous tag in the query to jump back to. +- `tag`: A previous tag in the query to jump back to. Example: + ```javascript // Start from all nodes, save them into start, follow any status links, // jump back to the starting node, and find who follows them. Return the result. @@ -146,80 +136,90 @@ Example: // {"id": "", "start": ""}, // {"id": "", "start": ""}, // {"id": "", "start": ""} -g.V().Tag("start").Out("").Back("start").In("").All() +g.V() + .Tag("start") + .out("") + .back("start") + .in("") + .all(); ``` - -### `path.Both([predicatePath], [tags])` +### `path.both([predicatePath], [tags])` Both follow the predicate in either direction. Same as Out or In. - Example: + ```javascript // Find all followers/followees of fred. Returns bob, emily and greg -g.V("").Both("").All() +g.V("") + .both("") + .all(); ``` - -### `path.Count()` +### `path.count()` Count returns a number of results and returns it as a value. Example: + ```javascript // Save count as a variable -var n = g.V().Count() +var n = g.V().count(); // Send it as a query result -g.Emit(n) +g.emit(n); ``` - -### `path.Difference(path)` +### `path.difference(path)` Difference is an alias for Except. - -### `path.Except(path)` +### `path.except(path)` Except removes all paths which match query from current path. -In a set-theoretic sense, this is (A - B). While `g.V().Except(path)` to achieve `U - B = !B` is supported, it's often very slow. +In a set-theoretic sense, this is (A - B). While `g.V().except(path)` to achieve `U - B = !B` is supported, it's often very slow. Example: + ```javascript -var cFollows = g.V("").Out("") -var dFollows = g.V("").Out("") +var cFollows = g.V("").out(""); +var dFollows = g.V("").out(""); // People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob. -cFollows.Except(dFollows).All() // The set (dani) -- what charlie follows that dani does not also follow. -// Equivalently, g.V("").Out("").Except(g.V("").Out("")).All() +cFollows.except(dFollows).all(); // The set (dani) -- what charlie follows that dani does not also follow. +// Equivalently, g.V("").out("").except(g.V("").out("")).all() ``` - -### `path.Filter(args)` +### `path.filter(args)` Filter applies constraints to a set of nodes. Can be used to filter values by range or match strings. -#### `path.Filter(regex(expression, includeIRIs))` +#### `path.filter(regex(expression, includeIRIs))` Filters by match a regular expression ([syntax](https://github.com/google/re2/wiki/Syntax)). By default works only on literals unless includeEntities is set to `true`. -### `path.Follow(path)` +### `path.follow(path)` Follow is the way to use a path prepared with Morphism. Applies the path chain on the morphism object to the current path. Starts as if at the g.M() and follows through the morphism path. Example: + ```javascript -var friendOfFriend = g.Morphism().Out("").Out("") +var friendOfFriend = g + .Morphism() + .out("") + .out(""); // Returns the followed people of who charlie follows -- a simplistic "friend of my friend" // and whether or not they have a "cool" status. Potential for recommending followers abounds. // Returns bob and greg -g.V("").Follow(friendOfFriend).Has("", "cool_person").All() +g.V("") + .follow(friendOfFriend) + .has("", "cool_person") + .all(); ``` - -### `path.FollowR(path)` +### `path.followR(path)` FollowR is the same as Follow but follows the chain in the reverse direction. Flips "In" and "Out" where appropriate, the net result being a virtual predicate followed in the reverse direction. @@ -227,338 +227,386 @@ the net result being a virtual predicate followed in the reverse direction. Starts at the end of the morphism and follows it backwards (with appropriate flipped directions) to the g.M() location. Example: + ```javascript -var friendOfFriend = g.Morphism().Out("").Out("") +var friendOfFriend = g + .Morphism() + .out("") + .out(""); // Returns the third-tier of influencers -- people who follow people who follow the cool people. // Returns charlie (from bob), charlie (from greg), bob and emily -g.V().Has("", "cool_person").FollowR(friendOfFriend).All() +g.V() + .has("", "cool_person") + .followR(friendOfFriend) + .all(); ``` - -### `path.FollowRecursive(*)` +### `path.followRecursive(*)` FollowRecursive is the same as Follow but follows the chain recursively. Starts as if at the g.M() and follows through the morphism path multiple times, returning all nodes encountered. Example: + ```javascript -var friend = g.Morphism().Out("") +var friend = g.Morphism().out(""); // Returns all people in Charlie's network. // Returns bob and dani (from charlie), fred (from bob) and greg (from dani). -g.V("").FollowRecursive(friend).All() +g.V("") + .followRecursive(friend) + .all(); ``` - -### `path.ForEach(callback) or (limit, callback)` +### `path.forEach(callback) or (limit, callback)` ForEach calls callback(data) for each result, where data is the tag-to-string map as in All case. - Arguments: -* `limit` (Optional): An integer value on the first `limit` paths to process. -* `callback`: A javascript function of the form `function(data)` +- `limit` (Optional): An integer value on the first `limit` paths to process. +- `callback`: A javascript function of the form `function(data)` Example: + ```javascript -// Simulate query.All().All() -graph.V("").ForEach(function(d) { g.Emit(d) } ) +// Simulate query.all().all() +graph.V("").ForEach(function(d) { + g.emit(d); +}); ``` - -### `path.GetLimit(limit)` +### `path.getLimit(limit)` GetLimit is the same as All, but limited to the first N unique nodes at the end of the path, and each of their possible traversals. - -### `path.Has(predicate, object)` +### `path.has(predicate, object)` Has filters all paths which are, at this point, on the subject for the given predicate and object, but do not follow the path, merely filter the possible paths. Usually useful for starting with all nodes, or limiting to a subset depending on some predicate/value pair. - - Arguments: -* `predicate`: A string for a predicate node. -* `object`: A string for a object node or a set of filters to find it. +- `predicate`: A string for a predicate node. +- `object`: A string for a object node or a set of filters to find it. Example: + ```javascript // Start from all nodes that follow bob -- results in alice, charlie and dani -g.V().Has("", "").All() +g.V() + .has("", "") + .all(); // People charlie follows who then follow fred. Results in bob. -g.V("").Out("").Has("", "").All() +g.V("") + .out("") + .has("", "") + .all(); // People with friends who have names sorting lower then "f". -g.V().Has("", gt("")).All() +g.V() + .has("", gt("")) + .all(); ``` - -### `path.HasR(*)` +### `path.hasR(*)` HasR is the same as Has, but sets constraint in reverse direction. - -### `path.In([predicatePath], [tags])` +### `path.in([predicatePath], [tags])` In is inverse of Out. Starting with the nodes in `path` on the object, follow the quads with predicates defined by `predicatePath` to their subjects. - Arguments: -* `predicatePath` (Optional): One of: - * null or undefined: All predicates pointing into this node - * a string: The predicate name to follow into this node - * a list of strings: The predicates to follow into this node - * a query path object: The target of which is a set of predicates to follow. -* `tags` (Optional): One of: - * null or undefined: No tags - * a string: A single tag to add the predicate used to the output set. - * a list of strings: Multiple tags to use as keys to save the predicate used to the output set. +- `predicatePath` (Optional): One of: + - null or undefined: All predicates pointing into this node + - a string: The predicate name to follow into this node + - a list of strings: The predicates to follow into this node + - a query path object: The target of which is a set of predicates to follow. +- `tags` (Optional): One of: + - null or undefined: No tags + - a string: A single tag to add the predicate used to the output set. + - a list of strings: Multiple tags to use as keys to save the predicate used to the output set. Example: ```javascript // Find the cool people, bob, dani and greg -g.V("cool_person").In("").All() +g.V("cool_person") + .in("") + .all(); // Find who follows bob, in this case, alice, charlie, and dani -g.V("").In("").All() +g.V("") + .in("") + .all(); // Find who follows the people emily follows, namely, bob and emily -g.V("").Out("").In("").All() +g.V("") + .out("") + .in("") + .all(); ``` - -### `path.InPredicates()` +### `path.inPredicates()` InPredicates gets the list of predicates that are pointing in to a node. Example: + ```javascript // bob only has "" predicates pointing inward // returns "" -g.V("").InPredicates().All() +g.V("") + .inPredicates() + .all(); ``` - -### `path.Intersect(path)` +### `path.intersect(path)` Intersect filters all paths by the result of another query path. This is essentially a join where, at the stage of each path, a node is shared. Example: + ```javascript -var cFollows = g.V("").Out("") -var dFollows = g.V("").Out("") +var cFollows = g.V("").out(""); +var dFollows = g.V("").out(""); // People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob. -cFollows.Intersect(dFollows).All() -// Equivalently, g.V("").Out("").And(g.V("").Out("")).All() +cFollows.intersect(dFollows).all(); +// Equivalently, g.V("").out("").And(g.V("").out("")).all() ``` - -### `path.Is(node, [node..])` +### `path.is(node, [node..])` Filter all paths to ones which, at this point, are on the given node. - Arguments: -* `node`: A string for a node. Can be repeated or a list of strings. +- `node`: A string for a node. Can be repeated or a list of strings. Example: + ```javascript // Starting from all nodes in the graph, find the paths that follow bob. -// Results in three paths for bob (from alice, charlie and dani).All() -g.V().Out("").Is("").All() +// Results in three paths for bob (from alice, charlie and dani).all() +g.V() + .out("") + .is("") + .all(); ``` - -### `path.LabelContext([labelPath], [tags])` +### `path.labelContext([labelPath], [tags])` LabelContext sets (or removes) the subgraph context to consider in the following traversals. Affects all In(), Out(), and Both() calls that follow it. The default LabelContext is null (all subgraphs). - Arguments: -* `predicatePath` (Optional): One of: - * null or undefined: In future traversals, consider all edges, regardless of subgraph. - * a string: The name of the subgraph to restrict traversals to. - * a list of strings: A set of subgraphs to restrict traversals to. - * a query path object: The target of which is a set of subgraphs. -* `tags` (Optional): One of: - * null or undefined: No tags - * a string: A single tag to add the last traversed label to the output set. - * a list of strings: Multiple tags to use as keys to save the label used to the output set. +- `predicatePath` (Optional): One of: + - null or undefined: In future traversals, consider all edges, regardless of subgraph. + - a string: The name of the subgraph to restrict traversals to. + - a list of strings: A set of subgraphs to restrict traversals to. + - a query path object: The target of which is a set of subgraphs. +- `tags` (Optional): One of: + - null or undefined: No tags + - a string: A single tag to add the last traversed label to the output set. + - a list of strings: Multiple tags to use as keys to save the label used to the output set. Example: + ```javascript // Find the status of people Dani follows -g.V("").Out("").Out("").All() +g.V("") + .out("") + .out("") + .all(); // Find only the statuses provided by the smart_graph -g.V("").Out("").LabelContext("").Out("").All() +g.V("") + .out("") + .labelContext("") + .out("") + .all(); // Find all people followed by people with statuses in the smart_graph. -g.V().LabelContext("").In("").LabelContext(null).In("").All() +g.V() + .labelContext("") + .in("") + .labelContext(null) + .in("") + .all(); ``` - -### `path.Labels()` +### `path.labels()` Labels gets the list of inbound and outbound quad labels - -### `path.Limit(limit)` +### `path.limit(limit)` Limit limits a number of nodes for current path. Arguments: -* `limit`: A number of nodes to limit results to. +- `limit`: A number of nodes to limit results to. Example: + ```javascript // Start from all nodes that follow bob, and limit them to 2 nodes -- results in alice and charlie -g.V().Has("", "").Limit(2).All() +g.V() + .has("", "") + .limit(2) + .all(); ``` - -### `path.Map(*)` +### `path.map(*)` Map is a alias for ForEach. - -### `path.Or(path)` +### `path.or(path)` Or is an alias for Union. - -### `path.Out([predicatePath], [tags])` +### `path.out([predicatePath], [tags])` Out is the work-a-day way to get between nodes, in the forward direction. Starting with the nodes in `path` on the subject, follow the quads with predicates defined by `predicatePath` to their objects. - Arguments: -* `predicatePath` (Optional): One of: - * null or undefined: All predicates pointing out from this node - * a string: The predicate name to follow out from this node - * a list of strings: The predicates to follow out from this node - * a query path object: The target of which is a set of predicates to follow. -* `tags` (Optional): One of: - * null or undefined: No tags - * a string: A single tag to add the predicate used to the output set. - * a list of strings: Multiple tags to use as keys to save the predicate used to the output set. +- `predicatePath` (Optional): One of: + - null or undefined: All predicates pointing out from this node + - a string: The predicate name to follow out from this node + - a list of strings: The predicates to follow out from this node + - a query path object: The target of which is a set of predicates to follow. +- `tags` (Optional): One of: + - null or undefined: No tags + - a string: A single tag to add the predicate used to the output set. + - a list of strings: Multiple tags to use as keys to save the predicate used to the output set. Example: ```javascript // The working set of this is bob and dani -g.V("").Out("").All() +g.V("") + .out("") + .all(); // The working set of this is fred, as alice follows bob and bob follows fred. -g.V("").Out("").Out("").All() +g.V("") + .out("") + .out("") + .all(); // Finds all things dani points at. Result is bob, greg and cool_person -g.V("").Out().All() +g.V("") + .out() + .all(); // Finds all things dani points at on the status linkage. // Result is bob, greg and cool_person -g.V("").Out(["", ""]).All() +g.V("") + .out(["", ""]) + .all(); // Finds all things dani points at on the status linkage, given from a separate query path. // Result is {"id": "cool_person", "pred": ""} -g.V("").Out(g.V(""), "pred").All() +g.V("") + .out(g.V(""), "pred") + .all(); ``` - -### `path.OutPredicates()` +### `path.outPredicates()` OutPredicates gets the list of predicates that are pointing out from a node. Example: + ```javascript // bob has "" and "" edges pointing outwards // returns "", "" -g.V("").OutPredicates().All() +g.V("") + .outPredicates() + .all(); ``` - -### `path.Save(predicate, tag)` +### `path.save(predicate, tag)` Save saves the object of all quads with predicate into tag, without traversal. - Arguments: -* `predicate`: A string for a predicate node. -* `tag`: A string for a tag key to store the object node. +- `predicate`: A string for a predicate node. +- `tag`: A string for a tag key to store the object node. Example: + ```javascript // Start from dani and bob and save who they follow into "target" // Returns: // {"id" : "", "target": "" }, // {"id" : "", "target": "" }, // {"id" : "", "target": "" } -g.V("", "").Save("", "target").All() +g.V("", "") + .save("", "target") + .all(); ``` - -### `path.SaveInPredicates(tag)` +### `path.saveInPredicates(tag)` SaveInPredicates tags the list of predicates that are pointing in to a node. Example: + ```javascript // bob only has "" predicates pointing inward // returns {"id":"", "pred":""} -g.V("").SaveInPredicates("pred").All() +g.V("") + .saveInPredicates("pred") + .all(); ``` - -### `path.SaveOpt(*)` +### `path.saveOpt(*)` SaveOpt is the same as Save, but returns empty tags if predicate does not exists. - -### `path.SaveOptR(*)` +### `path.saveOptR(*)` SaveOptR is the same as SaveOpt, but tags values via reverse predicate. - -### `path.SaveOutPredicates(tag)` +### `path.saveOutPredicates(tag)` SaveOutPredicates tags the list of predicates that are pointing out from a node. Example: + ```javascript // bob has "" and "" edges pointing outwards // returns {"id":"", "pred":""} -g.V("").SaveInPredicates("pred").All() +g.V("") + .saveInPredicates("pred") + .all(); ``` - -### `path.SaveR(*)` +### `path.saveR(*)` SaveR is the same as Save, but tags values via reverse predicate. - -### `path.Skip(offset)` +### `path.skip(offset)` Skip skips a number of nodes for current path. Arguments: -* `offset`: A number of nodes to skip. +- `offset`: A number of nodes to skip. Example: + ```javascript // Start from all nodes that follow bob, and skip 2 nodes -- results in dani -g.V().Has("", "").Skip(2).All() +g.V() + .has("", "") + .skip(2) + .all(); ``` - -### `path.Tag(tags)` +### `path.tag(tags)` Tag saves a list of nodes to a given tag. @@ -567,8 +615,9 @@ The simplest thing to do is to add a tag anywhere you'd like to put each node in Arguments: -* `tag`: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached "Tag" -Example: +- `tag`: A string or list of strings to act as a result key. The value for tag was the vertex the path was on at the time it reached "Tag" + Example: + ```javascript // Start from all nodes, save them into start, follow any status links, and return the result. // Results are: @@ -577,45 +626,52 @@ Example: // {"id": "cool_person", "start": ""}, // {"id": "smart_person", "start": ""}, // {"id": "smart_person", "start": ""} -g.V().Tag("start").Out("").All() +g.V() + .tag("start") + .out("") + .all(); ``` - -### `path.TagArray(*)` +### `path.tagArray(*)` TagArray is the same as ToArray, but instead of a list of top-level nodes, returns an Array of tag-to-string dictionaries, much as All would, except inside the JS environment. Example: + ```javascript // bobTags contains an Array of followers of bob (alice, charlie, dani). -var bobTags = g.V("").Tag("name").In("").TagArray() +var bobTags = g + .V("") + .tag("name") + .in("") + .tagArray(); // nameValue should be the string "" -var nameValue = bobTags[0]["name"] +var nameValue = bobTags[0]["name"]; ``` - -### `path.TagValue()` +### `path.tagValue()` TagValue is the same as TagArray, but limited to one result node. Returns a tag-to-string map. - -### `path.ToArray(*)` +### `path.toArray(*)` ToArray executes a query and returns the results at the end of the query path as an JS array. Example: + ```javascript // bobFollowers contains an Array of followers of bob (alice, charlie, dani). -var bobFollowers = g.V("").In("").ToArray() +var bobFollowers = g + .V("") + .in("") + .toArray(); ``` - -### `path.ToValue()` +### `path.toValue()` ToValue is the same as ToArray, but limited to one result node. - -### `path.Union(path)` +### `path.union(path)` Union returns the combined paths of the two queries. @@ -624,16 +680,14 @@ they might have had different ways of getting there (and different tags). See also: `path.Tag()` Example: + ```javascript -var cFollows = g.V("").Out("") -var dFollows = g.V("").Out("") +var cFollows = g.V("").out(""); +var dFollows = g.V("").out(""); // People followed by both charlie (bob and dani) and dani (bob and greg) -- returns bob (from charlie), dani, bob (from dani), and greg. -cFollows.Union(dFollows).All() +cFollows.union(dFollows).all(); ``` - -### `path.Unique()` +### `path.unique()` Unique removes duplicate values from the path. - - From b5c7ed6be68f40de03fcfe23a2cdefd89301bdfa Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 22:59:03 +0300 Subject: [PATCH 10/13] Updated examples in code --- docs/api/swagger.yml | 2 +- query/gizmo/environ.go | 6 +++--- query/gizmo/finals.go | 4 ++-- query/gizmo/traversals.go | 24 ++++++++++++------------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/api/swagger.yml b/docs/api/swagger.yml index 39b9e677e..64c64e177 100644 --- a/docs/api/swagger.yml +++ b/docs/api/swagger.yml @@ -366,7 +366,7 @@ paths: examples: gizmo: summary: "Gizmo: first 10 nodes" - value: "g.V().GetLimit(10)" + value: "g.V().getLimit(10)" graphql: summary: "GraphQL: first 10 nodes" value: "{\n nodes(first: 10){\n id\n }\n}" diff --git a/query/gizmo/environ.go b/query/gizmo/environ.go index 2c0be315b..127291214 100644 --- a/query/gizmo/environ.go +++ b/query/gizmo/environ.go @@ -95,9 +95,9 @@ func (g *graphObject) NewM() *pathObject { // Saving these to variables with // // // javascript -// var shorterPath = graph.Morphism().Out("foo").Out("bar") +// var shorterPath = graph.Morphism().out("foo").out("bar") // -// is the common use case. See also: path.Follow(), path.FollowR(). +// is the common use case. See also: path.follow(), path.followR(). func (g *graphObject) NewMorphism() *pathObject { return &pathObject{ s: g.s, @@ -108,7 +108,7 @@ func (g *graphObject) NewMorphism() *pathObject { // Emit adds data programmatically to the JSON result list. Can be any JSON type. // // // javascript -// g.Emit({name:"bob"}) // push {"name":"bob"} as a result +// g.emit({name:"bob"}) // push {"name":"bob"} as a result func (g *graphObject) Emit(call goja.FunctionCall) goja.Value { value := call.Argument(0) if !goja.IsNull(value) && !goja.IsUndefined(value) { diff --git a/query/gizmo/finals.go b/query/gizmo/finals.go index 487bdfcde..85bc0cc2f 100644 --- a/query/gizmo/finals.go +++ b/query/gizmo/finals.go @@ -160,9 +160,9 @@ func (p *pathObject) ForEach(call goja.FunctionCall) goja.Value { // Example: // // javascript // // Save count as a variable -// var n = g.V().Count() +// var n = g.V().count() // // Send it as a query result -// g.Emit(n) +// g.emit(n) func (p *pathObject) Count() (int64, error) { it := p.buildIteratorTree() return p.s.countResults(it) diff --git a/query/gizmo/traversals.go b/query/gizmo/traversals.go index 78b5a9725..a364e5104 100644 --- a/query/gizmo/traversals.go +++ b/query/gizmo/traversals.go @@ -94,8 +94,8 @@ func (p *pathObject) buildIteratorTree() graph.Iterator { // Example: // // javascript // // Starting from all nodes in the graph, find the paths that follow bob. -// // Results in three paths for bob (from alice, charlie and dani).All() -// g.V().Out("").Is("").All() +// // Results in three paths for bob (from alice, charlie and dani).all() +// g.V().out("").is("").all() func (p *pathObject) Is(call goja.FunctionCall) goja.Value { args, err := toQuadValues(exportArgs(call.Arguments)) if err != nil { @@ -138,11 +138,11 @@ func (p *pathObject) inout(call goja.FunctionCall, in bool) goja.Value { // // // javascript // // Find the cool people, bob, dani and greg -// g.V("cool_person").In("").All() +// g.V("cool_person").in("").all() // // Find who follows bob, in this case, alice, charlie, and dani -// g.V("").In("").All() +// g.V("").in("").all() // // Find who follows the people emily follows, namely, bob and emily -// g.V("").Out("").In("").All() +// g.V("").out("").in("").all() func (p *pathObject) In(call goja.FunctionCall) goja.Value { return p.inout(call, true) } @@ -167,17 +167,17 @@ func (p *pathObject) In(call goja.FunctionCall) goja.Value { // // // javascript // // The working set of this is bob and dani -// g.V("").Out("").All() +// g.V("").out("").all() // // The working set of this is fred, as alice follows bob and bob follows fred. -// g.V("").Out("").Out("").All() +// g.V("").out("").out("").all() // // Finds all things dani points at. Result is bob, greg and cool_person -// g.V("").Out().All() +// g.V("").out().all() // // Finds all things dani points at on the status linkage. // // Result is bob, greg and cool_person -// g.V("").Out(["", ""]).All() +// g.V("").out(["", ""]).all() // // Finds all things dani points at on the status linkage, given from a separate query path. // // Result is {"id": "cool_person", "pred": ""} -// g.V("").Out(g.V(""), "pred").All() +// g.V("").out(g.V(""), "pred").all() func (p *pathObject) Out(call goja.FunctionCall) goja.Value { return p.inout(call, false) } @@ -188,7 +188,7 @@ func (p *pathObject) Out(call goja.FunctionCall) goja.Value { // Example: // // javascript // // Find all followers/followees of fred. Returns bob, emily and greg -// g.V("").Both("").All() +// g.V("").both("").all() func (p *pathObject) Both(call goja.FunctionCall) goja.Value { preds, tags, ok := toViaData(exportArgs(call.Arguments)) if !ok { @@ -674,7 +674,7 @@ func (p *pathObject) Limit(limit int) *pathObject { // Example: // // javascript // // Start from all nodes that follow bob, and skip 2 nodes -- results in dani -// g.V().Has("", "").Skip(2).All() +// g.V().has("", "").skip(2).all() func (p *pathObject) Skip(offset int) *pathObject { np := p.clonePath().Skip(int64(offset)) return p.new(np) From 1f86a6d380a863585af783f1238e250aa39ece9a Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 23:00:23 +0300 Subject: [PATCH 11/13] Update more examples --- query/gizmo/traversals.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/query/gizmo/traversals.go b/query/gizmo/traversals.go index a364e5104..8705065f5 100644 --- a/query/gizmo/traversals.go +++ b/query/gizmo/traversals.go @@ -220,7 +220,7 @@ func (p *pathObject) follow(ep *pathObject, rev bool) *pathObject { // // Returns the followed people of who charlie follows -- a simplistic "friend of my friend" // // and whether or not they have a "cool" status. Potential for recommending followers abounds. // // Returns bob and greg -// g.V("").Follow(friendOfFriend).Has("", "cool_person").All() +// g.V("").follow(friendOfFriend).has("", "cool_person").all() func (p *pathObject) Follow(path *pathObject) *pathObject { return p.follow(path, false) } @@ -235,7 +235,7 @@ func (p *pathObject) Follow(path *pathObject) *pathObject { // var friendOfFriend = g.Morphism().Out("").Out("") // // Returns the third-tier of influencers -- people who follow people who follow the cool people. // // Returns charlie (from bob), charlie (from greg), bob and emily -// g.V().Has("", "cool_person").FollowR(friendOfFriend).All() +// g.V().has("", "cool_person").followR(friendOfFriend).all() func (p *pathObject) FollowR(path *pathObject) *pathObject { return p.follow(path, true) } @@ -246,10 +246,10 @@ func (p *pathObject) FollowR(path *pathObject) *pathObject { // // Example: // // javascript: -// var friend = g.Morphism().Out("") +// var friend = g.Morphism().out("") // // Returns all people in Charlie's network. // // Returns bob and dani (from charlie), fred (from bob) and greg (from dani). -// g.V("").FollowRecursive(friend).All() +// g.V("").followRecursive(friend).all() func (p *pathObject) FollowRecursive(call goja.FunctionCall) goja.Value { preds, maxDepth, tags, ok := toViaDepthData(exportArgs(call.Arguments)) if !ok || len(preds) == 0 { From 082f273e473da66b97696502efce9b1446684a69 Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Wed, 18 Sep 2019 23:04:36 +0300 Subject: [PATCH 12/13] Update more examples --- docs/Quickstart-As-Application.md | 100 +++++++++++++++++------------- query/gizmo/traversals.go | 18 +++--- 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/docs/Quickstart-As-Application.md b/docs/Quickstart-As-Application.md index 2bb1f92f7..64d55b4ad 100644 --- a/docs/Quickstart-As-Application.md +++ b/docs/Quickstart-As-Application.md @@ -7,9 +7,10 @@ This guide will take you through starting a persistent graph based on the provid Grab the latest [release binary](http://github.com/cayleygraph/cayley/releases) and extract it wherever you like. If you have Docker installed you can check [guide](Container.md) for running Cayley in container. -If you prefer to build from source, see [Contributing.md](Contributing.md) which has instructions. +If you prefer to build from source, see [Contributing.md](Contributing.md) which has instructions. + +### Quick preview -### Quick preview ### If you downloaded the correct binary the fastest way to have a peak into Cayley is to load one of the example data file in the ./data directory, and query them by the web interface. ```bash @@ -110,7 +111,6 @@ cayley> graph.Vertex("").All() cayley> graph.Vertex("").Out("").All() ``` - ### Serve Your Graph Just as before: @@ -127,15 +127,17 @@ listening on :64210, web interface at http://localhost:64210 If you visit that address (often, [http://localhost:64210](http://localhost:64210)) you'll see the full web interface and also have a graph ready to serve queries via the [HTTP API](HTTP.md) -#### Access from other machines #### +#### Access from other machines + When you want to reach the API or UI from another machine in the network you need to specify the host argument: + ```bash ./cayley http --config=cayley.cfg.overview --host=0.0.0.0:64210 ``` -This makes it listen on all interfaces. You can also give it the specific the IP address you want Cayley to bind to. -**Warning**: for security reasons you might not want to do this on a public accessible machine. +This makes it listen on all interfaces. You can also give it the specific the IP address you want Cayley to bind to. +**Warning**: for security reasons you might not want to do this on a public accessible machine. ## UI Overview @@ -143,21 +145,21 @@ This makes it listen on all interfaces. You can also give it the specific the IP Along the side are the various actions or views you can take. From the top, these are: -* Run Query (run the query) -* Gizmo (a dropdown, to pick your query language, MQL is the other) - * [GizmoAPI.md](GizmoAPI.md): This is the one of the two query languages used either via the REPL or HTTP interface. - * [MQL.md](MQL.md): The *other* query language the interfaces support. +- Run Query (run the query) +- Gizmo (a dropdown, to pick your query language, MQL is the other) + - [GizmoAPI.md](GizmoAPI.md): This is the one of the two query languages used either via the REPL or HTTP interface. + - [MQL.md](MQL.md): The _other_ query language the interfaces support. ----- +--- -* Query (a request/response editor for the query language) -* Query Shape (a visualization of the shape of the final query. Does not execute the query.) -* Visualize (runs a query and, if tagged correctly, gives a sigmajs view of the results) -* Write (an interface to write or remove individual quads or quad files) +- Query (a request/response editor for the query language) +- Query Shape (a visualization of the shape of the final query. Does not execute the query.) +- Visualize (runs a query and, if tagged correctly, gives a sigmajs view of the results) +- Write (an interface to write or remove individual quads or quad files) ----- +--- -* Documentation (this documentation) +- Documentation (this documentation) ### Visualize @@ -167,15 +169,15 @@ For example: ```javascript [ -{ - "source": "node1", - "target": "node2" -}, -{ - "source": "node1", - "target": "node3" -}, -] + { + source: "node1", + target: "node2" + }, + { + source: "node1", + target: "node3" + } +]; ``` Other keys are ignored. The upshot is that if you use the "Tag" functionality to add "source" and "target" tags, you can extract and quickly view subgraphs. @@ -184,12 +186,12 @@ Other keys are ignored. The upshot is that if you use the "Tag" functionality to // Visualize who dani follows. g.V("").Tag("source").Out("").Tag("target").All() ``` -The visualizer expects to tag nodes as either "source" or "target." Your source is represented as a blue node. + +The visualizer expects to tag nodes as either "source" or "target." Your source is represented as a blue node. While your target is represented as an orange node. The idea being that our node relationship goes from blue to orange (source to target). ----- - +--- **Sample Data** @@ -201,27 +203,32 @@ The simplest query is merely to return a single vertex. Using the 30kmoviedata.n ```javascript // Query all vertices in the graph, limit to the first 5 vertices found. -graph.Vertex().GetLimit(5) +graph.Vertex().getLimit(5); // Start with only one vertex, the literal name "Humphrey Bogart", and retrieve all of them. -graph.Vertex("Humphrey Bogart").All() +graph.Vertex("Humphrey Bogart").all(); // `g` and `V` are synonyms for `graph` and `Vertex` respectively, as they are quite common. -g.V("Humphrey Bogart").All() +g.V("Humphrey Bogart").all(); // "Humphrey Bogart" is a name, but not an entity. Let's find the entities with this name in our dataset. // Follow links that are pointing In to our "Humphrey Bogart" node with the predicate "". -g.V("Humphrey Bogart").In("").All() +g.V("Humphrey Bogart") + .in("") + .all(); // Notice that "" is a generic predicate in our dataset. // Starting with a movie gives a similar effect. -g.V("Casablanca").In("").All() +g.V("Casablanca") + .in("") + .all(); // Relatedly, we can ask the reverse; all ids with the name "Casablanca" -g.V().Has("", "Casablanca").All() +g.V() + .has("", "Casablanca") + .all(); ``` - You may start to notice a pattern here: with Gizmo, the query lines tend to: Start somewhere in the graph | Follow a path | Run the query with "All" or "GetLimit" @@ -232,16 +239,25 @@ And these pipelines continue... ```javascript // Let's get the list of actors in the film -g.V().Has("","Casablanca") - .Out("").Out("") - .Out("").All() +g.V() + .Has("", "Casablanca") + .Out("") + .Out("") + .Out("") + .All(); // But this is starting to get long. Let's use a morphism -- a pre-defined path stored in a variable -- as our linkage -var filmToActor = g.Morphism().Out("").Out("") - -g.V().Has("", "Casablanca").Follow(filmToActor).Out("").All() +var filmToActor = g + .Morphism() + .Out("") + .Out(""); +g.V() + .Has("", "Casablanca") + .Follow(filmToActor) + .Out("") + .All(); ``` There's more in the JavaScript API Documentation, but that should give you a feel for how to walk around the graph. diff --git a/query/gizmo/traversals.go b/query/gizmo/traversals.go index 8705065f5..7d710f80a 100644 --- a/query/gizmo/traversals.go +++ b/query/gizmo/traversals.go @@ -333,7 +333,7 @@ func (p *pathObject) Or(path *pathObject) *pathObject { // // {"id": "", "start": ""}, // // {"id": "", "start": ""}, // // {"id": "", "start": ""} -// g.V().Tag("start").Out("").Back("start").In("").All() +// g.V().tag("start").out("").back("start").in("").all() func (p *pathObject) Back(tag string) *pathObject { np := p.clonePath().Back(tag) return p.new(np) @@ -356,7 +356,7 @@ func (p *pathObject) Back(tag string) *pathObject { // // {"id": "cool_person", "start": ""}, // // {"id": "smart_person", "start": ""}, // // {"id": "smart_person", "start": ""} -// g.V().Tag("start").Out("").All() +// g.V().tag("start").out("").All() func (p *pathObject) Tag(tags ...string) *pathObject { np := p.clonePath().Tag(tags...) return p.new(np) @@ -382,11 +382,11 @@ func (p *pathObject) As(tags ...string) *pathObject { // Example: // // javascript // // Start from all nodes that follow bob -- results in alice, charlie and dani -// g.V().Has("", "").All() +// g.V().has("", "").all() // // People charlie follows who then follow fred. Results in bob. -// g.V("").Out("").Has("", "").All() +// g.V("").Out("").has("", "").all() // // People with friends who have names sorting lower then "f". -// g.V().Has("", gt("")).All() +// g.V().has("", gt("")).all() func (p *pathObject) Has(call goja.FunctionCall) goja.Value { return p.has(call, false) } @@ -623,11 +623,11 @@ func (p *pathObject) SaveOutPredicates(tag string) *pathObject { // Example: // // javascript // // Find the status of people Dani follows -// g.V("").Out("").Out("").All() +// g.V("").out("").out("").all() // // Find only the statuses provided by the smart_graph -// g.V("").Out("").LabelContext("").Out("").All() +// g.V("").out("").labelContext("").out("").all() // // Find all people followed by people with statuses in the smart_graph. -// g.V().LabelContext("").In("").LabelContext(null).In("").All() +// g.V().labelContext("").in("").labelContext(null).in("").all() func (p *pathObject) LabelContext(call goja.FunctionCall) goja.Value { labels, tags, ok := toViaData(exportArgs(call.Arguments)) if !ok { @@ -659,7 +659,7 @@ func (p *pathObject) Filter(args ...valFilter) (*pathObject, error) { // Example: // // javascript // // Start from all nodes that follow bob, and limit them to 2 nodes -- results in alice and charlie -// g.V().Has("", "").Limit(2).All() +// g.V().has("", "").limit(2).all() func (p *pathObject) Limit(limit int) *pathObject { np := p.clonePath().Limit(int64(limit)) return p.new(np) From 6f5818c4588cb8528fd06ac5c2a3aee006a3a80d Mon Sep 17 00:00:00 2001 From: Iddan Aaronsohn Date: Sun, 22 Sep 2019 05:00:24 +0300 Subject: [PATCH 13/13] Remove CapitalizedNew methods --- query/gizmo/environ.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/query/gizmo/environ.go b/query/gizmo/environ.go index 127291214..40782777f 100644 --- a/query/gizmo/environ.go +++ b/query/gizmo/environ.go @@ -133,18 +133,6 @@ func (g *graphObject) CapitalizedAddDefaultNamespaces() { func (g *graphObject) CapitalizedLoadNamespaces() error { return g.LoadNamespaces() } -func (g *graphObject) CapitalizedNewV(call goja.FunctionCall) goja.Value { - return g.NewV(call) -} -func (g *graphObject) CapitalizedNewVertex(call goja.FunctionCall) goja.Value { - return g.NewVertex(call) -} -func (g *graphObject) CapitalizedNewM() *pathObject { - return g.NewM() -} -func (g *graphObject) CapitalizedNewMorphism() *pathObject { - return g.NewMorphism() -} func (g *graphObject) CapitalizedEmit(call goja.FunctionCall) goja.Value { return g.Emit(call) }