mirror of https://github.com/perkeep/perkeep.git
search: allow searching by permanode location, not just file location.
Allows searching foursquare checkins by location. Change-Id: Ib33d0b435a9bbc17cb860549b41d119f727197f0
This commit is contained in:
parent
8d307672b2
commit
bf9f69bcfe
|
@ -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.
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue