// This is just like PermanodeOfSignerAttrValue except we return multiple and dup-suppress. // If request.Query is "", it is not used in the prefix search. func (x *Index) SearchPermanodesWithAttr(ctx context.Context, dest chan<- blob.Ref, request *camtypes.PermanodeByAttrRequest) (err error) { defer close(dest) if request.FuzzyMatch { // TODO(bradfitz): remove this for now? figure out how to handle it generically? return errors.New("TODO: SearchPermanodesWithAttr: generic indexer doesn't support FuzzyMatch on PermanodeByAttrRequest") } if request.Attribute == "" { return errors.New("index: missing Attribute in SearchPermanodesWithAttr") } keyId, err := x.KeyId(ctx, request.Signer) if err == sorted.ErrNotFound { return nil } if err != nil { return err } seen := make(map[string]bool) var it sorted.Iterator if request.Query == "" { it = x.queryPrefix(keySignerAttrValue, keyId, request.Attribute) } else { it = x.queryPrefix(keySignerAttrValue, keyId, request.Attribute, request.Query) } defer closeIterator(it, &err) before := request.At if before.IsZero() { before = time.Now() } for it.Next() { cl, ok := kvSignerAttrValue(it.Key(), it.Value()) if !ok { continue } if x.IsDeleted(cl.BlobRef) { continue } if x.IsDeleted(cl.Permanode) { continue } if cl.Date.After(before) { continue } pnstr := cl.Permanode.String() if seen[pnstr] { continue } seen[pnstr] = true dest <- cl.Permanode if len(seen) == request.MaxResults { break } } return nil }
func (x *Index) AppendClaims(dst []camtypes.Claim, permaNode blob.Ref, signerFilter blob.Ref, attrFilter string) ([]camtypes.Claim, error) { if x.corpus != nil { return x.corpus.AppendClaims(dst, permaNode, signerFilter, attrFilter) } var ( keyId string err error it sorted.Iterator ) if signerFilter.Valid() { keyId, err = x.KeyId(signerFilter) if err == sorted.ErrNotFound { return nil, nil } if err != nil { return nil, err } it = x.queryPrefix(keyPermanodeClaim, permaNode, keyId) } else { it = x.queryPrefix(keyPermanodeClaim, permaNode) } defer closeIterator(it, &err) // In the common case, an attribute filter is just a plain // token ("camliContent") unescaped. If so, fast path that // check to skip the row before we even split it. var mustHave string if attrFilter != "" && urle(attrFilter) == attrFilter { mustHave = attrFilter } for it.Next() { val := it.Value() if mustHave != "" && !strings.Contains(val, mustHave) { continue } cl, ok := kvClaim(it.Key(), val, blob.Parse) if !ok { continue } if x.IsDeleted(cl.BlobRef) { continue } if attrFilter != "" && cl.Attr != attrFilter { continue } if signerFilter.Valid() && cl.Signer != signerFilter { continue } dst = append(dst, cl) } return dst, nil }
func closeIterator(it sorted.Iterator, perr *error) { err := it.Close() if err != nil && *perr == nil { *perr = err } }