Skip to content

Instantly share code, notes, and snippets.

@fpapadopou
Created November 20, 2019 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fpapadopou/50cdfa0760e42ae74d4803c4492e5f3d to your computer and use it in GitHub Desktop.
Save fpapadopou/50cdfa0760e42ae74d4803c4492e5f3d to your computer and use it in GitHub Desktop.
A location-aware user database implemented using Google S2 Geometry and Hashicorp's go-memdb (insert/update operations)
package store
import (
"github.com/golang/geo/s1"
"github.com/golang/geo/s2"
"github.com/hashicorp/go-memdb"
)
// Insert adds a new user to th
func (s *Store) Insert(user int, lat float64, lon float64) error {
// Initialize write transaction.
txn := s.db.Txn(true)
err := txn.Insert("users", &User{
ID: user,
CellID: s2.CellIDFromLatLng(s2.LatLngFromDegrees(lat, lon)).String(),
})
// Abort transaction on error.
if err != nil {
txn.Abort()
return err
}
// Commit changes in order to complete the transaction.
txn.Commit()
return nil
}
// Near returns a set of users that are located with the specified radius around the provided lat/lon.
func (s *Store) Near(lat float64, lon float64, radius int) ([]User, error) {
// Create region around the provided point.
// First, create a cap on the earth sphere with the required area (based on center & radius).
// Cap is determined based on the provided point and the cap angle (derived from capRadius/earthRadius)
angle := s1.Angle(float64(radius) / 6371000)
sphereCap := s2.CapFromCenterAngle(s2.PointFromLatLng(s2.LatLngFromDegrees(lat, lon)), angle)
r := s2.Region(sphereCap)
// Get the CellUnion representing the specified area.
covering := s.coverer.Covering(r)
// Find users in each of the cells in a single transaction.
txn := s.db.Txn(false)
var users []User
for _, cell := range covering {
// MemDB supports prefix indexing without explicit definition.
iter, err := txn.Get("users", "cellID_prefix", cell.String())
if err != nil {
continue
}
user := iter.Next()
for {
if user == nil {
break
}
// Process user..
user = iter.Next()
}
}
return users, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment