mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-23 16:53:15 +08:00
add scan command
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
"github.com/hdt3213/godis/lib/wildcard"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
@@ -435,3 +436,47 @@ func (dict *ConcurrentDict) RWUnLocks(writeKeys []string, readKeys []string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stringsToBytes(strSlice []string) [][]byte {
|
||||
byteSlice := make([][]byte, len(strSlice))
|
||||
for i, str := range strSlice {
|
||||
byteSlice[i] = []byte(str)
|
||||
}
|
||||
return byteSlice
|
||||
}
|
||||
|
||||
func (dict *ConcurrentDict) DictScan(cursor int, count int, pattern string) ([][]byte, int) {
|
||||
size := dict.Len()
|
||||
result := make([][]byte, 0)
|
||||
|
||||
if pattern == "*" && count >= size {
|
||||
return stringsToBytes(dict.Keys()), 0
|
||||
}
|
||||
|
||||
matchKey, err := wildcard.CompilePattern(pattern)
|
||||
if err != nil {
|
||||
return result, -1
|
||||
}
|
||||
|
||||
shardCount := len(dict.table)
|
||||
shardIndex := cursor
|
||||
|
||||
for shardIndex < shardCount {
|
||||
shard := dict.table[shardIndex]
|
||||
shard.mutex.RLock()
|
||||
if len(result)+len(shard.m) > count && shardIndex > cursor {
|
||||
shard.mutex.RUnlock()
|
||||
return result, shardIndex
|
||||
}
|
||||
|
||||
for key := range shard.m {
|
||||
if pattern == "*" || matchKey.IsMatch(key) {
|
||||
result = append(result, []byte(key))
|
||||
}
|
||||
}
|
||||
shard.mutex.RUnlock()
|
||||
shardIndex++
|
||||
}
|
||||
|
||||
return result, 0
|
||||
}
|
||||
|
@@ -465,7 +465,7 @@ func TestConcurrentRemoveWithLock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//change t.Error remove->forEach
|
||||
// change t.Error remove->forEach
|
||||
func TestConcurrentForEach(t *testing.T) {
|
||||
d := MakeConcurrent(0)
|
||||
size := 100
|
||||
@@ -524,3 +524,51 @@ func TestConcurrentDict_Keys(t *testing.T) {
|
||||
t.Errorf("expect %d keys, actual: %d", size, len(d.Keys()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDictScan(t *testing.T) {
|
||||
d := MakeConcurrent(0)
|
||||
count := 100
|
||||
for i := 0; i < count; i++ {
|
||||
key := "kkk" + strconv.Itoa(i)
|
||||
d.Put(key, i)
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
key := "key" + strconv.Itoa(i)
|
||||
d.Put(key, i)
|
||||
}
|
||||
cursor := 0
|
||||
matchKey := "*"
|
||||
c := 20
|
||||
result := make([][]byte, 0)
|
||||
var returnKeys [][]byte
|
||||
for {
|
||||
returnKeys, cursor = d.DictScan(cursor, c, matchKey)
|
||||
result = append(result, returnKeys...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
result = utils.RemoveDuplicates(result)
|
||||
if len(result) != count*2 {
|
||||
t.Errorf("scan command result number error: %d, should be %d ", len(result), count*2)
|
||||
}
|
||||
matchKey = "key*"
|
||||
cursor = 0
|
||||
mresult := make([][]byte, 0)
|
||||
for {
|
||||
returnKeys, cursor = d.DictScan(cursor, c, matchKey)
|
||||
mresult = append(mresult, returnKeys...)
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
mresult = utils.RemoveDuplicates(mresult)
|
||||
if len(mresult) != count {
|
||||
t.Errorf("scan command result number error: %d, should be %d ", len(mresult), count)
|
||||
}
|
||||
matchKey = "no*"
|
||||
returnKeys, _ = d.DictScan(cursor, c, matchKey)
|
||||
if len(returnKeys) != 0 {
|
||||
t.Errorf("returnKeys should be empty")
|
||||
}
|
||||
}
|
||||
|
@@ -16,4 +16,5 @@ type Dict interface {
|
||||
RandomKeys(limit int) []string
|
||||
RandomDistinctKeys(limit int) []string
|
||||
Clear()
|
||||
DictScan(cursor int, count int, pattern string) ([][]byte, int)
|
||||
}
|
||||
|
@@ -120,3 +120,7 @@ func (dict *SimpleDict) RandomDistinctKeys(limit int) []string {
|
||||
func (dict *SimpleDict) Clear() {
|
||||
*dict = *MakeSimple()
|
||||
}
|
||||
|
||||
func (dict *SimpleDict) DictScan(cursor int, count int, pattern string) ([][]byte, int) {
|
||||
return stringsToBytes(dict.Keys()), 0
|
||||
}
|
||||
|
Reference in New Issue
Block a user