Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sets update for tabbed examples with bikes #2496

Merged
merged 2 commits into from
Aug 7, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 106 additions & 141 deletions docs/data-types/sets.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,169 +25,134 @@ See the [complete list of set commands](https://redis.io/commands/?group=set).

## Examples

* Store the set of favorited book IDs for users 123 and 456:
```
> SADD user:123:favorites 347
* Store the sets of bikes racing in France and the USA. Note that
if you add a member that already exists, it will be ignored.
{{< clients-example sets_tutorial sadd >}}
> SADD bikes:racing:france bike:1
(integer) 1
> SADD user:123:favorites 561
(integer) 1
> SADD user:123:favorites 742
(integer) 1
> SADD user:456:favorites 561
(integer) 1
```

* Check whether user 123 likes books 742 and 299
```
> SISMEMBER user:123:favorites 742
> SADD bikes:racing:france bike:1
(integer) 0
> SADD bikes:racing:france bike:2 bike:3
(integer) 2
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2
{{< /clients-example >}}

* Check whether bike:1 or bike:2 are racing in the US.
{{< clients-example sets_tutorial sismember >}}
> SISMEMBER bikes:racing:usa bike:1
(integer) 1
> SISMEMBER user:123:favorites 299
> SISMEMBER bikes:racing:usa bike:2
(integer) 0
```
{{< /clients-example >}}

* Do user 123 and 456 have any favorite books in common?
```
> SINTER user:123:favorites user:456:favorites
1) "561"
```
* Which bikes are competing in both races?
{{< clients-example sets_tutorial sinter >}}
> SINTER bikes:racing:france bikes:racing:usa
1) "bike:1"
{{< /clients-example >}}

* How many books has user 123 favorited?
```
> SCARD user:123:favorites
* How many bikes are racing in France?
{{< clients-example sets_tutorial scard >}}
> SCARD bikes:racing:france
(integer) 3
```

{{< /clients-example >}}
## Tutorial

The `SADD` command adds new elements to a set. It's also possible
to do a number of other operations against sets like testing if a given element
already exists, performing the intersection, union or difference between
multiple sets, and so forth.

> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2
{{< clients-example sets_tutorial sadd_smembers >}}
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SMEMBERS bikes:racing:france
1) bike:3
2) bike:1
3) bike:2
{{< /clients-example >}}

Here I've added three elements to my set and told Redis to return all the
elements. As you can see they are not sorted -- Redis is free to return the
elements in any order at every call, since there is no contract with the
user about element ordering.

Redis has commands to test for membership. For example, checking if an element exists:

> sismember myset 3
(integer) 1
> sismember myset 30
(integer) 0
elements. There is no order guarantee with a set. Redis is free to return the
elements in any order at every call.

"3" is a member of the set, while "30" is not.
Redis has commands to test for set membership. These commands can be used on single as well as multiple items:

Sets are good for expressing relations between objects.
For instance we can easily use sets in order to implement tags.

A simple way to model this problem is to have a set for every object we
want to tag. The set contains the IDs of the tags associated with the object.

One illustration is tagging news articles.
If article ID 1000 is tagged with tags 1, 2, 5 and 77, a set
can associate these tag IDs with the news item:

> sadd news:1000:tags 1 2 5 77
(integer) 4

We may also want to have the inverse relation as well: the list
of all the news tagged with a given tag:

> sadd tag:1:news 1000
(integer) 1
> sadd tag:2:news 1000
(integer) 1
> sadd tag:5:news 1000
(integer) 1
> sadd tag:77:news 1000
(integer) 1

To get all the tags for a given object is trivial:

> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2

Note: in the example we assume you have another data structure, for example
a Redis hash, which maps tag IDs to tag names.
{{< clients-example sets_tutorial smismember >}}
> SISMEMBER bikes:racing:france bike:1
(integer) 1
> SMISMEMBER bikes:racing:france bike:2 bike:3 bike:4
1) (integer) 1
2) (integer) 1
3) (integer) 0
{{< /clients-example >}}

We can also find the difference between two sets. For instance, we may want
to know which bikes are racing in France but not in the USA:

{{< clients-example sets_tutorial sdiff >}}
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2
> SDIFF bikes:racing:france bikes:racing:usa
1) "bike:3"
2) "bike:2"
{{< /clients-example >}}

There are other non trivial operations that are still easy to implement
using the right Redis commands. For instance we may want a list of all the
objects with the tags 1, 2, 10, and 27 together. We can do this using
bikes racing in France, the USA, and some other races. We can do this using
the `SINTER` command, which performs the intersection between different
sets. We can use:
sets. In addition to intersection you can also perform
unions, difference, and more. For example
if we add a third race we can see some of these commands in action:

> sinter tag:1:news tag:2:news tag:10:news tag:27:news
... results here ...

In addition to intersection you can also perform
unions, difference, extract a random element, and so forth.

The command to extract an element is called `SPOP`, and is handy to model
certain problems. For example in order to implement a web-based poker game,
you may want to represent your deck with a set. Imagine we use a one-char
prefix for (C)lubs, (D)iamonds, (H)earts, (S)pades:

> sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3
H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6
S7 S8 S9 S10 SJ SQ SK
(integer) 52

Now we want to provide each player with 5 cards. The `SPOP` command
removes a random element, returning it to the client, so it is the
perfect operation in this case.

However if we call it against our deck directly, in the next play of the
game we'll need to populate the deck of cards again, which may not be
ideal. So to start, we can make a copy of the set stored in the `deck` key
into the `game:1:deck` key.

This is accomplished using `SUNIONSTORE`, which normally performs the
union between multiple sets, and stores the result into another set.
However, since the union of a single set is itself, I can copy my deck
with:

> sunionstore game:1:deck deck
(integer) 52

Now I'm ready to provide the first player with five cards:

> spop game:1:deck
"C6"
> spop game:1:deck
"CQ"
> spop game:1:deck
"D1"
> spop game:1:deck
"CJ"
> spop game:1:deck
"SJ"

One pair of jacks, not great...

This is a good time to introduce the set command that provides the number
of elements inside a set. This is often called the *cardinality of a set*
in the context of set theory, so the Redis command is called `SCARD`.

> scard game:1:deck
(integer) 47

The math works: 52 - 5 = 47.

When you need to just get random elements without removing them from the
set, there is the `SRANDMEMBER` command suitable for the task. It also features
the ability to return both repeating and non-repeating elements.
{{< clients-example sets_tutorial multisets >}}
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2
> SADD bikes:racing:italy bike:1 bike:2 bike:3 bike:4
(integer) 4
> SINTER bikes:racing:france bikes:racing:usa bikes:racing:italy
1) "bike:1"
> SUNION bikes:racing:france bikes:racing:usa bikes:racing:italy
1) "bike:2"
2) "bike:1"
3) "bike:4"
4) "bike:3"
> SDIFF bikes:racing:france bikes:racing:usa bikes:racing:italy
(empty array)
> SDIFF bikes:racing:france bikes:racing:usa
1) "bike:3"
2) "bike:2"
> SDIFF bikes:racing:usa bikes:racing:france
1) "bike:4"
{{< /clients-example >}}

You'll note that the `SDIFF` command returns an empty array when the
difference between all sets is empty. You'll also note that the order of sets
passed to `SDIFF` matters, since the difference is not commutative.

When you want to remove items from a set, you can use the `SREM` command to
remove one or more items from a set, or you can use the `SPOP` command to
remove a random item from a set. You can also _return_ a random item from a
set without removing it using the `SRANDMEMBER` command:

{{< clients-example sets_tutorial srem >}}
> SADD bikes:racing:france bike:1 bike:2 bike:3 bike:4 bike:5
(integer) 3
> SREM bikes:racing:france bike:1
(integer) 1
> SPOP bikes:racing:france
"bike:3"
> SMEMBERS bikes:racing:france
1) "bike:2"
2) "bike:4"
3) "bike:5"
> SRANDMEMBER bikes:racing:france
"bike:2"
{{< /clients-example >}}

## Limits

Expand Down
Loading