Why I cannot use ZRANGEBYLEX on this sorted set in redis? - redis

In the redis documentation, there is this example of ZRANGEBYLEX
ZADD myindex 0 0056:0028.44:90
ZADD myindex 0 0034:0011.00:832
ZRANGEBYLEX myindex [0056:0010.00 [0056:0030.00
1) "0056:0028.44:90"
It is very straightforward.
However if I want to apply the same technique to the following example,
127.0.0.1:6379> zadd feedbacks 1 feedback1 2 feedback2 3 feedback3 1 feedback4
(integer) 4
127.0.0.1:6379> ZRANGEBYLEX feedbacks [feed [feed
(empty list or set)
I get an empty set.
I would expect to see the four values (feedback1 to feedback4)
Why ZRANGEBYLEX failed on my test sample?

It fails because they have different scores. ZRANGEBYLEX works only on same-score subsets.
See https://redis.io/commands/ZRANGEBYLEX
When all the elements in a sorted set are inserted with the same
score, in order to force lexicographical ordering, this command
returns all the elements in the sorted set at key with a value between
min and max.
If the elements in the sorted set have different scores, the returned
elements are unspecified.
Sorted sets have the property of being lexicographically ordered within same-score subsets. This gives them a second use-case, a lexicographically sorted set, but in this case, you add all elements with the same score.
So, you have to choose how to use your sorted set:
Sorted by score (with same-score sorted lex, for predictable order)
Sorted lexicographically, all elements given the same score
You cannot have both. You'd need two sorted sets then.

Related

Redis - ZRANGEBYSCORE with key matching a regex

I'm trying to get the value of the best key in a sorted set.
This is my query at the moment:
ZREVRANGEBYSCORE genre1 +inf -inf WITHSCORES LIMIT 0 1
This is an example of an add in my set:
ZADD "genre1|genre2|genre3" 3.25153 "film"
I'd like to use the query in a way like this
ZREVRANGEBYSCORE *genre1* +inf -inf WITHSCORES LIMIT 0 1
to match keys containing "...|genre1|..." and not only keys like "genre1".
Any help will be appreciated
This can be accomplished in two or three steps:
1) Use SCAN or KEYS to find the keys matching your pattern.
SCAN 0 MATCH "*genre1*"
1) "9"
2) 1) "genre1|genre2|genre3"
2) "genre1|genre4"
2) For each key, use TYPE to test if it is a Sorted Set. This is only important if you may have other genre1 keys on the db
TYPE "genre1|genre4"
zset
3) Run your ZREVRANGEBYSCORE <key> +inf -inf WITHSCORES LIMIT 0 1 for each key.
See this answer on how you can SCAN for a given type. You can modify the Lua script to include the ZREVRANGEBYSCORE and get your results atomically on a single call.
Finally, consider reviewing if storing the genre combinations is optimal in your case. You may use a sorted set per genre, and then use ZUNIONSTORE or ZINTERSTORE to get scored combinations.

Redis sorted set leader board ranking on same score

I'm using Redis sorted set to implement the leaderboard of my game, where I show the user ranking in descending order. I'm stuck in a case where two or more users have the same score. So in this case, I want the higher ranking of the user who gets the score first. For example, I'm adding the following entries in Redis.
127.0.0.1:6379> zadd testing-key 5 a
(integer) 1
127.0.0.1:6379> zadd testing-key 4 b
(integer) 1
127.0.0.1:6379> zadd testing-key 5 c
(integer) 1
and when I'm querying for the rank in reverse order, I'm getting this
127.0.0.1:6379> zrevrange testing-key 0 10
1) "c"
2) "a"
3) "b"
but in my case, the ranking should be like
1) "a"
2) "c"
3) "b"
So is there any provision in Redis to give higher precedence to the entity which entered first in the set with the same score?
I found one solution to this problem. In my case, the score is an integer so I converted it into decimal and added Long.MAX_VALUE - System.nanoTime() after decimal. So the final score code will be like
double finalScore = score.(Long.MAX_VALUE - System.nanoTime());
So the final score of the player who scored first would be higher than the second one. Please let me know if you have any better solution.
If your leaderboard's scores are "small" enough, you may get away with using a combination of the score and the timestamp (e.g. 123.111455234, where 123 is the score). However, since the Sorted Set score is a double floating point, you may lose precision.
Alternatively, keep two Sorted Sets - one with each player's leaderboard score and the other with each player's score timestamp, and use both to determine the order.
Or, use a single sorted set for the leader board, encode the timestamp as part of the member and rely on lexicographical ordering.

How to get DIFF on sorted set

How do I get most weighted elements from a sorted set, but excluding those found in another set(or list or hash).
>zadd all 1 one
>zadd all 2 two
>zadd all 3 three
>sadd disabled 2
>sdiff all disabled
(error) WRONGTYPE Operation against a key holding the wrong kind of value
Is my only option is to get elements from the sorted set one-by-one and compare to the list of "disabled" items? Wouldn't that be very slow because of so many transactions to a server?
What is the approach here?
Note: I assume you've meant sadd disabled two
As you've found out, SDIFF does not operate on sorted sets - that is because defining the difference between sorted sets isn't trivial.
What you could do is first create a temporary set with ZUNIONSTORE and set the intersect's scores to 0. Then do a range excluding the 0, e.g.:
127.0.0.1:6379> ZADD all 1 one 2 two 3 three
(integer) 3
127.0.0.1:6379> SADD disabled two
(integer) 1
127.0.0.1:6379> ZUNIONSTORE tmp 2 all disabled WEIGHTS 1 0 AGGREGATE MIN
(integer) 3
127.0.0.1:6379> ZREVRANGEBYSCORE tmp +inf 1 WITHSCORES
1) "three"
2) "3"
3) "one"
4) "1"

ordered sets in redis: random output in case of score ties

I have an ordered set in Redis (I am actually using a python client https://github.com/andymccurdy/redis-py), for example:
zadd myset 1 key1
zadd myset 1 key2
zadd myset 1 key3
zadd myset 0 key4
Note that 3 keys have the same score.
Using ZRANGE, i would like to get the top 2 entries (i.e lowest scores). "key4" will always be the first result as it has a lower value, but I would like the second return value to be randomly selected between the ties: key1,key2,key3. ZRANGE actually returns the keys in the order they are indexed: "keys1" is always my second result:
zrange myset 0 -1 WITHSCORES
1) "key4"
2) "0"
3) "key1"
4) "1"
5) "key2"
6) "1"
7) "key3"
8) "1"
any idea?
thanks,
J.
As kindly requested by Linus G Thiel, here are more details about my usecase:
I would like to use zsets to perform a simple ranking system. I have a list of items, for each one a score representing the relevance of the item. For the cold start of my system, most of the scores will be identical (i.e 0), and I would like to randomly select among the items having the same score. Otherwise I will always return the exact same lexicographic ordering, which will introduce a bias in the system.
The solution you propose, using one specific set for each duplicated score value will work. I will give it a try.
Thanks,

Get all members in Sorted Set

I have a Sorted set and want to get all members of set. How to identify a max/min score for command :
zrange key min max
?
You're in luck, as zrange does not take scores, but indices. 0 is the first index, and -1 will be interpreted as the last index:
zrange key 0 -1
To get a range by score, you would call zrangebyscore instead -- where -inf and +inf can be used to denote negative and positive infinity, respectively, as Didier Spezia notes in his comment:
zrangebyscore key -inf +inf
Starting with Redis 6.2.0,
To get all the keys and its value together in a single query using the below,
zrange <KEY> 0 -1 WITHSCORES
The optional WITHSCORES argument supplements the command's reply with the scores of elements returned. The returned list contains value1,score1,...,valueN,scoreN instead of value1,...,valueN. Client libraries can return a more appropriate data type (suggestion: an array with (value, score) arrays/tuples).
In newer versions of redis (>= v6.2.0), if you want to get all members of a sorted set between two scores, you should use:
ZRANGE key min max BYSCORE
Adding the BYSCORE option makes redis treat the min & max arguments as scores rather than indices.
(As of this writing, ZRANGEBYSCORE still works, but is considered deprecated.)