diff --git a/pkg/index/corpus.go b/pkg/index/corpus.go index 779dbd81b..e78f9c6b6 100644 --- a/pkg/index/corpus.go +++ b/pkg/index/corpus.go @@ -1156,6 +1156,11 @@ func (c *Corpus) GetMediaTagsLocked(fileRef blob.Ref) (map[string]string, error) return tags, nil } +func (c *Corpus) GetWholeRefLocked(fileRef blob.Ref) (wholeRef blob.Ref, ok bool) { + wholeRef, ok = c.fileWholeRef[fileRef] + return +} + func (c *Corpus) FileLatLongLocked(fileRef blob.Ref) (lat, long float64, ok bool) { wholeRef, ok := c.fileWholeRef[fileRef] if !ok { diff --git a/pkg/search/query.go b/pkg/search/query.go index 53e883dc5..d9e7418be 100644 --- a/pkg/search/query.go +++ b/pkg/search/query.go @@ -321,11 +321,17 @@ func (c *Constraint) onlyMatchesPermanode() bool { type FileConstraint struct { // (All non-zero fields must match) - FileSize *IntConstraint `json:"fileSize,omitempty"` - FileName *StringConstraint - MIMEType *StringConstraint - Time *TimeConstraint - ModTime *TimeConstraint + FileSize *IntConstraint `json:"fileSize,omitempty"` + FileName *StringConstraint `json:"fileName,omitempty"` + MIMEType *StringConstraint `json:"mimeType,omitempty"` + Time *TimeConstraint `json:"time,omitempty"` + ModTime *TimeConstraint `json:"modTime,omitempty"` + + // WholeRef if non-zero only matches if the entire checksum of the + // file (the concatenation of all its blobs) is equal to the + // provided blobref. The index may not have every file's digest for + // every known hash algorithm. + WholeRef blob.Ref `json:"wholeRef,omitempty"` // For images: IsImage bool `json:"isImage,omitempty"` @@ -1403,6 +1409,15 @@ func (c *FileConstraint) blobMatches(s *search, br blob.Ref, bm camtypes.BlobMet } } corpus := s.h.corpus + if c.WholeRef.Valid() { + if corpus == nil { + return false, nil + } + wholeRef, ok := corpus.GetWholeRefLocked(br) + if !ok || wholeRef != c.WholeRef { + return false, nil + } + } var width, height int64 if c.Width != nil || c.Height != nil || c.WHRatio != nil { if corpus == nil { diff --git a/pkg/search/query_test.go b/pkg/search/query_test.go index 184218e8d..f27437549 100644 --- a/pkg/search/query_test.go +++ b/pkg/search/query_test.go @@ -569,6 +569,35 @@ func TestQueryFileConstraint(t *testing.T) { }) } +func TestQueryFileConstraint_WholeRef(t *testing.T) { + testQueryTypes(t, memIndexTypes, func(qt *queryTest) { + id := qt.id + fileRef, _ := id.UploadFile("some-stuff.txt", "hello", time.Unix(123, 0)) + qt.t.Logf("fileRef = %q", fileRef) + p1 := id.NewPlannedPermanode("1") + id.SetAttribute(p1, "camliContent", fileRef.String()) + + fileRef2, _ := id.UploadFile("other-file", "hellooooo", time.Unix(456, 0)) + qt.t.Logf("fileRef2 = %q", fileRef2) + p2 := id.NewPlannedPermanode("2") + id.SetAttribute(p2, "camliContent", fileRef2.String()) + + sq := &SearchQuery{ + Constraint: &Constraint{ + Permanode: &PermanodeConstraint{ + Attr: "camliContent", + ValueInSet: &Constraint{ + File: &FileConstraint{ + WholeRef: blob.SHA1FromString("hello"), + }, + }, + }, + }, + } + qt.wantRes(sq, p1) + }) +} + func TestQueryPermanodeModtime(t *testing.T) { testQuery(t, func(qt *queryTest) { id := qt.id