wip. adding string value type.

This commit is contained in:
Josh Baker 2016-07-09 19:44:28 -07:00
parent bfa204067c
commit 8d89198eaf
18 changed files with 253 additions and 35 deletions

View File

@ -1,19 +1,38 @@
package collection
import (
"github.com/google/btree"
"github.com/tidwall/btree"
"github.com/tidwall/tile38/geojson"
"github.com/tidwall/tile38/index"
)
const (
idOrdered = 0
valueOrdered = 2
)
type itemT struct {
id string
object geojson.Object
fields []float64
}
func (i *itemT) Less(item btree.Item) bool {
func (i *itemT) Less(item btree.Item, ctx int) bool {
switch ctx {
default:
return false
case idOrdered:
return i.id < item.(*itemT).id
case valueOrdered:
if i.object.String() < item.(*itemT).object.String() {
return true
}
if i.object.String() > item.(*itemT).object.String() {
return false
}
return i.id < item.(*itemT).id
}
}
func (i *itemT) Rect() (minX, minY, maxX, maxY float64) {
@ -28,8 +47,9 @@ func (i *itemT) Point() (x, y float64) {
// Collection represents a collection of geojson objects.
type Collection struct {
items *btree.BTree
index *index.Index
items *btree.BTree // items sorted by keys
values *btree.BTree // items sorted by value+key
index *index.Index // items geospatially indexed
fieldMap map[string]int
weight int
points int
@ -42,7 +62,8 @@ var counter uint64
func New() *Collection {
col := &Collection{
index: index.New(),
items: btree.New(16),
items: btree.New(16, idOrdered),
values: btree.New(16, valueOrdered),
fieldMap: make(map[string]int),
}
return col
@ -60,23 +81,7 @@ func (c *Collection) PointCount() int {
// TotalWeight calculates the in-memory cost of the collection in bytes.
func (c *Collection) TotalWeight() int {
return c.weight + c.overheadWeight()
}
func (c *Collection) overheadWeight() int {
// the field map.
mapweight := 0
for field := range c.fieldMap {
mapweight += len(field) + 8 // key + value
}
mapweight = int((float64(mapweight) * 1.05) + 28.0) // about an 8% pad plus golang 28 byte map overhead.
// the btree. each object takes up 64bits for the interface head for each item.
btreeweight := (c.objects * 8)
// plus roughly one pointer for every item
btreeweight += (c.objects * 8)
// also the btree header weight
btreeweight += 24
return mapweight + btreeweight
return c.weight
}
// ReplaceOrInsert adds or replaces an object in the collection and returns the fields array.
@ -113,7 +118,11 @@ func (c *Collection) remove(id string) (item *itemT, ok bool) {
return nil, false
}
item = i.(*itemT)
if item.object.IsGeometry() {
c.index.Remove(item)
} else {
c.values.Delete(item)
}
c.weight -= len(item.fields) * 8
c.weight -= item.object.Weight() + len(item.id)
c.points -= item.object.PositionCount()
@ -123,7 +132,11 @@ func (c *Collection) remove(id string) (item *itemT, ok bool) {
func (c *Collection) insert(id string, obj geojson.Object) (item *itemT) {
item = &itemT{id: id, object: obj}
if obj.IsGeometry() {
c.index.Insert(item)
} else {
c.values.ReplaceOrInsert(item)
}
c.items.ReplaceOrInsert(item)
c.weight += obj.Weight() + len(id)
c.points += obj.PositionCount()

View File

@ -14,7 +14,7 @@ import (
"sync"
"time"
"github.com/google/btree"
"github.com/tidwall/btree"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/collection"
"github.com/tidwall/tile38/controller/log"
@ -45,7 +45,7 @@ type commandDetailsT struct {
timestamp time.Time
}
func (col *collectionT) Less(item btree.Item) bool {
func (col *collectionT) Less(item btree.Item, ctx int) bool {
return col.Key < item.(*collectionT).Key
}
@ -82,7 +82,7 @@ func ListenAndServe(host string, port int, dir string) error {
host: host,
port: port,
dir: dir,
cols: btree.New(16),
cols: btree.New(16, 0),
follows: make(map[*bytes.Buffer]bool),
fcond: sync.NewCond(&sync.Mutex{}),
lives: make(map[*liveBuffer]bool),
@ -387,7 +387,7 @@ func randomKey(n int) string {
func (c *Controller) reset() {
c.aofsz = 0
c.cols = btree.New(16)
c.cols = btree.New(16, 0)
}
func (c *Controller) command(msg *server.Message, w io.Writer) (res string, d commandDetailsT, err error) {

View File

@ -7,7 +7,7 @@ import (
"strings"
"time"
"github.com/google/btree"
"github.com/tidwall/btree"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/collection"
"github.com/tidwall/tile38/controller/server"
@ -92,7 +92,7 @@ func (c *Controller) cmdGet(msg *server.Message) (string, error) {
buf.WriteString(`,"object":`)
buf.WriteString(o.JSON())
} else {
vals = append(vals, resp.StringValue(o.JSON()))
vals = append(vals, resp.StringValue(o.String()))
}
} else {
switch strings.ToLower(typ) {
@ -300,7 +300,7 @@ func (c *Controller) cmdFlushDB(msg *server.Message) (res string, d commandDetai
err = errInvalidNumberOfArguments
return
}
c.cols = btree.New(16)
c.cols = btree.New(16, 0)
c.hooks = make(map[string]*Hook)
c.hookcols = make(map[string]map[string]*Hook)
d.command = "flushdb"
@ -379,6 +379,13 @@ func (c *Controller) parseSetArgs(vs []resp.Value) (d commandDetailsT, fields []
default:
err = errInvalidArgument(typ)
return
case lc(typ, "string"):
var str string
if vs, str, ok = tokenval(vs); !ok {
err = errInvalidNumberOfArguments
return
}
d.obj = geojson.String(str)
case lc(typ, "point"):
var slat, slon, sz string
if vs, slat, ok = tokenval(vs); !ok || slat == "" {

View File

@ -5,7 +5,7 @@ import (
"strings"
"time"
"github.com/google/btree"
"github.com/tidwall/btree"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/server"
)

View File

@ -325,7 +325,7 @@ func (sw *scanWriter) writeObject(id string, o geojson.Object, fields []float64,
} else {
switch sw.output {
case outputObjects:
vals = append(vals, resp.StringValue(o.JSON()))
vals = append(vals, resp.StringValue(o.String()))
case outputPoints:
point := o.CalculatedPoint()
if point.Z != 0 {

View File

@ -7,7 +7,7 @@ import (
"sort"
"time"
"github.com/google/btree"
"github.com/tidwall/btree"
"github.com/tidwall/resp"
"github.com/tidwall/tile38/controller/server"
)

View File

@ -150,6 +150,11 @@ func (g Feature) JSON() string {
return buf.String()
}
// String returns a string representation of the object. This might be JSON or something else.
func (g Feature) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g Feature) Bytes() []byte {
var buf bytes.Buffer
@ -226,3 +231,8 @@ func (g Feature) Nearby(center Position, meters float64) bool {
func (g Feature) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g Feature) IsGeometry() bool {
return true
}

View File

@ -126,6 +126,11 @@ func (g FeatureCollection) JSON() string {
return buf.String()
}
// String returns a string representation of the object. This might be JSON or something else.
func (g FeatureCollection) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g FeatureCollection) Bytes() []byte {
var buf bytes.Buffer
@ -248,3 +253,8 @@ func (g FeatureCollection) Nearby(center Position, meters float64) bool {
func (g FeatureCollection) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g FeatureCollection) IsGeometry() bool {
return true
}

View File

@ -126,6 +126,11 @@ func (g GeometryCollection) JSON() string {
return buf.String()
}
// String returns a string representation of the object. This might be JSON or something else.
func (g GeometryCollection) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g GeometryCollection) Bytes() []byte {
var buf bytes.Buffer
@ -248,3 +253,8 @@ func (g GeometryCollection) Nearby(center Position, meters float64) bool {
func (g GeometryCollection) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g GeometryCollection) IsGeometry() bool {
return true
}

View File

@ -56,6 +56,11 @@ func (g LineString) JSON() string {
return level2JSON("LineString", g.Coordinates, g.BBox)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g LineString) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g LineString) Bytes() []byte {
return level2Bytes(lineString, g.Coordinates, g.BBox)
@ -126,3 +131,8 @@ func (g LineString) Nearby(center Position, meters float64) bool {
func (g LineString) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g LineString) IsGeometry() bool {
return true
}

View File

@ -62,6 +62,11 @@ func (g MultiLineString) JSON() string {
return level3JSON("MultiLineString", g.Coordinates, g.BBox)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g MultiLineString) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g MultiLineString) Bytes() []byte {
return level3Bytes(multiLineString, g.Coordinates, g.BBox)
@ -184,3 +189,8 @@ func (g MultiLineString) Nearby(center Position, meters float64) bool {
func (g MultiLineString) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g MultiLineString) IsGeometry() bool {
return true
}

View File

@ -54,6 +54,11 @@ func (g MultiPoint) JSON() string {
return level2JSON("MultiPoint", g.Coordinates, g.BBox)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g MultiPoint) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g MultiPoint) Bytes() []byte {
return level2Bytes(multiPoint, g.Coordinates, g.BBox)
@ -163,3 +168,8 @@ func (g MultiPoint) Nearby(center Position, meters float64) bool {
func (g MultiPoint) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g MultiPoint) IsGeometry() bool {
return true
}

View File

@ -66,6 +66,11 @@ func (g MultiPolygon) JSON() string {
return level4JSON("MultiPolygon", g.Coordinates, g.BBox)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g MultiPolygon) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g MultiPolygon) Bytes() []byte {
return level4Bytes(multiPolygon, g.Coordinates, g.BBox)
@ -193,3 +198,8 @@ func (g MultiPolygon) Nearby(center Position, meters float64) bool {
func (g MultiPolygon) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g MultiPolygon) IsGeometry() bool {
return true
}

View File

@ -76,6 +76,8 @@ type Object interface {
CalculatedPoint() Position
// JSON is the json representation of the object. This might not be exactly the same as the original.
JSON() string
// String returns a string represenation of the object. This may be JSON or something else.
String() string
// Bytes is the bytes representation of the object.
Bytes() []byte
// PositionCount return the number of coordinates.
@ -88,6 +90,8 @@ type Object interface {
Geohash(precision int) (string, error)
// IsBBoxDefined returns true if the object has a defined bbox.
IsBBoxDefined() bool
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
IsGeometry() bool
}
func writeHeader(buf *bytes.Buffer, objType byte, bbox *BBox, isCordZ bool) {

View File

@ -55,6 +55,11 @@ func (g Point) JSON() string {
return level1JSON("Point", g.Coordinates, g.BBox)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g Point) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g Point) Bytes() []byte {
return level1Bytes(point, g.Coordinates, g.BBox)
@ -135,3 +140,8 @@ func (g Point) Nearby(center Position, meters float64) bool {
func (g Point) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g Point) IsGeometry() bool {
return true
}

View File

@ -69,6 +69,11 @@ func (g Polygon) JSON() string {
return level3JSON("Polygon", g.Coordinates, g.BBox)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g Polygon) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g Polygon) Bytes() []byte {
return level3Bytes(polygon, g.Coordinates, g.BBox)
@ -202,3 +207,8 @@ func (g Polygon) KML() string {
func (g Polygon) IsBBoxDefined() bool {
return g.BBox != nil
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g Polygon) IsGeometry() bool {
return true
}

View File

@ -59,6 +59,11 @@ func (g SimplePoint) JSON() string {
return level1JSON("Point", Position{X: g.X, Y: g.Y, Z: 0}, nil)
}
// String returns a string representation of the object. This might be JSON or something else.
func (g SimplePoint) String() string {
return g.JSON()
}
// Bytes is the bytes representation of the object.
func (g SimplePoint) Bytes() []byte {
return level1Bytes(point, Position{X: g.X, Y: g.Y, Z: 0}, nil)
@ -123,3 +128,8 @@ func (g SimplePoint) Nearby(center Position, meters float64) bool {
func (g SimplePoint) IsBBoxDefined() bool {
return false
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g SimplePoint) IsGeometry() bool {
return true
}

94
geojson/string.go Normal file
View File

@ -0,0 +1,94 @@
package geojson
import "encoding/json"
// String is a not a geojson object, but just a string
type String string
func (s String) bboxPtr() *BBox {
return nil
}
func (s String) hasPositions() bool {
return false
}
// WithinBBox detects if the object is fully contained inside a bbox.
func (s String) WithinBBox(bbox BBox) bool {
return false
}
// IntersectsBBox detects if the object intersects a bbox.
func (s String) IntersectsBBox(bbox BBox) bool {
return false
}
// Within detects if the object is fully contained inside another object.
func (s String) Within(o Object) bool {
return false
}
// Intersects detects if the object intersects another object.
func (s String) Intersects(o Object) bool {
return false
}
// Nearby detects if the object is nearby a position.
func (s String) Nearby(center Position, meters float64) bool {
return false
}
// CalculatedBBox is exterior bbox containing the object.
func (s String) CalculatedBBox() BBox {
return BBox{}
}
// CalculatedPoint is a point representation of the object.
func (s String) CalculatedPoint() Position {
return Position{}
}
// JSON is the json representation of the object. This might not be exactly the same as the original.
func (s String) JSON() string {
b, _ := s.MarshalJSON()
return string(b)
}
// String returns a string representation of the object. This might be JSON or something else.
func (s String) String() string {
return string(s)
}
// IsGeometry return true if the object is a geojson geometry object. false if it something else.
func (g String) IsGeometry() bool {
return false
}
// Bytes is the bytes representation of the object.
func (s String) Bytes() []byte {
return []byte(s.String())
}
// PositionCount return the number of coordinates.
func (s String) PositionCount() int {
return 0
}
// Weight returns the in-memory size of the object.
func (s String) Weight() int {
return len(s)
}
// MarshalJSON allows the object to be encoded in json.Marshal calls.
func (s String) MarshalJSON() ([]byte, error) {
return json.Marshal(string(s))
}
// Geohash converts the object to a geohash value.
func (s String) Geohash(precision int) (string, error) {
return "", nil
}
// IsBBoxDefined returns true if the object has a defined bbox.
func (s String) IsBBoxDefined() bool {
return false
}