diff --git a/gql/parser.go b/gql/parser.go index ae511c2c1726c83ab092faaa757867498c1471a7..39f5ddc7c29d4f27368b07ea349a7c21e7411d73 100644 --- a/gql/parser.go +++ b/gql/parser.go @@ -22,12 +22,20 @@ import ( "strconv" "github.com/dgraph-io/dgraph/lex" - "github.com/dgraph-io/dgraph/query" "github.com/dgraph-io/dgraph/x" ) var glog = x.Log("gql") +// GraphQuery stores the parsed Query in a tree format. This gets +// converted to internally used query.SubGraph before processing the query. +type GraphQuery struct { + UID uint64 + XID string + Attr string + Children []*GraphQuery +} + func run(l *lex.Lexer) { for state := lexText; state != nil; { state = state(l) @@ -35,11 +43,11 @@ func run(l *lex.Lexer) { close(l.Items) // No more tokens. } -func Parse(input string) (sg *query.SubGraph, rerr error) { +func Parse(input string) (gq *GraphQuery, rerr error) { l := lex.NewLexer(input) go run(l) - sg = nil + gq = nil for item := range l.Items { if item.Typ == itemText { continue @@ -50,23 +58,23 @@ func Parse(input string) (sg *query.SubGraph, rerr error) { } } if item.Typ == itemLeftCurl { - if sg == nil { - sg, rerr = getRoot(l) + if gq == nil { + gq, rerr = getRoot(l) if rerr != nil { x.Err(glog, rerr).Error("While retrieving subgraph root") return nil, rerr } } else { - if err := godeep(l, sg); err != nil { - return sg, err + if err := godeep(l, gq); err != nil { + return gq, err } } } } - return sg, nil + return gq, nil } -func getRoot(l *lex.Lexer) (sg *query.SubGraph, rerr error) { +func getRoot(l *lex.Lexer) (gq *GraphQuery, rerr error) { item := <-l.Items if item.Typ != itemName { return nil, fmt.Errorf("Expected some name. Got: %v", item) @@ -113,11 +121,14 @@ func getRoot(l *lex.Lexer) (sg *query.SubGraph, rerr error) { if item.Typ != itemRightRound { return nil, fmt.Errorf("Unexpected token. Got: %v", item) } - return query.NewGraph(uid, xid) + gq = new(GraphQuery) + gq.UID = uid + gq.XID = xid + return gq, nil } -func godeep(l *lex.Lexer, sg *query.SubGraph) error { - curp := sg // Used to track current node, for nesting. +func godeep(l *lex.Lexer, gq *GraphQuery) error { + curp := gq // Used to track current node, for nesting. for item := range l.Items { if item.Typ == lex.ItemError { return errors.New(item.Val) @@ -126,9 +137,9 @@ func godeep(l *lex.Lexer, sg *query.SubGraph) error { return nil } else if item.Typ == itemName { - child := new(query.SubGraph) + child := new(GraphQuery) child.Attr = item.Val - sg.Children = append(sg.Children, child) + gq.Children = append(gq.Children, child) curp = child } else if item.Typ == itemLeftCurl { diff --git a/gql/parser_test.go b/gql/parser_test.go index e39775c660206792444f3de322d336b3da3ee89e..d8be58c5c029197a6605607154efd9194ee3126d 100644 --- a/gql/parser_test.go +++ b/gql/parser_test.go @@ -19,11 +19,9 @@ package gql import ( "fmt" "testing" - - "github.com/dgraph-io/dgraph/query" ) -func checkAttr(g *query.SubGraph, attr string) error { +func checkAttr(g *GraphQuery, attr string) error { if g.Attr != attr { return fmt.Errorf("Expected: %v. Got: %v", attr, g.Attr) } @@ -43,31 +41,31 @@ func TestParse(t *testing.T) { } ` - sg, err := Parse(query) + gq, err := Parse(query) if err != nil { t.Error(err) } - if sg == nil { + if gq == nil { t.Error("subgraph is nil") return } - if len(sg.Children) != 4 { - t.Errorf("Expected 4 children. Got: %v", len(sg.Children)) + if len(gq.Children) != 4 { + t.Errorf("Expected 4 children. Got: %v", len(gq.Children)) return } - if err := checkAttr(sg.Children[0], "friends"); err != nil { + if err := checkAttr(gq.Children[0], "friends"); err != nil { t.Error(err) } - if err := checkAttr(sg.Children[1], "gender"); err != nil { + if err := checkAttr(gq.Children[1], "gender"); err != nil { t.Error(err) } - if err := checkAttr(sg.Children[2], "age"); err != nil { + if err := checkAttr(gq.Children[2], "age"); err != nil { t.Error(err) } - if err := checkAttr(sg.Children[3], "hometown"); err != nil { + if err := checkAttr(gq.Children[3], "hometown"); err != nil { t.Error(err) } - child := sg.Children[0] + child := gq.Children[0] if len(child.Children) != 1 { t.Errorf("Expected 1 child of friends. Got: %v", len(child.Children)) } @@ -84,19 +82,19 @@ func TestParseXid(t *testing.T) { type.object.name } }` - sg, err := Parse(query) + gq, err := Parse(query) if err != nil { t.Error(err) return } - if sg == nil { + if gq == nil { t.Error("subgraph is nil") return } - if len(sg.Children) != 1 { - t.Errorf("Expected 1 children. Got: %v", len(sg.Children)) + if len(gq.Children) != 1 { + t.Errorf("Expected 1 children. Got: %v", len(gq.Children)) } - if err := checkAttr(sg.Children[0], "type.object.name"); err != nil { + if err := checkAttr(gq.Children[0], "type.object.name"); err != nil { t.Error(err) } } @@ -143,22 +141,22 @@ func TestParse_pass1(t *testing.T) { } } ` - sg, err := Parse(query) + gq, err := Parse(query) if err != nil { t.Error(err) } - if len(sg.Children) != 2 { - t.Errorf("Expected 2. Got: %v", len(sg.Children)) + if len(gq.Children) != 2 { + t.Errorf("Expected 2. Got: %v", len(gq.Children)) } - if err := checkAttr(sg.Children[0], "name"); err != nil { + if err := checkAttr(gq.Children[0], "name"); err != nil { t.Error(err) } - if err := checkAttr(sg.Children[1], "friends"); err != nil { + if err := checkAttr(gq.Children[1], "friends"); err != nil { t.Error(err) } - f := sg.Children[1] + f := gq.Children[1] if len(f.Children) != 0 { - t.Errorf("Expected 0. Got: %v", len(sg.Children)) + t.Errorf("Expected 0. Got: %v", len(gq.Children)) } } @@ -170,14 +168,14 @@ func TestParse_block(t *testing.T) { } } ` - sg, err := Parse(query) + gq, err := Parse(query) if err != nil { t.Error(err) } - if len(sg.Children) != 1 { - t.Errorf("Expected 1. Got: %v", len(sg.Children)) + if len(gq.Children) != 1 { + t.Errorf("Expected 1. Got: %v", len(gq.Children)) } - if err := checkAttr(sg.Children[0], "type.object.name.es-419"); err != nil { + if err := checkAttr(gq.Children[0], "type.object.name.es-419"); err != nil { t.Error(err) } } diff --git a/query/query.go b/query/query.go index 4d2e28fe5d8e352c6fca59296918fc833b84fd58..6d076f03ecdfe2c4c8c46b2ff52e66ca2c7dfff9 100644 --- a/query/query.go +++ b/query/query.go @@ -24,6 +24,7 @@ import ( "time" "github.com/Sirupsen/logrus" + "github.com/dgraph-io/dgraph/gql" "github.com/dgraph-io/dgraph/posting" "github.com/dgraph-io/dgraph/task" "github.com/dgraph-io/dgraph/uid" @@ -251,7 +252,25 @@ func (g *SubGraph) ToJson(l *Latency) (js []byte, rerr error) { return json.Marshal(r) } -func NewGraph(euid uint64, exid string) (*SubGraph, error) { +func treeCopy(gq *gql.GraphQuery, sg *SubGraph) { + for _, gchild := range gq.Children { + dst := new(SubGraph) + dst.Attr = gchild.Attr + sg.Children = append(sg.Children, dst) + treeCopy(gchild, dst) + } +} + +func ToSubGraph(gq *gql.GraphQuery) (*SubGraph, error) { + sg, err := newGraph(gq.UID, gq.XID) + if err != nil { + return nil, err + } + treeCopy(gq, sg) + return sg, nil +} + +func newGraph(euid uint64, exid string) (*SubGraph, error) { // This would set the Result field in SubGraph, // and populate the children for attributes. if len(exid) > 0 { diff --git a/query/query_test.go b/query/query_test.go index 549127bb6df81a8d3840baf4032e27b1408bf650..c11f2b1aadc82909754b429757a1b9fc8856a8a7 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/dgraph-io/dgraph/commit" + "github.com/dgraph-io/dgraph/gql" "github.com/dgraph-io/dgraph/posting" "github.com/dgraph-io/dgraph/store" "github.com/dgraph-io/dgraph/task" @@ -38,85 +39,6 @@ func setErr(err *error, nerr error) { *err = nerr } -/* -func populateList(key []byte) error { - pl := posting.Get(key) - - t := x.DirectedEdge{ - ValueId: 9, - Source: "query_test", - Timestamp: time.Now(), - } - var err error - setErr(&err, pl.AddMutation(t, posting.Set)) - - t.ValueId = 19 - setErr(&err, pl.AddMutation(t, posting.Set)) - - t.ValueId = 29 - setErr(&err, pl.AddMutation(t, posting.Set)) - - t.Value = "abracadabra" - setErr(&err, pl.AddMutation(t, posting.Set)) - - return err -} - -func TestRun(t *testing.T) { - logrus.SetLevel(logrus.DebugLevel) - - pdir := NewStore(t) - defer os.RemoveAll(pdir) - ps := new(store.Store) - ps.Init(pdir) - - mdir := NewStore(t) - defer os.RemoveAll(mdir) - ms := new(store.Store) - ms.Init(mdir) - posting.Init(ps, ms) - - key := posting.Key(11, "testing") - if err := populateList(key); err != nil { - t.Error(err) - } - key = posting.Key(9, "name") - - m := Message{Id: 11} - ma := Mattr{Attr: "testing"} - m.Attrs = append(m.Attrs, ma) - - if err := Run(&m); err != nil { - t.Error(err) - } - ma = m.Attrs[0] - uids := result.GetRootAsUids(ma.ResultUids, 0) - if uids.UidLength() != 3 { - t.Errorf("Expected 3. Got: %v", uids.UidLength()) - } - var v uint64 - v = 9 - for i := 0; i < uids.UidLength(); i++ { - if uids.Uid(i) == math.MaxUint64 { - t.Error("Value posting encountered at index:", i) - } - if v != uids.Uid(i) { - t.Errorf("Expected: %v. Got: %v", v, uids.Uid(i)) - } - v += 10 - } - log.Debugf("ResultUid buffer size: %v", len(ma.ResultUids)) - - var val string - if err := posting.ParseValue(&val, ma.ResultValue); err != nil { - t.Error(err) - } - if val != "abracadabra" { - t.Errorf("Expected abracadabra. Got: [%q]", val) - } -} -*/ - func addEdge(t *testing.T, edge x.DirectedEdge, l *posting.List) { if err := l.AddMutation(edge, posting.Set); err != nil { t.Error(err) @@ -166,7 +88,7 @@ func checkSingleValue(t *testing.T, child *SubGraph, func TestNewGraph(t *testing.T) { var ex uint64 ex = 101 - sg, err := NewGraph(ex, "") + sg, err := newGraph(ex, "") if err != nil { t.Error(err) } @@ -256,29 +178,26 @@ func TestProcessGraph(t *testing.T) { defer os.RemoveAll(dir) // Alright. Now we have everything set up. Let's create the query. - sg, err := NewGraph(1, "") + query := ` + { + me(_uid_: 0x01) { + friend { + name + } + name + gender + status + } + } + ` + gq, err := gql.Parse(query) + if err != nil { + t.Error(err) + } + sg, err := ToSubGraph(gq) if err != nil { t.Error(err) } - - // Retrieve friends, and their names. - csg := new(SubGraph) - csg.Attr = "friend" - gsg := new(SubGraph) - gsg.Attr = "name" - csg.Children = append(csg.Children, gsg) - sg.Children = append(sg.Children, csg) - - // Retireve profile information for uid:1. - csg = new(SubGraph) - csg.Attr = "name" - sg.Children = append(sg.Children, csg) - csg = new(SubGraph) - csg.Attr = "gender" - sg.Children = append(sg.Children, csg) - csg = new(SubGraph) - csg.Attr = "status" - sg.Children = append(sg.Children, csg) ch := make(chan error) go ProcessGraph(sg, ch) @@ -350,28 +269,27 @@ func TestToJson(t *testing.T) { defer os.RemoveAll(dir) // Alright. Now we have everything set up. Let's create the query. - sg, err := NewGraph(1, "") + query := ` + { + me(_uid_:0x01) { + name + gender + status + friend { + name + } + } + } + ` + + gq, err := gql.Parse(query) + if err != nil { + t.Error(err) + } + sg, err := ToSubGraph(gq) if err != nil { t.Error(err) } - - // Retireve profile information for uid:1. - csg := new(SubGraph) - csg.Attr = "name" - sg.Children = append(sg.Children, csg) - csg = new(SubGraph) - csg.Attr = "gender" - sg.Children = append(sg.Children, csg) - csg = new(SubGraph) - csg.Attr = "status" - sg.Children = append(sg.Children, csg) - - gsg := new(SubGraph) - gsg.Attr = "name" - csg = new(SubGraph) - csg.Attr = "friend" - csg.Children = append(csg.Children, gsg) - sg.Children = append(sg.Children, csg) ch := make(chan error) go ProcessGraph(sg, ch) diff --git a/server/main.go b/server/main.go index 2b8ed635cd22b2143ce4e2161fdef4a10653bb2a..5085ff9fdef228fa478b87c3cd792cd117d2008f 100644 --- a/server/main.go +++ b/server/main.go @@ -70,15 +70,23 @@ func queryHandler(w http.ResponseWriter, r *http.Request) { x.SetStatus(w, x.E_INVALID_REQUEST, "Invalid request encountered.") return } + glog.WithField("q", string(q)).Debug("Query received.") - sg, err := gql.Parse(string(q)) + gq, err := gql.Parse(string(q)) if err != nil { x.Err(glog, err).Error("While parsing query") x.SetStatus(w, x.E_INVALID_REQUEST, err.Error()) return } + sg, err := query.ToSubGraph(gq) + if err != nil { + x.Err(glog, err).Error("While conversion to internal format") + x.SetStatus(w, x.E_INVALID_REQUEST, err.Error()) + return + } l.Parsing = time.Since(l.Start) glog.WithField("q", string(q)).Debug("Query parsed.") + rch := make(chan error) go query.ProcessGraph(sg, rch) err = <-rch diff --git a/server/main_test.go b/server/main_test.go index 369700266c50773bf611f3c4e2b0746b37d17d20..0c384f160c57d5b934f2ee43921a92b82295f601 100644 --- a/server/main_test.go +++ b/server/main_test.go @@ -88,11 +88,17 @@ func TestQuery(t *testing.T) { defer closeAll(dir1, dir2, clog) // Parse GQL into internal query representation. - g, err := gql.Parse(q0) + gq, err := gql.Parse(q0) if err != nil { t.Error(err) return } + g, err := query.ToSubGraph(gq) + if err != nil { + t.Error(err) + return + } + // Test internal query representation. if len(g.Children) != 3 { t.Errorf("Expected 3 children. Got: %v", len(g.Children)) @@ -178,11 +184,17 @@ func BenchmarkQuery(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - g, err := gql.Parse(q1) + gq, err := gql.Parse(q1) if err != nil { b.Error(err) return } + g, err := query.ToSubGraph(gq) + if err != nil { + b.Error(err) + return + } + ch := make(chan error) go query.ProcessGraph(g, ch) if err := <-ch; err != nil {