Skip to content

Commit eb8e567

Browse files
committed
Merge pull request #547 from Talkdesk/545-zadd-options
Add support for ZADD options (NX, XX, CH, INCR)
2 parents 1ffdaa9 + 8d926bb commit eb8e567

File tree

2 files changed

+123
-9
lines changed

2 files changed

+123
-9
lines changed

lib/redis.rb

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,20 +1410,50 @@ 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 args.last.is_a?(Hash)
1435+
options = args.pop
1436+
1437+
nx = options[:nx]
1438+
zadd_options << "NX" if nx
1439+
1440+
xx = options[:xx]
1441+
zadd_options << "XX" if xx
1442+
1443+
ch = options[:ch]
1444+
zadd_options << "CH" if ch
1445+
1446+
incr = options[:incr]
1447+
zadd_options << "INCR" if incr
1448+
end
1449+
14201450
synchronize do |client|
14211451
if args.size == 1 && args[0].is_a?(Array)
1422-
# Variadic: return integer
1423-
client.call([:zadd, key] + args[0])
1452+
# Variadic: return float if INCR, integer if !INCR
1453+
client.call([:zadd, key] + zadd_options + args[0], &(incr ? _floatify : _identity))
14241454
elsif args.size == 2
1425-
# Single pair: return boolean
1426-
client.call([:zadd, key, args[0], args[1]], &_boolify)
1455+
# Single pair: return float if INCR, boolean if !INCR
1456+
client.call([:zadd, key] + zadd_options + args, &(incr ? _floatify : _boolify))
14271457
else
14281458
raise ArgumentError, "wrong number of arguments"
14291459
end
@@ -2614,6 +2644,12 @@ def _pairify(array)
26142644
array.each_slice(2).to_a
26152645
end
26162646

2647+
def _identity
2648+
lambda { |value|
2649+
value
2650+
}
2651+
end
2652+
26172653
def _subscription(method, channels, block)
26182654
return @client.call([method] + channels) if subscribed?
26192655

test/lint/sorted_sets.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,43 @@ 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+
assert_equal 1.0, r.zadd("foo", 1, "s1", :incr => true)
41+
assert_equal 11.0, r.zadd("foo", 10, "s1", :incr => true)
42+
assert_equal -Infinity, r.zadd("bar", "-inf", "s1", :incr => true)
43+
assert_equal +Infinity, r.zadd("bar", "+inf", "s2", :incr => true)
44+
r.del "foo", "bar"
45+
46+
# Incompatible options combination
47+
assert_raise(Redis::CommandError) { r.zadd("foo", 1, "s1", :xx => true, :nx => true) }
48+
end
1249
end
1350

1451
def test_variadic_zadd
@@ -31,6 +68,47 @@ def test_variadic_zadd
3168
assert_raise(Redis::CommandError) { r.zadd("foo", ["bar"]) }
3269
assert_raise(Redis::CommandError) { r.zadd("foo", ["bar", "qux", "zap"]) }
3370
end
71+
72+
target_version "3.0.2" do
73+
# XX option
74+
assert_equal 0, r.zcard("foo")
75+
assert_equal 0, r.zadd("foo", [1, "s1", 2, "s2"], :xx => true)
76+
r.zadd("foo", [1, "s1", 2, "s2"])
77+
assert_equal 0, r.zadd("foo", [2, "s1", 3, "s2", 4, "s3"], :xx => true)
78+
assert_equal 2, r.zscore("foo", "s1")
79+
assert_equal 3, r.zscore("foo", "s2")
80+
assert_equal nil, r.zscore("foo", "s3")
81+
assert_equal 2, r.zcard("foo")
82+
r.del "foo"
83+
84+
# NX option
85+
assert_equal 0, r.zcard("foo")
86+
assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"], :nx => true)
87+
assert_equal 1, r.zadd("foo", [2, "s1", 3, "s2", 4, "s3"], :nx => true)
88+
assert_equal 1, r.zscore("foo", "s1")
89+
assert_equal 2, r.zscore("foo", "s2")
90+
assert_equal 4, r.zscore("foo", "s3")
91+
assert_equal 3, r.zcard("foo")
92+
r.del "foo"
93+
94+
# CH option
95+
assert_equal 0, r.zcard("foo")
96+
assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"], :ch => true)
97+
assert_equal 2, r.zadd("foo", [1, "s1", 3, "s2", 4, "s3"], :ch => true)
98+
assert_equal 3, r.zcard("foo")
99+
r.del "foo"
100+
101+
# INCR option
102+
assert_equal 1.0, r.zadd("foo", [1, "s1"], :incr => true)
103+
assert_equal 11.0, r.zadd("foo", [10, "s1"], :incr => true)
104+
assert_equal -Infinity, r.zadd("bar", ["-inf", "s1"], :incr => true)
105+
assert_equal +Infinity, r.zadd("bar", ["+inf", "s2"], :incr => true)
106+
assert_raise(Redis::CommandError) { r.zadd("foo", [1, "s1", 2, "s2"], :incr => true) }
107+
r.del "foo", "bar"
108+
109+
# Incompatible options combination
110+
assert_raise(Redis::CommandError) { r.zadd("foo", [1, "s1"], :xx => true, :nx => true) }
111+
end
34112
end
35113

36114
def test_zrem

0 commit comments

Comments
 (0)