Skip to content

Commit 7bb6b63

Browse files
authored
Merge pull request #995 from SkipKayhil/add-zinter
add ZINTER command, added to Redis in 6.2
2 parents f759203 + 68fe7b5 commit 7bb6b63

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

lib/redis.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,45 @@ def zcount(key, min, max)
20622062
end
20632063
end
20642064

2065+
# Return the intersection of multiple sorted sets
2066+
#
2067+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
2068+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
2069+
# # => ["v1", "v2"]
2070+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
2071+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
2072+
# # => [["v1", 3.0], ["v2", 6.0]]
2073+
#
2074+
# @param [String, Array<String>] keys one or more keys to intersect
2075+
# @param [Hash] options
2076+
# - `:weights => [Float, Float, ...]`: weights to associate with source
2077+
# sorted sets
2078+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2079+
# - `:with_scores => true`: include scores in output
2080+
#
2081+
# @return [Array<String>, Array<[String, Float]>]
2082+
# - when `:with_scores` is not specified, an array of members
2083+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
2084+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
2085+
args = [:zinter, keys.size, *keys]
2086+
2087+
if weights
2088+
args << "WEIGHTS"
2089+
args.concat(weights)
2090+
end
2091+
2092+
args << "AGGREGATE" << aggregate if aggregate
2093+
2094+
if with_scores
2095+
args << "WITHSCORES"
2096+
block = FloatifyPairs
2097+
end
2098+
2099+
synchronize do |client|
2100+
client.call(args, &block)
2101+
end
2102+
end
2103+
20652104
# Intersect multiple sorted sets and store the resulting sorted set in a new
20662105
# key.
20672106
#

lib/redis/distributed.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,13 @@ def zcount(key, min, max)
674674
node_for(key).zcount(key, min, max)
675675
end
676676

677+
# Get the intersection of multiple sorted sets
678+
def zinter(*keys, **options)
679+
ensure_same_node(:zinter, keys) do |node|
680+
node.zinter(*keys, **options)
681+
end
682+
end
683+
677684
# Intersect multiple sorted sets and store the resulting sorted set in a new
678685
# key.
679686
def zinterstore(destination, keys, **options)

test/cluster_commands_on_sorted_sets_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ class TestClusterCommandsOnSortedSets < Minitest::Test
99
include Helper::Cluster
1010
include Lint::SortedSets
1111

12+
def test_zinter
13+
assert_raises(Redis::CommandError) { super }
14+
end
15+
16+
def test_zinter_with_aggregate
17+
assert_raises(Redis::CommandError) { super }
18+
end
19+
20+
def test_zinter_with_weights
21+
assert_raises(Redis::CommandError) { super }
22+
end
23+
1224
def test_zinterstore
1325
assert_raises(Redis::CommandError) { super }
1426
end

test/distributed_commands_on_sorted_sets_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ class TestDistributedCommandsOnSortedSets < Minitest::Test
77
include Helper::Distributed
88
include Lint::SortedSets
99

10+
def test_zinter
11+
assert_raises(Redis::Distributed::CannotDistribute) { super }
12+
end
13+
14+
def test_zinter_with_aggregate
15+
assert_raises(Redis::Distributed::CannotDistribute) { super }
16+
end
17+
18+
def test_zinter_with_weights
19+
assert_raises(Redis::Distributed::CannotDistribute) { super }
20+
end
21+
1022
def test_zinterstore
1123
assert_raises(Redis::Distributed::CannotDistribute) { super }
1224
end

test/lint/sorted_sets.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,55 @@ def test_zunionstore_expand
432432
assert_equal 5, r.zunionstore('{1}baz', %w[{1}foo {1}bar])
433433
end
434434

435+
def test_zinter
436+
target_version("6.2") do
437+
r.zadd 'foo', 1, 's1'
438+
r.zadd 'bar', 2, 's1'
439+
r.zadd 'foo', 3, 's3'
440+
r.zadd 'bar', 4, 's4'
441+
442+
assert_equal ['s1'], r.zinter('foo', 'bar')
443+
assert_equal [['s1', 3.0]], r.zinter('foo', 'bar', with_scores: true)
444+
end
445+
end
446+
447+
def test_zinter_with_weights
448+
target_version("6.2") do
449+
r.zadd 'foo', 1, 's1'
450+
r.zadd 'foo', 2, 's2'
451+
r.zadd 'foo', 3, 's3'
452+
r.zadd 'bar', 20, 's2'
453+
r.zadd 'bar', 30, 's3'
454+
r.zadd 'bar', 40, 's4'
455+
456+
assert_equal %w[s2 s3], r.zinter('foo', 'bar')
457+
assert_equal [['s2', 22.0], ['s3', 33.0]], r.zinter('foo', 'bar', with_scores: true)
458+
459+
assert_equal %w[s2 s3], r.zinter('foo', 'bar', weights: [10, 1])
460+
assert_equal [['s2', 40.0], ['s3', 60.0]], r.zinter('foo', 'bar', weights: [10, 1], with_scores: true)
461+
end
462+
end
463+
464+
def test_zinter_with_aggregate
465+
target_version("6.2") do
466+
r.zadd 'foo', 1, 's1'
467+
r.zadd 'foo', 2, 's2'
468+
r.zadd 'foo', 3, 's3'
469+
r.zadd 'bar', 20, 's2'
470+
r.zadd 'bar', 30, 's3'
471+
r.zadd 'bar', 40, 's4'
472+
473+
assert_equal %w[s2 s3], r.zinter('foo', 'bar')
474+
assert_equal [['s2', 22.0], ['s3', 33.0]], r.zinter('foo', 'bar', with_scores: true)
475+
476+
assert_equal %w[s2 s3], r.zinter('foo', 'bar', aggregate: :min)
477+
assert_equal [['s2', 2.0], ['s3', 3.0]], r.zinter('foo', 'bar', aggregate: :min, with_scores: true)
478+
479+
assert_equal %w[s2 s3], r.zinter('foo', 'bar', aggregate: :max)
480+
assert_equal [['s2', 20.0], ['s3', 30.0]], r.zinter('foo', 'bar', aggregate: :max, with_scores: true)
481+
end
482+
end
483+
435484
def test_zinterstore
436485
r.zadd 'foo', 1, 's1'
437486
r.zadd 'bar', 2, 's1'

0 commit comments

Comments
 (0)