Skip to content

Commit 2bc8f6b

Browse files
author
Joao Fernandes
committed
Add support for ZADD options (NX, XX, CH, INCR)
Issue #545
1 parent 98e3e7a commit 2bc8f6b

File tree

2 files changed

+121
-8
lines changed

2 files changed

+121
-8
lines changed

lib/redis.rb

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,20 +1410,53 @@ def zcard(key)
14101410
# @param [[Float, String], Array<[Float, String]>] args
14111411
# - a single `[score, member]` pair
14121412
# - an array of `[score, member]` pairs
1413-
#
1414-
# @return [Boolean, Fixnum]
1413+
# @param [Hash] options
1414+
# - `:xx => true`: Only update elements that already exist (never
1415+
# add elements)
1416+
# - `:nx => true`: Don't update already existing elements (always
1417+
# add new elements)
1418+
# - `:ch => true`: Modify the return value from the number of new
1419+
# elements added, to the total number of elements changed (CH is an
1420+
# abbreviation of changed); changed elements are new elements added
1421+
# and elements already existing for which the score was updated
1422+
# - `:incr => true`: When this option is specified ZADD acts like
1423+
# ZINCRBY; only one score-element pair can be specified in this mode
1424+
#
1425+
# @return [Boolean, Fixnum, Float]
14151426
# - `Boolean` when a single pair is specified, holding whether or not it was
1416-
# **added** to the sorted set
1427+
# **added** to the sorted set.
14171428
# - `Fixnum` when an array of pairs is specified, holding the number of
1418-
# pairs that were **added** to the sorted set
1419-
def zadd(key, *args)
1429+
# pairs that were **added** to the sorted set.
1430+
# - `Float` when option :incr is specified, holding the score of the member
1431+
# after incrementing it.
1432+
def zadd(key, *args, options)
1433+
zadd_options = []
1434+
if options.is_a?(Hash)
1435+
nx = options[:nx]
1436+
zadd_options << "NX" if nx
1437+
1438+
xx = options[:xx]
1439+
zadd_options << "XX" if xx
1440+
1441+
raise ArgumentError, "XX and NX options at the same time are not compatible" if nx && xx
1442+
1443+
ch = options[:ch]
1444+
zadd_options << "CH" if ch
1445+
1446+
incr = options[:incr]
1447+
zadd_options << "INCR" if incr
1448+
else
1449+
args << options
1450+
end
1451+
14201452
synchronize do |client|
14211453
if args.size == 1 && args[0].is_a?(Array)
14221454
# Variadic: return integer
1423-
client.call([:zadd, key] + args[0])
1455+
raise ArgumentError, "only one score-element pair can be specified with the :incr option" if incr
1456+
client.call([:zadd, key] + zadd_options + args[0])
14241457
elsif args.size == 2
1425-
# Single pair: return boolean
1426-
client.call([:zadd, key, args[0], args[1]], &_boolify)
1458+
# Single pair: return float if INCR, boolean if !INCR
1459+
client.call([:zadd, key, *zadd_options, args[0], args[1]], &(incr ? _floatify : _boolify))
14271460
else
14281461
raise ArgumentError, "wrong number of arguments"
14291462
end

test/lint/sorted_sets.rb

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,50 @@ def test_zadd
99
assert_equal true, r.zadd("foo", 1, "s1")
1010
assert_equal false, r.zadd("foo", 1, "s1")
1111
assert_equal 1, r.zcard("foo")
12+
r.del "foo"
13+
14+
target_version "3.0.2" do
15+
# XX option
16+
assert_equal 0, r.zcard("foo")
17+
assert_equal false, r.zadd("foo", 1, "s1", xx: true)
18+
r.zadd("foo", 1, "s1")
19+
assert_equal false, r.zadd("foo", 2, "s1", xx: true)
20+
assert_equal 2, r.zscore("foo", "s1")
21+
r.del "foo"
22+
23+
# NX option
24+
assert_equal 0, r.zcard("foo")
25+
assert_equal true, r.zadd("foo", 1, "s1", nx: true)
26+
assert_equal false, r.zadd("foo", 2, "s1", nx: true)
27+
assert_equal 1, r.zscore("foo", "s1")
28+
assert_equal 1, r.zcard("foo")
29+
r.del "foo"
30+
31+
# CH option
32+
assert_equal 0, r.zcard("foo")
33+
assert_equal true, r.zadd("foo", 1, "s1", ch: true)
34+
assert_equal false, r.zadd("foo", 1, "s1", ch: true)
35+
assert_equal true, r.zadd("foo", 2, "s1", ch: true)
36+
assert_equal 1, r.zcard("foo")
37+
r.del "foo"
38+
39+
# INCR option
40+
rv = r.zadd("foo", 1, "s1", incr: true)
41+
assert_equal 1.0, rv
42+
43+
rv = r.zadd("foo", 10, "s1", incr: true)
44+
assert_equal 11.0, rv
45+
46+
rv = r.zadd("bar", "-inf", "s1", incr: true)
47+
assert_equal(-Infinity, rv)
48+
49+
rv = r.zadd("bar", "+inf", "s2", incr: true)
50+
assert_equal(+Infinity, rv)
51+
r.del "foo", "bar"
52+
53+
# Incompatible options combinations
54+
assert_raise(ArgumentError) { r.zadd("foo", 1, "s1", xx: true, nx: true) }
55+
end
1256
end
1357

1458
def test_variadic_zadd
@@ -31,6 +75,42 @@ def test_variadic_zadd
3175
assert_raise(Redis::CommandError) { r.zadd("foo", ["bar"]) }
3276
assert_raise(Redis::CommandError) { r.zadd("foo", ["bar", "qux", "zap"]) }
3377
end
78+
79+
target_version "3.0.2" do
80+
# XX option
81+
assert_equal 0, r.zcard("foo")
82+
assert_equal 0, r.zadd("foo", [1, "s1", 2, "s2"], xx: true)
83+
r.zadd("foo", [1, "s1", 2, "s2"])
84+
assert_equal 0, r.zadd("foo", [2, "s1", 3, "s2", 4, "s3"], xx: true)
85+
assert_equal 2, r.zscore("foo", "s1")
86+
assert_equal 3, r.zscore("foo", "s2")
87+
assert_equal nil, r.zscore("foo", "s3")
88+
assert_equal 2, r.zcard("foo")
89+
r.del "foo"
90+
91+
# NX option
92+
assert_equal 0, r.zcard("foo")
93+
assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"], nx: true)
94+
assert_equal 1, r.zadd("foo", [2, "s1", 3, "s2", 4, "s3"], nx: true)
95+
assert_equal 1, r.zscore("foo", "s1")
96+
assert_equal 2, r.zscore("foo", "s2")
97+
assert_equal 4, r.zscore("foo", "s3")
98+
assert_equal 3, r.zcard("foo")
99+
r.del "foo"
100+
101+
# CH option
102+
assert_equal 0, r.zcard("foo")
103+
assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"], ch: true)
104+
assert_equal 2, r.zadd("foo", [1, "s1", 3, "s2", 4, "s3"], ch: true)
105+
assert_equal 3, r.zcard("foo")
106+
r.del "foo"
107+
108+
# INCR option
109+
assert_raise(ArgumentError) { r.zadd("foo", [1, "s1"], incr: true) }
110+
111+
# Incompatible options combinations
112+
assert_raise(ArgumentError) { r.zadd("foo", [1, "s1"], xx: true, nx: true) }
113+
end
34114
end
35115

36116
def test_zrem

0 commit comments

Comments
 (0)