index: add ClaimsAttrValue

ClaimsAttrValue can be used in clients of pkg index, such
as in search on values returned by Index.AppendClaims to
query permanode attributes.

Related: issue #777

Change-Id: I5a8fa2a970d88f9ddbcc3de350215a196e124b64
This commit is contained in:
Attila Tajti 2016-05-12 06:05:43 +02:00
parent 06036c2612
commit b5bdb7d502
3 changed files with 161 additions and 35 deletions

View File

@ -1008,41 +1008,7 @@ func (c *Corpus) PermanodeAttrValue(permaNode blob.Ref,
}
return ""
}
if at.IsZero() {
at = time.Now()
}
var v []string
for _, cl := range pm.Claims {
if cl.Attr != attr || cl.Date.After(at) {
continue
}
if signerFilter.Valid() && signerFilter != cl.Signer {
continue
}
switch cl.Type {
case string(schema.DelAttributeClaim):
if cl.Value == "" {
v = v[:0]
} else {
i := 0
for _, w := range v {
if w != cl.Value {
v[i] = w
i++
}
}
v = v[:i]
}
case string(schema.SetAttributeClaim):
v = append(v[:0], cl.Value)
case string(schema.AddAttributeClaim):
v = append(v, cl.Value)
}
}
if len(v) != 0 {
return v[0]
}
return ""
return claimPtrsAttrValue(pm.Claims, attr, at, signerFilter)
}
// AppendPermanodeAttrValues appends to dst all the values for the attribute

View File

@ -18,6 +18,11 @@ package index
import (
"net/url"
"time"
"camlistore.org/pkg/blob"
"camlistore.org/pkg/schema"
"camlistore.org/pkg/types/camtypes"
)
var urle = url.QueryEscape
@ -42,3 +47,71 @@ func (s *dupSkipper) Dup(v string) bool {
s.m[v] = true
return false
}
// ClaimsAttrValue returns the value of attr from claims,
// or the empty string if not found.
// Claims should be sorted by claim.Date.
func ClaimsAttrValue(claims []camtypes.Claim, attr string, at time.Time, signerFilter blob.Ref) string {
return claimsIntfAttrValue(claimSlice(claims), attr, at, signerFilter)
}
// claimPtrsAttrValue returns the value of attr from claims,
// or the empty string if not found.
// Claims should be sorted by claim.Date.
func claimPtrsAttrValue(claims []*camtypes.Claim, attr string, at time.Time, signerFilter blob.Ref) string {
return claimsIntfAttrValue(claimPtrSlice(claims), attr, at, signerFilter)
}
type claimsIntf interface {
Len() int
Claim(i int) *camtypes.Claim
}
type claimSlice []camtypes.Claim
func (s claimSlice) Len() int { return len(s) }
func (s claimSlice) Claim(i int) *camtypes.Claim { return &s[i] }
type claimPtrSlice []*camtypes.Claim
func (s claimPtrSlice) Len() int { return len(s) }
func (s claimPtrSlice) Claim(i int) *camtypes.Claim { return s[i] }
func claimsIntfAttrValue(claims claimsIntf, attr string, at time.Time, signerFilter blob.Ref) string {
if at.IsZero() {
at = time.Now()
}
var v []string
for i := 0; i < claims.Len(); i++ {
cl := claims.Claim(i)
if cl.Attr != attr || cl.Date.After(at) {
continue
}
if signerFilter.Valid() && signerFilter != cl.Signer {
continue
}
switch cl.Type {
case string(schema.DelAttributeClaim):
if cl.Value == "" {
v = v[:0]
} else {
i := 0
for _, w := range v {
if w != cl.Value {
v[i] = w
i++
}
}
v = v[:i]
}
case string(schema.SetAttributeClaim):
v = append(v[:0], cl.Value)
case string(schema.AddAttributeClaim):
v = append(v, cl.Value)
}
}
if len(v) != 0 {
return v[0]
}
return ""
}

87
pkg/index/util_test.go Normal file
View File

@ -0,0 +1,87 @@
/*
Copyright 2016 The Camlistore AUTHORS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package index
import (
"testing"
"time"
"camlistore.org/pkg/blob"
"camlistore.org/pkg/types/camtypes"
)
func TestClaimsAttrValue(t *testing.T) {
tm := time.Unix(99, 0)
claim := func(verb, attr, val string) camtypes.Claim {
tm = tm.Add(time.Second)
return camtypes.Claim{
Type: verb + "-attribute",
Attr: attr,
Value: val,
Date: tm,
}
}
claims := []camtypes.Claim{
claim("set", "foo", "foov"), // time 100
claim("add", "tag", "a"), // time 101
claim("add", "tag", "b"), // time 102
claim("del", "tag", ""),
claim("add", "tag", "c"),
claim("add", "tag", "d"),
claim("add", "tag", "e"),
claim("del", "tag", "d"),
claim("add", "DelAll", "a"),
claim("add", "DelAll", "b"),
claim("add", "DelAll", "c"),
claim("del", "DelAll", ""),
claim("add", "DelOne", "a"),
claim("add", "DelOne", "b"),
claim("add", "DelOne", "c"),
claim("add", "DelOne", "d"),
claim("del", "DelOne", "d"),
claim("del", "DelOne", "a"),
claim("add", "SetAfterAdd", "a"),
claim("add", "SetAfterAdd", "b"),
claim("set", "SetAfterAdd", "setv"),
}
tests := []struct {
attr string
want string
t time.Time
}{
{attr: "foo", want: "foov"},
{attr: "tag", want: "c"},
{attr: "tag", want: "a", t: time.Unix(102, 0)},
{attr: "DelAll", want: ""},
{attr: "DelOne", want: "b"},
{attr: "SetAfterAdd", want: "setv"},
}
for i, tt := range tests {
got := ClaimsAttrValue(claims, tt.attr, tt.t, blob.Ref{})
if got != tt.want {
t.Errorf("%d. attr %q = %v; want %v",
i, tt.attr, got, tt.want)
}
}
}