skip to Main Content

I need to store the lowest score for each key I add to the set, but when I do ZADD, Redis overwrites the score with the new value even if the score is higher.

ZADD myorderset 1 'one' 2 'two' 3 'three'
(integer) 3

ZRANGE myorderset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"

ZADD myorderset 5 'three'
(integer) 0

ZRANGE myorderset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "5"

In the example case, I need the key ‘three’ not be updated since the new score is higher (5) than the existing (3). Is there a way to do this natively or do I need to create a script in Lua?

I’ve been researching ZADD modifiers (XX, NX, CH) but none of them do what I need.

Thank you very much!

2

Answers


  1. There is no single command or command option to do it both. You can either use combination of ZSCORE with ZADD(in lua). Alternatively("It is/looks like over-engineered") you may use ZUNIONSTORE with the aggregate option MIN.

    With the AGGREGATE option, it is possible to specify how the results of the union are aggregated. This option defaults to SUM, where the score of an element is summed across the inputs where it exists. When this option is set to either MIN or MAX, the resulting set will contain the minimum or maximum score of an element across the inputs where it exists.

    127.0.0.1:6379> ZADD base 1 a 2 b 5 c
    (integer) 3
    127.0.0.1:6379> ZADD new 3 c
    (integer) 1
    127.0.0.1:6379> ZUNIONSTORE base 2 base new AGGREGATE MIN
    (integer) 3
    127.0.0.1:6379> DEL new
    (integer) 1
    127.0.0.1:6379> ZRANGE base 0 -1 WITHSCORES
    1) "a"
    2) "1"
    3) "b"
    4) "2"
    5) "c"
    6) "3"
    127.0.0.1:6379> ZADD new 5 b
    (integer) 1
    127.0.0.1:6379> ZUNIONSTORE base 2 base new AGGREGATE MIN
    (integer) 3
    127.0.0.1:6379> DEL new
    (integer) 1
    127.0.0.1:6379> ZRANGE base 0 -1 WITHSCORES
    1) "a"
    2) "1"
    3) "b"
    4) "2"
    5) "c"
    6) "3"
    127.0.0.1:6379>
    

    If you prefer,

    • You may generate new set name with random string in application level
    • Put EXPIRE to this new set, no need to DEL the new key manually after the ZUNIONSTORE, it will be expired eventually.
    • It can be done in MULTI/EXEC in a single transaction.
    Login or Signup to reply.
  2. A Lua script for this CAS use case would be the simplest and idiomatic solution:

    127.0.0.1:6379> ZADD myorderset 1 'one' 2 'two' 3 'three'
    (integer) 3
    127.0.0.1:6379> EVAL "local s = redis.call('ZSCORE', KEYS[1], ARGV[2]) if not s or s > ARGV[1] then redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2]) end" 1 myorderset 5 'three'
    (nil)
    127.0.0.1:6379> ZRANGE myorderset 0 -1 WITHSCORES
    1) "one"
    2) "1"
    3) "two"
    4) "2"
    5) "three"
    6) "3"
    127.0.0.1:6379> EVAL "local s = redis.call('ZSCORE', KEYS[1], ARGV[2]) if not s or s > ARGV[1] then redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2]) end" 1 myorderset 2 'three'
    (nil)
    127.0.0.1:6379> ZRANGE myorderset 0 -1 WITHSCORES
    1) "one"
    2) "1"
    3) "three"
    4) "2"
    5) "two"
    6) "2"
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search