Redis ZUNIONSTORE Command Explained

In Redis, the ZUNIONSTORE command works in the same way that ZUNION works, except that it stores the result in a key that we specify.

In other words, it computes the union of the specified sorted sets, then stores the result in the specified key.

Syntax

The syntax goes like this:

ZUNIONSTORE destination numkeys key [key ...] 
  [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]

If the destination key already exists, it’s overwritten.

The following examples demonstrate how the arguments are used.

Example

Suppose we create the following sorted sets:

ZADD cats 1 meow 2 fluffy 3 scratch

And:

ZADD dogs 1 bark 2 woof 3 fluffy 4 scratch

Let’s use the ZUNIONSTORE command to compute the union of those two sorted sets and store it in a specified key:

ZUNIONSTORE petnames 2 cats dogs

Result:

(integer) 5

The integer reply of 5 indicates that five members were added to the specified key called petnames.

Let’s take a look at the new key:

ZRANGE petnames 0 -1 WITHSCORES

Result:

 1) "bark"
 2) "1"
 3) "meow"
 4) "1"
 5) "woof"
 6) "2"
 7) "fluffy"
 8) "5"
 9) "scratch"
10) "7"

We can see that both fluffy and scratch appear in both sorted sets, but each of these only appear once in the resulting sorted set.

By default, the scores for each member are added together from their respective sorted sets. In this example, fluffy has a score of 5 (2 from cats plus 3 from dogs) and scratch has a score of 7 (3 from cats and 4 from dogs).

The AGGREGATE Argument

In the previous example we saw how the scores were added together to produce an aggregate score. We can change this with the AGGREGATE argument so that it returns either the minimum or maximum score from the original sorted set.

To do this we use the AGGREGATE keyword followed by the option we want to use.

We can use MIN for “minimum”:

ZUNIONSTORE petnames 2 cats dogs AGGREGATE MIN

Result:

(integer) 5

Check the contents:

ZRANGE petnames 0 -1 WITHSCORES

Result:

 1) "bark"
 2) "1"
 3) "meow"
 4) "1"
 5) "fluffy"
 6) "2"
 7) "woof"
 8) "2"
 9) "scratch"
10) "3"

As mentioned, if the destination key already exists it’s overwritten (which is what happened in this example).

And MAX for “maximum”:

ZUNIONSTORE petnames 2 cats dogs AGGREGATE MAX

Result:

(integer) 5

Check the contents:

ZRANGE petnames 0 -1 WITHSCORES

Result:

 1) "bark"
 2) "1"
 3) "meow"
 4) "1"
 5) "woof"
 6) "2"
 7) "fluffy"
 8) "3"
 9) "scratch"
10) "4"

We can also explicitly state SUM for the default behaviour of adding the scores:

ZUNIONSTORE petnames 2 cats dogs AGGREGATE SUM

Result:

(integer) 5

Check the contents:

ZRANGE petnames 0 -1 WITHSCORES

Result:

 1) "bark"
 2) "1"
 3) "meow"
 4) "1"
 5) "woof"
 6) "2"
 7) "fluffy"
 8) "5"
 9) "scratch"
10) "7"

The WEIGHTS Argument

We can use the WEIGHTS argument to specify a multiplication factor for each input sorted set. This means that the score of every element in the input sorted set is multiplied by this factor before being passed to the aggregation function.

We can apply a different weighting to each sorted set.

Example:

ZUNIONSTORE petnames 2 cats dogs WEIGHTS 100 100

Result:

(integer) 5

And check the contents:

ZRANGE petnames 0 -1 WITHSCORES

Result:

 1) "bark"
 2) "100"
 3) "meow"
 4) "100"
 5) "woof"
 6) "200"
 7) "fluffy"
 8) "500"
 9) "scratch"
10) "700"

In this example, I specified that the members of both sets should be multiplied by 100 and the result reflects this.

Here’s an example of applying a different weighting to each set:

ZUNIONSTORE petnames 2 cats dogs WEIGHTS 100 200

Result:

(integer) 5

And check the contents:

ZRANGE petnames 0 -1 WITHSCORES

Result:

 1) "meow"
 2) "100"
 3) "bark"
 4) "200"
 5) "woof"
 6) "400"
 7) "fluffy"
 8) "800"
 9) "scratch"
10) "1100"

This time each element in the cats sorted set was multiplied by 100, while each element in the dogs sorted set was multiplied by 200.

The default multiplication factor is 1.

When the Source Keys Don’t Exist

Here’s what happens when the source keys don’t exist:

ZUNIONSTORE petnames 3 oops1 oops2 oops3

Result:

(integer) 0

Let’s use EXISTS to check for the existence of the destination key:

EXISTS petnames

Result:

(integer) 0

The destination key has disappeared.

But if only one key exists, and it contains a sorted set, we get the members of that sorted set:

ZUNIONSTORE petnames 3 cats oops2 oops3

Result:

(integer) 3

Let’s check the contents of the destination key:

ZRANGE petnames 0 -1 WITHSCORES

Result:

1) "meow"
2) "1"
3) "fluffy"
4) "2"
5) "scratch"
6) "3"

As expected.

Wrong Data Type

If any of the keys contain the wrong data type, an error occurs:

ZUNIONSTORE placenames 2 countries places

Result:

(error) WRONGTYPE Operation against a key holding the wrong kind of value

In my case, the places key contains a list and so we get the error.

We can use the TYPE command to check the key’s data type:

TYPE places

Result:

list

As suspected, it’s a list.

Actually, the countries key contains a set (not a sorted set) but it isn’t the culprit. Let’s check its type:

TYPE countries

Result:

set

The reason I say it’s not the culprit is because we can use it as the sole argument without any errors occurring:

ZUNIONSTORE placenames 1 countries

Result:

(integer) 2

Conversely, if we pass the places list as the sole argument, we get the error:

ZUNIONSTORE 1 places

Result:

(error) WRONGTYPE Operation against a key holding the wrong kind of value