redis get top 5 from 2 sorted set - redis

I have 2 sorted set acting as ranking. I want to get the top 5 from the union between them. the scores are the same.
zadd rank1 1 aaa
zadd rank1 2 bbb
zadd rank1 3 ccc
zadd rank1 4 ddd
zadd rank2 1 aaa
zadd rank2 2 bbb
zadd rank2 3 ccc
zadd rank2 4 ddd
What is the best approach to do so?
ZUINON 10 rank1 rank2 AGGREGATE MAX 5
I would assume something like that, but max 5 doesn't exists.
EDIT
Just figured out that even ZUNION wouldn't help as my redis version is 6.0.5 and not 6.2.0

my sorted sets are huge - i.e million of keys in each set. plus, this union will happen a lot of times per sec (it is the top query in my site). is this the fastest approach?
Just take the top 5 from each SortedSet and choose top 5 among those 10 elements at your server(/client) process. This would be the fastest and least complex for your scenario.
You can get top N elements from one SortedSet using ZREVRANGE command. But to unify/merge 2xN elements and choose top M elements, you would also require the respective scores of those elements. ZREVRANGE command with WITHSCORES keyword returns top N elements with their scores.
ZREVRANGE rank1 0 4 WITHSCORES
ZREVRANGE rank2 0 4 WITHSCORES

It depends on what you would like to do with the scores of the same key. For the two 'aaa', do you want to add them (aggregate) and then get the top five aggregated result?
You can use ZUNIONSTORE, with the option of storing the temporary result somewhere else, and then get the top five result. (supports Redis version below 6)
To do this atomically, you'll need a lua script to combine the following two commands
ZUNIONSTORE out 2 rank1 rank2 --(the temporary result is stored in a zset called 'out')
ZREVRANGE out 0 4 ---- (ranking from highest to lowest, get top five)

Related

Reduce two columns of IDs with a many to many relationship

I have a dataset with two columns of non-unique IDs (ID-A and ID-B respectively).
A single ID-A can have multiple ID-Bs and vice versa. I am trying to generate a third, set identifier using transitivity (calling it ID-C) which is set to the same value for all records with either ID-A or ID-B in common. Two records having neither ID-A nor ID-B in common should only share an ID-C set identifier if there is a transitive chain of records between them.
To visualize, I have something like the first two columns, and want to generate the third column(ID-C)
ID-A ID-B ID-C
1 1 1
1 2 1
1 3 1
2 2 1
2 4 1
3 4 1
4 5 2
5 5 2
5 6 2
6 7 3
I am using Presto SQL inside of AWS Athena, so I cannot use any variables or loops, that I am aware of.

Inner Joins in redis

I have a problem that is very similar to one mentioned here...
https://grokbase.com/t/gg/redis-db/123wv39cnt/filtering-zset-by-hash-field-value
But I am not able to understand the answer provided in that thread.
zadd scores 1.0 mary
zadd scores 2.0 sue
zadd scores 3.0 bob
zadd scores 4.0 bruce
zadd scores 5.0 maggie
sadd females mary sue maggie
zinterstore femscores 2 scores females
zrange femscores 0 50 withscores
How are the values 2, 3 and 6 calculated?
1) "mary"
2) "2"
3) "sue"
4) "3"
5) "maggie"
6) "6"
It is the result of the default WEIGHTS and AGGREGATE as described in ZUNIONSTORE.
Default WEIGHTS is 1, default AGGREGATE is SUM, so you are seeing the scores incremented by 1.
If you want the scores score without modification, simply set to zero the weight for females:
ZINTERSTORE femscores 2 scores females WEIGHTS 1 0

ZREVRANK 'fair' ranking in REDIS

When I have a sorted set with scores, I'd like to have the right rank even when multiple items have the same score.
For instance, when there are 5 items with scores: 1, 2, 2, 2, 3, I'd like to have those three central items to have the same rank (1), while the highest score gets rank 0 (with ZREVRANGE), and the lowest gets rank 4.
I see that it's possible to query the amount of keys with the same score somewhat efficiently O(log(N)), but it looks like if I want to have the scores as I want them, I'd have to use zscan, which is O(N).
Edit: add complete example based on the accepted solution
Our dataset is a sorted set with scores. For example: a has score 1, b, c and d have score 2, and e has score 3:
127.0.0.1:6379> zadd aset 1 a
(integer) 1
127.0.0.1:6379> zadd aset 2 b
(integer) 1
127.0.0.1:6379> zadd aset 2 c
(integer) 1
127.0.0.1:6379> zadd aset 2 d
(integer) 1
127.0.0.1:6379> zadd aset 3 e
(integer) 1
ZREVRANK works for those items with a unique score:
127.0.0.1:6379> zrevrank aset a
(integer) 4
127.0.0.1:6379> zrevrank aset e
(integer) 0
But it fails for those items with the same score:
127.0.0.1:6379> zrevrank aset b
(integer) 3
127.0.0.1:6379> zrevrank aset c
(integer) 2
127.0.0.1:6379> zrevrank aset d
(integer) 1
To solve that, first get the score with ZSCORE:
127.0.0.1:6379> zscore aset c
"2"
The other items have the same score, of course:
127.0.0.1:6379> zscore aset b
"2"
127.0.0.1:6379> zscore aset d
"2"
To get their rank, just use ZCOUNT with the score:
127.0.0.1:6379> zcount aset (2 +inf
(integer) 1
This also works for those items that have a unique score:
127.0.0.1:6379> zcount aset (1 +inf
(integer) 4
127.0.0.1:6379> zcount aset (3 +inf
(integer) 0
Writing this as an atomic lua script is left as an exercise for the reader.
For a given item with score x, you can determine its rank in O(log(N)) time with ZCOUNT (X +inf.
Exactly how you make use of that will depend on the details of your implementation.
ZREVRANGEBYLEX could be used in this case. The time complexity in this case would be O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. Please look at ZRANGEBYLEX for syntax.
Lex family of sorted set commands allow you to specify lexicographical ordering for keys with same values.

Find the upper bound or lower bound of the specific number in Redis

For example,I have a set in Redis
5 7 11 15 19 2 1
I want to find the upper bound or lower bound of 12 in Redis.
They are 15 and 11 in this example.
How can I do it efficiently.
I can use set or ordered set
Thanks!
I can think of two ways, neither of them perfect.
if they are all in a set, you can check ($r->sIsMember()) for that number 12 and then iteratively walk up and down until a match is found for each. This is not great, and I would suggest a LUA script to avoid a ton back-and-forth if you go that route.
second, put them in a sorted set as scores to a primary key of sorts. Then you will zRangeByScore() and get the zRank() of that member of the sorted set, and then zRange() to get the value before and after. I will do that here:
add them to a sorted set:
zadd mysset 5 "one"
zadd mysset 7 "two"
zadd mysset 11 "three"
zadd mysset 15 "four"
zadd mysset 19 "five"
zadd mysset 2 "six"
zadd mysset 1 "seven"
add one on the fly for reference
zadd mysset 12 "center"
now get the rank of that reference:
zRank mysset "center"
// 6 in this case
now get the range of the one above and below withscores:
zRange mysset 7 7 WITHSCORES
zRange mysset 5 5 WITHSCORES
// "four" 15
// "three" 11
good luck, have fun!
edit: oh yeh, remove the reference number:
zrem mysset "center"

How can Redis sort according to two different sorted sets?

I have two different sorted sets.
One is for editor ID:
article_id editor_id
101 10
102 11
103 10
104 10
The other sorted set is for date sorting:
article_id day
101 29
102 27
103 25
104 27
I want to merge these sets which shows first editor second day sorted state.
Which commands should I use?
Assuming that article_id is your members' value and that editor_id/day are the scores in the respective Sorted Set, and assuming each article_id is present in both Sorted Sets, you can do the following:
ZINTERSTORE t 2 k1 k2 WEIGHTS 100 1 AGGREGATE SUM
Explanation:
t is a temporary key that will hold the result
k1 is the Sorted Set that stores the editor_id
k2 is the Sorted Set that stores the day
the weight 100 multiplies editor_id by 100 (i.e. "shifts" it two places to the right)
the AGGREGATE SUM results in the following score: editor_id * 100 + day
Notes:
you can use ZUNIONSTORE instead for the same result
the use of weight 100 assumes that day is a 2-digit value