Repository: zeromicro/go-zero
Pull request #2689 (2022-12-12 01:42:21)

add zset withsocre float
add zset withsocre support  float

codecov-commenter: # [Codecov](https://codecov.io/gh/zeromicro/go-zero/pull/2689?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#2689](https://codecov.io/gh/zeromicro/go-zero/pull/2689?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (91b6c67) into [master](https://codecov.io/gh/zeromicro/go-zero/commit/74e0676617be0d0e2ecb028aa098dc350f77cef3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (74e0676) will **decrease** coverage by `0.08%`.
> The diff coverage is `74.63%`.


| [Impacted Files](https://codecov.io/gh/zeromicro/go-zero/pull/2689?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [core/stores/redis/redis.go](https://codecov.io/gh/zeromicro/go-zero/pull/2689?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y29yZS9zdG9yZXMvcmVkaXMvcmVkaXMuZ28=) | `86.78% <74.63%> (-1.22%)` | :arrow_down: |


Patch:
diff --git a/core/stores/redis/redis.go b/core/stores/redis/redis.go
index 199fcd14..3eba78a0 100644
--- a/core/stores/redis/redis.go
+++ b/core/stores/redis/redis.go
@@ -42,6 +42,10 @@ type (
 		Score int64
 	}
 
+	PairFloat struct {
+		Key   string
+		Score float64
+	}
 	// Redis defines a redis node/cluster. It is thread-safe.
 	Redis struct {
 		Addr string
@@ -786,6 +790,28 @@ func (s *Redis) HincrbyCtx(ctx context.Context, key, field string, increment int
 	return
 }
 
+// HincrbyFloat is the implementation of redis hincrby command.
+func (s *Redis) HincrbyFloat(key, field string, increment float64) (float64, error) {
+	return s.HincrbyFloatCtx(context.Background(), key, field, increment)
+}
+
+// HincrbyFloatCtx is the implementation of redis hincrby command.
+func (s *Redis) HincrbyFloatCtx(ctx context.Context, key, field string, increment float64) (val float64, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+		val, err = conn.HIncrByFloat(ctx, key, field, increment).Result()
+		if err != nil {
+			return err
+		}
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // Hkeys is the implementation of redis hkeys command.
 func (s *Redis) Hkeys(key string) ([]string, error) {
 	return s.HkeysCtx(context.Background(), key)
@@ -976,7 +1002,6 @@ func (s *Redis) IncrCtx(ctx context.Context, key string) (val int64, err error)
 
 	return
 }
-
 // Incrby is the implementation of redis incrby command.
 func (s *Redis) Incrby(key string, increment int64) (int64, error) {
 	return s.IncrbyCtx(context.Background(), key, increment)
@@ -996,7 +1021,25 @@ func (s *Redis) IncrbyCtx(ctx context.Context, key string, increment int64) (val
 
 	return
 }
+// Incrby is the implementation of redis incrby command.
+func (s *Redis) IncrbyFloat(key string, increment float64) (float64, error) {
+	return s.IncrbyFloatCtx(context.Background(), key, increment)
+}
 
+// IncrbyFloatCtx is the implementation of redis incrby command.
+func (s *Redis) IncrbyFloatCtx(ctx context.Context, key string, increment float64) (val float64, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		val, err = conn.IncrByFloat(ctx, key, increment).Result()
+		return err
+	}, acceptable)
+
+	return
+}
 // Keys is the implementation of redis keys command.
 func (s *Redis) Keys(pattern string) ([]string, error) {
 	return s.KeysCtx(context.Background(), pattern)
@@ -2017,6 +2060,25 @@ func (s *Redis) ZscoreCtx(ctx context.Context, key, value string) (val int64, er
 	return
 }
 
+// ZscoreByFloat is the implementation of redis zscore command score by float.
+func (s *Redis) ZscoreByFloat(key, value string) (float64, error) {
+	return s.ZscoreByFloatCtx(context.Background(), key, value)
+}
+
+// ZscoreByFloatCtx is the implementation of redis zscore command score by float.
+func (s *Redis) ZscoreByFloatCtx(ctx context.Context, key, value string) (val float64, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+		val, err = conn.ZScore(ctx, key, value).Result()
+		return err
+	}, acceptable)
+
+	return
+}
+
 // Zscan is the implementation of redis zscan command.
 func (s *Redis) Zscan(key string, cursor uint64, match string, count int64) (
 	keys []string, cur uint64, err error) {
@@ -2184,6 +2246,32 @@ func (s *Redis) ZrangeWithScoresCtx(ctx context.Context, key string, start, stop
 	return
 }
 
+// ZrangeWithScoresByFloat is the implementation of redis zrange command with scores by float64.
+func (s *Redis) ZrangeWithScoresByFloat(key string, start, stop int64) ([]PairFloat, error) {
+	return s.ZrangeWithScoresByFloatCtx(context.Background(), key, start, stop)
+}
+
+// ZrangeWithScoresByFloatCtx is the implementation of redis zrange command with scores by float64.
+func (s *Redis) ZrangeWithScoresByFloatCtx(ctx context.Context, key string, start, stop int64) (
+	val []PairFloat, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		v, err := conn.ZRangeWithScores(ctx, key, start, stop).Result()
+		if err != nil {
+			return err
+		}
+
+		val = toPairsByFloat(v)
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // ZRevRangeWithScores is the implementation of redis zrevrange command with scores.
 func (s *Redis) ZRevRangeWithScores(key string, start, stop int64) ([]Pair, error) {
 	return s.ZRevRangeWithScoresCtx(context.Background(), key, start, stop)
@@ -2210,6 +2298,32 @@ func (s *Redis) ZRevRangeWithScoresCtx(ctx context.Context, key string, start, s
 	return
 }
 
+// ZRevRangeWithScoresByFloat is the implementation of redis zrevrange command with scores by float.
+func (s *Redis) ZRevRangeWithScoresByFloat(key string, start, stop int64) ([]PairFloat, error) {
+	return s.ZRevRangeWithScoresByFloatCtx(context.Background(), key, start, stop)
+}
+
+// ZRevRangeWithScoresByFloatCtx is the implementation of redis zrevrange command with scores by float.
+func (s *Redis) ZRevRangeWithScoresByFloatCtx(ctx context.Context, key string, start, stop int64) (
+	val []PairFloat, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		v, err := conn.ZRevRangeWithScores(ctx, key, start, stop).Result()
+		if err != nil {
+			return err
+		}
+
+		val = toPairsByFloat(v)
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // ZrangebyscoreWithScores is the implementation of redis zrangebyscore command with scores.
 func (s *Redis) ZrangebyscoreWithScores(key string, start, stop int64) ([]Pair, error) {
 	return s.ZrangebyscoreWithScoresCtx(context.Background(), key, start, stop)
@@ -2239,6 +2353,35 @@ func (s *Redis) ZrangebyscoreWithScoresCtx(ctx context.Context, key string, star
 	return
 }
 
+// ZrangebyscoreWithScoresByFloat is the implementation of redis zrangebyscore command with scores by float.
+func (s *Redis) ZrangebyscoreWithScoresByFloat(key string, start, stop float64) ([]PairFloat, error) {
+	return s.ZrangebyscoreWithScoresByFloatCtx(context.Background(), key, start, stop)
+}
+
+// ZrangebyscoreWithScoresByFloatCtx is the implementation of redis zrangebyscore command with scores by float.
+func (s *Redis) ZrangebyscoreWithScoresByFloatCtx(ctx context.Context, key string, start, stop float64) (
+	val []PairFloat, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{
+			Min: fmt.Sprintf("%v", start),
+			Max: fmt.Sprintf("%v", stop),
+		}).Result()
+		if err != nil {
+			return err
+		}
+
+		val = toPairsByFloat(v)
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // ZrangebyscoreWithScoresAndLimit is the implementation of redis zrangebyscore command
 // with scores and limit.
 func (s *Redis) ZrangebyscoreWithScoresAndLimit(key string, start, stop int64,
@@ -2277,6 +2420,44 @@ func (s *Redis) ZrangebyscoreWithScoresAndLimitCtx(ctx context.Context, key stri
 	return
 }
 
+// ZrangebyscoreWithScoresByFloatAndLimit is the implementation of redis zrangebyscore command
+// with scores by float and limit.
+func (s *Redis) ZrangebyscoreWithScoresByFloatAndLimit(key string, start, stop float64,
+	page, size int) ([]PairFloat, error) {
+	return s.ZrangebyscoreWithScoresByFloatAndLimitCtx(context.Background(), key, start, stop, page, size)
+}
+
+// ZrangebyscoreWithScoresByFloatAndLimitCtx is the implementation of redis zrangebyscore command
+// with scores by float and limit.
+func (s *Redis) ZrangebyscoreWithScoresByFloatAndLimitCtx(ctx context.Context, key string, start,
+	stop float64, page, size int) (val []PairFloat, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		if size <= 0 {
+			return nil
+		}
+
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		v, err := conn.ZRangeByScoreWithScores(ctx, key, &red.ZRangeBy{
+			Min:    fmt.Sprintf("%v", start),
+			Max:    fmt.Sprintf("%v", stop),
+			Offset: int64(page * size),
+			Count:  int64(size),
+		}).Result()
+		if err != nil {
+			return err
+		}
+
+		val = toPairsByFloat(v)
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // Zrevrange is the implementation of redis zrevrange command.
 func (s *Redis) Zrevrange(key string, start, stop int64) ([]string, error) {
 	return s.ZrevrangeCtx(context.Background(), key, start, stop)
@@ -2327,6 +2508,35 @@ func (s *Redis) ZrevrangebyscoreWithScoresCtx(ctx context.Context, key string, s
 	return
 }
 
+// ZrevrangebyscoreWithScoresByFloat is the implementation of redis zrevrangebyscore command with scores by float.
+func (s *Redis) ZrevrangebyscoreWithScoresByFloat(key string, start, stop float64) ([]PairFloat, error) {
+	return s.ZrevrangebyscoreWithScoresByFloatCtx(context.Background(), key, start, stop)
+}
+
+// ZrevrangebyscoreWithScoresByFloatCtx is the implementation of redis zrevrangebyscore command with scores by float.
+func (s *Redis) ZrevrangebyscoreWithScoresByFloatCtx(ctx context.Context, key string, start, stop float64) (
+	val []PairFloat, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{
+			Min: fmt.Sprintf("%v", start),
+			Max: fmt.Sprintf("%v", stop),
+		}).Result()
+		if err != nil {
+			return err
+		}
+
+		val = toPairsByFloat(v)
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // ZrevrangebyscoreWithScoresAndLimit is the implementation of redis zrevrangebyscore command
 // with scores and limit.
 func (s *Redis) ZrevrangebyscoreWithScoresAndLimit(key string, start, stop int64,
@@ -2365,6 +2575,44 @@ func (s *Redis) ZrevrangebyscoreWithScoresAndLimitCtx(ctx context.Context, key s
 	return
 }
 
+// ZrevrangebyscoreWithScoresByFloatAndLimit is the implementation of redis zrevrangebyscore command
+// with scores by float and limit.
+func (s *Redis) ZrevrangebyscoreWithScoresByFloatAndLimit(key string, start, stop float64,
+	page, size int) ([]PairFloat, error) {
+	return s.ZrevrangebyscoreWithScoresByFloatAndLimitCtx(context.Background(), key, start, stop, page, size)
+}
+
+// ZrevrangebyscoreWithScoresByFloatAndLimitCtx is the implementation of redis zrevrangebyscore command
+// with scores by float and limit.
+func (s *Redis) ZrevrangebyscoreWithScoresByFloatAndLimitCtx(ctx context.Context, key string,
+	start, stop float64, page, size int) (val []PairFloat, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		if size <= 0 {
+			return nil
+		}
+
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		v, err := conn.ZRevRangeByScoreWithScores(ctx, key, &red.ZRangeBy{
+			Min:    fmt.Sprintf("%v", start),
+			Max:    fmt.Sprintf("%v", stop),
+			Offset: int64(page * size),
+			Count:  int64(size),
+		}).Result()
+		if err != nil {
+			return err
+		}
+
+		val = toPairsByFloat(v)
+		return nil
+	}, acceptable)
+
+	return
+}
+
 // Zrevrank is the implementation of redis zrevrank command.
 func (s *Redis) Zrevrank(key, field string) (int64, error) {
 	return s.ZrevrankCtx(context.Background(), key, field)
@@ -2465,7 +2713,24 @@ func toPairs(vals []red.Z) []Pair {
 	}
 	return pairs
 }
-
+func toPairsByFloat(vals []red.Z) []PairFloat {
+	pairs := make([]PairFloat, len(vals))
+	for i, val := range vals {
+		switch member := val.Member.(type) {
+		case string:
+			pairs[i] = PairFloat{
+				Key:   member,
+				Score: val.Score,
+			}
+		default:
+			pairs[i] = PairFloat{
+				Key:   mapping.Repr(val.Member),
+				Score: val.Score,
+			}
+		}
+	}
+	return pairs
+}
 func toStrings(vals []interface{}) []string {
 	ret := make([]string, len(vals))
 	for i, val := range vals {


Test patch:
diff --git a/core/stores/redis/redis_test.go b/core/stores/redis/redis_test.go
index 8460481f..3e428655 100644
--- a/core/stores/redis/redis_test.go
+++ b/core/stores/redis/redis_test.go
@@ -1008,6 +1008,111 @@ func TestRedis_SortedSet(t *testing.T) {
 	})
 }
 
+func TestRedis_SortedSetByFloat64(t *testing.T) {
+	runOnRedis(t, func(client *Redis) {
+		ok, err := client.ZaddFloat("key", 10.345, "value1")
+		assert.Nil(t, err)
+		assert.True(t, ok)
+		val, err := client.ZscoreByFloat("key", "value1")
+		assert.Nil(t, err)
+		assert.Equal(t, 10.345, val)
+		_, _ = client.ZaddFloat("key", 10.346, "value2")
+		_, err = New(client.Addr, badType()).ZRevRangeWithScoresByFloat("key", 0, -1)
+		assert.NotNil(t, err)
+		pairs, err := client.ZRevRangeWithScoresByFloat("key", 0, -1)
+		assert.Nil(t, err)
+		assert.EqualValues(t, []PairFloat{
+			{
+				Key:   "value2",
+				Score: 10.346,
+			},
+			{
+				Key:   "value1",
+				Score: 10.345,
+			},
+		}, pairs)
+
+		pairs, err = client.ZrangeWithScoresByFloat("key", 0, -1)
+		assert.Nil(t, err)
+		assert.EqualValues(t, []PairFloat{
+			{
+				Key:   "value1",
+				Score: 10.345,
+			},
+			{
+				Key:   "value2",
+				Score: 10.346,
+			},
+		}, pairs)
+		_, err = New(client.Addr, badType()).ZrangebyscoreWithScoresByFloat("key", 0, 20)
+		assert.NotNil(t, err)
+		pairs, err = client.ZrangebyscoreWithScoresByFloat("key", 0,20)
+		assert.Nil(t, err)
+		assert.EqualValues(t, []PairFloat{
+			{
+				Key:   "value1",
+				Score: 10.345,
+			},
+			{
+				Key:   "value2",
+				Score: 10.346,
+			},
+		}, pairs)
+		_, err = New(client.Addr, badType()).ZrangebyscoreWithScoresByFloatAndLimit(
+			"key", 10.1, 12.2, 1, 1)
+		assert.NotNil(t, err)
+		pairs, err = client.ZrangebyscoreWithScoresByFloatAndLimit("key", 10.1, 12.2, 1, 1)
+		assert.Nil(t, err)
+		assert.EqualValues(t, []PairFloat{
+			{
+				Key:   "value2",
+				Score: 10.346,
+			},
+		}, pairs)
+		_, err = New(client.Addr, badType()).ZrevrangebyscoreWithScoresByFloat("key", 10, 12)
+		assert.NotNil(t, err)
+		pairs, err = client.ZrevrangebyscoreWithScoresByFloat("key", 10, 12)
+		assert.Nil(t, err)
+		assert.EqualValues(t, []PairFloat{
+			{
+				Key:   "value2",
+				Score: 10.346,
+			},
+			{
+				Key:   "value1",
+				Score: 10.345,
+			},
+		}, pairs)
+		_, err = New(client.Addr, badType()).ZrevrangebyscoreWithScoresByFloatAndLimit(
+			"key", 10, 12, 1, 1)
+		assert.NotNil(t, err)
+		pairs, err = client.ZrevrangebyscoreWithScoresByFloatAndLimit("key", 10, 12, 1, 1)
+		assert.Nil(t, err)
+		assert.EqualValues(t, []PairFloat{
+			{
+				Key:   "value1",
+				Score: 10.345,
+			},
+		}, pairs)
+
+	})
+}
+func TestRedis_IncrbyFloat(t *testing.T) {
+	runOnRedis(t, func(client *Redis) {
+		incrVal, err := client.IncrbyFloat("key", 0.002)
+		assert.Nil(t, err)
+		assert.Equal(t, 0.002,incrVal)
+		incrVal2, err := client.IncrbyFloat("key", -0.001)
+		assert.Nil(t, err)
+		assert.Equal(t, 0.001,incrVal2)
+		hincrVal ,err := client.HincrbyFloat("hkey","i",0.002)
+		assert.Nil(t, err)
+		assert.Equal(t, 0.002,hincrVal)
+		hincrVal2 ,err := client.HincrbyFloat("hkey","i",-0.001)
+		assert.Nil(t, err)
+		assert.Equal(t, 0.001,hincrVal2)
+	})
+}
 func TestRedis_Pipelined(t *testing.T) {
 	runOnRedis(t, func(client *Redis) {
 		assert.NotNil(t, New(client.Addr, badType()).Pipelined(func(pipeliner Pipeliner) error {