Added naive BigMin interleaved implementation for interleaved spatial query.

This commit is contained in:
Sascha L. Teichmann 2014-09-01 13:46:23 +02:00
parent 0021854000
commit f7a8c1fdec
2 changed files with 109 additions and 9 deletions

View File

@ -21,6 +21,10 @@ type (
X, Y, Z int16 X, Y, Z int16
} }
Cuboid struct {
P1, P2 Coord
}
KeyTransformer func(int64) int64 KeyTransformer func(int64) int64
KeyEncoder func(int64) ([]byte, error) KeyEncoder func(int64) ([]byte, error)
KeyDecoder func([]byte) (int64, error) KeyDecoder func([]byte) (int64, error)
@ -29,6 +33,12 @@ type (
KeyJoiner func(Coord) int64 KeyJoiner func(Coord) int64
) )
func (cub Cuboid) Contains(c Coord) bool {
return c.X >= cub.P1.X && c.X <= cub.P2.X &&
c.Y >= cub.P1.Y && c.Y <= cub.P2.Y &&
c.Z >= cub.P1.Z && c.Z <= cub.P2.Z
}
func (c Coord) String() string { func (c Coord) String() string {
return fmt.Sprintf("(%d, %d, %d)", c.X, c.Y, c.Z) return fmt.Sprintf("(%d, %d, %d)", c.X, c.Y, c.Z)
} }
@ -204,3 +214,23 @@ func TranscodeInterleavedToPlain(key []byte) ([]byte, error) {
return EncodeStringToBytes(TransformInterleavedToPlain(pos)) return EncodeStringToBytes(TransformInterleavedToPlain(pos))
} }
} }
// For correctness checks only.
func NaiveBigMin(minz, maxz, zcode int64) int64 {
c1, c2 := InterleavedToCoord(minz), InterleavedToCoord(maxz)
cand := minz
for x := c1.X; x <= c2.X; x++ {
for y := c1.Y; y <= c2.Y; y++ {
for z := c1.Z; z <= c2.Z; z++ {
z := CoordToInterleaved(Coord{X: x, Y: y, Z: z})
if z > zcode && z < cand {
cand = z
}
}
}
}
return cand
}

View File

@ -23,7 +23,7 @@ const (
insertSql = "INSERT INTO blocks (pos, data) VALUES (?, ?)" insertSql = "INSERT INTO blocks (pos, data) VALUES (?, ?)"
countSql = "SELECT count(*) FROM blocks" countSql = "SELECT count(*) FROM blocks"
keysSql = "SELECT pos FROM blocks" keysSql = "SELECT pos FROM blocks"
rangeSql = "SELECT pos, data FROM blocks WHERE pos BETWEEN ? AND ?" rangeSql = "SELECT pos, data FROM blocks WHERE pos BETWEEN ? AND ? ORDER BY pos"
) )
type SqliteBackend struct { type SqliteBackend struct {
@ -283,15 +283,13 @@ func (ss *SqliteSession) AllKeys(hash []byte, done chan struct{}) (keys chan []b
return return
} }
func (ss *SqliteSession) SpatialQuery(hash, first, second []byte, done chan struct{}) (blocks chan Block, err error) { func (ss *SqliteSession) SpatialQuery(hash, first, second []byte, done chan struct{}) (chan Block, error) {
// No implementation for the interleaved case, yet.
if ss.backend.interleaved { if ss.backend.interleaved {
err = ErrNotImplemented return ss.interleavedSpatialQuery(first, second, done)
return
} }
return ss.PlainSpatialQuery(first, second, done) return ss.plainSpatialQuery(first, second, done)
} }
func order(a, b int64) (int64, int64) { func order(a, b int64) (int64, int64) {
@ -301,7 +299,79 @@ func order(a, b int64) (int64, int64) {
return b, a return b, a
} }
func (ss *SqliteSession) PlainSpatialQuery(first, second []byte, done chan struct{}) (blocks chan Block, err error) { func (ss *SqliteSession) interleavedSpatialQuery(first, second []byte, done chan struct{}) (blocks chan Block, err error) {
var (
firstKey int64
secondKey int64
)
if firstKey, err = common.DecodeStringFromBytes(first); err != nil {
return
}
if secondKey, err = common.DecodeStringFromBytes(second); err != nil {
return
}
c1 := common.PlainToCoord(firstKey)
c2 := common.PlainToCoord(secondKey)
c1, c2 = common.MinCoord(c1, c2), common.MaxCoord(c1, c2)
blocks = make(chan Block)
globalLock.RLock()
go func() {
defer close(blocks)
defer globalLock.RUnlock()
zmin, zmax := common.CoordToInterleaved(c1), common.CoordToInterleaved(c2)
// Should not be necessary.
zmin, zmax = order(zmin, zmax)
cub := common.Cuboid{P1: c1, P2: c2}
rangeStmt := ss.txStmt(ss.backend.rangeStmt)
OUTER:
for {
var (
err error
rows *sql.Rows
)
if rows, err = rangeStmt.Query(zmin, zmax); err != nil {
return
}
for rows.Next() {
var zcode int64
var data []byte
if err = rows.Scan(&zcode, &data); err != nil {
log.Printf("Error in range query: %s", err)
break
}
c := common.InterleavedToCoord(zcode)
if cub.Contains(c) {
var encodedKey []byte
if encodedKey, err = common.EncodeStringToBytes(common.CoordToPlain(c)); err != nil {
log.Printf("Key encoding failed: %s", err)
break
}
select {
case blocks <- Block{Key: encodedKey, Data: data}:
case <-done:
rows.Close()
return
}
} else { // Left the cuboid
rows.Close()
zmin = common.NaiveBigMin(zmin, zmax, zcode)
continue OUTER
}
}
if err = rows.Err(); err != nil {
log.Printf("Error in range query: %s", err)
}
rows.Close()
}
}()
return
}
func (ss *SqliteSession) plainSpatialQuery(first, second []byte, done chan struct{}) (blocks chan Block, err error) {
var ( var (
firstKey int64 firstKey int64
@ -317,10 +387,10 @@ func (ss *SqliteSession) PlainSpatialQuery(first, second []byte, done chan struc
c2 := common.PlainToCoord(secondKey) c2 := common.PlainToCoord(secondKey)
c1, c2 = common.MinCoord(c1, c2), common.MaxCoord(c1, c2) c1, c2 = common.MinCoord(c1, c2), common.MaxCoord(c1, c2)
globalLock.RLock()
blocks = make(chan Block) blocks = make(chan Block)
globalLock.RLock()
go func() { go func() {
defer globalLock.RUnlock() defer globalLock.RUnlock()
defer close(blocks) defer close(blocks)