Skip to content

Commit

Permalink
Enable Pagination(Support for offset and after)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashwin95r committed Jul 28, 2016
1 parent 9d91939 commit b6052a0
Show file tree
Hide file tree
Showing 16 changed files with 505 additions and 60 deletions.
20 changes: 19 additions & 1 deletion gql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type GraphQuery struct {
XID string
Attr string
First int
Offset int
After uint64
Children []*GraphQuery
}

Expand Down Expand Up @@ -240,7 +242,6 @@ func godeep(l *lex.Lexer, gq *GraphQuery) error {
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)
Expand All @@ -249,6 +250,23 @@ func godeep(l *lex.Lexer, gq *GraphQuery) error {
}
curp.First = int(count)
}
if p.Key == "offset" {
count, err := strconv.ParseInt(p.Val, 0, 32)
if err != nil {
return err
}
if count < 0 {
return errors.New("offset cannot be less than 0")
}
curp.Offset = int(count)
}
if p.Key == "after" {
afterUid, err := strconv.ParseUint(p.Val, 0, 64)
if err != nil {
return err
}
curp.After = uint64(afterUid)
}
}
}
}
Expand Down
92 changes: 92 additions & 0 deletions gql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,98 @@ func TestParseFirst_error(t *testing.T) {
}
}

func TestParseAfter(t *testing.T) {
query := `
query {
user(_xid_: m.abcd) {
type.object.name
friends (first: 10, after: 3) {
}
}
}`
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)
}
if gq.Children[1].After != 3 {
t.Errorf("Expected after to be 3. Got: %v", gq.Children[1].Offset)
}
}

func TestParseOffset(t *testing.T) {
query := `
query {
user(_xid_: m.abcd) {
type.object.name
friends (first: 10, offset: 3) {
}
}
}`
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)
}
if gq.Children[1].Offset != 3 {
t.Errorf("Expected Offset 3. Got: %v", gq.Children[1].Offset)
}
}

func TestParseOffset_error(t *testing.T) {
query := `
query {
user(_xid_: m.abcd) {
type.object.name
friends (first: 10, offset: -3) {
}
}
}`
_, _, err := Parse(query)
if err == nil {
t.Error("Expected error on negative offset")
return
}
}

func TestParse_error2(t *testing.T) {
query := `
query {
Expand Down
44 changes: 29 additions & 15 deletions posting/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"log"
"math"
"sort"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -80,6 +81,12 @@ func NewList() *List {
return l
}

type ListOptions struct {
Offset int
Count int
AfterUid uint64
}

type ByUid []*types.Posting

func (pa ByUid) Len() int { return len(pa) }
Expand Down Expand Up @@ -674,35 +681,42 @@ func (l *List) LastCompactionTs() time.Time {
return l.lastCompact
}

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

if offset < 0 {
log.Fatalf("Unexpected offset: %v", offset)
if opt.Offset < 0 {
log.Fatalf("Unexpected offset: %v", opt.Offset)
return make([]uint64, 0)
}

if count < 0 {
count = 0 - count
offset = l.length() - count
var p types.Posting
if opt.AfterUid > 0 {
// sort.Search returns the index of the first element > AfterUid.
opt.Offset = sort.Search(l.length(), func(i int) bool {
l.get(&p, i)
return p.Uid() > opt.AfterUid
})
}
if opt.Count < 0 {
opt.Count = 0 - opt.Count
opt.Offset = l.length() - opt.Count
}
if offset < 0 {
offset = 0
if opt.Offset < 0 {
opt.Offset = 0
}

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

result := make([]uint64, count)
result := make([]uint64, opt.Count)
result = result[:0]
var p types.Posting
for i := offset; i < count+offset && i < l.length(); i++ {
for i := opt.Offset; i < opt.Count+opt.Offset && i < l.length(); i++ {
if ok := l.get(&p, i); !ok || p.Uid() == math.MaxUint64 {
break
}
Expand Down
37 changes: 33 additions & 4 deletions posting/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,58 @@ func checkUids(t *testing.T, l *List, uids ...uint64) error {
}
}
if len(uids) >= 3 {
ruids := l.GetUids(1, 2)
opts := ListOptions{1, 2, 0}
ruids := l.Uids(opts)
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])
return fmt.Errorf("Uids expected: %v. Got: %v", uids[1+i], ruids[i])
}
}

ruids = l.GetUids(1, -2) // offset should be ignored.
opts = ListOptions{1, -2, 0}
ruids = l.Uids(opts) // 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",
return fmt.Errorf("Uids neg count expected: %v. Got: %v",
uids[ulen-2+i], ruids[i])
}
}

// Tests for "after"
opts = ListOptions{0, 2, 10}
ruids = l.Uids(opts)
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("Uids expected: %v. Got: %v", uids[1+i], ruids[i])
}
}

opts = ListOptions{0, 2, 80}
ruids = l.Uids(opts)
if len(ruids) != 1 {
return fmt.Errorf("Expected result of length: 1. Got: %v", len(ruids))
}
if ruids[0] != 81 {
return fmt.Errorf("Uids expected: %v. Got: %v", uids[2], ruids[0])
}

opts = ListOptions{0, 2, 82}
ruids = l.Uids(opts)
if len(ruids) != 0 {
return fmt.Errorf("Expected result of length: 0. Got: %v", len(ruids))
}
}

return nil
Expand Down
5 changes: 5 additions & 0 deletions query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ type SubGraph struct {
Attr string
Count int
Offset int
AfterUid uint64
Children []*SubGraph

Query []byte
Expand Down Expand Up @@ -374,6 +375,8 @@ func treeCopy(gq *gql.GraphQuery, sg *SubGraph) {
for _, gchild := range gq.Children {
dst := new(SubGraph)
dst.Attr = gchild.Attr
dst.Offset = gchild.Offset
dst.AfterUid = gchild.After
dst.Count = gchild.First
sg.Children = append(sg.Children, dst)
treeCopy(gchild, dst)
Expand Down Expand Up @@ -460,6 +463,8 @@ func createTaskQuery(sg *SubGraph, sorted []uint64) []byte {
task.QueryAddAttr(b, ao)
task.QueryAddUids(b, vend)
task.QueryAddCount(b, int32(sg.Count))
task.QueryAddOffset(b, int32(sg.Offset))
task.QueryAddAfterUid(b, uint64(sg.AfterUid))

qend := task.QueryEnd(b)
b.Finish(qend)
Expand Down
1 change: 1 addition & 0 deletions task.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ table Query {
uids:[ulong];
count:int;
offset:int;
afterUid:ulong;
}

table Value {
Expand Down
25 changes: 23 additions & 2 deletions task/Query.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// automatically generated, do not modify
// automatically generated by the FlatBuffers compiler, do not modify

package task

Expand Down Expand Up @@ -47,6 +47,10 @@ func (rcv *Query) Count() int32 {
return 0
}

func (rcv *Query) MutateCount(n int32) bool {
return rcv._tab.MutateInt32Slot(8, n)
}

func (rcv *Query) Offset() int32 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
if o != 0 {
Expand All @@ -55,11 +59,28 @@ func (rcv *Query) Offset() int32 {
return 0
}

func QueryStart(builder *flatbuffers.Builder) { builder.StartObject(4) }
func (rcv *Query) MutateOffset(n int32) bool {
return rcv._tab.MutateInt32Slot(10, n)
}

func (rcv *Query) AfterUid() uint64 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(12))
if o != 0 {
return rcv._tab.GetUint64(o + rcv._tab.Pos)
}
return 0
}

func (rcv *Query) MutateAfterUid(n uint64) bool {
return rcv._tab.MutateUint64Slot(12, n)
}

func QueryStart(builder *flatbuffers.Builder) { builder.StartObject(5) }
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 QueryAddAfterUid(builder *flatbuffers.Builder, afterUid uint64) { builder.PrependUint64Slot(4, afterUid, 0) }
func QueryEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
2 changes: 1 addition & 1 deletion task/Result.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// automatically generated, do not modify
// automatically generated by the FlatBuffers compiler, do not modify

package task

Expand Down
2 changes: 1 addition & 1 deletion task/UidList.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// automatically generated, do not modify
// automatically generated by the FlatBuffers compiler, do not modify

package task

Expand Down
2 changes: 1 addition & 1 deletion task/Value.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// automatically generated, do not modify
// automatically generated by the FlatBuffers compiler, do not modify

package task

Expand Down
2 changes: 1 addition & 1 deletion task/XidList.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// automatically generated, do not modify
// automatically generated by the FlatBuffers compiler, do not modify

package task

Expand Down
Loading

0 comments on commit b6052a0

Please sign in to comment.