// applyWindow applies windowing to sg.sorted. func (sg *SubGraph) applyPagination(ctx context.Context) error { params := sg.Params if params.Count == 0 && params.Offset == 0 { // No pagination. return nil } x.AssertTrue(len(sg.SrcUIDs.Uids) == len(sg.uidMatrix)) for _, l := range sg.uidMatrix { algo.IntersectWith(l, sg.DestUIDs) start, end := pageRange(&sg.Params, len(l.Uids)) l.Uids = l.Uids[start:end] } // Re-merge the UID matrix. sg.DestUIDs = algo.MergeSorted(sg.uidMatrix) return nil }
// ProcessGraph processes the SubGraph instance accumulating result for the query // from different instances. Note: taskQuery is nil for root node. func ProcessGraph(ctx context.Context, sg, parent *SubGraph, rch chan error) { var err error if len(sg.Attr) == 0 { // If we have a filter SubGraph which only contains an operator, // it won't have any attribute to work on. // This is to allow providing SrcUIDs to the filter children. sg.DestUIDs = sg.SrcUIDs } else if parent == nil && len(sg.SrcFunc) == 0 { // I am root. I don't have any function to execute, and my // result has been prepared for me already. sg.DestUIDs = algo.MergeSorted(sg.uidMatrix) // Could also be = sg.SrcUIDs } else { taskQuery := createTaskQuery(sg) result, err := worker.ProcessTaskOverNetwork(ctx, taskQuery) if err != nil { x.TraceError(ctx, x.Wrapf(err, "Error while processing task")) rch <- err return } sg.uidMatrix = result.UidMatrix sg.values = result.Values if len(sg.values) > 0 { v := sg.values[0] x.Trace(ctx, "Sample value for attr: %v Val: %v", sg.Attr, string(v.Val)) } sg.counts = result.Counts if sg.Params.DoCount && len(sg.Filters) == 0 { // If there is a filter, we need to do more work to get the actual count. x.Trace(ctx, "Zero uids. Only count requested") rch <- nil return } if result.IntersectDest { sg.DestUIDs = algo.IntersectSorted(result.UidMatrix) } else { sg.DestUIDs = algo.MergeSorted(result.UidMatrix) } } if len(sg.DestUIDs.Uids) == 0 { // Looks like we're done here. Be careful with nil srcUIDs! x.Trace(ctx, "Zero uids for %q. Num attr children: %v", sg.Attr, len(sg.Children)) rch <- nil return } // Apply filters if any. if len(sg.Filters) > 0 { // Run all filters in parallel. filterChan := make(chan error, len(sg.Filters)) for _, filter := range sg.Filters { filter.SrcUIDs = sg.DestUIDs go ProcessGraph(ctx, filter, sg, filterChan) } for _ = range sg.Filters { select { case err = <-filterChan: if err != nil { x.TraceError(ctx, x.Wrapf(err, "Error while processing filter task")) rch <- err return } case <-ctx.Done(): x.TraceError(ctx, x.Wrapf(ctx.Err(), "Context done before full execution")) rch <- ctx.Err() return } } // Now apply the results from filter. var lists []*task.List for _, filter := range sg.Filters { lists = append(lists, filter.DestUIDs) } if sg.FilterOp == "|" { sg.DestUIDs = algo.MergeSorted(lists) } else { sg.DestUIDs = algo.IntersectSorted(lists) } } if len(sg.Params.Order) == 0 { // There is no ordering. Just apply pagination and return. if err = sg.applyPagination(ctx); err != nil { rch <- err return } } else { // We need to sort first before pagination. if err = sg.applyOrderAndPagination(ctx); err != nil { rch <- err return } } // Here we consider handling _count_ with filtering. We do this after // pagination because otherwise, we need to do the count with pagination // taken into account. For example, a PL might have only 50 entries but the // user wants to skip 100 entries and return 10 entries. In this case, you // should return a count of 0, not 10. if sg.Params.DoCount { x.AssertTrue(len(sg.Filters) > 0) sg.counts = make([]uint32, len(sg.uidMatrix)) for i, ul := range sg.uidMatrix { // A possible optimization is to return the size of the intersection // without forming the intersection. algo.IntersectWith(ul, sg.DestUIDs) sg.counts[i] = uint32(len(ul.Uids)) } rch <- nil return } childChan := make(chan error, len(sg.Children)) for i := 0; i < len(sg.Children); i++ { child := sg.Children[i] child.SrcUIDs = sg.DestUIDs // Make the connection. go ProcessGraph(ctx, child, sg, childChan) } // Now get all the results back. for _ = range sg.Children { select { case err = <-childChan: if err != nil { x.TraceError(ctx, x.Wrapf(err, "Error while processing child task")) rch <- err return } case <-ctx.Done(): x.TraceError(ctx, x.Wrapf(ctx.Err(), "Context done before full execution")) rch <- ctx.Err() return } } rch <- nil }