Skip to content

Commit

Permalink
Merge pull request #77 from dgraph-io/gql
Browse files Browse the repository at this point in the history
Functionality to find first N results
  • Loading branch information
manishrjain committed May 3, 2016
2 parents e754a36 + dc5bb7c commit d726dea
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 41 deletions.
91 changes: 59 additions & 32 deletions gql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type GraphQuery struct {
UID uint64
XID string
Attr string
First int
Children []*GraphQuery
}

Expand All @@ -41,6 +42,11 @@ type Mutation struct {
Del string
}

type pair struct {
Key string
Val string
}

func run(l *lex.Lexer) {
for state := lexText; state != nil; {
state = state(l)
Expand Down Expand Up @@ -142,6 +148,35 @@ func parseMutationOp(l *lex.Lexer, op string, mu *Mutation) error {
return errors.New("Invalid mutation formatting.")
}

func parseArguments(l *lex.Lexer) (result []pair, rerr error) {
for {
var p pair

// Get key
item := <-l.Items
if item.Typ == itemArgName {
p.Key = item.Val

} else if item.Typ == itemRightRound {
break

} else {
return result, fmt.Errorf("Expecting argument name. Got: %v", item)
}

// Get value
item = <-l.Items
if item.Typ == itemArgVal {
p.Val = item.Val
} else {
return result, fmt.Errorf("Expecting argument value. Got: %v", item)
}

result = append(result, p)
}
return result, nil
}

func getRoot(l *lex.Lexer) (gq *GraphQuery, rerr error) {
item := <-l.Items
if item.Typ != itemName {
Expand All @@ -155,40 +190,24 @@ func getRoot(l *lex.Lexer) (gq *GraphQuery, rerr error) {

var uid uint64
var xid string
for {
var key, val string
// Get key or close bracket
item = <-l.Items
if item.Typ == itemArgName {
key = item.Val
} else if item.Typ == itemRightRound {
break
} else {
return nil, fmt.Errorf("Expecting argument name. Got: %v", item)
}

// Get corresponding value.
item = <-l.Items
if item.Typ == itemArgVal {
val = item.Val
} else {
return nil, fmt.Errorf("Expecting argument va Got: %v", item)
}

if key == "_uid_" {
uid, rerr = strconv.ParseUint(val, 0, 64)
args, err := parseArguments(l)
if err != nil {
return nil, err
}
for _, p := range args {
if p.Key == "_uid_" {
uid, rerr = strconv.ParseUint(p.Val, 0, 64)
if rerr != nil {
return nil, rerr
}
} else if key == "_xid_" {
xid = val
} else if p.Key == "_xid_" {
xid = p.Val

} else {
return nil, fmt.Errorf("Expecting _uid_ or _xid_. Got: %v", item)
return nil, fmt.Errorf("Expecting _uid_ or _xid_. Got: %+v", p)
}
}
if item.Typ != itemRightRound {
return nil, fmt.Errorf("Unexpected token. Got: %v", item)
}

gq = new(GraphQuery)
gq.UID = uid
gq.XID = xid
Expand Down Expand Up @@ -219,10 +238,18 @@ func godeep(l *lex.Lexer, gq *GraphQuery) error {
return nil

} else if item.Typ == itemLeftRound {
// absorb all these, we don't use them right now.
for ti := range l.Items {
if ti.Typ == itemRightRound || ti.Typ == lex.ItemEOF {
return nil
args, err := parseArguments(l)
if err != nil {
return err
}
// We only use argument 'first' for now.
for _, p := range args {
if p.Key == "first" {
count, err := strconv.ParseInt(p.Val, 0, 32)
if err != nil {
return err
}
curp.First = int(count)
}
}
}
Expand Down
53 changes: 53 additions & 0 deletions gql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func TestParse(t *testing.T) {

func TestParseXid(t *testing.T) {
// logrus.SetLevel(logrus.DebugLevel)
// TODO: Why does the query not have _xid_ attribute?
query := `
query {
user(_uid_: 0x11) {
Expand All @@ -100,6 +101,58 @@ func TestParseXid(t *testing.T) {
}
}

func TestParseFirst(t *testing.T) {
query := `
query {
user(_xid_: m.abcd) {
type.object.name
friends (first: 10) {
}
}
}`
gq, _, err := Parse(query)
if err != nil {
t.Error(err)
return
}
if gq == nil {
t.Error("subgraph is nil")
return
}
if len(gq.Children) != 2 {
t.Errorf("Expected 2 children. Got: %v", len(gq.Children))
}
if err := checkAttr(gq.Children[0], "type.object.name"); err != nil {
t.Error(err)
}
if gq.Children[0].First != 0 {
t.Errorf("Expected count 0. Got: %v", gq.Children[0].First)
}
if err := checkAttr(gq.Children[1], "friends"); err != nil {
t.Error(err)
}
if gq.Children[1].First != 10 {
t.Errorf("Expected count 10. Got: %v", gq.Children[1].First)
}
}

func TestParseFirst_error(t *testing.T) {
query := `
query {
user(_xid_: m.abcd) {
type.object.name
friends (first: ) {
}
}
}`
var err error
_, _, err = Parse(query)
t.Log(err)
if err == nil {
t.Error("Expected error")
}
}

func TestParse_error2(t *testing.T) {
query := `
query {
Expand Down
26 changes: 23 additions & 3 deletions posting/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,15 +720,35 @@ func (l *List) LastCompactionTs() time.Time {
return l.lastCompact
}

func (l *List) GetUids() []uint64 {
func (l *List) GetUids(offset, count int) []uint64 {
l.wg.Wait()
l.RLock()
defer l.RUnlock()

result := make([]uint64, l.length())
if offset < 0 {
glog.WithField("offset", offset).Fatal("Unexpected offset")
return make([]uint64, 0)
}

if count < 0 {
count = 0 - count
offset = l.length() - count
}
if offset < 0 {
offset = 0
}

if count == 0 || count > l.length()-offset {
count = l.length() - offset
}
if count < 0 {
count = 0
}

result := make([]uint64, count)
result = result[:0]
var p types.Posting
for i := 0; i < l.length(); i++ {
for i := offset; i < count+offset && i < l.length(); i++ {
if ok := l.get(&p, i); !ok || p.Uid() == math.MaxUint64 {
break
}
Expand Down
26 changes: 26 additions & 0 deletions posting/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ func checkUids(t *testing.T, l *List, uids ...uint64) error {
return fmt.Errorf("Expected: %v. Got: %v", uids[i], p.Uid())
}
}
if len(uids) >= 3 {
ruids := l.GetUids(1, 2)
if len(ruids) != 2 {
return fmt.Errorf("Expected result of length: 2. Got: %v", len(ruids))
}

for i := 0; i < len(ruids); i++ {
if ruids[i] != uids[1+i] {
return fmt.Errorf("GetUids expected: %v. Got: %v", uids[1+i], ruids[i])
}
}

ruids = l.GetUids(1, -2) // offset should be ignored.
ulen := len(uids)
if ulen > 2 && len(ruids) != 2 {
return fmt.Errorf("Expected result of length: 2. Got: %v", len(ruids))
}

for i := 0; i < len(ruids); i++ {
if ruids[i] != uids[ulen-2+i] {
return fmt.Errorf("GetUids neg count expected: %v. Got: %v",
uids[ulen-2+i], ruids[i])
}
}
}

return nil
}

Expand Down
17 changes: 13 additions & 4 deletions query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ func (l *Latency) ToMap() map[string]string {
// client convenient formats, like GraphQL / JSON.
type SubGraph struct {
Attr string
Count int
Offset int
Children []*SubGraph

query []byte
Expand Down Expand Up @@ -374,9 +376,14 @@ func (g *SubGraph) ToProtocolBuffer(l *Latency) (n *graph.Node, rerr error) {
}

func treeCopy(gq *gql.GraphQuery, sg *SubGraph) {
// Typically you act on the current node, and leave recursion to deal with
// children. But, in this case, we don't want to muck with the current
// node, because of the way we're dealing with the root node.
// So, we work on the children, and then recurse for grand children.
for _, gchild := range gq.Children {
dst := new(SubGraph)
dst.Attr = gchild.Attr
dst.Count = gchild.First
sg.Children = append(sg.Children, dst)
treeCopy(gchild, dst)
}
Expand Down Expand Up @@ -443,14 +450,14 @@ func newGraph(euid uint64, exid string) (*SubGraph, error) {
sg.Attr = "_root_"
sg.result = b.Bytes[b.Head():]
// Also add query for consistency and to allow for ToJson() later.
sg.query = createTaskQuery(sg.Attr, []uint64{euid})
sg.query = createTaskQuery(sg, []uint64{euid})
return sg, nil
}

// createTaskQuery generates the query buffer.
func createTaskQuery(attr string, sorted []uint64) []byte {
func createTaskQuery(sg *SubGraph, sorted []uint64) []byte {
b := flatbuffers.NewBuilder(0)
ao := b.CreateString(attr)
ao := b.CreateString(sg.Attr)

task.QueryStartUidsVector(b, len(sorted))
for i := len(sorted) - 1; i >= 0; i-- {
Expand All @@ -461,6 +468,8 @@ func createTaskQuery(attr string, sorted []uint64) []byte {
task.QueryStart(b)
task.QueryAddAttr(b, ao)
task.QueryAddUids(b, vend)
task.QueryAddCount(b, int32(sg.Count))

qend := task.QueryEnd(b)
b.Finish(qend)
return b.Bytes[b.Head():]
Expand Down Expand Up @@ -580,7 +589,7 @@ func ProcessGraph(sg *SubGraph, rch chan error, td time.Duration) {
childchan := make(chan error, len(sg.Children))
for i := 0; i < len(sg.Children); i++ {
child := sg.Children[i]
child.query = createTaskQuery(child.Attr, sorted)
child.query = createTaskQuery(child, sorted)
go ProcessGraph(child, childchan, timeleft)
}

Expand Down
2 changes: 2 additions & 0 deletions task.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ namespace task;
table Query {
attr:string;
uids:[ulong];
count:int;
offset:int;
}

table Value {
Expand Down
20 changes: 19 additions & 1 deletion task/Query.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,27 @@ func (rcv *Query) UidsLength() int {
return 0
}

func QueryStart(builder *flatbuffers.Builder) { builder.StartObject(2) }
func (rcv *Query) Count() int32 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(8))
if o != 0 {
return rcv._tab.GetInt32(o + rcv._tab.Pos)
}
return 0
}

func (rcv *Query) Offset() int32 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
if o != 0 {
return rcv._tab.GetInt32(o + rcv._tab.Pos)
}
return 0
}

func QueryStart(builder *flatbuffers.Builder) { builder.StartObject(4) }
func QueryAddAttr(builder *flatbuffers.Builder, attr flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(attr), 0) }
func QueryAddUids(builder *flatbuffers.Builder, uids flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(1, flatbuffers.UOffsetT(uids), 0) }
func QueryStartUidsVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(8, numElems, 8)
}
func QueryAddCount(builder *flatbuffers.Builder, count int32) { builder.PrependInt32Slot(2, count, 0) }
func QueryAddOffset(builder *flatbuffers.Builder, offset int32) { builder.PrependInt32Slot(3, offset, 0) }
func QueryEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
2 changes: 1 addition & 1 deletion worker/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func processTask(query []byte) (result []byte, rerr error) {
task.ValueAddVal(b, valoffset)
voffsets[i] = task.ValueEnd(b)

ulist := pl.GetUids()
ulist := pl.GetUids(int(q.Offset()), int(q.Count()))
uoffsets[i] = x.UidlistOffset(b, ulist)
}
task.ResultStartValuesVector(b, len(voffsets))
Expand Down

0 comments on commit d726dea

Please sign in to comment.