Команда SET
работает только для обычных ключей, но не для отсортированных наборов.
В отсортированных наборах у вас есть пары очков-членов, поэтому номенклатура пар ключ-значение в вашем примере немного сбивает с толку. Я предполагаю, key_1, key_2, key_3, ...
являются членами, а val_1, val_2, ...
являются баллами.
Давайте создадим отсортированные наборы следующим образом, чтобы рассмотреть решение:
> ZADD set_1 1 key_1 2 key_2 3 key_3
(integer) 3
> ZADD set_2 1001 key_1 1003 key_3 1004 key_4
(integer) 3
По умолчанию AGGREGATE
равно SUM
, это то, что мы будем использовать повсюду.
Мы создадим два отсортированных набора с пересечением обоих: один со счетами set_1
и один со счетами set_2
.
> ZINTERSTORE intersect_set_1 2 set_1 set_2 WEIGHTS 1 0
(integer) 2
> ZINTERSTORE intersect_set_2 2 set_1 set_2 WEIGHTS 0 1
(integer) 2
Теперь мы создаем набор промежуточных шагов для set_1
, где мы устанавливаем нулевое значение для тех, кто находится в set_2
:
> ZUNIONSTORE pre_set_1 2 set_1 intersect_set_1 WEIGHTS 1 -1
(integer) 3
Теперь мы готовы обновить set_1, объединяя:
pre_set_1
: все set_1
, но с теми, которые также в set_2
установлены на ноль баллов. intersect_set_2
: пересечение set_1
и set_2
, со счетами set_2
.
Вот последняя команда:
> ZUNIONSTORE set_1 2 pre_set_1 intersect_set_2
(integer) 3
Давайте посмотрим на результат:
> ZRANGE set_1 0 -1 WITHSCORES
1) "key_2"
2) "2"
3) "key_1"
4) "1001"
5) "key_3"
6) "1003"
Не забудьте очистить:
> UNLINK pre_set_1 intersect_set_1 intersect_set_2
Это решение не является оптимальным, так как использует несколько средних шагов, есть риск для членов, добавленных к исходным наборам между ними, и он использует больше памяти, чем необходимо.
Оптимальным решением будет Lua script :
local set2 = redis.call('ZRANGE', KEYS[1], '0', '-1', 'WITHSCORES')
local set2length = table.getn(set2)
for i=1,set2length,2 do redis.call('ZADD', KEYS[2], 'XX', set2[i+1], set2[i]) end
return set2length/2
Это перебирает set_2
, обновляя set_1
. Обратите внимание на использование XX
в команде ZADD
, чтобы только обновить, если оно существует.
Использовать как:
EVAL "local set2 = redis.call('ZRANGE', KEYS[1], '0', '-1', 'WITHSCORES') \n local set2length = table.getn(set2) \n for i=1,set2length,2 do print(1) redis.call('ZADD', KEYS[2], 'XX', set2[i+1], set2[i]) end \n return set2length/2" 2 set_2 set_1
Сценарий Lua это атом c из-за однопоточной природы Redis.