Redis zrange and zrangebyscore - redis

Are these 2 Redis commands different, except for the second having optional LIMIT argument?
http://redis.io/commands/zrange
http://redis.io/commands/zrangebyscore

They are different:
ZRANGE key start stop ...: start and stop are zero-based indexes (i.e they correspond to a position of an element within the sorted set),
ZRANGEBYSCORE key min max ...: min and max refer to scores (i.e they are used to specify a score range).
So, the first one operates by indexes while the second one (as its name implies) operates by scores. Thus they are used for different purposes.

say, the sorted set is:
value score
tom 0
bob 1
alice 100
lucy 102
when you use zrangebyscores, and the max score is 2, the min score is 0, then, you will get tom and bob;
when you use zrange, and the start is 0, the stop is 2, then you will get tom, bob and alice.
127.0.0.1:6379> zadd example 0 tom 1 bob 100 alice 102 lucy
127.0.0.1:6379> zrange example 0 2 WITHSCORES
1) "tom"
2) "0"
3) "bob"
4) "1"
5) "alice"
6) "100"
127.0.0.1:6379> zrangebyscore example 0 2 WITHSCORES
1) "tom"
2) "0"
3) "bob"
4) "1"
this is the key difference.

Related

Redis sort by score and date

I'm trying to strategies how to calculate position/rank based on score and date
I have a two pieces of data with the same score shown in the image below.
If i run the command
$ZADD usersScores 11 userId6
It will add it above userId5 which is not something I want
Is it possible to add it below userId1 maybe based on date?
This is my goal essentially where i would like to show the position based on the amount and the oldest would be ontop via date
Thanks!
Sorted Set ordering
Sorted sets are ordered by their scores first, and then lexigraphically by their members if the scores are equivalent. So in your case if you added the following keys:
127.0.0.1:6379> ZADD myset 11 user1
(integer) 1
127.0.0.1:6379> ZADD myset 11 user5
(integer) 1
127.0.0.1:6379> ZADD myset 24 user3
(integer) 1
127.0.0.1:6379> zadd myset 54 user2
(integer) 1
127.0.0.1:6379> zadd myset 11 user6
(integer) 1
and then did a ZRANGE, you'd get them in ascending order with the members ordered in alphabetical order:
127.0.0.1:6379> zrange myset 0 -1 WITHSCORES
1) "user1"
2) "11"
3) "user5"
4) "11"
5) "user6"
6) "11"
7) "user3"
8) "24"
9) "user2"
10) "54"
What it looks like, is your GUI is using, or are just passing in the rev argument to ZRANGE under the hood, which orders everything in reverse (scores descending, reverse alphabetical)
127.0.0.1:6379> ZRANGE myset 0 -1 WITHSCORES REV
1) "user2"
2) "54"
3) "user3"
4) "24"
5) "user6"
6) "11"
7) "user5"
8) "11"
9) "user1"
10) "11"
Second sort-by
There are a couple of ways you could handle the second sort by with a sorted set, let's assume you wanted to do this with a 32 bit unix timestamp
You could prepend the second sortby field to the member name so if user6's score came in at 1659579650, you'd just make the member name be 1659579650:user1, then when it orders it, it would do so lexigraphically
You could combine the score with the second, since 11 is the score, we could shift 11 32 bits to the left and add/or the timestamp to it, which comes out to 48,904,219,906, those will then order correctly, and then when you want to find the score, all you need to do is right-shift the score by 32 bits.

Redis get member where score is between min and max

I have a table in sql with 3 columns: BIGINT StartNumber, BIGINT EndNumber, BIGINT LocationId, and I need to be able to do something like this
Select LocationId where StartNumber < #number and EndNumber > #number.
for example:
StartNumber EndNumber LocationId
1 5 1
6 9 1
10 16 2
and when I have #number = 7 I should get LocationId = 1
How can I do this in redis?
I was thinking to move this table to redis, use sorted set and ZRANGEBYSCORE but it did't work for me:
1) When I am using ZADD key score member [score] [member], I am unable to add 2 elements with the same member and different score even with nx parameter:
zadd myset nx 1 "17" 2 "17" - it will add one element and then update its score instead of adding two elements.
2) when I am adding this: zadd set1 2 "a" 4 "b" 6 "c" 10 "d" and then trying to do zrangebyscore set1 3 3 (want to get member whose score include 3) I em getting empty result
P.s. All commands are executed on the example pages of redis website.
So as I understood the task, you don't have overlaps and each interval maps to only one location (?) and intervals don't have gaps. Based on this you can use only one sorted list with lower (or upper) bound values:
ZADD StartNumber 1 "1:5:1" 6 "6:9:1" 10 "10:16:2"
Then you can use:
ZREVRANGEBYSCORE StartNumber 7 -inf LIMIT 0 1
And it will be O(log(N)).
Put differently, your question is "how can I map N ranges of numbers to a location". One way of doing this is using two Sorted Sets, one for the StartNumber and the other one for EndNumber. Since members have to be unique, we'll also need to ensure that by using the Start/End values as part of the member. For example, with your example data, this could be done like so:
ZADD StartNumber 1 "1:5:1" 6 "6:9:1" 10 "10:16:2"
ZADD EndNumber 5 "1:5:1" 9 "6:9:1" 16 "10:16:2"
To find the location for #number=7, do ZRANGEBYSCORE StartNumber -inf 7 and ZRANGEBYSCORE EndNumber 7 +inf and intersect the results. All that remains is to split the intesect's result(s) on the colon (:) and use the 3rd element as the location.
Note: if your app ensures that there are no overlapping ranges and that there can be only one location per "number", you can get the same results with only one set.
(this is the first time that I'm giving two answers to the same question - maybe I'll get a badge or sumthin' ;))
The double Sorted Set approach is a generalization and, as such, aims to solve a bigger set of problems than what the OP needs (as put in the comments to the first answer). That approach is also not effective as the query is O(logn)+O(N) so when N is large (e.g. 5M) that's probably not a good idea.
However, to satisfy the requirements and given that the ranges do not overlap, one could actually use only a single Sorted Set and a simpler query. The set's members should be added by concatenating the EndNumber and LocationId and the their scores should be set to their respective StartNumber, so for the sake of the example:
ZADD ranges 1 "5:1" 6 "9:1" 10 "16:2"
Given #number, obtain the relevant LocationId with the following Redis Lua code (O(logn)):
-- rangelookup.lua
-- http://stackoverflow.com/questions/32185898/redis-get-member-where-score-is-between-min-and-max/32186675
-- A **non inclusive** range search on a Sorted Set with the following data:
-- score = <StartNumber>
-- member = <EndNumber>:<LocationId>
--
-- KEYS[1] - Sorted Set key name
-- ARGV[1] - the number to search
--
-- reply - the relevant id, nil if range doesn't exist
--
-- usage example: redis-cli --eval rangelookup.lua ranges , 7
local number = tonumber(ARGV[1])
local data = redis.call('ZREVRANGEBYSCORE', KEYS[1], number, '-inf', 'WITHSCORES', 'LIMIT', 0, 1)
local reply = nil
if data ~= nil and number > tonumber(data[2]) then
local to, id = data[1]:match( '(.*):(.*)' )
if tonumber(to) > number then
reply = id
end
end
return reply
Sample output:
$ redis-cli --eval rangelookup.lua ranges , 7
"1"
$ redis-cli --eval rangelookup.lua ranges , 9
(nil)
$ redis-cli --eval rangelookup.lua ranges , 99
(nil)

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,

Redis - how to sort by hash field in redis as opposed to key?

Let's suppose I want have redis hash a = {1:10, 2:15, 3:5, 4:0, 5:20}, and a set b = (5,3,4). I want to get a list containing elements from b, sorted by values of a[b] (result in this case is [4,3,5]).
When I try to do this, it doesn't work well.
redis 127.0.0.1:6379> hmset a 1 10 2 15 3 5 4 0 5 20
redis 127.0.0.1:6379> sadd b 5 3 4
redis 127.0.0.1:6379> sort b by a->*
1) "3"
2) "4"
3) "5"
Obviously, asterisk in hash field placeholder doesn't work. Are there other ways beside declaring a:1-a:5 to do this task by means of Redis?
P.S. This is not a duplicate of Redis : How can I sort my hash by keys?, as that question clearly discusses the a:* approach.
this is a know issue: link
you could do the following:
redis 127.0.0.1:6379> sadd b 5 3 4
redis 127.0.0.1:6379> zadd a 10 1 15 2 5 3 0 4 20 5
redis 127.0.0.1:6379> zinterstore result 2 a b
redis 127.0.0.1:6379> zrange result 0 -1
1) "4"
2) "3"
3) "5"
Maybe you can model it using sorted sets instead? Use the values as score, and the keys as members. Sorted sets are more or less like hashes sorted by value. I'd love to give you an example, but I'm not sure exactly what the problem you're trying to solve is. If you could elaborate maybe I could help.