Skip to content

Commit

Permalink
Merge pull request #2541 from sav-norem/patch-1
Browse files Browse the repository at this point in the history
Update sorted-sets.md to use bike racing story and hugo short codes
  • Loading branch information
dmaier-redislabs authored Sep 20, 2023
2 parents 676f49d + 8224b5d commit 9e828fe
Showing 1 changed file with 106 additions and 139 deletions.
245 changes: 106 additions & 139 deletions docs/data-types/sorted-sets.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
---
title: "Redis sorted sets"
linkTitle: "Sorted sets"
weight: 50
Expand Down Expand Up @@ -29,27 +29,16 @@ represent sorted sets). They are ordered according to the following rule:
* If B and A are two elements with a different score, then A > B if A.score is > B.score.
* If B and A have exactly the same score, then A > B if the A string is lexicographically greater than the B string. B and A strings can't be equal since sorted sets only have unique elements.

Let's start with a simple example, adding a few selected hackers names as
sorted set elements, with their year of birth as "score".

> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer) 1
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1
Let's start with a simple example, we'll add all our racers and the score they got in the first race:

{{< clients-example ss_tutorial zadd >}}
> ZADD racer_scores 10 "Norem"
(integer) 1
> ZADD racer_scores 12 "Castilla"
(integer) 1
> ZADD racer_scores 8 "Sam-Bodden" 10 "Royce" 6 "Ford" 14 "Prickett"
(integer) 4
{{< /clients-example >}}


As you can see `ZADD` is similar to `SADD`, but takes one additional argument
Expand All @@ -64,93 +53,94 @@ Implementation note: Sorted sets are implemented via a
dual-ported data structure containing both a skip list and a hash table, so
every time we add an element Redis performs an O(log(N)) operation. That's
good, but when we ask for sorted elements Redis does not have to do any work at
all, it's already all sorted:

> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"
all, it's already sorted. Note that the `ZRANGE` order is low to high, while the `ZREVRANGE` order is high to low:

{{< clients-example ss_tutorial zrange >}}
> ZRANGE racer_scores 0 -1
1) "Ford"
2) "Sam-Bodden"
3) "Norem"
4) "Royce"
5) "Castilla"
6) "Prickett"
> ZREVRANGE racer_scores 0 -1
1) "Prickett"
2) "Castilla"
3) "Royce"
4) "Norem"
5) "Sam-Bodden"
6) "Ford"
{{< /clients-example >}}

Note: 0 and -1 means from element index 0 to the last element (-1 works
here just as it does in the case of the `LRANGE` command).

What if I want to order them the opposite way, youngest to oldest?
Use `ZREVRANGE` instead of `ZRANGE`:

> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"

It is possible to return scores as well, using the `WITHSCORES` argument:

> zrange hackers 0 -1 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"
{{< clients-example ss_tutorial zrange_withscores >}}
> ZRANGE racer_scores 0 -1 withscores
1) "Ford"
2) "6"
3) "Sam-Bodden"
4) "8"
5) "Norem"
6) "10"
7) "Royce"
8) "10"
9) "Castilla"
10) "12"
11) "Prickett"
12) "14"
{{< /clients-example >}}

### Operating on ranges

Sorted sets are more powerful than this. They can operate on ranges.
Let's get all the individuals that were born up to 1950 inclusive. We
Let's get all the racers with 10 or fewer points. We
use the `ZRANGEBYSCORE` command to do it:

> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
{{< clients-example ss_tutorial zrangebyscore >}}
> ZRANGEBYSCORE racer_scores -inf 10
1) "Ford"
2) "Sam-Bodden"
3) "Norem"
4) "Royce"
{{< /clients-example >}}

We asked Redis to return all the elements with a score between negative
infinity and 1950 (both extremes are included).
infinity and 10 (both extremes are included).

It's also possible to remove ranges of elements. Let's remove all
the hackers born between 1940 and 1960 from the sorted set:
To remove an element we'd simply call `ZREM` with the racer's name.
It's also possible to remove ranges of elements. Let's remove racer Castilla along with all
the racers with strictly fewer than 10 points:

> zremrangebyscore hackers 1940 1960
(integer) 4
{{< clients-example ss_tutorial zremrangebyscore >}}
> ZREM racer_scores "Castilla"
(integer) 1
> ZREMRANGEBYSCORE racer_scores -inf 9
(integer) 2
> ZRANGE racer_scores 0 -1
1) "Norem"
2) "Royce"
3) "Prickett"
{{< /clients-example >}}

`ZREMRANGEBYSCORE` is perhaps not the best command name,
but it can be very useful, and returns the number of removed elements.

Another extremely useful operation defined for sorted set elements
is the get-rank operation. It is possible to ask what is the
position of an element in the set of the ordered elements.

> zrank hackers "Anita Borg"
(integer) 4

position of an element in the set of ordered elements.
The `ZREVRANK` command is also available in order to get the rank, considering
the elements sorted a descending way.

{{< clients-example ss_tutorial zrank >}}
> ZRANK racer_scores "Norem"
(integer) 0
> ZREVRANK racer_scores "Norem"
(integer) 3
{{< /clients-example >}}

### Lexicographical scores

In version Redis 2.8, a new feature was introduced that allows
Expand All @@ -163,32 +153,22 @@ The main commands to operate with lexicographical ranges are `ZRANGEBYLEX`,
`ZREVRANGEBYLEX`, `ZREMRANGEBYLEX` and `ZLEXCOUNT`.

For example, let's add again our list of famous hackers, but this time
use a score of zero for all the elements:

> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0
"Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon"
0 "Linus Torvalds" 0 "Alan Turing"

Because of the sorted sets ordering rules, they are already sorted
lexicographically:

> zrange hackers 0 -1
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"

Using `ZRANGEBYLEX` we can ask for lexicographical ranges:

> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"
using a score of zero for all the elements. We'll see that because of the sorted sets ordering rules, they are already sorted lexicographically. Using `ZRANGEBYLEX` we can ask for lexicographical ranges:

{{< clients-example ss_tutorial zadd_lex >}}
> ZADD racer_scores 0 "Norem" 0 "Sam-Bodden" 0 "Royce" 0 "Castilla" 0 "Prickett" 0 "Ford"
(integer) 3
> ZRANGE racer_scores 0 -1
1) "Castilla"
2) "Ford"
3) "Norem"
4) "Prickett"
5) "Royce"
6) "Sam-Bodden"
> ZRANGEBYLEX racer_scores [A [L
1) "Castilla"
2) "Ford"
{{< /clients-example >}}

Ranges can be inclusive or exclusive (depending on the first character),
also string infinite and minus infinite are specified respectively with
Expand Down Expand Up @@ -223,38 +203,21 @@ the #4932 best score here").

## Examples

* Update a real-time leaderboard as players' scores change:
```
> ZADD leaderboard:455 100 user:1
(integer) 1
> ZADD leaderboard:455 75 user:2
(integer) 1
> ZADD leaderboard:455 101 user:3
* There are two ways we can use a sorted set to represent a leaderbaord. If we know a racer's new score, we can update it directly via the `ZADD` command. However, if we want to add points to an existing score, we can use the `ZINCRBY` command.
{{< clients-example ss_tutorial leaderboard >}}
> ZADD racer_scores 100 "Wood"
(integer) 1
> ZADD leaderboard:455 15 user:4
> ZADD racer_scores 100 "Henshaw"
(integer) 1
> ZADD leaderboard:455 275 user:2
> ZADD racer_scores 150 "Henshaw"
(integer) 0
```

Notice that `user:2`'s score is updated in the final `ZADD` call.

* Get the top 3 players' scores:
```
> ZRANGE leaderboard:455 0 2 REV WITHSCORES
1) "user:2"
2) "275"
3) "user:3"
4) "101"
5) "user:1"
6) "100"
```

* What's the rank of user 2?
```
> ZREVRANK leaderboard:455 user:2
(integer) 0
```
> ZINCRBY racer_scores 50 "Wood"
"150"
> ZINCRBY racer_scores 50 "Henshaw"
"200"
{{< /clients-example >}}

You'll see that `ZADD` returns 0 when the member already exists (the score is updated), while `ZINCRBY` returns the new score. The score for racer Henshaw went from 100, was changed to 150 with no regard for what score was there before, and then was incremented by 50 to 200.

## Basic commands

Expand All @@ -281,3 +244,7 @@ If you need to index and query your data, consider the [JSON](/docs/stack/json)

* [Redis Sorted Sets Explained](https://www.youtube.com/watch?v=MUKlxdBQZ7g) is an entertaining introduction to sorted sets in Redis.
* [Redis University's RU101](https://university.redis.com/courses/ru101/) explores Redis sorted sets in detail.


* [Redis Sorted Sets Explained](https://www.youtube.com/watch?v=MUKlxdBQZ7g) is an entertaining introduction to sorted sets in Redis.
* [Redis University's RU101](https://university.redis.com/courses/ru101/) explores Redis sorted sets in detail.

0 comments on commit 9e828fe

Please sign in to comment.