Repository: zeromicro/go-zero (go)

Issue:
Why core.stores.redis.ZaddCtx score is int64 instead of float64
I want save a float64 to zset,but ZaddCtx only support the type of int64,is there any other way to save it?

Pull request description:
feat(redis): add ZaddFloat & ZaddFloatCtx
resolve #2289

Patch:
diff --git a/core/stores/kv/store.go b/core/stores/kv/store.go
index f7ee6b42..fcaf07c1 100644
--- a/core/stores/kv/store.go
+++ b/core/stores/kv/store.go
@@ -110,7 +110,9 @@ type (
 		Ttl(key string) (int, error)
 		TtlCtx(ctx context.Context, key string) (int, error)
 		Zadd(key string, score int64, value string) (bool, error)
+		ZaddFloat(key string, score float64, value string) (bool, error)
 		ZaddCtx(ctx context.Context, key string, score int64, value string) (bool, error)
+		ZaddFloatCtx(ctx context.Context, key string, score float64, value string) (bool, error)
 		Zadds(key string, ps ...redis.Pair) (int64, error)
 		ZaddsCtx(ctx context.Context, key string, ps ...redis.Pair) (int64, error)
 		Zcard(key string) (int, error)
@@ -787,13 +789,21 @@ func (cs clusterStore) Zadd(key string, score int64, value string) (bool, error)
 	return cs.ZaddCtx(context.Background(), key, score, value)
 }
 
+func (cs clusterStore) ZaddFloat(key string, score float64, value string) (bool, error) {
+	return cs.ZaddFloatCtx(context.Background(), key, score, value)
+}
+
 func (cs clusterStore) ZaddCtx(ctx context.Context, key string, score int64, value string) (bool, error) {
+	return cs.ZaddFloatCtx(ctx, key, float64(score), value)
+}
+
+func (cs clusterStore) ZaddFloatCtx(ctx context.Context, key string, score float64, value string) (bool, error) {
 	node, err := cs.getRedis(key)
 	if err != nil {
 		return false, err
 	}
 
-	return node.ZaddCtx(ctx, key, score, value)
+	return node.ZaddFloatCtx(ctx, key, score, value)
 }
 
 func (cs clusterStore) Zadds(key string, ps ...redis.Pair) (int64, error) {
diff --git a/core/stores/redis/redis.go b/core/stores/redis/redis.go
index b31eb874..c4d9c401 100644
--- a/core/stores/redis/redis.go
+++ b/core/stores/redis/redis.go
@@ -1836,8 +1836,19 @@ func (s *Redis) Zadd(key string, score int64, value string) (bool, error) {
 	return s.ZaddCtx(context.Background(), key, score, value)
 }
 
+// ZaddFloat is the implementation of redis zadd command.
+func (s *Redis) ZaddFloat(key string, score float64, value string) (bool, error) {
+	return s.ZaddFloatCtx(context.Background(), key, score, value)
+}
+
 // ZaddCtx is the implementation of redis zadd command.
 func (s *Redis) ZaddCtx(ctx context.Context, key string, score int64, value string) (
+	val bool, err error) {
+	return s.ZaddFloatCtx(ctx, key, float64(score), value)
+}
+
+// ZaddFloatCtx is the implementation of redis zadd command.
+func (s *Redis) ZaddFloatCtx(ctx context.Context, key string, score float64, value string) (
 	val bool, err error) {
 	err = s.brk.DoWithAcceptable(func() error {
 		conn, err := getRedis(s)
@@ -1846,7 +1857,7 @@ func (s *Redis) ZaddCtx(ctx context.Context, key string, score int64, value stri
 		}
 
 		v, err := conn.ZAdd(ctx, key, &red.Z{
-			Score:  float64(score),
+			Score:  score,
 			Member: value,
 		}).Result()
 		if err != nil {


Test patch:
diff --git a/core/stores/kv/store_test.go b/core/stores/kv/store_test.go
index 1803d015..96d0e29b 100644
--- a/core/stores/kv/store_test.go
+++ b/core/stores/kv/store_test.go
@@ -22,7 +22,7 @@ func TestRedis_Decr(t *testing.T) {
 	_, err := store.Decr("a")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.Decr("a")
 		assert.Nil(t, err)
 		assert.Equal(t, int64(-1), val)
@@ -37,7 +37,7 @@ func TestRedis_DecrBy(t *testing.T) {
 	_, err := store.Incrby("a", 2)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.Decrby("a", 2)
 		assert.Nil(t, err)
 		assert.Equal(t, int64(-2), val)
@@ -52,7 +52,7 @@ func TestRedis_Exists(t *testing.T) {
 	_, err := store.Exists("foo")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		ok, err := client.Exists("a")
 		assert.Nil(t, err)
 		assert.False(t, ok)
@@ -68,7 +68,7 @@ func TestRedis_Eval(t *testing.T) {
 	_, err := store.Eval(`redis.call("EXISTS", KEYS[1])`, "key1")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		_, err := client.Eval(`redis.call("EXISTS", KEYS[1])`, "notexist")
 		assert.Equal(t, redis.Nil, err)
 		err = client.Set("key1", "value1")
@@ -88,7 +88,7 @@ func TestRedis_Hgetall(t *testing.T) {
 	_, err = store.Hgetall("a")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hset("a", "aa", "aaa"))
 		assert.Nil(t, client.Hset("a", "bb", "bbb"))
 		vals, err := client.Hgetall("a")
@@ -105,7 +105,7 @@ func TestRedis_Hvals(t *testing.T) {
 	_, err := store.Hvals("a")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hset("a", "aa", "aaa"))
 		assert.Nil(t, client.Hset("a", "bb", "bbb"))
 		vals, err := client.Hvals("a")
@@ -119,7 +119,7 @@ func TestRedis_Hsetnx(t *testing.T) {
 	_, err := store.Hsetnx("a", "dd", "ddd")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hset("a", "aa", "aaa"))
 		assert.Nil(t, client.Hset("a", "bb", "bbb"))
 		ok, err := client.Hsetnx("a", "bb", "ccc")
@@ -141,7 +141,7 @@ func TestRedis_HdelHlen(t *testing.T) {
 	_, err = store.Hlen("a")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hset("a", "aa", "aaa"))
 		assert.Nil(t, client.Hset("a", "bb", "bbb"))
 		num, err := client.Hlen("a")
@@ -161,7 +161,7 @@ func TestRedis_HIncrBy(t *testing.T) {
 	_, err := store.Hincrby("key", "field", 3)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.Hincrby("key", "field", 2)
 		assert.Nil(t, err)
 		assert.Equal(t, 2, val)
@@ -176,7 +176,7 @@ func TestRedis_Hkeys(t *testing.T) {
 	_, err := store.Hkeys("a")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hset("a", "aa", "aaa"))
 		assert.Nil(t, client.Hset("a", "bb", "bbb"))
 		vals, err := client.Hkeys("a")
@@ -190,7 +190,7 @@ func TestRedis_Hmget(t *testing.T) {
 	_, err := store.Hmget("a", "aa", "bb")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hset("a", "aa", "aaa"))
 		assert.Nil(t, client.Hset("a", "bb", "bbb"))
 		vals, err := client.Hmget("a", "aa", "bb")
@@ -209,7 +209,7 @@ func TestRedis_Hmset(t *testing.T) {
 	})
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		assert.Nil(t, client.Hmset("a", map[string]string{
 			"aa": "aaa",
 			"bb": "bbb",
@@ -225,7 +225,7 @@ func TestRedis_Incr(t *testing.T) {
 	_, err := store.Incr("a")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.Incr("a")
 		assert.Nil(t, err)
 		assert.Equal(t, int64(1), val)
@@ -240,7 +240,7 @@ func TestRedis_IncrBy(t *testing.T) {
 	_, err := store.Incrby("a", 2)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.Incrby("a", 2)
 		assert.Nil(t, err)
 		assert.Equal(t, int64(2), val)
@@ -267,7 +267,7 @@ func TestRedis_List(t *testing.T) {
 	_, err = store.Lindex("key", 0)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.Lpush("key", "value1", "value2")
 		assert.Nil(t, err)
 		assert.Equal(t, 2, val)
@@ -316,7 +316,7 @@ func TestRedis_Persist(t *testing.T) {
 	err = store.Expireat("key", time.Now().Unix()+5)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		ok, err := client.Persist("key")
 		assert.Nil(t, err)
 		assert.False(t, ok)
@@ -348,7 +348,7 @@ func TestRedis_Sscan(t *testing.T) {
 	_, err = store.Del(key)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		var list []string
 		for i := 0; i < 1550; i++ {
 			list = append(list, stringx.Randn(i))
@@ -390,7 +390,7 @@ func TestRedis_Set(t *testing.T) {
 	_, err = store.Spop("key")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		num, err := client.Sadd("key", 1, 2, 3, 4)
 		assert.Nil(t, err)
 		assert.Equal(t, 4, num)
@@ -434,7 +434,7 @@ func TestRedis_SetGetDel(t *testing.T) {
 	_, err = store.Del("hello")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		err := client.Set("hello", "world")
 		assert.Nil(t, err)
 		val, err := client.Get("hello")
@@ -457,7 +457,7 @@ func TestRedis_SetExNx(t *testing.T) {
 	_, err = store.SetnxEx("newhello", "newworld", 5)
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		err := client.Setex("hello", "world", 5)
 		assert.Nil(t, err)
 		ok, err := client.Setnx("hello", "newworld")
@@ -495,7 +495,7 @@ func TestRedis_Getset(t *testing.T) {
 	_, err := store.GetSet("hello", "world")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		val, err := client.GetSet("hello", "world")
 		assert.Nil(t, err)
 		assert.Equal(t, "", val)
@@ -524,7 +524,7 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
 	_, err = store.Hdel("key", "field")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
+	runOnCluster(func(client Store) {
 		err := client.Hset("key", "field", "value")
 		assert.Nil(t, err)
 		val, err := client.Hget("key", "field")
@@ -587,8 +587,8 @@ func TestRedis_SortedSet(t *testing.T) {
 	})
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(client Store) {
-		ok, err := client.Zadd("key", 1, "value1")
+	runOnCluster(func(client Store) {
+		ok, err := client.ZaddFloat("key", 1, "value1")
 		assert.Nil(t, err)
 		assert.True(t, ok)
 		ok, err = client.Zadd("key", 2, "value1")
@@ -724,7 +724,7 @@ func TestRedis_HyperLogLog(t *testing.T) {
 	_, err = store.Pfcount("key")
 	assert.NotNil(t, err)
 
-	runOnCluster(t, func(cluster Store) {
+	runOnCluster(func(cluster Store) {
 		ok, err := cluster.Pfadd("key", "value")
 		assert.Nil(t, err)
 		assert.True(t, ok)
@@ -734,7 +734,7 @@ func TestRedis_HyperLogLog(t *testing.T) {
 	})
 }
 
-func runOnCluster(t *testing.T, fn func(cluster Store)) {
+func runOnCluster(fn func(cluster Store)) {
 	s1.FlushAll()
 	s2.FlushAll()
 
diff --git a/core/stores/redis/redis_test.go b/core/stores/redis/redis_test.go
index fdf90e71..4f960909 100644
--- a/core/stores/redis/redis_test.go
+++ b/core/stores/redis/redis_test.go
@@ -810,7 +810,7 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
 
 func TestRedis_SortedSet(t *testing.T) {
 	runOnRedis(t, func(client *Redis) {
-		ok, err := client.Zadd("key", 1, "value1")
+		ok, err := client.ZaddFloat("key", 1, "value1")
 		assert.Nil(t, err)
 		assert.True(t, ok)
 		ok, err = client.Zadd("key", 2, "value1")
@@ -988,8 +988,8 @@ func TestRedis_SortedSet(t *testing.T) {
 		assert.Equal(t, 0, len(pairs))
 		_, err = New(client.Addr, badType()).Zrevrank("key", "value")
 		assert.NotNil(t, err)
-		client.Zadd("second", 2, "aa")
-		client.Zadd("third", 3, "bbb")
+		_, _ = client.Zadd("second", 2, "aa")
+		_, _ = client.Zadd("third", 3, "bbb")
 		val, err = client.Zunionstore("union", &ZStore{
 			Keys:      []string{"second", "third"},
 			Weights:   []float64{1, 2},
@@ -1176,7 +1176,7 @@ func runOnRedis(t *testing.T, fn func(client *Redis)) {
 		}
 
 		if client != nil {
-			client.Close()
+			_ = client.Close()
 		}
 	}()
 	fn(New(s.Addr()))
@@ -1198,7 +1198,7 @@ func runOnRedisTLS(t *testing.T, fn func(client *Redis)) {
 			t.Error(err)
 		}
 		if client != nil {
-			client.Close()
+			_ = client.Close()
 		}
 	}()
 	fn(New(s.Addr(), WithTLS()))
@@ -1214,6 +1214,6 @@ type mockedNode struct {
 	RedisNode
 }
 
-func (n mockedNode) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *red.StringSliceCmd {
+func (n mockedNode) BLPop(_ context.Context, _ time.Duration, _ ...string) *red.StringSliceCmd {
 	return red.NewStringSliceCmd(context.Background(), "foo", "bar")
 }