mirror of https://github.com/ledisdb/ledisdb.git
refactor scan
This commit is contained in:
parent
30b58b2b46
commit
54ed8e0d38
|
@ -0,0 +1,86 @@
|
||||||
|
package ledis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/siddontang/ledisdb/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errDataType = errors.New("error data type")
|
||||||
|
var errMetaKey = errors.New("error meta key")
|
||||||
|
|
||||||
|
func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool) ([][]byte, error) {
|
||||||
|
var minKey, maxKey []byte
|
||||||
|
var err error
|
||||||
|
if key != nil {
|
||||||
|
if err = db.checkKeySize(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if minKey, err = db.encodeOffsetKey(dataType, key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if minKey, err = db.encodeMinKey(dataType); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxKey, err = db.encodeMaxKey(dataType); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count <= 0 {
|
||||||
|
count = defaultScanCount
|
||||||
|
}
|
||||||
|
|
||||||
|
v := make([][]byte, 0, count)
|
||||||
|
|
||||||
|
rangeType := store.RangeROpen
|
||||||
|
if !inclusive {
|
||||||
|
rangeType = store.RangeOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
||||||
|
for ; it.Valid(); it.Next() {
|
||||||
|
if k, err := db.decodeMetaKey(dataType, it.Key()); err != nil {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
v = append(v, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.Close()
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) encodeMinKey(dataType byte) ([]byte, error) {
|
||||||
|
return db.encodeOffsetKey(dataType, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) encodeMaxKey(dataType byte) ([]byte, error) {
|
||||||
|
k := db.encodeOffsetKey(dataType, nil)
|
||||||
|
k[len(k)-1] = dataType + 1
|
||||||
|
return k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) encodeMetaKey(dataType byte, key []byte) ([]byte, error) {
|
||||||
|
switch dataType {
|
||||||
|
case KVType:
|
||||||
|
return db.encodeKVKey(key), nil
|
||||||
|
case LMetaType:
|
||||||
|
return db.lEncodeMetaKey(key), nil
|
||||||
|
case HSizeType:
|
||||||
|
return db.hEncodeSizeKey(key), nil
|
||||||
|
case ZSizeType:
|
||||||
|
return db.zEncodeSizeKey(key), nil
|
||||||
|
case BitMetaType:
|
||||||
|
return db.bEncodeMetaKey(key), nil
|
||||||
|
default:
|
||||||
|
return nil, errInvalidDataType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (db *DB) decodeMetaKey(dataType, byte, ek []byte) ([]byte, error) {
|
||||||
|
if len(ek) < 2 || ek[0] != db.index || ek[1] != dataType {
|
||||||
|
return nil, errMetaKey
|
||||||
|
}
|
||||||
|
return ek[2:], nil
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package ledis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDBScan(t *testing.T) {
|
||||||
|
db := getTestDB()
|
||||||
|
|
||||||
|
db.FlushAll()
|
||||||
|
|
||||||
|
if v, err := db.Scan(nil, 10, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 0 {
|
||||||
|
t.Fatal(len(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Set([]byte("a"), []byte{})
|
||||||
|
db.Set([]byte("b"), []byte{})
|
||||||
|
db.Set([]byte("c"), []byte{})
|
||||||
|
|
||||||
|
if v, err := db.Scan(nil, 1, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 1 {
|
||||||
|
t.Fatal(len(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.Scan([]byte("a"), 2, false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 2 {
|
||||||
|
t.Fatal(len(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.Scan(nil, 3, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 3 {
|
||||||
|
t.Fatal(len(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDBHScan(t *testing.T) {
|
||||||
|
db := getTestDB()
|
||||||
|
|
||||||
|
db.hFlush()
|
||||||
|
|
||||||
|
k1 := []byte("k1")
|
||||||
|
db.HSet(k1, []byte("1"), []byte{})
|
||||||
|
|
||||||
|
k2 := []byte("k2")
|
||||||
|
db.HSet(k2, []byte("2"), []byte{})
|
||||||
|
|
||||||
|
k3 := []byte("k3")
|
||||||
|
db.HSet(k3, []byte("3"), []byte{})
|
||||||
|
|
||||||
|
if v, err := db.HScan(nil, 1, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 1 {
|
||||||
|
t.Fatal("invalid length ", len(v))
|
||||||
|
} else if string(v[0]) != "k1" {
|
||||||
|
t.Fatal("invalid value ", string(v[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.HScan(k1, 2, true); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 2 {
|
||||||
|
t.Fatal("invalid length ", len(v))
|
||||||
|
} else if string(v[0]) != "k1" {
|
||||||
|
t.Fatal("invalid value ", string(v[0]))
|
||||||
|
} else if string(v[1]) != "k2" {
|
||||||
|
t.Fatal("invalid value ", string(v[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := db.HScan(k1, 2, false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(v) != 2 {
|
||||||
|
t.Fatal("invalid length ", len(v))
|
||||||
|
} else if string(v[0]) != "k2" {
|
||||||
|
t.Fatal("invalid value ", string(v[0]))
|
||||||
|
} else if string(v[1]) != "k3" {
|
||||||
|
t.Fatal("invalid value ", string(v[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -472,48 +472,8 @@ func (db *DB) hFlush() (drop int64, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) hEncodeMinKey() []byte {
|
|
||||||
return db.hEncodeSizeKey(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) hEncodeMaxKey() []byte {
|
|
||||||
ek := db.hEncodeSizeKey(nil)
|
|
||||||
ek[len(ek)-1] = HSizeType + 1
|
|
||||||
return ek
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) HScan(key []byte, count int, inclusive bool) ([][]byte, error) {
|
func (db *DB) HScan(key []byte, count int, inclusive bool) ([][]byte, error) {
|
||||||
var minKey []byte
|
return db.scan(HSizeType, key, count, inclusive)
|
||||||
if key != nil {
|
|
||||||
minKey = db.hEncodeSizeKey(key)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
minKey = db.hEncodeMinKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
maxKey := db.hEncodeMaxKey()
|
|
||||||
|
|
||||||
if count <= 0 {
|
|
||||||
count = defaultScanCount
|
|
||||||
}
|
|
||||||
|
|
||||||
v := make([][]byte, 0, count)
|
|
||||||
|
|
||||||
rangeType := store.RangeROpen
|
|
||||||
if !inclusive {
|
|
||||||
rangeType = store.RangeOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
|
||||||
for ; it.Valid(); it.Next() {
|
|
||||||
if k, err := db.hDecodeSizeKey(it.Key()); err != nil {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
v = append(v, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.Close()
|
|
||||||
return v, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) HExpire(key []byte, duration int64) (int64, error) {
|
func (db *DB) HExpire(key []byte, duration int64) (int64, error) {
|
||||||
|
|
|
@ -56,50 +56,6 @@ func TestDBHash(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDBHScan(t *testing.T) {
|
|
||||||
db := getTestDB()
|
|
||||||
|
|
||||||
db.hFlush()
|
|
||||||
|
|
||||||
k1 := []byte("k1")
|
|
||||||
db.HSet(k1, []byte("1"), []byte{})
|
|
||||||
|
|
||||||
k2 := []byte("k2")
|
|
||||||
db.HSet(k2, []byte("2"), []byte{})
|
|
||||||
|
|
||||||
k3 := []byte("k3")
|
|
||||||
db.HSet(k3, []byte("3"), []byte{})
|
|
||||||
|
|
||||||
if v, err := db.HScan(nil, 1, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 1 {
|
|
||||||
t.Fatal("invalid length ", len(v))
|
|
||||||
} else if string(v[0]) != "k1" {
|
|
||||||
t.Fatal("invalid value ", string(v[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := db.HScan(k1, 2, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 2 {
|
|
||||||
t.Fatal("invalid length ", len(v))
|
|
||||||
} else if string(v[0]) != "k1" {
|
|
||||||
t.Fatal("invalid value ", string(v[0]))
|
|
||||||
} else if string(v[1]) != "k2" {
|
|
||||||
t.Fatal("invalid value ", string(v[1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := db.HScan(k1, 2, false); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 2 {
|
|
||||||
t.Fatal("invalid length ", len(v))
|
|
||||||
} else if string(v[0]) != "k2" {
|
|
||||||
t.Fatal("invalid value ", string(v[0]))
|
|
||||||
} else if string(v[1]) != "k3" {
|
|
||||||
t.Fatal("invalid value ", string(v[1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashPersist(t *testing.T) {
|
func TestHashPersist(t *testing.T) {
|
||||||
db := getTestDB()
|
db := getTestDB()
|
||||||
|
|
||||||
|
|
|
@ -324,41 +324,8 @@ func (db *DB) flush() (drop int64, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//if inclusive is true, scan range [key, inf) else (key, inf)
|
//if inclusive is true, scan range [key, inf) else (key, inf)
|
||||||
func (db *DB) Scan(key []byte, count int, inclusive bool) ([]KVPair, error) {
|
func (db *DB) Scan(key []byte, count int, inclusive bool) ([][]byte, error) {
|
||||||
var minKey []byte
|
return db.scan(KVType, key, count, inclusive)
|
||||||
if key != nil {
|
|
||||||
if err := checkKeySize(key); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
minKey = db.encodeKVKey(key)
|
|
||||||
} else {
|
|
||||||
minKey = db.encodeKVMinKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
maxKey := db.encodeKVMaxKey()
|
|
||||||
|
|
||||||
if count <= 0 {
|
|
||||||
count = defaultScanCount
|
|
||||||
}
|
|
||||||
|
|
||||||
v := make([]KVPair, 0, 2*count)
|
|
||||||
|
|
||||||
rangeType := store.RangeROpen
|
|
||||||
if !inclusive {
|
|
||||||
rangeType = store.RangeOpen
|
|
||||||
}
|
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(minKey, maxKey, rangeType, 0, count)
|
|
||||||
for ; it.Valid(); it.Next() {
|
|
||||||
if key, err := db.decodeKVKey(it.Key()); err != nil {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
v = append(v, KVPair{Key: key, Value: it.Value()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.Close()
|
|
||||||
|
|
||||||
return v, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Expire(key []byte, duration int64) (int64, error) {
|
func (db *DB) Expire(key []byte, duration int64) (int64, error) {
|
||||||
|
|
|
@ -43,40 +43,6 @@ func TestDBKV(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDBScan(t *testing.T) {
|
|
||||||
db := getTestDB()
|
|
||||||
|
|
||||||
db.FlushAll()
|
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 10, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 0 {
|
|
||||||
t.Fatal(len(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Set([]byte("a"), []byte{})
|
|
||||||
db.Set([]byte("b"), []byte{})
|
|
||||||
db.Set([]byte("c"), []byte{})
|
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 1, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 1 {
|
|
||||||
t.Fatal(len(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := db.Scan([]byte("a"), 2, false); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 2 {
|
|
||||||
t.Fatal(len(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, err := db.Scan(nil, 3, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if len(v) != 3 {
|
|
||||||
t.Fatal(len(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKVPersist(t *testing.T) {
|
func TestKVPersist(t *testing.T) {
|
||||||
db := getTestDB()
|
db := getTestDB()
|
||||||
|
|
||||||
|
|
|
@ -484,3 +484,17 @@ func (db *DB) LPersist(key []byte) (int64, error) {
|
||||||
err = t.Commit()
|
err = t.Commit()
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) LScan(key []byte, count int, inclusive bool) ([][]byte, error) {
|
||||||
|
return db.scan(LMetaType, key, count, inclusive)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) lEncodeMinKey() []byte {
|
||||||
|
return db.lEncodeMetaKey(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) lEncodeMaxKey() []byte {
|
||||||
|
ek := db.lEncodeMetaKey(nil)
|
||||||
|
ek[len(ek)-1] = LMetaType + 1
|
||||||
|
return ek
|
||||||
|
}
|
||||||
|
|
|
@ -1004,3 +1004,7 @@ func (db *DB) ZInterStore(destKey []byte, srcKeys [][]byte, weights []int64, agg
|
||||||
}
|
}
|
||||||
return int64(len(keptMembers)), nil
|
return int64(len(keptMembers)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) ZScan(key []byte, count int, inclusive bool) ([][]byte, error) {
|
||||||
|
return db.scan(ZSizeType, key, count, inclusive)
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/siddontang/go-bson/bson"
|
||||||
"github.com/siddontang/go-log/log"
|
"github.com/siddontang/go-log/log"
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/siddontang/go-bson/bson"
|
|
||||||
"github.com/ugorji/go/codec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var allowedContentTypes = map[string]struct{}{
|
var allowedContentTypes = map[string]struct{}{
|
||||||
|
|
Loading…
Reference in New Issue