diff --git a/pkg/index/indextest/tests.go b/pkg/index/indextest/tests.go index 7c7d63780..463e9c1fe 100644 --- a/pkg/index/indextest/tests.go +++ b/pkg/index/indextest/tests.go @@ -18,7 +18,6 @@ package indextest import ( "fmt" - "log" "os" "path/filepath" "reflect" @@ -110,6 +109,12 @@ func (id *IndexDeps) SetAttribute(permaNode *blobref.BlobRef, attr, value string return id.uploadAndSignMap(m) } +func (id *IndexDeps) AddAttribute(permaNode *blobref.BlobRef, attr, value string) *blobref.BlobRef { + m := schema.NewAddAttributeClaim(permaNode, attr, value) + m["claimDate"] = id.advanceTime() + return id.uploadAndSignMap(m) +} + func (id *IndexDeps) UploadFile(fileName string, contents string) (fileRef, wholeRef *blobref.BlobRef) { cb := &test.Blob{Contents: contents} id.BlobSource.AddBlob(cb) @@ -130,7 +135,6 @@ func (id *IndexDeps) UploadFile(fileName string, contents string) (fileRef, whol panic(err) } fb := &test.Blob{Contents: fjson} - log.Printf("Blob is: %s", fjson) id.BlobSource.AddBlob(fb) fileRef = fb.BlobRef() _, err = id.Index.ReceiveBlob(fileRef, fb.Reader()) @@ -212,6 +216,12 @@ func Index(t *testing.T, initIdx func() *index.Index) { rootClaimTime := id.lastTime() t.Logf("set attribute %q", rootClaim) + pnChild := id.NewPermanode() + memberRef := id.AddAttribute(pn, "camliMember", pnChild.String()) + t.Logf("add-attribute claim %q points to member permanode %q", memberRef, pnChild) + memberRefTime := id.lastTime() + + lastPermanodeMutation := id.lastTime() id.dumpIndex(t) key := "signerkeyid:sha1-ad87ca5c78bd0ce1195c46f7c98e6025abbaf007" @@ -240,6 +250,11 @@ func Index(t *testing.T, initIdx func() *index.Index) { t.Fatalf("%q = %q, want %q (permanode)", key, g, e) } + key = fmt.Sprintf("edgeback|%s|%s|%s", pnChild, pn, memberRef) + if g, e := id.Get(key), "permanode|"; g != e { + t.Fatalf("edgeback row %q = %q, want %q", key, g, e) + } + // PermanodeOfSignerAttrValue { gotPN, err := id.Index.PermanodeOfSignerAttrValue(id.SignerBlobRef, "camliRoot", "rootval") @@ -270,7 +285,7 @@ func Index(t *testing.T, initIdx func() *index.Index) { &search.Result{ BlobRef: pn, Signer: id.SignerBlobRef, - LastModTime: 1322443959, + LastModTime: lastPermanodeMutation.Unix(), }, } if !reflect.DeepEqual(got, want) { @@ -332,6 +347,15 @@ func Index(t *testing.T, initIdx func() *index.Index) { Attr: "camliRoot", Value: "rootval", }, + &search.Claim{ + BlobRef: memberRef, + Permanode: pn, + Signer: id.SignerBlobRef, + Date: memberRefTime.UTC(), + Type: "add-attribute", + Attr: "camliMember", + Value: pnChild.String(), + }, }) if !reflect.DeepEqual(claims, want) { t.Errorf("GetOwnerClaims results differ.\n got: %v\nwant: %v", diff --git a/pkg/index/keys.go b/pkg/index/keys.go index c13dde105..3a1f4658d 100644 --- a/pkg/index/keys.go +++ b/pkg/index/keys.go @@ -196,4 +196,24 @@ var ( }, nil, } + + // Given a blobref (permanode or static file or directory), provide a mapping + // to potential parents (they may no longer be parents, in the case of permanodes). + // In the case of permanodes, camliMember or camliContent constitutes a forward + // edge. In the case of static directories, the forward path is dir->static set->file, + // and that's what's indexed here, inverted. + keyEdgeBackward = &keyType{ + "edgeback", + []part{ + {"child", typeBlobRef}, // the thing we want to find parent(s) of + {"parent", typeBlobRef}, // the parent (e.g. permanode blobref) + // the blobref is the blob establishing the relationship + // (for a permanode: the claim; for static: often same as parent) + {"blobref", typeBlobRef}, + }, + []part{ + {"parenttype", typeStr}, // either "permanode" or the camliType ("file", "static-set", etc) + {"name", typeStr}, // the name, if static. + }, + } ) diff --git a/pkg/index/receive.go b/pkg/index/receive.go index 55e81a719..23b3fdcbc 100644 --- a/pkg/index/receive.go +++ b/pkg/index/receive.go @@ -197,6 +197,15 @@ func (ix *Index) populateClaim(br *blobref.BlobRef, ss *schema.Superset, sniffer key := keySignerAttrValue.Key(verifiedKeyId, ss.Attribute, ss.Value, ss.ClaimDate, br) bm.Set(key, keySignerAttrValue.Val(pnbr)) } + + if search.IsBlobReferenceAttribute(ss.Attribute) { + targetRef := blobref.Parse(ss.Value) + if targetRef != nil { + key := keyEdgeBackward.Key(targetRef, pnbr, br) + bm.Set(key, keyEdgeBackward.Val("permanode", "")) + } + } + return nil } diff --git a/pkg/search/search.go b/pkg/search/search.go index eb6cb1dc5..bd72620c1 100644 --- a/pkg/search/search.go +++ b/pkg/search/search.go @@ -210,6 +210,18 @@ func IsIndexedAttribute(attr string) bool { return false } +// IsBlobReferenceAttribute returns whether attr is an attribute whose +// value is a blob reference (e.g. camliMember) and thus something the +// indexers should keep inverted indexes on for parent/child-type +// relationships. +func IsBlobReferenceAttribute(attr string) bool { + switch attr { + case "camliMember": + return true + } + return false +} + func IsFulltextAttribute(attr string) bool { switch attr { case "tag", "title":