mirror of https://github.com/perkeep/perkeep.git
162 lines
4.5 KiB
Go
162 lines
4.5 KiB
Go
package index
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"perkeep.org/pkg/blob"
|
|
"perkeep.org/pkg/schema/nodeattr"
|
|
"perkeep.org/pkg/types/camtypes"
|
|
)
|
|
|
|
// LocationHelper queries permanode locations.
|
|
//
|
|
// A LocationHelper is not safe for concurrent use.
|
|
// Callers should use Lock or RLock on the underlying index instead.
|
|
type LocationHelper struct {
|
|
index *Index
|
|
corpus *Corpus // may be nil
|
|
}
|
|
|
|
// NewLocationHelper returns a new location handler
|
|
// that uses ix to query blob attributes.
|
|
func NewLocationHelper(ix *Index) *LocationHelper {
|
|
lh := &LocationHelper{index: ix}
|
|
if ix.corpus != nil {
|
|
lh.corpus = ix.corpus
|
|
}
|
|
return lh
|
|
}
|
|
|
|
// SetCorpus sets the corpus to be used
|
|
// for location lookups.
|
|
func (lh *LocationHelper) SetCorpus(corpus *Corpus) {
|
|
lh.corpus = corpus
|
|
}
|
|
|
|
// altLocationRef maps camliNodeType to a slice of attributes
|
|
// whose values may refer to permanodes with location information.
|
|
var altLocationRef = map[string][]string{
|
|
// TODO(mpl): twitter.
|
|
"foursquare.com:checkin": {"foursquareVenuePermanode"},
|
|
}
|
|
|
|
// PermanodeLocation returns the location info for a permanode,
|
|
// from one of the following sources:
|
|
// 1. Permanode attributes "latitude" and "longitude"
|
|
// 2. Referenced permanode attributes (eg. for "foursquare.com:checkin"
|
|
// its "foursquareVenuePermanode")
|
|
// 3. Location in permanode camliContent file metadata
|
|
// The sources are checked in this order, the location from
|
|
// the first source yielding a valid result is returned.
|
|
func (lh *LocationHelper) PermanodeLocation(ctx context.Context, permaNode blob.Ref,
|
|
at time.Time, signer blob.Ref) (camtypes.Location, error) {
|
|
|
|
return lh.permanodeLocation(ctx, permaNode, at, signer, true)
|
|
}
|
|
|
|
func (lh *LocationHelper) permanodeLocation(ctx context.Context,
|
|
pn blob.Ref, at time.Time, signer blob.Ref,
|
|
useRef bool) (loc camtypes.Location, err error) {
|
|
|
|
pa := permAttr{at: at, signerFilter: signer}
|
|
if lh.corpus != nil {
|
|
var claims []*camtypes.Claim
|
|
pa.attrs, claims = lh.corpus.permanodeAttrsOrClaims(pn, at, signer)
|
|
if claims != nil {
|
|
pa.claims = claimPtrSlice(claims)
|
|
}
|
|
} else {
|
|
var claims []camtypes.Claim
|
|
claims, err = lh.index.AppendClaims(ctx, nil, pn, signer, "")
|
|
if err != nil {
|
|
return camtypes.Location{}, err
|
|
}
|
|
pa.claims = claimSlice(claims)
|
|
}
|
|
|
|
// Rule 1: if permanode has an explicit latitude and longitude,
|
|
// then this is its location.
|
|
slat, slong := pa.get(nodeattr.Latitude), pa.get(nodeattr.Longitude)
|
|
if slat != "" && slong != "" {
|
|
lat, latErr := strconv.ParseFloat(slat, 64)
|
|
long, longErr := strconv.ParseFloat(slong, 64)
|
|
switch {
|
|
case latErr != nil:
|
|
err = fmt.Errorf("invalid latitude in %v: %v", pn, latErr)
|
|
case longErr != nil:
|
|
err = fmt.Errorf("invalid longitude in %v: %v", pn, longErr)
|
|
default:
|
|
err = nil
|
|
}
|
|
return camtypes.Location{Latitude: lat, Longitude: long}, err
|
|
}
|
|
|
|
if useRef {
|
|
// Rule 2: referenced permanode attributes
|
|
nodeType := pa.get(nodeattr.Type)
|
|
if nodeType != "" {
|
|
for _, a := range altLocationRef[nodeType] {
|
|
refPn, hasRef := blob.Parse(pa.get(a))
|
|
if !hasRef {
|
|
continue
|
|
}
|
|
loc, err = lh.permanodeLocation(ctx, refPn, at, signer, false)
|
|
if err == nil {
|
|
return loc, err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rule 3: location in permanode camliContent file metadata.
|
|
// Use this only if pn was the argument passed to sh.getPermanodeLocation,
|
|
// and is not something found through a reference via altLocationRef.
|
|
if content, ok := blob.Parse(pa.get(nodeattr.CamliContent)); ok {
|
|
return lh.index.GetFileLocation(ctx, content)
|
|
}
|
|
}
|
|
|
|
return camtypes.Location{}, os.ErrNotExist
|
|
}
|
|
|
|
// permAttr represents attributes of a permanode
|
|
// for a given owner at a given time, either
|
|
// from a Corpus or Index/Interface.
|
|
type permAttr struct {
|
|
// permanode attributes from Corpus.PermanodeAttrs
|
|
// This may be nil if corpus has no cache for the permanode
|
|
// for the given time, in that case claims must be used.
|
|
attrs map[string][]string
|
|
|
|
// claims of permanode
|
|
// Populated only when attrs is not valid; using AppendClaims
|
|
// of corpus, or of the index in the absence of a corpus.
|
|
// Both attrs and claims may be nil if the permanode
|
|
// does not exist, or all attributes of it are from
|
|
// signer(s) other than the signerFilter.
|
|
claims claimsIntf
|
|
|
|
at time.Time
|
|
signerFilter blob.Ref
|
|
}
|
|
|
|
// get returns the value of attr.
|
|
func (pa permAttr) get(attr string) string {
|
|
if pa.attrs != nil {
|
|
v := pa.attrs[attr]
|
|
if len(v) != 0 {
|
|
return v[0]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
if pa.claims != nil {
|
|
return claimsIntfAttrValue(pa.claims, attr, pa.at, pa.signerFilter)
|
|
}
|
|
|
|
return ""
|
|
}
|