diff --git a/posting/list.go b/posting/list.go index 34f75880c93..a6e40336bb9 100644 --- a/posting/list.go +++ b/posting/list.go @@ -1709,45 +1709,80 @@ func (l *List) Uids(opt ListOptions) (*pb.List, error) { if opt.First == 0 { opt.First = math.MaxInt32 } - // Pre-assign length to make it faster. - l.RLock() - // Use approximate length for initial capacity. - res := make([]uint64, 0, l.mutationMap.len()+codec.ApproxLen(l.plist.Pack)) - out := &pb.List{} - if l.mutationMap.len() == 0 && opt.Intersect != nil && len(l.plist.Splits) == 0 { - if opt.ReadTs < l.minTs { - l.RUnlock() - return out, errors.Wrapf(ErrTsTooOld, "While reading UIDs") + + getUidList := func() (*pb.List, error, bool) { + // Pre-assign length to make it faster. + l.RLock() + defer l.RUnlock() + // Use approximate length for initial capacity. + res := make([]uint64, 0, l.ApproxLen()) + out := &pb.List{} + + if opt.Intersect != nil && len(opt.Intersect.Uids) < l.ApproxLen() { + for _, uid := range opt.Intersect.Uids { + ok, _, err := l.findPosting(uid, opt.ReadTs) + if err != nil { + return nil, err, false + } + if ok { + res = append(res, uid) + } + } + out.Uids = res + return out, nil, false } - algo.IntersectCompressedWith(l.plist.Pack, opt.AfterUid, opt.Intersect, out) - l.RUnlock() - return out, nil - } - err := l.iterate(opt.ReadTs, opt.AfterUid, func(p *pb.Posting) error { - if p.PostingType == pb.Posting_REF { - res = append(res, p.Uid) - if opt.First < 0 { - // We need the last N. - // TODO: This could be optimized by only considering some of the last UidBlocks. - if len(res) > -opt.First { - res = res[1:] + if l.mutationMap.len() == 0 && opt.Intersect != nil && len(l.plist.Splits) == 0 { + if opt.ReadTs < l.minTs { + return out, errors.Wrapf(ErrTsTooOld, "While reading UIDs"), false + } + algo.IntersectCompressedWith(l.plist.Pack, opt.AfterUid, opt.Intersect, out) + return out, nil, false + } + + var uidMin, uidMax uint64 = 0, 0 + if opt.Intersect != nil && len(opt.Intersect.Uids) > 0 { + uidMin = opt.Intersect.Uids[0] + uidMax = opt.Intersect.Uids[len(opt.Intersect.Uids)-1] + } + + err := l.iterate(opt.ReadTs, opt.AfterUid, func(p *pb.Posting) error { + if p.PostingType == pb.Posting_REF { + if p.Uid < uidMin { + return nil + } + if p.Uid > uidMax && uidMax > 0 { + return ErrStopIteration + } + res = append(res, p.Uid) + + if opt.First < 0 { + // We need the last N. + // TODO: This could be optimized by only considering some of the last UidBlocks. + if len(res) > -opt.First { + res = res[1:] + } + } else if len(res) > opt.First { + return ErrStopIteration } - } else if len(res) > opt.First { - return ErrStopIteration } + return nil + }) + if err != nil { + return out, errors.Wrapf(err, "cannot retrieve UIDs from list with key %s", + hex.EncodeToString(l.key)), false } - return nil - }) - l.RUnlock() - if err != nil { - return out, errors.Wrapf(err, "cannot retrieve UIDs from list with key %s", - hex.EncodeToString(l.key)) + out.Uids = res + return out, nil, true } // Do The intersection here as it's optimized. - out.Uids = res - lenBefore := len(res) + out, err, applyIntersectWith := getUidList() + if err != nil || !applyIntersectWith { + return out, err + } + + lenBefore := len(out.Uids) if opt.Intersect != nil { algo.IntersectWith(out, opt.Intersect, out) }