diff --git a/gql/parser.go b/gql/parser.go index bc63388140dd6b88861fce94a37fb1e04867635b..1e9d86438614b99197fa218bb308a38b376696c0 100644 --- a/gql/parser.go +++ b/gql/parser.go @@ -152,7 +152,7 @@ func parseArguments(l *lex.Lexer) (result []pair, rerr error) { for { var p pair - // Get key. + // Get key item := <-l.Items if item.Typ == itemArgName { p.Key = item.Val @@ -164,7 +164,7 @@ func parseArguments(l *lex.Lexer) (result []pair, rerr error) { return result, fmt.Errorf("Expecting argument name. Got: %v", item) } - // Get value. + // Get value item = <-l.Items if item.Typ == itemArgVal { p.Val = item.Val @@ -238,7 +238,6 @@ func godeep(l *lex.Lexer, gq *GraphQuery) error { return nil } else if item.Typ == itemLeftRound { - args, err := parseArguments(l) if err != nil { return err diff --git a/posting/list.go b/posting/list.go index 6f7ae0088b2b0fdcc7d99f282f6205fcdb6e799c..e858cf676d64b99a6ed15c1fc496f31130b427f3 100644 --- a/posting/list.go +++ b/posting/list.go @@ -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 count == 0 { + count = l.length() - offset + + } else if 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 } diff --git a/posting/list_test.go b/posting/list_test.go index 824573680a726f867f195df4b88f553974655d2e..79cde4bcbef35fe0dcb99f5287c5e975a3963034 100644 --- a/posting/list_test.go +++ b/posting/list_test.go @@ -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 } diff --git a/query/query.go b/query/query.go index e06a58761e36128aeb3a81c95a9408f9818e3ebb..ee2643e0364134761b3c55015709a02930e9e3dd 100644 --- a/query/query.go +++ b/query/query.go @@ -110,6 +110,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 @@ -325,9 +327,14 @@ func (g *SubGraph) PreTraverse() (gr *pb.GraphResponse, 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) } @@ -394,14 +401,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-- { @@ -412,6 +419,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():] @@ -531,7 +540,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) } diff --git a/task.fbs b/task.fbs index 2e3acbb08719ae83954a5b6c0620f3a9aca6406b..3e6f687d1a077760c72ac05c20e312c5fc55603b 100644 --- a/task.fbs +++ b/task.fbs @@ -3,6 +3,8 @@ namespace task; table Query { attr:string; uids:[ulong]; + count:int; + offset:int; } table Value { diff --git a/task/Query.go b/task/Query.go index 9a2b8e25d4af593d1828ed2f60b400c9dd53f3bd..d28c04a77ca5f8789b980ac748ca3a57e6df45ff 100644 --- a/task/Query.go +++ b/task/Query.go @@ -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() } diff --git a/worker/task.go b/worker/task.go index e6e2f7eee6ec431171e4f690cb076a983dc0b9be..358fe322371ea9e7a89620707bba201f7a08edf8 100644 --- a/worker/task.go +++ b/worker/task.go @@ -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))