Feat:Add hrandfield Command.

This commit is contained in:
Eriri
2022-06-01 13:50:53 +08:00
committed by finley
parent 24b785e023
commit 951a844439
4 changed files with 131 additions and 0 deletions

View File

@@ -67,6 +67,7 @@ func makeRouter() map[string]CmdFunc {
routerMap["hgetall"] = defaultFunc routerMap["hgetall"] = defaultFunc
routerMap["hincrby"] = defaultFunc routerMap["hincrby"] = defaultFunc
routerMap["hincrbyfloat"] = defaultFunc routerMap["hincrbyfloat"] = defaultFunc
routerMap["hrandfield"] = defaultFunc
routerMap["sadd"] = defaultFunc routerMap["sadd"] = defaultFunc
routerMap["sismember"] = defaultFunc routerMap["sismember"] = defaultFunc

View File

@@ -60,6 +60,7 @@
- hgetall - hgetall
- hincrby - hincrby
- hincrbyfloat - hincrbyfloat
- hrandfield
- Set - Set
- sadd - sadd
- sismember - sismember

View File

@@ -8,6 +8,7 @@ import (
"github.com/hdt3213/godis/redis/protocol" "github.com/hdt3213/godis/redis/protocol"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"strconv" "strconv"
"strings"
) )
func (db *DB) getAsDict(key string) (Dict.Dict, protocol.ErrorReply) { func (db *DB) getAsDict(key string) (Dict.Dict, protocol.ErrorReply) {
@@ -398,6 +399,82 @@ func execHIncrByFloat(db *DB, args [][]byte) redis.Reply {
return protocol.MakeBulkReply(resultBytes) return protocol.MakeBulkReply(resultBytes)
} }
// execHRandField return a random field(or field-value) from the hash value stored at key.
func execHRandField(db *DB, args [][]byte) redis.Reply {
key := string(args[0])
count := 1
withvalues := 0
if len(args) > 3 {
return protocol.MakeErrReply("ERR wrong number of arguments for 'hrandfield' command")
}
if len(args) == 3 {
if strings.ToLower(string(args[2])) == "withvalues" {
withvalues = 1
} else {
return protocol.MakeSyntaxErrReply()
}
}
if len(args) >= 2 {
count64, err := strconv.ParseInt(string(args[1]), 10, 64)
if err != nil {
return protocol.MakeErrReply("ERR value is not an integer or out of range")
}
count = int(count64)
}
dict, errReply := db.getAsDict(key)
if errReply != nil {
return errReply
}
if dict == nil {
return &protocol.EmptyMultiBulkReply{}
}
if count > 0 {
fields := dict.RandomDistinctKeys(count)
Numfield := len(fields)
if withvalues == 0 {
result := make([][]byte, Numfield)
for i, v := range fields {
result[i] = []byte(v)
}
return protocol.MakeMultiBulkReply(result)
} else {
result := make([][]byte, 2*Numfield)
for i, v := range fields {
result[2*i] = []byte(v)
raw, _ := dict.Get(v)
result[2*i+1] = raw.([]byte)
}
return protocol.MakeMultiBulkReply(result)
}
} else if count < 0 {
fields := dict.RandomKeys(-count)
Numfield := len(fields)
if withvalues == 0 {
result := make([][]byte, Numfield)
for i, v := range fields {
result[i] = []byte(v)
}
return protocol.MakeMultiBulkReply(result)
} else {
result := make([][]byte, 2*Numfield)
for i, v := range fields {
result[2*i] = []byte(v)
raw, _ := dict.Get(v)
result[2*i+1] = raw.([]byte)
}
return protocol.MakeMultiBulkReply(result)
}
}
// 'count' is 0 will reach.
return &protocol.EmptyMultiBulkReply{}
}
func init() { func init() {
RegisterCommand("HSet", execHSet, writeFirstKey, undoHSet, 4) RegisterCommand("HSet", execHSet, writeFirstKey, undoHSet, 4)
RegisterCommand("HSetNX", execHSetNX, writeFirstKey, undoHSet, 4) RegisterCommand("HSetNX", execHSetNX, writeFirstKey, undoHSet, 4)
@@ -413,4 +490,5 @@ func init() {
RegisterCommand("HGetAll", execHGetAll, readFirstKey, nil, 2) RegisterCommand("HGetAll", execHGetAll, readFirstKey, nil, 2)
RegisterCommand("HIncrBy", execHIncrBy, writeFirstKey, undoHIncr, 4) RegisterCommand("HIncrBy", execHIncrBy, writeFirstKey, undoHIncr, 4)
RegisterCommand("HIncrByFloat", execHIncrByFloat, writeFirstKey, undoHIncr, 4) RegisterCommand("HIncrByFloat", execHIncrByFloat, writeFirstKey, undoHIncr, 4)
RegisterCommand("HRandField", execHRandField, readFirstKey, nil, -2)
} }

View File

@@ -174,6 +174,57 @@ func TestHGetAll(t *testing.T) {
t.Error(fmt.Sprintf("unexpected value %s", value)) t.Error(fmt.Sprintf("unexpected value %s", value))
} }
} }
// test HRandField
// test HRandField count of 0 is handled correctly -- "emptyarray"
result = testDB.Exec(nil, utils.ToCmdLine("hrandfield", key, "0"))
multiBulk, ok = result.(*protocol.MultiBulkReply)
if !ok {
resultBytes := string(result.ToBytes())
if resultBytes != "*0\r\n" {
t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", resultBytes))
}
}
// test HRandField count > size
result = testDB.Exec(nil, utils.ToCmdLine("hrandfield", key, strconv.Itoa(size+100)))
multiBulk, ok = result.(*protocol.MultiBulkReply)
if !ok {
t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes())))
}
if len(fields) != len(multiBulk.Args) {
t.Error(fmt.Sprintf("expected %d items , actually %d ", len(fields), len(multiBulk.Args)))
}
// test HRandField count > size withvalues
result = testDB.Exec(nil, utils.ToCmdLine("hrandfield", key, strconv.Itoa(size+100), "withvalues"))
multiBulk, ok = result.(*protocol.MultiBulkReply)
if !ok {
t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes())))
}
if 2*len(fields) != len(multiBulk.Args) {
t.Error(fmt.Sprintf("expected %d items , actually %d ", 2*len(fields), len(multiBulk.Args)))
}
// test HRandField count < size
result = testDB.Exec(nil, utils.ToCmdLine("hrandfield", key, strconv.Itoa(-size-10)))
multiBulk, ok = result.(*protocol.MultiBulkReply)
if !ok {
t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes())))
}
if len(fields)+10 != len(multiBulk.Args) {
t.Error(fmt.Sprintf("expected %d items , actually %d ", len(fields)+10, len(multiBulk.Args)))
}
// test HRandField count < size withvalues
result = testDB.Exec(nil, utils.ToCmdLine("hrandfield", key, strconv.Itoa(-size-10), "withvalues"))
multiBulk, ok = result.(*protocol.MultiBulkReply)
if !ok {
t.Error(fmt.Sprintf("expected MultiBulkReply, actually %s", string(result.ToBytes())))
}
if 2*(len(fields)+10) != len(multiBulk.Args) {
t.Error(fmt.Sprintf("expected %d items , actually %d ", 2*(len(fields)+10), len(multiBulk.Args)))
}
} }
func TestHIncrBy(t *testing.T) { func TestHIncrBy(t *testing.T) {