search: allow searching by permanode location, not just file location.

Allows searching foursquare checkins by location.

Change-Id: Ib33d0b435a9bbc17cb860549b41d119f727197f0
This commit is contained in:
Brad Fitzpatrick 2014-04-23 11:13:47 -07:00
parent 8d307672b2
commit bf9f69bcfe
3 changed files with 103 additions and 15 deletions

View File

@ -868,8 +868,44 @@ func (c *Corpus) AppendPermanodeAttrValues(dst []string,
return c.AppendPermanodeAttrValuesLocked(dst, permaNode, attr, at, signerFilter)
}
// AppendPermanodeAttrValuesLocked is the version of AppendPermanodeAttrValues that assumes
// the Corpus is already locked with RLock.
// PermanodeAttrValueLocked returns a single-valued attribute or "".
func (c *Corpus) PermanodeAttrValueLocked(permaNode blob.Ref,
attr string,
at time.Time,
signerFilter blob.Ref) string {
pm, ok := c.permanodes[permaNode]
if !ok {
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 = ""
} else if v == cl.Value {
v = ""
}
case string(schema.SetAttributeClaim):
v = cl.Value
case string(schema.AddAttributeClaim):
if v == "" {
v = cl.Value
}
}
}
return v
}
func (c *Corpus) AppendPermanodeAttrValuesLocked(dst []string,
permaNode blob.Ref,
attr string,
@ -997,6 +1033,36 @@ func (c *Corpus) FileLatLongLocked(fileRef blob.Ref) (lat, long float64, ok bool
return ll.lat, ll.long, true
}
// zero value of at means current
func (c *Corpus) PermanodeLatLongLocked(pn blob.Ref, at time.Time) (lat, long float64, ok bool) {
nodeType := c.PermanodeAttrValueLocked(pn, "camliNodeType", at, blob.Ref{})
if nodeType == "" {
return
}
// TODO: make these pluggable, e.g. registered from an importer or something?
// How will that work when they're out-of-process?
if nodeType == "foursquare.com:checkin" {
venuePn, hasVenue := blob.Parse(c.PermanodeAttrValueLocked(pn, "foursquareVenuePermanode", at, blob.Ref{}))
if !hasVenue {
return
}
return c.PermanodeLatLongLocked(venuePn, at)
}
if nodeType == "foursquare.com:venue" {
var err error
lat, err = strconv.ParseFloat(c.PermanodeAttrValueLocked(pn, "latitude", at, blob.Ref{}), 64)
if err != nil {
return
}
long, err = strconv.ParseFloat(c.PermanodeAttrValueLocked(pn, "longitude", at, blob.Ref{}), 64)
if err != nil {
return
}
return lat, long, true
}
return
}
// ForeachClaimBackLocked calls fn for each claim with a value referencing br.
// If at is zero, all claims are yielded.
// If at is non-zero, claims after that point are skipped.

View File

@ -504,24 +504,31 @@ func parseLocationAtom(ctx *context.Context, word string) (*Constraint, error) {
if len(rects) == 0 {
return nil, fmt.Errorf("No location found for %q", where)
}
var locConstraint *Constraint
var c *Constraint
for i, rect := range rects {
rectConstraint := permOfFile(&FileConstraint{
IsImage: true,
Location: &LocationConstraint{
West: rect.SouthWest.Long,
East: rect.NorthEast.Long,
North: rect.NorthEast.Lat,
South: rect.SouthWest.Lat,
},
loc := &LocationConstraint{
West: rect.SouthWest.Long,
East: rect.NorthEast.Long,
North: rect.NorthEast.Lat,
South: rect.SouthWest.Lat,
}
fileLoc := permOfFile(&FileConstraint{
IsImage: true,
Location: loc,
})
permLoc := &Constraint{
Permanode: &PermanodeConstraint{
Location: loc,
},
}
rectConstraint := orConst(fileLoc, permLoc)
if i == 0 {
locConstraint = rectConstraint
c = rectConstraint
} else {
locConstraint = orConst(locConstraint, rectConstraint)
c = orConst(c, rectConstraint)
}
}
return locConstraint, nil
return c, nil
}
if word == "has:location" {
c := permOfFile(&FileConstraint{

View File

@ -601,6 +601,11 @@ type PermanodeConstraint struct {
// child, or progeny.
Relation *RelationConstraint `json:"relation,omitempty"`
// Location optionally restricts matches to permanodes having
// this location. This only affects permanodes with a known
// type to have an lat/long location.
Location *LocationConstraint `json:"location,omitempty"`
// Continue is for internal use.
Continue *PermanodeContinueConstraint `json:"-"`
@ -1120,7 +1125,7 @@ var numPermanodeFields = reflect.TypeOf(PermanodeConstraint{}).NumField()
// hasValueConstraint returns true if one or more constraints that check an attribute's value are set.
func (c *PermanodeConstraint) hasValueConstraint() bool {
// If a field has been added or removed, update this after adding the new field to the return statement if necessary.
const expectedFields = 14
const expectedFields = 15
if numPermanodeFields != expectedFields {
panic(fmt.Sprintf("PermanodeConstraint field count changed (now %v rather than %v)", numPermanodeFields, expectedFields))
}
@ -1206,6 +1211,16 @@ func (c *PermanodeConstraint) blobMatches(s *search, br blob.Ref, bm camtypes.Bl
}
}
if c.Location != nil {
if corpus == nil {
return false, nil
}
lat, long, ok := corpus.PermanodeLatLongLocked(br, c.At)
if !ok || !c.Location.matchesLatLong(lat, long) {
return false, nil
}
}
if cc := c.Continue; cc != nil {
if corpus == nil {
// Requires an in-memory index for infinite