diff --git a/ledis/ledis_db.go b/ledis/ledis_db.go index 104ac6b..c774d03 100644 --- a/ledis/ledis_db.go +++ b/ledis/ledis_db.go @@ -146,7 +146,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) { } var keys [][]byte - keys, err = db.scan(metaDataType, nil, 1024, false) + keys, err = db.scan(metaDataType, nil, 1024, false, "") for len(keys) != 0 || err != nil { for _, key := range keys { deleteFunc(t, key) @@ -159,7 +159,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) { } else { drop += int64(len(keys)) } - keys, err = db.scan(metaDataType, nil, 1024, false) + keys, err = db.scan(metaDataType, nil, 1024, false, "") } return } diff --git a/ledis/scan.go b/ledis/scan.go index 205bc07..64a3f97 100644 --- a/ledis/scan.go +++ b/ledis/scan.go @@ -1,16 +1,25 @@ package ledis import ( + "bytes" "errors" - "github.com/siddontang/ledisdb/store" + "regexp" ) 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) { +func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) { var minKey, maxKey []byte var err error + var r *regexp.Regexp + + if len(match) > 0 { + if r, err = regexp.Compile(match); err != nil { + return nil, err + } + } + if key != nil { if err = checkKeySize(key); err != nil { return nil, err @@ -35,18 +44,23 @@ func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool) ([][]by v := make([][]byte, 0, count) - rangeType := store.RangeROpen + it := db.bucket.NewIterator() + it.Seek(minKey) + if !inclusive { - rangeType = store.RangeOpen + if it.Valid() && bytes.Equal(it.RawKey(), minKey) { + it.Next() + } } - it := db.bucket.RangeLimitIterator(minKey, maxKey, rangeType, 0, count) - - for ; it.Valid(); it.Next() { + for i := 0; it.Valid() && i < count && bytes.Compare(it.RawKey(), maxKey) < 0; it.Next() { if k, err := db.decodeMetaKey(dataType, it.Key()); err != nil { continue + } else if r != nil && !r.Match(k) { + continue } else { v = append(v, k) + i++ } } it.Close() diff --git a/ledis/scan_test.go b/ledis/scan_test.go index e0a476c..5753b7f 100644 --- a/ledis/scan_test.go +++ b/ledis/scan_test.go @@ -9,7 +9,7 @@ func TestDBScan(t *testing.T) { db.FlushAll() - if v, err := db.Scan(nil, 10, true); err != nil { + if v, err := db.Scan(nil, 10, true, ""); err != nil { t.Fatal(err) } else if len(v) != 0 { t.Fatal(len(v)) @@ -19,23 +19,42 @@ func TestDBScan(t *testing.T) { db.Set([]byte("b"), []byte{}) db.Set([]byte("c"), []byte{}) - if v, err := db.Scan(nil, 1, true); err != nil { + 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 { + 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 { + if v, err := db.Scan(nil, 3, true, ""); err != nil { t.Fatal(err) } else if len(v) != 3 { t.Fatal(len(v)) } + + if v, err := db.Scan(nil, 3, true, "b"); err != nil { + t.Fatal(err) + } else if len(v) != 1 { + 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)) + } + + if v, err := db.Scan(nil, 3, true, "a+"); err != nil { + t.Fatal(err) + } else if len(v) != 1 { + t.Fatal(len(v)) + } + } func TestDBHScan(t *testing.T) { @@ -52,7 +71,7 @@ func TestDBHScan(t *testing.T) { k3 := []byte("k3") db.HSet(k3, []byte("3"), []byte{}) - if v, err := db.HScan(nil, 1, true); err != nil { + if v, err := db.HScan(nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -60,7 +79,7 @@ func TestDBHScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.HScan(k1, 2, true); err != nil { + if v, err := db.HScan(k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -70,7 +89,7 @@ func TestDBHScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.HScan(k1, 2, false); err != nil { + if v, err := db.HScan(k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -96,7 +115,7 @@ func TestDBZScan(t *testing.T) { k3 := []byte("k3") db.ZAdd(k3, ScorePair{3, []byte("m")}) - if v, err := db.ZScan(nil, 1, true); err != nil { + if v, err := db.ZScan(nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -104,7 +123,7 @@ func TestDBZScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.ZScan(k1, 2, true); err != nil { + if v, err := db.ZScan(k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -114,7 +133,7 @@ func TestDBZScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.ZScan(k1, 2, false); err != nil { + if v, err := db.ZScan(k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -146,7 +165,7 @@ func TestDBLScan(t *testing.T) { t.Fatal(err.Error()) } - if v, err := db.LScan(nil, 1, true); err != nil { + if v, err := db.LScan(nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -154,7 +173,7 @@ func TestDBLScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.LScan(k1, 2, true); err != nil { + if v, err := db.LScan(k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -164,7 +183,7 @@ func TestDBLScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.LScan(k1, 2, false); err != nil { + if v, err := db.LScan(k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -196,7 +215,7 @@ func TestDBBScan(t *testing.T) { t.Fatal(err.Error()) } - if v, err := db.BScan(nil, 1, true); err != nil { + if v, err := db.BScan(nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -204,7 +223,7 @@ func TestDBBScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.BScan(k1, 2, true); err != nil { + if v, err := db.BScan(k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -214,7 +233,7 @@ func TestDBBScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.BScan(k1, 2, false); err != nil { + if v, err := db.BScan(k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -246,7 +265,7 @@ func TestDBSScan(t *testing.T) { t.Fatal(err.Error()) } - if v, err := db.SScan(nil, 1, true); err != nil { + if v, err := db.SScan(nil, 1, true, ""); err != nil { t.Fatal(err) } else if len(v) != 1 { t.Fatal("invalid length ", len(v)) @@ -254,7 +273,7 @@ func TestDBSScan(t *testing.T) { t.Fatal("invalid value ", string(v[0])) } - if v, err := db.SScan(k1, 2, true); err != nil { + if v, err := db.SScan(k1, 2, true, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) @@ -264,7 +283,7 @@ func TestDBSScan(t *testing.T) { t.Fatal("invalid value ", string(v[1])) } - if v, err := db.SScan(k1, 2, false); err != nil { + if v, err := db.SScan(k1, 2, false, ""); err != nil { t.Fatal(err) } else if len(v) != 2 { t.Fatal("invalid length ", len(v)) diff --git a/ledis/t_bit.go b/ledis/t_bit.go index 3af59dc..fadab4d 100644 --- a/ledis/t_bit.go +++ b/ledis/t_bit.go @@ -908,8 +908,8 @@ func (db *DB) BPersist(key []byte) (int64, error) { return n, err } -func (db *DB) BScan(key []byte, count int, inclusive bool) ([][]byte, error) { - return db.scan(BitMetaType, key, count, inclusive) +func (db *DB) BScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + return db.scan(BitMetaType, key, count, inclusive, match) } func (db *DB) bFlush() (drop int64, err error) { diff --git a/ledis/t_bit_test.go b/ledis/t_bit_test.go index e04fe65..c62b07a 100644 --- a/ledis/t_bit_test.go +++ b/ledis/t_bit_test.go @@ -551,7 +551,7 @@ func testBFlush(t *testing.T) { } } - if v, err := db.BScan(nil, 3000, true); err != nil { + if v, err := db.BScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -573,7 +573,7 @@ func testBFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.BScan(nil, 3000, true); err != nil { + if v, err := db.BScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_hash.go b/ledis/t_hash.go index bdcf16d..8ee199e 100644 --- a/ledis/t_hash.go +++ b/ledis/t_hash.go @@ -461,8 +461,8 @@ func (db *DB) hFlush() (drop int64, err error) { return db.flushType(t, HashType) } -func (db *DB) HScan(key []byte, count int, inclusive bool) ([][]byte, error) { - return db.scan(HSizeType, key, count, inclusive) +func (db *DB) HScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + return db.scan(HSizeType, key, count, inclusive, match) } func (db *DB) HExpire(key []byte, duration int64) (int64, error) { diff --git a/ledis/t_hash_test.go b/ledis/t_hash_test.go index bc79b06..294746d 100644 --- a/ledis/t_hash_test.go +++ b/ledis/t_hash_test.go @@ -91,7 +91,7 @@ func TestHFlush(t *testing.T) { } } - if v, err := db.HScan(nil, 3000, true); err != nil { + if v, err := db.HScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -112,7 +112,7 @@ func TestHFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.HScan(nil, 3000, true); err != nil { + if v, err := db.HScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_kv.go b/ledis/t_kv.go index 3d5bb0f..1dd540a 100644 --- a/ledis/t_kv.go +++ b/ledis/t_kv.go @@ -313,8 +313,8 @@ func (db *DB) flush() (drop int64, err error) { } //if inclusive is true, scan range [key, inf) else (key, inf) -func (db *DB) Scan(key []byte, count int, inclusive bool) ([][]byte, error) { - return db.scan(KVType, key, count, inclusive) +func (db *DB) Scan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + return db.scan(KVType, key, count, inclusive, match) } func (db *DB) Expire(key []byte, duration int64) (int64, error) { diff --git a/ledis/t_kv_test.go b/ledis/t_kv_test.go index 4a2c761..b0f3437 100644 --- a/ledis/t_kv_test.go +++ b/ledis/t_kv_test.go @@ -77,7 +77,7 @@ func TestKVFlush(t *testing.T) { } } - if v, err := db.Scan(nil, 3000, true); err != nil { + if v, err := db.Scan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -98,7 +98,7 @@ func TestKVFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.Scan(nil, 3000, true); err != nil { + if v, err := db.Scan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_list.go b/ledis/t_list.go index 599dc9f..dd6e04b 100644 --- a/ledis/t_list.go +++ b/ledis/t_list.go @@ -472,8 +472,8 @@ func (db *DB) LPersist(key []byte) (int64, error) { 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) LScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + return db.scan(LMetaType, key, count, inclusive, match) } func (db *DB) lEncodeMinKey() []byte { diff --git a/ledis/t_list_test.go b/ledis/t_list_test.go index eb417ec..8373a43 100644 --- a/ledis/t_list_test.go +++ b/ledis/t_list_test.go @@ -113,7 +113,7 @@ func TestLFlush(t *testing.T) { } } - if v, err := db.LScan(nil, 3000, true); err != nil { + if v, err := db.LScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -125,32 +125,7 @@ func TestLFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.LScan(nil, 3000, true); err != nil { - t.Fatal(err.Error()) - } else if len(v) != 0 { - t.Fatal("invalid value length ", len(v)) - } - - for i := 0; i < 2000; i++ { - key := fmt.Sprintf("%d", i) - if _, err := db.ZAdd([]byte(key), ScorePair{1, []byte("v")}); err != nil { - t.Fatal(err.Error()) - } - } - - if v, err := db.ZScan(nil, 3000, true); err != nil { - t.Fatal(err.Error()) - } else if len(v) != 2000 { - t.Fatal("invalid value ", len(v)) - } - - if n, err := db.zFlush(); err != nil { - t.Fatal(err.Error()) - } else if n != 2000 { - t.Fatal("invalid value ", n) - } - - if v, err := db.ZScan(nil, 3000, true); err != nil { + if v, err := db.LScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_set.go b/ledis/t_set.go index 5a656c1..330462a 100644 --- a/ledis/t_set.go +++ b/ledis/t_set.go @@ -595,6 +595,6 @@ func (db *DB) SPersist(key []byte) (int64, error) { return n, err } -func (db *DB) SScan(key []byte, count int, inclusive bool) ([][]byte, error) { - return db.scan(SSizeType, key, count, inclusive) +func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + return db.scan(SSizeType, key, count, inclusive, match) } diff --git a/ledis/t_set_test.go b/ledis/t_set_test.go index 53bb3a3..cee8023 100644 --- a/ledis/t_set_test.go +++ b/ledis/t_set_test.go @@ -352,7 +352,7 @@ func TestSFlush(t *testing.T) { } } - if v, err := db.SScan(nil, 3000, true); err != nil { + if v, err := db.SScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 2000 { t.Fatal("invalid value ", len(v)) @@ -364,7 +364,7 @@ func TestSFlush(t *testing.T) { t.Fatal("invalid value ", n) } - if v, err := db.SScan(nil, 3000, true); err != nil { + if v, err := db.SScan(nil, 3000, true, ""); err != nil { t.Fatal(err.Error()) } else if len(v) != 0 { t.Fatal("invalid value length ", len(v)) diff --git a/ledis/t_zset.go b/ledis/t_zset.go index c7edb6c..47af6ec 100644 --- a/ledis/t_zset.go +++ b/ledis/t_zset.go @@ -937,6 +937,6 @@ func (db *DB) ZInterStore(destKey []byte, srcKeys [][]byte, weights []int64, agg return num, nil } -func (db *DB) ZScan(key []byte, count int, inclusive bool) ([][]byte, error) { - return db.scan(ZSizeType, key, count, inclusive) +func (db *DB) ZScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) { + return db.scan(ZSizeType, key, count, inclusive, match) } diff --git a/ledis/t_zset_test.go b/ledis/t_zset_test.go index 212a02d..a1754ed 100644 --- a/ledis/t_zset_test.go +++ b/ledis/t_zset_test.go @@ -377,3 +377,33 @@ func TestZInterStore(t *testing.T) { t.Fatal("invalid value ", n) } } + +func TestZScan(t *testing.T) { + db := getTestDB() + db.FlushAll() + + for i := 0; i < 2000; i++ { + key := fmt.Sprintf("%d", i) + if _, err := db.ZAdd([]byte(key), ScorePair{1, []byte("v")}); err != nil { + t.Fatal(err.Error()) + } + } + + if v, err := db.ZScan(nil, 3000, true, ""); err != nil { + t.Fatal(err.Error()) + } else if len(v) != 2000 { + t.Fatal("invalid value ", len(v)) + } + + if n, err := db.zFlush(); err != nil { + t.Fatal(err.Error()) + } else if n != 2000 { + t.Fatal("invalid value ", n) + } + + if v, err := db.ZScan(nil, 3000, true, ""); err != nil { + t.Fatal(err.Error()) + } else if len(v) != 0 { + t.Fatal("invalid value length ", len(v)) + } +}