redis 4.4.0 → 5.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +150 -0
- data/README.md +95 -160
- data/lib/redis/client.rb +84 -608
- data/lib/redis/commands/bitmaps.rb +66 -0
- data/lib/redis/commands/cluster.rb +28 -0
- data/lib/redis/commands/connection.rb +53 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +254 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +437 -0
- data/lib/redis/commands/lists.rb +339 -0
- data/lib/redis/commands/pubsub.rb +54 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +214 -0
- data/lib/redis/commands/sorted_sets.rb +884 -0
- data/lib/redis/commands/streams.rb +402 -0
- data/lib/redis/commands/strings.rb +314 -0
- data/lib/redis/commands/transactions.rb +115 -0
- data/lib/redis/commands.rb +237 -0
- data/lib/redis/distributed.rb +208 -70
- data/lib/redis/errors.rb +15 -41
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +66 -120
- data/lib/redis/subscribe.rb +23 -15
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +109 -3546
- metadata +27 -54
- data/lib/redis/cluster/command.rb +0 -81
- data/lib/redis/cluster/command_loader.rb +0 -34
- data/lib/redis/cluster/key_slot_converter.rb +0 -72
- data/lib/redis/cluster/node.rb +0 -108
- data/lib/redis/cluster/node_key.rb +0 -31
- data/lib/redis/cluster/node_loader.rb +0 -37
- data/lib/redis/cluster/option.rb +0 -93
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -49
- data/lib/redis/cluster.rb +0 -291
- data/lib/redis/connection/command_helper.rb +0 -39
- data/lib/redis/connection/hiredis.rb +0 -67
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -427
- data/lib/redis/connection/synchrony.rb +0 -146
- data/lib/redis/connection.rb +0 -11
@@ -0,0 +1,884 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module Commands
|
5
|
+
module SortedSets
|
6
|
+
# Get the number of members in a sorted set.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# redis.zcard("zset")
|
10
|
+
# # => 4
|
11
|
+
#
|
12
|
+
# @param [String] key
|
13
|
+
# @return [Integer]
|
14
|
+
def zcard(key)
|
15
|
+
send_command([:zcard, key])
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add one or more members to a sorted set, or update the score for members
|
19
|
+
# that already exist.
|
20
|
+
#
|
21
|
+
# @example Add a single `[score, member]` pair to a sorted set
|
22
|
+
# redis.zadd("zset", 32.0, "member")
|
23
|
+
# @example Add an array of `[score, member]` pairs to a sorted set
|
24
|
+
# redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
|
25
|
+
#
|
26
|
+
# @param [String] key
|
27
|
+
# @param [[Float, String], Array<[Float, String]>] args
|
28
|
+
# - a single `[score, member]` pair
|
29
|
+
# - an array of `[score, member]` pairs
|
30
|
+
# @param [Hash] options
|
31
|
+
# - `:xx => true`: Only update elements that already exist (never
|
32
|
+
# add elements)
|
33
|
+
# - `:nx => true`: Don't update already existing elements (always
|
34
|
+
# add new elements)
|
35
|
+
# - `:lt => true`: Only update existing elements if the new score
|
36
|
+
# is less than the current score
|
37
|
+
# - `:gt => true`: Only update existing elements if the new score
|
38
|
+
# is greater than the current score
|
39
|
+
# - `:ch => true`: Modify the return value from the number of new
|
40
|
+
# elements added, to the total number of elements changed (CH is an
|
41
|
+
# abbreviation of changed); changed elements are new elements added
|
42
|
+
# and elements already existing for which the score was updated
|
43
|
+
# - `:incr => true`: When this option is specified ZADD acts like
|
44
|
+
# ZINCRBY; only one score-element pair can be specified in this mode
|
45
|
+
#
|
46
|
+
# @return [Boolean, Integer, Float]
|
47
|
+
# - `Boolean` when a single pair is specified, holding whether or not it was
|
48
|
+
# **added** to the sorted set.
|
49
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
50
|
+
# pairs that were **added** to the sorted set.
|
51
|
+
# - `Float` when option :incr is specified, holding the score of the member
|
52
|
+
# after incrementing it.
|
53
|
+
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
54
|
+
command = [:zadd, key]
|
55
|
+
command << "NX" if nx
|
56
|
+
command << "XX" if xx
|
57
|
+
command << "LT" if lt
|
58
|
+
command << "GT" if gt
|
59
|
+
command << "CH" if ch
|
60
|
+
command << "INCR" if incr
|
61
|
+
|
62
|
+
if args.size == 1 && args[0].is_a?(Array)
|
63
|
+
members_to_add = args[0]
|
64
|
+
return 0 if members_to_add.empty?
|
65
|
+
|
66
|
+
# Variadic: return float if INCR, integer if !INCR
|
67
|
+
send_command(command + members_to_add, &(incr ? Floatify : nil))
|
68
|
+
elsif args.size == 2
|
69
|
+
# Single pair: return float if INCR, boolean if !INCR
|
70
|
+
send_command(command + args, &(incr ? Floatify : Boolify))
|
71
|
+
else
|
72
|
+
raise ArgumentError, "wrong number of arguments"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Increment the score of a member in a sorted set.
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# redis.zincrby("zset", 32.0, "a")
|
80
|
+
# # => 64.0
|
81
|
+
#
|
82
|
+
# @param [String] key
|
83
|
+
# @param [Float] increment
|
84
|
+
# @param [String] member
|
85
|
+
# @return [Float] score of the member after incrementing it
|
86
|
+
def zincrby(key, increment, member)
|
87
|
+
send_command([:zincrby, key, increment, member], &Floatify)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Remove one or more members from a sorted set.
|
91
|
+
#
|
92
|
+
# @example Remove a single member from a sorted set
|
93
|
+
# redis.zrem("zset", "a")
|
94
|
+
# @example Remove an array of members from a sorted set
|
95
|
+
# redis.zrem("zset", ["a", "b"])
|
96
|
+
#
|
97
|
+
# @param [String] key
|
98
|
+
# @param [String, Array<String>] member
|
99
|
+
# - a single member
|
100
|
+
# - an array of members
|
101
|
+
#
|
102
|
+
# @return [Boolean, Integer]
|
103
|
+
# - `Boolean` when a single member is specified, holding whether or not it
|
104
|
+
# was removed from the sorted set
|
105
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
106
|
+
# members that were removed to the sorted set
|
107
|
+
def zrem(key, member)
|
108
|
+
if member.is_a?(Array)
|
109
|
+
members_to_remove = member
|
110
|
+
return 0 if members_to_remove.empty?
|
111
|
+
end
|
112
|
+
|
113
|
+
send_command([:zrem, key, member]) do |reply|
|
114
|
+
if member.is_a? Array
|
115
|
+
# Variadic: return integer
|
116
|
+
reply
|
117
|
+
else
|
118
|
+
# Single argument: return boolean
|
119
|
+
Boolify.call(reply)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
125
|
+
#
|
126
|
+
# @example Popping a member
|
127
|
+
# redis.zpopmax('zset')
|
128
|
+
# #=> ['b', 2.0]
|
129
|
+
# @example With count option
|
130
|
+
# redis.zpopmax('zset', 2)
|
131
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
132
|
+
#
|
133
|
+
# @params key [String] a key of the sorted set
|
134
|
+
# @params count [Integer] a number of members
|
135
|
+
#
|
136
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
137
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
138
|
+
def zpopmax(key, count = nil)
|
139
|
+
command = [:zpopmax, key]
|
140
|
+
command << Integer(count) if count
|
141
|
+
send_command(command) do |members|
|
142
|
+
members = FloatifyPairs.call(members)
|
143
|
+
count.to_i > 1 ? members : members.first
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
148
|
+
#
|
149
|
+
# @example Popping a member
|
150
|
+
# redis.zpopmin('zset')
|
151
|
+
# #=> ['a', 1.0]
|
152
|
+
# @example With count option
|
153
|
+
# redis.zpopmin('zset', 2)
|
154
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
155
|
+
#
|
156
|
+
# @params key [String] a key of the sorted set
|
157
|
+
# @params count [Integer] a number of members
|
158
|
+
#
|
159
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
160
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
161
|
+
def zpopmin(key, count = nil)
|
162
|
+
command = [:zpopmin, key]
|
163
|
+
command << Integer(count) if count
|
164
|
+
send_command(command) do |members|
|
165
|
+
members = FloatifyPairs.call(members)
|
166
|
+
count.to_i > 1 ? members : members.first
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Removes and returns up to count members with scores in the sorted set stored at key.
|
171
|
+
#
|
172
|
+
# @example Popping a member
|
173
|
+
# redis.bzmpop('zset')
|
174
|
+
# #=> ['zset', ['a', 1.0]]
|
175
|
+
# @example With count option
|
176
|
+
# redis.bzmpop('zset', count: 2)
|
177
|
+
# #=> ['zset', [['a', 1.0], ['b', 2.0]]
|
178
|
+
#
|
179
|
+
# @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
|
180
|
+
# A timeout of zero can be used to block indefinitely.
|
181
|
+
# @params key [String, Array<String>] one or more keys with sorted sets
|
182
|
+
# @params modifier [String]
|
183
|
+
# - when `"MIN"` - the elements popped are those with lowest scores
|
184
|
+
# - when `"MAX"` - the elements popped are those with the highest scores
|
185
|
+
# @params count [Integer] a number of members to pop
|
186
|
+
#
|
187
|
+
# @return [Array<String, Array<String, Float>>] list of popped elements and scores
|
188
|
+
def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
|
189
|
+
raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
|
190
|
+
|
191
|
+
args = [:bzmpop, timeout, keys.size, *keys, modifier]
|
192
|
+
args << "COUNT" << Integer(count) if count
|
193
|
+
|
194
|
+
send_blocking_command(args, timeout) do |response|
|
195
|
+
response&.map do |entry|
|
196
|
+
case entry
|
197
|
+
when String then entry
|
198
|
+
when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Removes and returns up to count members with scores in the sorted set stored at key.
|
205
|
+
#
|
206
|
+
# @example Popping a member
|
207
|
+
# redis.zmpop('zset')
|
208
|
+
# #=> ['zset', ['a', 1.0]]
|
209
|
+
# @example With count option
|
210
|
+
# redis.zmpop('zset', count: 2)
|
211
|
+
# #=> ['zset', [['a', 1.0], ['b', 2.0]]
|
212
|
+
#
|
213
|
+
# @params key [String, Array<String>] one or more keys with sorted sets
|
214
|
+
# @params modifier [String]
|
215
|
+
# - when `"MIN"` - the elements popped are those with lowest scores
|
216
|
+
# - when `"MAX"` - the elements popped are those with the highest scores
|
217
|
+
# @params count [Integer] a number of members to pop
|
218
|
+
#
|
219
|
+
# @return [Array<String, Array<String, Float>>] list of popped elements and scores
|
220
|
+
def zmpop(*keys, modifier: "MIN", count: nil)
|
221
|
+
raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
|
222
|
+
|
223
|
+
args = [:zmpop, keys.size, *keys, modifier]
|
224
|
+
args << "COUNT" << Integer(count) if count
|
225
|
+
|
226
|
+
send_command(args) do |response|
|
227
|
+
response&.map do |entry|
|
228
|
+
case entry
|
229
|
+
when String then entry
|
230
|
+
when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
237
|
+
# or block until one is available.
|
238
|
+
#
|
239
|
+
# @example Popping a member from a sorted set
|
240
|
+
# redis.bzpopmax('zset', 1)
|
241
|
+
# #=> ['zset', 'b', 2.0]
|
242
|
+
# @example Popping a member from multiple sorted sets
|
243
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
244
|
+
# #=> ['zset1', 'b', 2.0]
|
245
|
+
#
|
246
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
247
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
248
|
+
#
|
249
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
250
|
+
# @return [nil] when no element could be popped and the timeout expired
|
251
|
+
def bzpopmax(*args)
|
252
|
+
_bpop(:bzpopmax, args) do |reply|
|
253
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
258
|
+
# or block until one is available.
|
259
|
+
#
|
260
|
+
# @example Popping a member from a sorted set
|
261
|
+
# redis.bzpopmin('zset', 1)
|
262
|
+
# #=> ['zset', 'a', 1.0]
|
263
|
+
# @example Popping a member from multiple sorted sets
|
264
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
265
|
+
# #=> ['zset1', 'a', 1.0]
|
266
|
+
#
|
267
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
268
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
269
|
+
#
|
270
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
271
|
+
# @return [nil] when no element could be popped and the timeout expired
|
272
|
+
def bzpopmin(*args)
|
273
|
+
_bpop(:bzpopmin, args) do |reply|
|
274
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Get the score associated with the given member in a sorted set.
|
279
|
+
#
|
280
|
+
# @example Get the score for member "a"
|
281
|
+
# redis.zscore("zset", "a")
|
282
|
+
# # => 32.0
|
283
|
+
#
|
284
|
+
# @param [String] key
|
285
|
+
# @param [String] member
|
286
|
+
# @return [Float] score of the member
|
287
|
+
def zscore(key, member)
|
288
|
+
send_command([:zscore, key, member], &Floatify)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Get the scores associated with the given members in a sorted set.
|
292
|
+
#
|
293
|
+
# @example Get the scores for members "a" and "b"
|
294
|
+
# redis.zmscore("zset", "a", "b")
|
295
|
+
# # => [32.0, 48.0]
|
296
|
+
#
|
297
|
+
# @param [String] key
|
298
|
+
# @param [String, Array<String>] members
|
299
|
+
# @return [Array<Float>] scores of the members
|
300
|
+
def zmscore(key, *members)
|
301
|
+
send_command([:zmscore, key, *members]) do |reply|
|
302
|
+
reply.map(&Floatify)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Get one or more random members from a sorted set.
|
307
|
+
#
|
308
|
+
# @example Get one random member
|
309
|
+
# redis.zrandmember("zset")
|
310
|
+
# # => "a"
|
311
|
+
# @example Get multiple random members
|
312
|
+
# redis.zrandmember("zset", 2)
|
313
|
+
# # => ["a", "b"]
|
314
|
+
# @example Get multiple random members with scores
|
315
|
+
# redis.zrandmember("zset", 2, with_scores: true)
|
316
|
+
# # => [["a", 2.0], ["b", 3.0]]
|
317
|
+
#
|
318
|
+
# @param [String] key
|
319
|
+
# @param [Integer] count
|
320
|
+
# @param [Hash] options
|
321
|
+
# - `:with_scores => true`: include scores in output
|
322
|
+
#
|
323
|
+
# @return [nil, String, Array<String>, Array<[String, Float]>]
|
324
|
+
# - when `key` does not exist or set is empty, `nil`
|
325
|
+
# - when `count` is not specified, a member
|
326
|
+
# - when `count` is specified and `:with_scores` is not specified, an array of members
|
327
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
328
|
+
def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
|
329
|
+
if with_scores && count.nil?
|
330
|
+
raise ArgumentError, "count argument must be specified"
|
331
|
+
end
|
332
|
+
|
333
|
+
args = [:zrandmember, key]
|
334
|
+
args << Integer(count) if count
|
335
|
+
|
336
|
+
if with_scores
|
337
|
+
args << "WITHSCORES"
|
338
|
+
block = FloatifyPairs
|
339
|
+
end
|
340
|
+
|
341
|
+
send_command(args, &block)
|
342
|
+
end
|
343
|
+
|
344
|
+
# Return a range of members in a sorted set, by index, score or lexicographical ordering.
|
345
|
+
#
|
346
|
+
# @example Retrieve all members from a sorted set, by index
|
347
|
+
# redis.zrange("zset", 0, -1)
|
348
|
+
# # => ["a", "b"]
|
349
|
+
# @example Retrieve all members and their scores from a sorted set
|
350
|
+
# redis.zrange("zset", 0, -1, :with_scores => true)
|
351
|
+
# # => [["a", 32.0], ["b", 64.0]]
|
352
|
+
#
|
353
|
+
# @param [String] key
|
354
|
+
# @param [Integer] start start index
|
355
|
+
# @param [Integer] stop stop index
|
356
|
+
# @param [Hash] options
|
357
|
+
# - `:by_score => false`: return members by score
|
358
|
+
# - `:by_lex => false`: return members by lexicographical ordering
|
359
|
+
# - `:rev => false`: reverse the ordering, from highest to lowest
|
360
|
+
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
361
|
+
# `count` members
|
362
|
+
# - `:with_scores => true`: include scores in output
|
363
|
+
#
|
364
|
+
# @return [Array<String>, Array<[String, Float]>]
|
365
|
+
# - when `:with_scores` is not specified, an array of members
|
366
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
367
|
+
def zrange(key, start, stop, byscore: false, by_score: byscore, bylex: false, by_lex: bylex,
|
368
|
+
rev: false, limit: nil, withscores: false, with_scores: withscores)
|
369
|
+
|
370
|
+
if by_score && by_lex
|
371
|
+
raise ArgumentError, "only one of :by_score or :by_lex can be specified"
|
372
|
+
end
|
373
|
+
|
374
|
+
args = [:zrange, key, start, stop]
|
375
|
+
|
376
|
+
if by_score
|
377
|
+
args << "BYSCORE"
|
378
|
+
elsif by_lex
|
379
|
+
args << "BYLEX"
|
380
|
+
end
|
381
|
+
|
382
|
+
args << "REV" if rev
|
383
|
+
|
384
|
+
if limit
|
385
|
+
args << "LIMIT"
|
386
|
+
args.concat(limit.map { |l| Integer(l) })
|
387
|
+
end
|
388
|
+
|
389
|
+
if with_scores
|
390
|
+
args << "WITHSCORES"
|
391
|
+
block = FloatifyPairs
|
392
|
+
end
|
393
|
+
|
394
|
+
send_command(args, &block)
|
395
|
+
end
|
396
|
+
|
397
|
+
# Select a range of members in a sorted set, by index, score or lexicographical ordering
|
398
|
+
# and store the resulting sorted set in a new key.
|
399
|
+
#
|
400
|
+
# @example
|
401
|
+
# redis.zadd("foo", [[1.0, "s1"], [2.0, "s2"], [3.0, "s3"]])
|
402
|
+
# redis.zrangestore("bar", "foo", 0, 1)
|
403
|
+
# # => 2
|
404
|
+
# redis.zrange("bar", 0, -1)
|
405
|
+
# # => ["s1", "s2"]
|
406
|
+
#
|
407
|
+
# @return [Integer] the number of elements in the resulting sorted set
|
408
|
+
# @see #zrange
|
409
|
+
def zrangestore(dest_key, src_key, start, stop, byscore: false, by_score: byscore,
|
410
|
+
bylex: false, by_lex: bylex, rev: false, limit: nil)
|
411
|
+
if by_score && by_lex
|
412
|
+
raise ArgumentError, "only one of :by_score or :by_lex can be specified"
|
413
|
+
end
|
414
|
+
|
415
|
+
args = [:zrangestore, dest_key, src_key, start, stop]
|
416
|
+
|
417
|
+
if by_score
|
418
|
+
args << "BYSCORE"
|
419
|
+
elsif by_lex
|
420
|
+
args << "BYLEX"
|
421
|
+
end
|
422
|
+
|
423
|
+
args << "REV" if rev
|
424
|
+
|
425
|
+
if limit
|
426
|
+
args << "LIMIT"
|
427
|
+
args.concat(limit.map { |l| Integer(l) })
|
428
|
+
end
|
429
|
+
|
430
|
+
send_command(args)
|
431
|
+
end
|
432
|
+
|
433
|
+
# Return a range of members in a sorted set, by index, with scores ordered
|
434
|
+
# from high to low.
|
435
|
+
#
|
436
|
+
# @example Retrieve all members from a sorted set
|
437
|
+
# redis.zrevrange("zset", 0, -1)
|
438
|
+
# # => ["b", "a"]
|
439
|
+
# @example Retrieve all members and their scores from a sorted set
|
440
|
+
# redis.zrevrange("zset", 0, -1, :with_scores => true)
|
441
|
+
# # => [["b", 64.0], ["a", 32.0]]
|
442
|
+
#
|
443
|
+
# @see #zrange
|
444
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
445
|
+
args = [:zrevrange, key, Integer(start), Integer(stop)]
|
446
|
+
|
447
|
+
if with_scores
|
448
|
+
args << "WITHSCORES"
|
449
|
+
block = FloatifyPairs
|
450
|
+
end
|
451
|
+
|
452
|
+
send_command(args, &block)
|
453
|
+
end
|
454
|
+
|
455
|
+
# Determine the index of a member in a sorted set.
|
456
|
+
#
|
457
|
+
# @param [String] key
|
458
|
+
# @param [String] member
|
459
|
+
# @return [Integer]
|
460
|
+
def zrank(key, member)
|
461
|
+
send_command([:zrank, key, member])
|
462
|
+
end
|
463
|
+
|
464
|
+
# Determine the index of a member in a sorted set, with scores ordered from
|
465
|
+
# high to low.
|
466
|
+
#
|
467
|
+
# @param [String] key
|
468
|
+
# @param [String] member
|
469
|
+
# @return [Integer]
|
470
|
+
def zrevrank(key, member)
|
471
|
+
send_command([:zrevrank, key, member])
|
472
|
+
end
|
473
|
+
|
474
|
+
# Remove all members in a sorted set within the given indexes.
|
475
|
+
#
|
476
|
+
# @example Remove first 5 members
|
477
|
+
# redis.zremrangebyrank("zset", 0, 4)
|
478
|
+
# # => 5
|
479
|
+
# @example Remove last 5 members
|
480
|
+
# redis.zremrangebyrank("zset", -5, -1)
|
481
|
+
# # => 5
|
482
|
+
#
|
483
|
+
# @param [String] key
|
484
|
+
# @param [Integer] start start index
|
485
|
+
# @param [Integer] stop stop index
|
486
|
+
# @return [Integer] number of members that were removed
|
487
|
+
def zremrangebyrank(key, start, stop)
|
488
|
+
send_command([:zremrangebyrank, key, start, stop])
|
489
|
+
end
|
490
|
+
|
491
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
492
|
+
#
|
493
|
+
# @example Count members matching a
|
494
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
495
|
+
# # => 1
|
496
|
+
# @example Count members matching a-z
|
497
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
498
|
+
# # => 26
|
499
|
+
#
|
500
|
+
# @param [String] key
|
501
|
+
# @param [String] min
|
502
|
+
# - inclusive minimum is specified by prefixing `(`
|
503
|
+
# - exclusive minimum is specified by prefixing `[`
|
504
|
+
# @param [String] max
|
505
|
+
# - inclusive maximum is specified by prefixing `(`
|
506
|
+
# - exclusive maximum is specified by prefixing `[`
|
507
|
+
#
|
508
|
+
# @return [Integer] number of members within the specified lexicographical range
|
509
|
+
def zlexcount(key, min, max)
|
510
|
+
send_command([:zlexcount, key, min, max])
|
511
|
+
end
|
512
|
+
|
513
|
+
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
514
|
+
#
|
515
|
+
# @example Retrieve members matching a
|
516
|
+
# redis.zrangebylex("zset", "[a", "[a\xff")
|
517
|
+
# # => ["aaren", "aarika", "abagael", "abby"]
|
518
|
+
# @example Retrieve the first 2 members matching a
|
519
|
+
# redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
|
520
|
+
# # => ["aaren", "aarika"]
|
521
|
+
#
|
522
|
+
# @param [String] key
|
523
|
+
# @param [String] min
|
524
|
+
# - inclusive minimum is specified by prefixing `(`
|
525
|
+
# - exclusive minimum is specified by prefixing `[`
|
526
|
+
# @param [String] max
|
527
|
+
# - inclusive maximum is specified by prefixing `(`
|
528
|
+
# - exclusive maximum is specified by prefixing `[`
|
529
|
+
# @param [Hash] options
|
530
|
+
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
531
|
+
# `count` members
|
532
|
+
#
|
533
|
+
# @return [Array<String>, Array<[String, Float]>]
|
534
|
+
def zrangebylex(key, min, max, limit: nil)
|
535
|
+
args = [:zrangebylex, key, min, max]
|
536
|
+
|
537
|
+
if limit
|
538
|
+
args << "LIMIT"
|
539
|
+
args.concat(limit.map { |l| Integer(l) })
|
540
|
+
end
|
541
|
+
|
542
|
+
send_command(args)
|
543
|
+
end
|
544
|
+
|
545
|
+
# Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
|
546
|
+
# Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
|
547
|
+
#
|
548
|
+
# @example Retrieve members matching a
|
549
|
+
# redis.zrevrangebylex("zset", "[a", "[a\xff")
|
550
|
+
# # => ["abbygail", "abby", "abagael", "aaren"]
|
551
|
+
# @example Retrieve the last 2 members matching a
|
552
|
+
# redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
|
553
|
+
# # => ["abbygail", "abby"]
|
554
|
+
#
|
555
|
+
# @see #zrangebylex
|
556
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
557
|
+
args = [:zrevrangebylex, key, max, min]
|
558
|
+
|
559
|
+
if limit
|
560
|
+
args << "LIMIT"
|
561
|
+
args.concat(limit.map { |l| Integer(l) })
|
562
|
+
end
|
563
|
+
|
564
|
+
send_command(args)
|
565
|
+
end
|
566
|
+
|
567
|
+
# Return a range of members in a sorted set, by score.
|
568
|
+
#
|
569
|
+
# @example Retrieve members with score `>= 5` and `< 100`
|
570
|
+
# redis.zrangebyscore("zset", "5", "(100")
|
571
|
+
# # => ["a", "b"]
|
572
|
+
# @example Retrieve the first 2 members with score `>= 0`
|
573
|
+
# redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
|
574
|
+
# # => ["a", "b"]
|
575
|
+
# @example Retrieve members and their scores with scores `> 5`
|
576
|
+
# redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
|
577
|
+
# # => [["a", 32.0], ["b", 64.0]]
|
578
|
+
#
|
579
|
+
# @param [String] key
|
580
|
+
# @param [String] min
|
581
|
+
# - inclusive minimum score is specified verbatim
|
582
|
+
# - exclusive minimum score is specified by prefixing `(`
|
583
|
+
# @param [String] max
|
584
|
+
# - inclusive maximum score is specified verbatim
|
585
|
+
# - exclusive maximum score is specified by prefixing `(`
|
586
|
+
# @param [Hash] options
|
587
|
+
# - `:with_scores => true`: include scores in output
|
588
|
+
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
589
|
+
# `count` members
|
590
|
+
#
|
591
|
+
# @return [Array<String>, Array<[String, Float]>]
|
592
|
+
# - when `:with_scores` is not specified, an array of members
|
593
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
594
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
595
|
+
args = [:zrangebyscore, key, min, max]
|
596
|
+
|
597
|
+
if with_scores
|
598
|
+
args << "WITHSCORES"
|
599
|
+
block = FloatifyPairs
|
600
|
+
end
|
601
|
+
|
602
|
+
if limit
|
603
|
+
args << "LIMIT"
|
604
|
+
args.concat(limit.map { |l| Integer(l) })
|
605
|
+
end
|
606
|
+
|
607
|
+
send_command(args, &block)
|
608
|
+
end
|
609
|
+
|
610
|
+
# Return a range of members in a sorted set, by score, with scores ordered
|
611
|
+
# from high to low.
|
612
|
+
#
|
613
|
+
# @example Retrieve members with score `< 100` and `>= 5`
|
614
|
+
# redis.zrevrangebyscore("zset", "(100", "5")
|
615
|
+
# # => ["b", "a"]
|
616
|
+
# @example Retrieve the first 2 members with score `<= 0`
|
617
|
+
# redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
|
618
|
+
# # => ["b", "a"]
|
619
|
+
# @example Retrieve members and their scores with scores `> 5`
|
620
|
+
# redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
|
621
|
+
# # => [["b", 64.0], ["a", 32.0]]
|
622
|
+
#
|
623
|
+
# @see #zrangebyscore
|
624
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
625
|
+
args = [:zrevrangebyscore, key, max, min]
|
626
|
+
|
627
|
+
if with_scores
|
628
|
+
args << "WITHSCORES"
|
629
|
+
block = FloatifyPairs
|
630
|
+
end
|
631
|
+
|
632
|
+
if limit
|
633
|
+
args << "LIMIT"
|
634
|
+
args.concat(limit.map { |l| Integer(l) })
|
635
|
+
end
|
636
|
+
|
637
|
+
send_command(args, &block)
|
638
|
+
end
|
639
|
+
|
640
|
+
# Remove all members in a sorted set within the given scores.
|
641
|
+
#
|
642
|
+
# @example Remove members with score `>= 5` and `< 100`
|
643
|
+
# redis.zremrangebyscore("zset", "5", "(100")
|
644
|
+
# # => 2
|
645
|
+
# @example Remove members with scores `> 5`
|
646
|
+
# redis.zremrangebyscore("zset", "(5", "+inf")
|
647
|
+
# # => 2
|
648
|
+
#
|
649
|
+
# @param [String] key
|
650
|
+
# @param [String] min
|
651
|
+
# - inclusive minimum score is specified verbatim
|
652
|
+
# - exclusive minimum score is specified by prefixing `(`
|
653
|
+
# @param [String] max
|
654
|
+
# - inclusive maximum score is specified verbatim
|
655
|
+
# - exclusive maximum score is specified by prefixing `(`
|
656
|
+
# @return [Integer] number of members that were removed
|
657
|
+
def zremrangebyscore(key, min, max)
|
658
|
+
send_command([:zremrangebyscore, key, min, max])
|
659
|
+
end
|
660
|
+
|
661
|
+
# Count the members in a sorted set with scores within the given values.
|
662
|
+
#
|
663
|
+
# @example Count members with score `>= 5` and `< 100`
|
664
|
+
# redis.zcount("zset", "5", "(100")
|
665
|
+
# # => 2
|
666
|
+
# @example Count members with scores `> 5`
|
667
|
+
# redis.zcount("zset", "(5", "+inf")
|
668
|
+
# # => 2
|
669
|
+
#
|
670
|
+
# @param [String] key
|
671
|
+
# @param [String] min
|
672
|
+
# - inclusive minimum score is specified verbatim
|
673
|
+
# - exclusive minimum score is specified by prefixing `(`
|
674
|
+
# @param [String] max
|
675
|
+
# - inclusive maximum score is specified verbatim
|
676
|
+
# - exclusive maximum score is specified by prefixing `(`
|
677
|
+
# @return [Integer] number of members in within the specified range
|
678
|
+
def zcount(key, min, max)
|
679
|
+
send_command([:zcount, key, min, max])
|
680
|
+
end
|
681
|
+
|
682
|
+
# Return the intersection of multiple sorted sets
|
683
|
+
#
|
684
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
685
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
686
|
+
# # => ["v1", "v2"]
|
687
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
688
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
689
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
690
|
+
#
|
691
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
692
|
+
# @param [Hash] options
|
693
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
694
|
+
# sorted sets
|
695
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
696
|
+
# - `:with_scores => true`: include scores in output
|
697
|
+
#
|
698
|
+
# @return [Array<String>, Array<[String, Float]>]
|
699
|
+
# - when `:with_scores` is not specified, an array of members
|
700
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
701
|
+
def zinter(*args)
|
702
|
+
_zsets_operation(:zinter, *args)
|
703
|
+
end
|
704
|
+
ruby2_keywords(:zinter) if respond_to?(:ruby2_keywords, true)
|
705
|
+
|
706
|
+
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
707
|
+
# key.
|
708
|
+
#
|
709
|
+
# @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
|
710
|
+
# redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
|
711
|
+
# # => 4
|
712
|
+
#
|
713
|
+
# @param [String] destination destination key
|
714
|
+
# @param [Array<String>] keys source keys
|
715
|
+
# @param [Hash] options
|
716
|
+
# - `:weights => [Array<Float>]`: weights to associate with source
|
717
|
+
# sorted sets
|
718
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max)
|
719
|
+
# @return [Integer] number of elements in the resulting sorted set
|
720
|
+
def zinterstore(*args)
|
721
|
+
_zsets_operation_store(:zinterstore, *args)
|
722
|
+
end
|
723
|
+
ruby2_keywords(:zinterstore) if respond_to?(:ruby2_keywords, true)
|
724
|
+
|
725
|
+
# Return the union of multiple sorted sets
|
726
|
+
#
|
727
|
+
# @example Retrieve the union of `2*zsetA` and `1*zsetB`
|
728
|
+
# redis.zunion("zsetA", "zsetB", :weights => [2.0, 1.0])
|
729
|
+
# # => ["v1", "v2"]
|
730
|
+
# @example Retrieve the union of `2*zsetA` and `1*zsetB`, and their scores
|
731
|
+
# redis.zunion("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
732
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
733
|
+
#
|
734
|
+
# @param [String, Array<String>] keys one or more keys to union
|
735
|
+
# @param [Hash] options
|
736
|
+
# - `:weights => [Array<Float>]`: weights to associate with source
|
737
|
+
# sorted sets
|
738
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max)
|
739
|
+
# - `:with_scores => true`: include scores in output
|
740
|
+
#
|
741
|
+
# @return [Array<String>, Array<[String, Float]>]
|
742
|
+
# - when `:with_scores` is not specified, an array of members
|
743
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
744
|
+
def zunion(*args)
|
745
|
+
_zsets_operation(:zunion, *args)
|
746
|
+
end
|
747
|
+
ruby2_keywords(:zunion) if respond_to?(:ruby2_keywords, true)
|
748
|
+
|
749
|
+
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
750
|
+
#
|
751
|
+
# @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
|
752
|
+
# redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
|
753
|
+
# # => 8
|
754
|
+
#
|
755
|
+
# @param [String] destination destination key
|
756
|
+
# @param [Array<String>] keys source keys
|
757
|
+
# @param [Hash] options
|
758
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
759
|
+
# sorted sets
|
760
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
761
|
+
# @return [Integer] number of elements in the resulting sorted set
|
762
|
+
def zunionstore(*args)
|
763
|
+
_zsets_operation_store(:zunionstore, *args)
|
764
|
+
end
|
765
|
+
ruby2_keywords(:zunionstore) if respond_to?(:ruby2_keywords, true)
|
766
|
+
|
767
|
+
# Return the difference between the first and all successive input sorted sets
|
768
|
+
#
|
769
|
+
# @example
|
770
|
+
# redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
|
771
|
+
# redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
|
772
|
+
# redis.zdiff("zsetA", "zsetB")
|
773
|
+
# => ["v1"]
|
774
|
+
# @example With scores
|
775
|
+
# redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
|
776
|
+
# redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
|
777
|
+
# redis.zdiff("zsetA", "zsetB", :with_scores => true)
|
778
|
+
# => [["v1", 1.0]]
|
779
|
+
#
|
780
|
+
# @param [String, Array<String>] keys one or more keys to compute the difference
|
781
|
+
# @param [Hash] options
|
782
|
+
# - `:with_scores => true`: include scores in output
|
783
|
+
#
|
784
|
+
# @return [Array<String>, Array<[String, Float]>]
|
785
|
+
# - when `:with_scores` is not specified, an array of members
|
786
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
787
|
+
def zdiff(*keys, with_scores: false)
|
788
|
+
_zsets_operation(:zdiff, *keys, with_scores: with_scores)
|
789
|
+
end
|
790
|
+
|
791
|
+
# Compute the difference between the first and all successive input sorted sets
|
792
|
+
# and store the resulting sorted set in a new key
|
793
|
+
#
|
794
|
+
# @example
|
795
|
+
# redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
|
796
|
+
# redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
|
797
|
+
# redis.zdiffstore("zsetA", "zsetB")
|
798
|
+
# # => 1
|
799
|
+
#
|
800
|
+
# @param [String] destination destination key
|
801
|
+
# @param [Array<String>] keys source keys
|
802
|
+
# @return [Integer] number of elements in the resulting sorted set
|
803
|
+
def zdiffstore(*args)
|
804
|
+
_zsets_operation_store(:zdiffstore, *args)
|
805
|
+
end
|
806
|
+
ruby2_keywords(:zdiffstore) if respond_to?(:ruby2_keywords, true)
|
807
|
+
|
808
|
+
# Scan a sorted set
|
809
|
+
#
|
810
|
+
# @example Retrieve the first batch of key/value pairs in a hash
|
811
|
+
# redis.zscan("zset", 0)
|
812
|
+
#
|
813
|
+
# @param [String, Integer] cursor the cursor of the iteration
|
814
|
+
# @param [Hash] options
|
815
|
+
# - `:match => String`: only return keys matching the pattern
|
816
|
+
# - `:count => Integer`: return count keys at most per iteration
|
817
|
+
#
|
818
|
+
# @return [String, Array<[String, Float]>] the next cursor and all found
|
819
|
+
# members and scores
|
820
|
+
def zscan(key, cursor, **options)
|
821
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
822
|
+
[reply[0], FloatifyPairs.call(reply[1])]
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
# Scan a sorted set
|
827
|
+
#
|
828
|
+
# @example Retrieve all of the members/scores in a sorted set
|
829
|
+
# redis.zscan_each("zset").to_a
|
830
|
+
# # => [["key70", "70"], ["key80", "80"]]
|
831
|
+
#
|
832
|
+
# @param [Hash] options
|
833
|
+
# - `:match => String`: only return keys matching the pattern
|
834
|
+
# - `:count => Integer`: return count keys at most per iteration
|
835
|
+
#
|
836
|
+
# @return [Enumerator] an enumerator for all found scores and members
|
837
|
+
def zscan_each(key, **options, &block)
|
838
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
839
|
+
|
840
|
+
cursor = 0
|
841
|
+
loop do
|
842
|
+
cursor, values = zscan(key, cursor, **options)
|
843
|
+
values.each(&block)
|
844
|
+
break if cursor == "0"
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
private
|
849
|
+
|
850
|
+
def _zsets_operation(cmd, *keys, weights: nil, aggregate: nil, with_scores: false)
|
851
|
+
keys.flatten!(1)
|
852
|
+
command = [cmd, keys.size].concat(keys)
|
853
|
+
|
854
|
+
if weights
|
855
|
+
command << "WEIGHTS"
|
856
|
+
command.concat(weights)
|
857
|
+
end
|
858
|
+
|
859
|
+
command << "AGGREGATE" << aggregate if aggregate
|
860
|
+
|
861
|
+
if with_scores
|
862
|
+
command << "WITHSCORES"
|
863
|
+
block = FloatifyPairs
|
864
|
+
end
|
865
|
+
|
866
|
+
send_command(command, &block)
|
867
|
+
end
|
868
|
+
|
869
|
+
def _zsets_operation_store(cmd, destination, keys, weights: nil, aggregate: nil)
|
870
|
+
keys.flatten!(1)
|
871
|
+
command = [cmd, destination, keys.size].concat(keys)
|
872
|
+
|
873
|
+
if weights
|
874
|
+
command << "WEIGHTS"
|
875
|
+
command.concat(weights)
|
876
|
+
end
|
877
|
+
|
878
|
+
command << "AGGREGATE" << aggregate if aggregate
|
879
|
+
|
880
|
+
send_command(command)
|
881
|
+
end
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|