redis 4.5.1 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -0
- data/README.md +10 -10
- data/lib/redis/client.rb +14 -13
- data/lib/redis/cluster/command.rb +4 -6
- data/lib/redis/cluster/node.rb +12 -0
- data/lib/redis/cluster.rb +24 -0
- data/lib/redis/commands/bitmaps.rb +63 -0
- data/lib/redis/commands/cluster.rb +45 -0
- data/lib/redis/commands/connection.rb +58 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +251 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +411 -0
- data/lib/redis/commands/lists.rb +289 -0
- data/lib/redis/commands/pubsub.rb +72 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +207 -0
- data/lib/redis/commands/sorted_sets.rb +804 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +92 -0
- data/lib/redis/commands.rb +242 -0
- data/lib/redis/connection/hiredis.rb +3 -2
- data/lib/redis/connection/ruby.rb +8 -5
- data/lib/redis/connection/synchrony.rb +10 -8
- data/lib/redis/connection.rb +1 -1
- data/lib/redis/distributed.rb +46 -9
- data/lib/redis/pipeline.rb +95 -2
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +133 -3675
- metadata +21 -4
data/lib/redis.rb
CHANGED
@@ -1,34 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "monitor"
|
4
|
-
|
4
|
+
require "redis/errors"
|
5
|
+
require "redis/commands"
|
5
6
|
|
6
7
|
class Redis
|
8
|
+
BASE_PATH = __dir__
|
7
9
|
@exists_returns_integer = true
|
8
10
|
|
11
|
+
Deprecated = Class.new(StandardError)
|
12
|
+
|
9
13
|
class << self
|
10
14
|
attr_reader :exists_returns_integer
|
15
|
+
attr_accessor :silence_deprecations, :raise_deprecations
|
11
16
|
|
12
17
|
def exists_returns_integer=(value)
|
13
18
|
unless value
|
14
|
-
|
19
|
+
deprecate!(
|
20
|
+
"`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
15
21
|
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
16
22
|
"`exists?` instead."
|
17
|
-
|
18
|
-
::Kernel.warn(message)
|
23
|
+
)
|
19
24
|
end
|
20
25
|
|
21
26
|
@exists_returns_integer = value
|
22
27
|
end
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
def deprecate!(message)
|
30
|
+
unless silence_deprecations
|
31
|
+
if raise_deprecations
|
32
|
+
raise Deprecated, message
|
33
|
+
else
|
34
|
+
::Kernel.warn(message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def current
|
40
|
+
deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
|
41
|
+
@current ||= Redis.new
|
42
|
+
end
|
26
43
|
|
27
|
-
|
28
|
-
|
44
|
+
def current=(redis)
|
45
|
+
deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
|
46
|
+
@current = redis
|
47
|
+
end
|
29
48
|
end
|
30
49
|
|
31
|
-
include
|
50
|
+
include Commands
|
32
51
|
|
33
52
|
# Create a new client instance
|
34
53
|
#
|
@@ -64,12 +83,7 @@ class Redis
|
|
64
83
|
client = @cluster_mode ? Cluster : Client
|
65
84
|
@original_client = @client = client.new(options)
|
66
85
|
@queue = Hash.new { |h, k| h[k] = [] }
|
67
|
-
|
68
|
-
super() # Monitor#initialize
|
69
|
-
end
|
70
|
-
|
71
|
-
def synchronize
|
72
|
-
mon_synchronize { yield(@client) }
|
86
|
+
@monitor = Monitor.new
|
73
87
|
end
|
74
88
|
|
75
89
|
# Run code with the client reconnecting
|
@@ -95,37 +109,33 @@ class Redis
|
|
95
109
|
end
|
96
110
|
alias disconnect! close
|
97
111
|
|
98
|
-
#
|
99
|
-
#
|
100
|
-
# Replies are converted to Ruby objects according to the RESP protocol, so
|
101
|
-
# you can expect a Ruby array, integer or nil when Redis sends one. Higher
|
102
|
-
# level transformations, such as converting an array of pairs into a Ruby
|
103
|
-
# hash, are up to consumers.
|
104
|
-
#
|
105
|
-
# Redis error replies are raised as Ruby exceptions.
|
106
|
-
def call(*command)
|
107
|
-
synchronize do |client|
|
108
|
-
client.call(command)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# Queues a command for pipelining.
|
112
|
+
# @deprecated Queues a command for pipelining.
|
113
113
|
#
|
114
114
|
# Commands in the queue are executed with the Redis#commit method.
|
115
115
|
#
|
116
116
|
# See http://redis.io/topics/pipelining for more details.
|
117
117
|
#
|
118
118
|
def queue(*command)
|
119
|
+
::Redis.deprecate!(
|
120
|
+
"Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
|
121
|
+
"(called from: #{caller(1, 1).first})"
|
122
|
+
)
|
123
|
+
|
119
124
|
synchronize do
|
120
125
|
@queue[Thread.current.object_id] << command
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
124
|
-
# Sends all commands in the queue.
|
129
|
+
# @deprecated Sends all commands in the queue.
|
125
130
|
#
|
126
131
|
# See http://redis.io/topics/pipelining for more details.
|
127
132
|
#
|
128
133
|
def commit
|
134
|
+
::Redis.deprecate!(
|
135
|
+
"Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
|
136
|
+
"(called from: #{Kernel.caller(1, 1).first})"
|
137
|
+
)
|
138
|
+
|
129
139
|
synchronize do |client|
|
130
140
|
begin
|
131
141
|
pipeline = Pipeline.new(client)
|
@@ -144,3693 +154,141 @@ class Redis
|
|
144
154
|
@client
|
145
155
|
end
|
146
156
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
# @see https://redis.io/commands/auth AUTH command
|
153
|
-
def auth(*args)
|
154
|
-
synchronize do |client|
|
155
|
-
client.call([:auth, *args])
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Change the selected database for the current connection.
|
160
|
-
#
|
161
|
-
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
162
|
-
# @return [String] `OK`
|
163
|
-
def select(db)
|
164
|
-
synchronize do |client|
|
165
|
-
client.db = db
|
166
|
-
client.call([:select, db])
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# Ping the server.
|
171
|
-
#
|
172
|
-
# @param [optional, String] message
|
173
|
-
# @return [String] `PONG`
|
174
|
-
def ping(message = nil)
|
175
|
-
synchronize do |client|
|
176
|
-
client.call([:ping, message].compact)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# Echo the given string.
|
181
|
-
#
|
182
|
-
# @param [String] value
|
183
|
-
# @return [String]
|
184
|
-
def echo(value)
|
185
|
-
synchronize do |client|
|
186
|
-
client.call([:echo, value])
|
157
|
+
def pipelined(&block)
|
158
|
+
deprecation_displayed = false
|
159
|
+
if block&.arity == 0
|
160
|
+
Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
|
161
|
+
deprecation_displayed = true
|
187
162
|
end
|
188
|
-
end
|
189
163
|
|
190
|
-
|
191
|
-
#
|
192
|
-
# @return [String] `OK`
|
193
|
-
def quit
|
194
|
-
synchronize do |client|
|
164
|
+
synchronize do |prior_client|
|
195
165
|
begin
|
196
|
-
|
197
|
-
|
166
|
+
pipeline = Pipeline.new(prior_client)
|
167
|
+
@client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
|
168
|
+
pipelined_connection = PipelinedConnection.new(pipeline)
|
169
|
+
yield pipelined_connection
|
170
|
+
prior_client.call_pipeline(pipeline)
|
198
171
|
ensure
|
199
|
-
client
|
172
|
+
@client = prior_client
|
200
173
|
end
|
201
174
|
end
|
202
175
|
end
|
203
176
|
|
204
|
-
#
|
205
|
-
#
|
206
|
-
# @return [String] `OK`
|
207
|
-
def bgrewriteaof
|
208
|
-
synchronize do |client|
|
209
|
-
client.call([:bgrewriteaof])
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
# Asynchronously save the dataset to disk.
|
214
|
-
#
|
215
|
-
# @return [String] `OK`
|
216
|
-
def bgsave
|
217
|
-
synchronize do |client|
|
218
|
-
client.call([:bgsave])
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# Get or set server configuration parameters.
|
177
|
+
# Mark the start of a transaction block.
|
223
178
|
#
|
224
|
-
#
|
225
|
-
# @return [String, Hash] string reply, or hash when retrieving more than one
|
226
|
-
# property with `CONFIG GET`
|
227
|
-
def config(action, *args)
|
228
|
-
synchronize do |client|
|
229
|
-
client.call([:config, action] + args) do |reply|
|
230
|
-
if reply.is_a?(Array) && action == :get
|
231
|
-
Hashify.call(reply)
|
232
|
-
else
|
233
|
-
reply
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# Manage client connections.
|
179
|
+
# Passing a block is optional.
|
240
180
|
#
|
241
|
-
# @
|
242
|
-
#
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
if subcommand.to_s == "list"
|
247
|
-
reply.lines.map do |line|
|
248
|
-
entries = line.chomp.split(/[ =]/)
|
249
|
-
Hash[entries.each_slice(2).to_a]
|
250
|
-
end
|
251
|
-
else
|
252
|
-
reply
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
# Return the number of keys in the selected database.
|
181
|
+
# @example With a block
|
182
|
+
# redis.multi do |multi|
|
183
|
+
# multi.set("key", "value")
|
184
|
+
# multi.incr("counter")
|
185
|
+
# end # => ["OK", 6]
|
259
186
|
#
|
260
|
-
# @
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
client.call([:debug] + args)
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
# Remove all keys from all databases.
|
187
|
+
# @example Without a block
|
188
|
+
# redis.multi
|
189
|
+
# # => "OK"
|
190
|
+
# redis.set("key", "value")
|
191
|
+
# # => "QUEUED"
|
192
|
+
# redis.incr("counter")
|
193
|
+
# # => "QUEUED"
|
194
|
+
# redis.exec
|
195
|
+
# # => ["OK", 6]
|
274
196
|
#
|
275
|
-
# @
|
276
|
-
#
|
277
|
-
# @
|
278
|
-
def flushall(options = nil)
|
279
|
-
synchronize do |client|
|
280
|
-
if options && options[:async]
|
281
|
-
client.call(%i[flushall async])
|
282
|
-
else
|
283
|
-
client.call([:flushall])
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
# Remove all keys from the current database.
|
197
|
+
# @yield [multi] the commands that are called inside this block are cached
|
198
|
+
# and written to the server upon returning from it
|
199
|
+
# @yieldparam [Redis] multi `self`
|
289
200
|
#
|
290
|
-
# @
|
291
|
-
# -
|
292
|
-
#
|
293
|
-
def flushdb(options = nil)
|
294
|
-
synchronize do |client|
|
295
|
-
if options && options[:async]
|
296
|
-
client.call(%i[flushdb async])
|
297
|
-
else
|
298
|
-
client.call([:flushdb])
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
# Get information and statistics about the server.
|
201
|
+
# @return [String, Array<...>]
|
202
|
+
# - when a block is not given, `OK`
|
203
|
+
# - when a block is given, an array with replies
|
304
204
|
#
|
305
|
-
# @
|
306
|
-
# @
|
307
|
-
def
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
if cmd && cmd.to_s == "commandstats"
|
314
|
-
# Extract nested hashes for INFO COMMANDSTATS
|
315
|
-
reply = Hash[reply.map do |k, v|
|
316
|
-
v = v.split(",").map { |e| e.split("=") }
|
317
|
-
[k[/^cmdstat_(.*)$/, 1], Hash[v]]
|
318
|
-
end]
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
reply
|
205
|
+
# @see #watch
|
206
|
+
# @see #unwatch
|
207
|
+
def multi(&block)
|
208
|
+
if block_given?
|
209
|
+
deprecation_displayed = false
|
210
|
+
if block&.arity == 0
|
211
|
+
Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
|
212
|
+
deprecation_displayed = true
|
323
213
|
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
# Get the UNIX time stamp of the last successful save to disk.
|
328
|
-
#
|
329
|
-
# @return [Integer]
|
330
|
-
def lastsave
|
331
|
-
synchronize do |client|
|
332
|
-
client.call([:lastsave])
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
# Listen for all requests received by the server in real time.
|
337
|
-
#
|
338
|
-
# There is no way to interrupt this command.
|
339
|
-
#
|
340
|
-
# @yield a block to be called for every line of output
|
341
|
-
# @yieldparam [String] line timestamp and command that was executed
|
342
|
-
def monitor(&block)
|
343
|
-
synchronize do |client|
|
344
|
-
client.call_loop([:monitor], &block)
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
# Synchronously save the dataset to disk.
|
349
|
-
#
|
350
|
-
# @return [String]
|
351
|
-
def save
|
352
|
-
synchronize do |client|
|
353
|
-
client.call([:save])
|
354
|
-
end
|
355
|
-
end
|
356
214
|
|
357
|
-
|
358
|
-
def shutdown
|
359
|
-
synchronize do |client|
|
360
|
-
client.with_reconnect(false) do
|
215
|
+
synchronize do |prior_client|
|
361
216
|
begin
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
217
|
+
pipeline = Pipeline::Multi.new(prior_client)
|
218
|
+
@client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
|
219
|
+
pipelined_connection = PipelinedConnection.new(pipeline)
|
220
|
+
yield pipelined_connection
|
221
|
+
prior_client.call_pipeline(pipeline)
|
222
|
+
ensure
|
223
|
+
@client = prior_client
|
366
224
|
end
|
367
225
|
end
|
226
|
+
else
|
227
|
+
send_command([:multi])
|
368
228
|
end
|
369
229
|
end
|
370
230
|
|
371
|
-
|
372
|
-
|
373
|
-
synchronize do |client|
|
374
|
-
client.call([:slaveof, host, port])
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
# Interact with the slowlog (get, len, reset)
|
379
|
-
#
|
380
|
-
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
381
|
-
# @param [Integer] length maximum number of entries to return
|
382
|
-
# @return [Array<String>, Integer, String] depends on subcommand
|
383
|
-
def slowlog(subcommand, length = nil)
|
384
|
-
synchronize do |client|
|
385
|
-
args = [:slowlog, subcommand]
|
386
|
-
args << length if length
|
387
|
-
client.call args
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
# Internal command used for replication.
|
392
|
-
def sync
|
393
|
-
synchronize do |client|
|
394
|
-
client.call([:sync])
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
# Return the server time.
|
399
|
-
#
|
400
|
-
# @example
|
401
|
-
# r.time # => [ 1333093196, 606806 ]
|
402
|
-
#
|
403
|
-
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
404
|
-
# microseconds in the current second
|
405
|
-
def time
|
406
|
-
synchronize do |client|
|
407
|
-
client.call([:time]) do |reply|
|
408
|
-
reply&.map(&:to_i)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
# Remove the expiration from a key.
|
414
|
-
#
|
415
|
-
# @param [String] key
|
416
|
-
# @return [Boolean] whether the timeout was removed or not
|
417
|
-
def persist(key)
|
418
|
-
synchronize do |client|
|
419
|
-
client.call([:persist, key], &Boolify)
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
# Set a key's time to live in seconds.
|
424
|
-
#
|
425
|
-
# @param [String] key
|
426
|
-
# @param [Integer] seconds time to live
|
427
|
-
# @return [Boolean] whether the timeout was set or not
|
428
|
-
def expire(key, seconds)
|
429
|
-
synchronize do |client|
|
430
|
-
client.call([:expire, key, seconds], &Boolify)
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
# Set the expiration for a key as a UNIX timestamp.
|
435
|
-
#
|
436
|
-
# @param [String] key
|
437
|
-
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
438
|
-
# @return [Boolean] whether the timeout was set or not
|
439
|
-
def expireat(key, unix_time)
|
440
|
-
synchronize do |client|
|
441
|
-
client.call([:expireat, key, unix_time], &Boolify)
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
# Get the time to live (in seconds) for a key.
|
446
|
-
#
|
447
|
-
# @param [String] key
|
448
|
-
# @return [Integer] remaining time to live in seconds.
|
449
|
-
#
|
450
|
-
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
451
|
-
# the key exist but has no associated expire.
|
452
|
-
#
|
453
|
-
# Starting with Redis 2.8 the return value in case of error changed:
|
454
|
-
#
|
455
|
-
# - The command returns -2 if the key does not exist.
|
456
|
-
# - The command returns -1 if the key exists but has no associated expire.
|
457
|
-
def ttl(key)
|
458
|
-
synchronize do |client|
|
459
|
-
client.call([:ttl, key])
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
# Set a key's time to live in milliseconds.
|
464
|
-
#
|
465
|
-
# @param [String] key
|
466
|
-
# @param [Integer] milliseconds time to live
|
467
|
-
# @return [Boolean] whether the timeout was set or not
|
468
|
-
def pexpire(key, milliseconds)
|
469
|
-
synchronize do |client|
|
470
|
-
client.call([:pexpire, key, milliseconds], &Boolify)
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
475
|
-
#
|
476
|
-
# @param [String] key
|
477
|
-
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
478
|
-
# @return [Boolean] whether the timeout was set or not
|
479
|
-
def pexpireat(key, ms_unix_time)
|
480
|
-
synchronize do |client|
|
481
|
-
client.call([:pexpireat, key, ms_unix_time], &Boolify)
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
# Get the time to live (in milliseconds) for a key.
|
486
|
-
#
|
487
|
-
# @param [String] key
|
488
|
-
# @return [Integer] remaining time to live in milliseconds
|
489
|
-
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
490
|
-
# the key exist but has no associated expire.
|
491
|
-
#
|
492
|
-
# Starting with Redis 2.8 the return value in case of error changed:
|
493
|
-
#
|
494
|
-
# - The command returns -2 if the key does not exist.
|
495
|
-
# - The command returns -1 if the key exists but has no associated expire.
|
496
|
-
def pttl(key)
|
497
|
-
synchronize do |client|
|
498
|
-
client.call([:pttl, key])
|
499
|
-
end
|
231
|
+
def id
|
232
|
+
@original_client.id
|
500
233
|
end
|
501
234
|
|
502
|
-
|
503
|
-
|
504
|
-
# @param [String] key
|
505
|
-
# @return [String] serialized_value
|
506
|
-
def dump(key)
|
507
|
-
synchronize do |client|
|
508
|
-
client.call([:dump, key])
|
509
|
-
end
|
235
|
+
def inspect
|
236
|
+
"#<Redis client v#{Redis::VERSION} for #{id}>"
|
510
237
|
end
|
511
238
|
|
512
|
-
|
513
|
-
|
514
|
-
# @param [String] key
|
515
|
-
# @param [String] ttl
|
516
|
-
# @param [String] serialized_value
|
517
|
-
# @param [Hash] options
|
518
|
-
# - `:replace => Boolean`: if false, raises an error if key already exists
|
519
|
-
# @raise [Redis::CommandError]
|
520
|
-
# @return [String] `"OK"`
|
521
|
-
def restore(key, ttl, serialized_value, replace: nil)
|
522
|
-
args = [:restore, key, ttl, serialized_value]
|
523
|
-
args << 'REPLACE' if replace
|
524
|
-
|
525
|
-
synchronize do |client|
|
526
|
-
client.call(args)
|
527
|
-
end
|
239
|
+
def dup
|
240
|
+
self.class.new(@options)
|
528
241
|
end
|
529
242
|
|
530
|
-
|
531
|
-
|
532
|
-
# @param [String, Array<String>] key
|
533
|
-
# @param [Hash] options
|
534
|
-
# - `:host => String`: host of instance to migrate to
|
535
|
-
# - `:port => Integer`: port of instance to migrate to
|
536
|
-
# - `:db => Integer`: database to migrate to (default: same as source)
|
537
|
-
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
538
|
-
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
539
|
-
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
540
|
-
# @return [String] `"OK"`
|
541
|
-
def migrate(key, options)
|
542
|
-
args = [:migrate]
|
543
|
-
args << (options[:host] || raise(':host not specified'))
|
544
|
-
args << (options[:port] || raise(':port not specified'))
|
545
|
-
args << (key.is_a?(String) ? key : '')
|
546
|
-
args << (options[:db] || @client.db).to_i
|
547
|
-
args << (options[:timeout] || @client.timeout).to_i
|
548
|
-
args << 'COPY' if options[:copy]
|
549
|
-
args << 'REPLACE' if options[:replace]
|
550
|
-
args += ['KEYS', *key] if key.is_a?(Array)
|
243
|
+
def connection
|
244
|
+
return @original_client.connection_info if @cluster_mode
|
551
245
|
|
552
|
-
|
246
|
+
{
|
247
|
+
host: @original_client.host,
|
248
|
+
port: @original_client.port,
|
249
|
+
db: @original_client.db,
|
250
|
+
id: @original_client.id,
|
251
|
+
location: @original_client.location
|
252
|
+
}
|
553
253
|
end
|
554
254
|
|
555
|
-
|
556
|
-
#
|
557
|
-
# @param [String, Array<String>] keys
|
558
|
-
# @return [Integer] number of keys that were deleted
|
559
|
-
def del(*keys)
|
560
|
-
keys.flatten!(1)
|
561
|
-
return 0 if keys.empty?
|
255
|
+
private
|
562
256
|
|
563
|
-
|
564
|
-
|
565
|
-
end
|
257
|
+
def synchronize
|
258
|
+
@monitor.synchronize { yield(@client) }
|
566
259
|
end
|
567
260
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
# @return [Integer] number of keys that were unlinked
|
572
|
-
def unlink(*keys)
|
573
|
-
synchronize do |client|
|
574
|
-
client.call([:unlink] + keys)
|
261
|
+
def send_command(command, &block)
|
262
|
+
@monitor.synchronize do
|
263
|
+
@client.call(command, &block)
|
575
264
|
end
|
576
265
|
end
|
577
266
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
# @return [Integer]
|
582
|
-
def exists(*keys)
|
583
|
-
if !Redis.exists_returns_integer && keys.size == 1
|
584
|
-
if Redis.exists_returns_integer.nil?
|
585
|
-
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
586
|
-
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
587
|
-
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
588
|
-
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
589
|
-
"(#{::Kernel.caller(1, 1).first})\n"
|
590
|
-
|
591
|
-
::Kernel.warn(message)
|
592
|
-
end
|
593
|
-
|
594
|
-
exists?(*keys)
|
595
|
-
else
|
596
|
-
_exists(*keys)
|
267
|
+
def send_blocking_command(command, timeout, &block)
|
268
|
+
@monitor.synchronize do
|
269
|
+
@client.call_with_timeout(command, timeout, &block)
|
597
270
|
end
|
598
271
|
end
|
599
272
|
|
600
|
-
def
|
601
|
-
|
602
|
-
client.call([:exists, *keys])
|
603
|
-
end
|
604
|
-
end
|
273
|
+
def _subscription(method, timeout, channels, block)
|
274
|
+
return @client.call([method] + channels) if subscribed?
|
605
275
|
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
client.call([:exists, *keys]) do |value|
|
613
|
-
value > 0
|
276
|
+
begin
|
277
|
+
original, @client = @client, SubscribedClient.new(@client)
|
278
|
+
if timeout > 0
|
279
|
+
@client.send(method, timeout, *channels, &block)
|
280
|
+
else
|
281
|
+
@client.send(method, *channels, &block)
|
614
282
|
end
|
283
|
+
ensure
|
284
|
+
@client = original
|
615
285
|
end
|
616
286
|
end
|
617
|
-
|
618
|
-
# Find all keys matching the given pattern.
|
619
|
-
#
|
620
|
-
# @param [String] pattern
|
621
|
-
# @return [Array<String>]
|
622
|
-
def keys(pattern = "*")
|
623
|
-
synchronize do |client|
|
624
|
-
client.call([:keys, pattern]) do |reply|
|
625
|
-
if reply.is_a?(String)
|
626
|
-
reply.split(" ")
|
627
|
-
else
|
628
|
-
reply
|
629
|
-
end
|
630
|
-
end
|
631
|
-
end
|
632
|
-
end
|
633
|
-
|
634
|
-
# Move a key to another database.
|
635
|
-
#
|
636
|
-
# @example Move a key to another database
|
637
|
-
# redis.set "foo", "bar"
|
638
|
-
# # => "OK"
|
639
|
-
# redis.move "foo", 2
|
640
|
-
# # => true
|
641
|
-
# redis.exists "foo"
|
642
|
-
# # => false
|
643
|
-
# redis.select 2
|
644
|
-
# # => "OK"
|
645
|
-
# redis.exists "foo"
|
646
|
-
# # => true
|
647
|
-
# redis.get "foo"
|
648
|
-
# # => "bar"
|
649
|
-
#
|
650
|
-
# @param [String] key
|
651
|
-
# @param [Integer] db
|
652
|
-
# @return [Boolean] whether the key was moved or not
|
653
|
-
def move(key, db)
|
654
|
-
synchronize do |client|
|
655
|
-
client.call([:move, key, db], &Boolify)
|
656
|
-
end
|
657
|
-
end
|
658
|
-
|
659
|
-
def object(*args)
|
660
|
-
synchronize do |client|
|
661
|
-
client.call([:object] + args)
|
662
|
-
end
|
663
|
-
end
|
664
|
-
|
665
|
-
# Return a random key from the keyspace.
|
666
|
-
#
|
667
|
-
# @return [String]
|
668
|
-
def randomkey
|
669
|
-
synchronize do |client|
|
670
|
-
client.call([:randomkey])
|
671
|
-
end
|
672
|
-
end
|
673
|
-
|
674
|
-
# Rename a key. If the new key already exists it is overwritten.
|
675
|
-
#
|
676
|
-
# @param [String] old_name
|
677
|
-
# @param [String] new_name
|
678
|
-
# @return [String] `OK`
|
679
|
-
def rename(old_name, new_name)
|
680
|
-
synchronize do |client|
|
681
|
-
client.call([:rename, old_name, new_name])
|
682
|
-
end
|
683
|
-
end
|
684
|
-
|
685
|
-
# Rename a key, only if the new key does not exist.
|
686
|
-
#
|
687
|
-
# @param [String] old_name
|
688
|
-
# @param [String] new_name
|
689
|
-
# @return [Boolean] whether the key was renamed or not
|
690
|
-
def renamenx(old_name, new_name)
|
691
|
-
synchronize do |client|
|
692
|
-
client.call([:renamenx, old_name, new_name], &Boolify)
|
693
|
-
end
|
694
|
-
end
|
695
|
-
|
696
|
-
# Sort the elements in a list, set or sorted set.
|
697
|
-
#
|
698
|
-
# @example Retrieve the first 2 elements from an alphabetically sorted "list"
|
699
|
-
# redis.sort("list", :order => "alpha", :limit => [0, 2])
|
700
|
-
# # => ["a", "b"]
|
701
|
-
# @example Store an alphabetically descending list in "target"
|
702
|
-
# redis.sort("list", :order => "desc alpha", :store => "target")
|
703
|
-
# # => 26
|
704
|
-
#
|
705
|
-
# @param [String] key
|
706
|
-
# @param [Hash] options
|
707
|
-
# - `:by => String`: use external key to sort elements by
|
708
|
-
# - `:limit => [offset, count]`: skip `offset` elements, return a maximum
|
709
|
-
# of `count` elements
|
710
|
-
# - `:get => [String, Array<String>]`: single key or array of keys to
|
711
|
-
# retrieve per element in the result
|
712
|
-
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
713
|
-
# - `:store => String`: key to store the result at
|
714
|
-
#
|
715
|
-
# @return [Array<String>, Array<Array<String>>, Integer]
|
716
|
-
# - when `:get` is not specified, or holds a single element, an array of elements
|
717
|
-
# - when `:get` is specified, and holds more than one element, an array of
|
718
|
-
# elements where every element is an array with the result for every
|
719
|
-
# element specified in `:get`
|
720
|
-
# - when `:store` is specified, the number of elements in the stored result
|
721
|
-
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
722
|
-
args = [:sort, key]
|
723
|
-
args << "BY" << by if by
|
724
|
-
|
725
|
-
if limit
|
726
|
-
args << "LIMIT"
|
727
|
-
args.concat(limit)
|
728
|
-
end
|
729
|
-
|
730
|
-
get = Array(get)
|
731
|
-
get.each do |item|
|
732
|
-
args << "GET" << item
|
733
|
-
end
|
734
|
-
|
735
|
-
args.concat(order.split(" ")) if order
|
736
|
-
args << "STORE" << store if store
|
737
|
-
|
738
|
-
synchronize do |client|
|
739
|
-
client.call(args) do |reply|
|
740
|
-
if get.size > 1 && !store
|
741
|
-
reply.each_slice(get.size).to_a if reply
|
742
|
-
else
|
743
|
-
reply
|
744
|
-
end
|
745
|
-
end
|
746
|
-
end
|
747
|
-
end
|
748
|
-
|
749
|
-
# Determine the type stored at key.
|
750
|
-
#
|
751
|
-
# @param [String] key
|
752
|
-
# @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
|
753
|
-
def type(key)
|
754
|
-
synchronize do |client|
|
755
|
-
client.call([:type, key])
|
756
|
-
end
|
757
|
-
end
|
758
|
-
|
759
|
-
# Decrement the integer value of a key by one.
|
760
|
-
#
|
761
|
-
# @example
|
762
|
-
# redis.decr("value")
|
763
|
-
# # => 4
|
764
|
-
#
|
765
|
-
# @param [String] key
|
766
|
-
# @return [Integer] value after decrementing it
|
767
|
-
def decr(key)
|
768
|
-
synchronize do |client|
|
769
|
-
client.call([:decr, key])
|
770
|
-
end
|
771
|
-
end
|
772
|
-
|
773
|
-
# Decrement the integer value of a key by the given number.
|
774
|
-
#
|
775
|
-
# @example
|
776
|
-
# redis.decrby("value", 5)
|
777
|
-
# # => 0
|
778
|
-
#
|
779
|
-
# @param [String] key
|
780
|
-
# @param [Integer] decrement
|
781
|
-
# @return [Integer] value after decrementing it
|
782
|
-
def decrby(key, decrement)
|
783
|
-
synchronize do |client|
|
784
|
-
client.call([:decrby, key, decrement])
|
785
|
-
end
|
786
|
-
end
|
787
|
-
|
788
|
-
# Increment the integer value of a key by one.
|
789
|
-
#
|
790
|
-
# @example
|
791
|
-
# redis.incr("value")
|
792
|
-
# # => 6
|
793
|
-
#
|
794
|
-
# @param [String] key
|
795
|
-
# @return [Integer] value after incrementing it
|
796
|
-
def incr(key)
|
797
|
-
synchronize do |client|
|
798
|
-
client.call([:incr, key])
|
799
|
-
end
|
800
|
-
end
|
801
|
-
|
802
|
-
# Increment the integer value of a key by the given integer number.
|
803
|
-
#
|
804
|
-
# @example
|
805
|
-
# redis.incrby("value", 5)
|
806
|
-
# # => 10
|
807
|
-
#
|
808
|
-
# @param [String] key
|
809
|
-
# @param [Integer] increment
|
810
|
-
# @return [Integer] value after incrementing it
|
811
|
-
def incrby(key, increment)
|
812
|
-
synchronize do |client|
|
813
|
-
client.call([:incrby, key, increment])
|
814
|
-
end
|
815
|
-
end
|
816
|
-
|
817
|
-
# Increment the numeric value of a key by the given float number.
|
818
|
-
#
|
819
|
-
# @example
|
820
|
-
# redis.incrbyfloat("value", 1.23)
|
821
|
-
# # => 1.23
|
822
|
-
#
|
823
|
-
# @param [String] key
|
824
|
-
# @param [Float] increment
|
825
|
-
# @return [Float] value after incrementing it
|
826
|
-
def incrbyfloat(key, increment)
|
827
|
-
synchronize do |client|
|
828
|
-
client.call([:incrbyfloat, key, increment], &Floatify)
|
829
|
-
end
|
830
|
-
end
|
831
|
-
|
832
|
-
# Set the string value of a key.
|
833
|
-
#
|
834
|
-
# @param [String] key
|
835
|
-
# @param [String] value
|
836
|
-
# @param [Hash] options
|
837
|
-
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
838
|
-
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
839
|
-
# - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
|
840
|
-
# - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
|
841
|
-
# - `:nx => true`: Only set the key if it does not already exist.
|
842
|
-
# - `:xx => true`: Only set the key if it already exist.
|
843
|
-
# - `:keepttl => true`: Retain the time to live associated with the key.
|
844
|
-
# - `:get => true`: Return the old string stored at key, or nil if key did not exist.
|
845
|
-
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
846
|
-
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
847
|
-
args = [:set, key, value.to_s]
|
848
|
-
args << "EX" << ex if ex
|
849
|
-
args << "PX" << px if px
|
850
|
-
args << "EXAT" << exat if exat
|
851
|
-
args << "PXAT" << pxat if pxat
|
852
|
-
args << "NX" if nx
|
853
|
-
args << "XX" if xx
|
854
|
-
args << "KEEPTTL" if keepttl
|
855
|
-
args << "GET" if get
|
856
|
-
|
857
|
-
synchronize do |client|
|
858
|
-
if nx || xx
|
859
|
-
client.call(args, &BoolifySet)
|
860
|
-
else
|
861
|
-
client.call(args)
|
862
|
-
end
|
863
|
-
end
|
864
|
-
end
|
865
|
-
|
866
|
-
# Set the time to live in seconds of a key.
|
867
|
-
#
|
868
|
-
# @param [String] key
|
869
|
-
# @param [Integer] ttl
|
870
|
-
# @param [String] value
|
871
|
-
# @return [String] `"OK"`
|
872
|
-
def setex(key, ttl, value)
|
873
|
-
synchronize do |client|
|
874
|
-
client.call([:setex, key, ttl, value.to_s])
|
875
|
-
end
|
876
|
-
end
|
877
|
-
|
878
|
-
# Set the time to live in milliseconds of a key.
|
879
|
-
#
|
880
|
-
# @param [String] key
|
881
|
-
# @param [Integer] ttl
|
882
|
-
# @param [String] value
|
883
|
-
# @return [String] `"OK"`
|
884
|
-
def psetex(key, ttl, value)
|
885
|
-
synchronize do |client|
|
886
|
-
client.call([:psetex, key, ttl, value.to_s])
|
887
|
-
end
|
888
|
-
end
|
889
|
-
|
890
|
-
# Set the value of a key, only if the key does not exist.
|
891
|
-
#
|
892
|
-
# @param [String] key
|
893
|
-
# @param [String] value
|
894
|
-
# @return [Boolean] whether the key was set or not
|
895
|
-
def setnx(key, value)
|
896
|
-
synchronize do |client|
|
897
|
-
client.call([:setnx, key, value.to_s], &Boolify)
|
898
|
-
end
|
899
|
-
end
|
900
|
-
|
901
|
-
# Set one or more values.
|
902
|
-
#
|
903
|
-
# @example
|
904
|
-
# redis.mset("key1", "v1", "key2", "v2")
|
905
|
-
# # => "OK"
|
906
|
-
#
|
907
|
-
# @param [Array<String>] args array of keys and values
|
908
|
-
# @return [String] `"OK"`
|
909
|
-
#
|
910
|
-
# @see #mapped_mset
|
911
|
-
def mset(*args)
|
912
|
-
synchronize do |client|
|
913
|
-
client.call([:mset] + args)
|
914
|
-
end
|
915
|
-
end
|
916
|
-
|
917
|
-
# Set one or more values.
|
918
|
-
#
|
919
|
-
# @example
|
920
|
-
# redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
|
921
|
-
# # => "OK"
|
922
|
-
#
|
923
|
-
# @param [Hash] hash keys mapping to values
|
924
|
-
# @return [String] `"OK"`
|
925
|
-
#
|
926
|
-
# @see #mset
|
927
|
-
def mapped_mset(hash)
|
928
|
-
mset(hash.to_a.flatten)
|
929
|
-
end
|
930
|
-
|
931
|
-
# Set one or more values, only if none of the keys exist.
|
932
|
-
#
|
933
|
-
# @example
|
934
|
-
# redis.msetnx("key1", "v1", "key2", "v2")
|
935
|
-
# # => true
|
936
|
-
#
|
937
|
-
# @param [Array<String>] args array of keys and values
|
938
|
-
# @return [Boolean] whether or not all values were set
|
939
|
-
#
|
940
|
-
# @see #mapped_msetnx
|
941
|
-
def msetnx(*args)
|
942
|
-
synchronize do |client|
|
943
|
-
client.call([:msetnx, *args], &Boolify)
|
944
|
-
end
|
945
|
-
end
|
946
|
-
|
947
|
-
# Set one or more values, only if none of the keys exist.
|
948
|
-
#
|
949
|
-
# @example
|
950
|
-
# redis.mapped_msetnx({ "key1" => "v1", "key2" => "v2" })
|
951
|
-
# # => true
|
952
|
-
#
|
953
|
-
# @param [Hash] hash keys mapping to values
|
954
|
-
# @return [Boolean] whether or not all values were set
|
955
|
-
#
|
956
|
-
# @see #msetnx
|
957
|
-
def mapped_msetnx(hash)
|
958
|
-
msetnx(hash.to_a.flatten)
|
959
|
-
end
|
960
|
-
|
961
|
-
# Get the value of a key.
|
962
|
-
#
|
963
|
-
# @param [String] key
|
964
|
-
# @return [String]
|
965
|
-
def get(key)
|
966
|
-
synchronize do |client|
|
967
|
-
client.call([:get, key])
|
968
|
-
end
|
969
|
-
end
|
970
|
-
|
971
|
-
# Get the values of all the given keys.
|
972
|
-
#
|
973
|
-
# @example
|
974
|
-
# redis.mget("key1", "key2")
|
975
|
-
# # => ["v1", "v2"]
|
976
|
-
#
|
977
|
-
# @param [Array<String>] keys
|
978
|
-
# @return [Array<String>] an array of values for the specified keys
|
979
|
-
#
|
980
|
-
# @see #mapped_mget
|
981
|
-
def mget(*keys, &blk)
|
982
|
-
synchronize do |client|
|
983
|
-
client.call([:mget, *keys], &blk)
|
984
|
-
end
|
985
|
-
end
|
986
|
-
|
987
|
-
# Get the values of all the given keys.
|
988
|
-
#
|
989
|
-
# @example
|
990
|
-
# redis.mapped_mget("key1", "key2")
|
991
|
-
# # => { "key1" => "v1", "key2" => "v2" }
|
992
|
-
#
|
993
|
-
# @param [Array<String>] keys array of keys
|
994
|
-
# @return [Hash] a hash mapping the specified keys to their values
|
995
|
-
#
|
996
|
-
# @see #mget
|
997
|
-
def mapped_mget(*keys)
|
998
|
-
mget(*keys) do |reply|
|
999
|
-
if reply.is_a?(Array)
|
1000
|
-
Hash[keys.zip(reply)]
|
1001
|
-
else
|
1002
|
-
reply
|
1003
|
-
end
|
1004
|
-
end
|
1005
|
-
end
|
1006
|
-
|
1007
|
-
# Overwrite part of a string at key starting at the specified offset.
|
1008
|
-
#
|
1009
|
-
# @param [String] key
|
1010
|
-
# @param [Integer] offset byte offset
|
1011
|
-
# @param [String] value
|
1012
|
-
# @return [Integer] length of the string after it was modified
|
1013
|
-
def setrange(key, offset, value)
|
1014
|
-
synchronize do |client|
|
1015
|
-
client.call([:setrange, key, offset, value.to_s])
|
1016
|
-
end
|
1017
|
-
end
|
1018
|
-
|
1019
|
-
# Get a substring of the string stored at a key.
|
1020
|
-
#
|
1021
|
-
# @param [String] key
|
1022
|
-
# @param [Integer] start zero-based start offset
|
1023
|
-
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
1024
|
-
# the end of the string
|
1025
|
-
# @return [Integer] `0` or `1`
|
1026
|
-
def getrange(key, start, stop)
|
1027
|
-
synchronize do |client|
|
1028
|
-
client.call([:getrange, key, start, stop])
|
1029
|
-
end
|
1030
|
-
end
|
1031
|
-
|
1032
|
-
# Sets or clears the bit at offset in the string value stored at key.
|
1033
|
-
#
|
1034
|
-
# @param [String] key
|
1035
|
-
# @param [Integer] offset bit offset
|
1036
|
-
# @param [Integer] value bit value `0` or `1`
|
1037
|
-
# @return [Integer] the original bit value stored at `offset`
|
1038
|
-
def setbit(key, offset, value)
|
1039
|
-
synchronize do |client|
|
1040
|
-
client.call([:setbit, key, offset, value])
|
1041
|
-
end
|
1042
|
-
end
|
1043
|
-
|
1044
|
-
# Returns the bit value at offset in the string value stored at key.
|
1045
|
-
#
|
1046
|
-
# @param [String] key
|
1047
|
-
# @param [Integer] offset bit offset
|
1048
|
-
# @return [Integer] `0` or `1`
|
1049
|
-
def getbit(key, offset)
|
1050
|
-
synchronize do |client|
|
1051
|
-
client.call([:getbit, key, offset])
|
1052
|
-
end
|
1053
|
-
end
|
1054
|
-
|
1055
|
-
# Append a value to a key.
|
1056
|
-
#
|
1057
|
-
# @param [String] key
|
1058
|
-
# @param [String] value value to append
|
1059
|
-
# @return [Integer] length of the string after appending
|
1060
|
-
def append(key, value)
|
1061
|
-
synchronize do |client|
|
1062
|
-
client.call([:append, key, value])
|
1063
|
-
end
|
1064
|
-
end
|
1065
|
-
|
1066
|
-
# Count the number of set bits in a range of the string value stored at key.
|
1067
|
-
#
|
1068
|
-
# @param [String] key
|
1069
|
-
# @param [Integer] start start index
|
1070
|
-
# @param [Integer] stop stop index
|
1071
|
-
# @return [Integer] the number of bits set to 1
|
1072
|
-
def bitcount(key, start = 0, stop = -1)
|
1073
|
-
synchronize do |client|
|
1074
|
-
client.call([:bitcount, key, start, stop])
|
1075
|
-
end
|
1076
|
-
end
|
1077
|
-
|
1078
|
-
# Perform a bitwise operation between strings and store the resulting string in a key.
|
1079
|
-
#
|
1080
|
-
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
1081
|
-
# @param [String] destkey destination key
|
1082
|
-
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
1083
|
-
# @return [Integer] the length of the string stored in `destkey`
|
1084
|
-
def bitop(operation, destkey, *keys)
|
1085
|
-
synchronize do |client|
|
1086
|
-
client.call([:bitop, operation, destkey, *keys])
|
1087
|
-
end
|
1088
|
-
end
|
1089
|
-
|
1090
|
-
# Return the position of the first bit set to 1 or 0 in a string.
|
1091
|
-
#
|
1092
|
-
# @param [String] key
|
1093
|
-
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1094
|
-
# @param [Integer] start start index
|
1095
|
-
# @param [Integer] stop stop index
|
1096
|
-
# @return [Integer] the position of the first 1/0 bit.
|
1097
|
-
# -1 if looking for 1 and it is not found or start and stop are given.
|
1098
|
-
def bitpos(key, bit, start = nil, stop = nil)
|
1099
|
-
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1100
|
-
|
1101
|
-
synchronize do |client|
|
1102
|
-
command = [:bitpos, key, bit]
|
1103
|
-
command << start if start
|
1104
|
-
command << stop if stop
|
1105
|
-
client.call(command)
|
1106
|
-
end
|
1107
|
-
end
|
1108
|
-
|
1109
|
-
# Set the string value of a key and return its old value.
|
1110
|
-
#
|
1111
|
-
# @param [String] key
|
1112
|
-
# @param [String] value value to replace the current value with
|
1113
|
-
# @return [String] the old value stored in the key, or `nil` if the key
|
1114
|
-
# did not exist
|
1115
|
-
def getset(key, value)
|
1116
|
-
synchronize do |client|
|
1117
|
-
client.call([:getset, key, value.to_s])
|
1118
|
-
end
|
1119
|
-
end
|
1120
|
-
|
1121
|
-
# Get the value of key and delete the key. This command is similar to GET,
|
1122
|
-
# except for the fact that it also deletes the key on success.
|
1123
|
-
#
|
1124
|
-
# @param [String] key
|
1125
|
-
# @return [String] the old value stored in the key, or `nil` if the key
|
1126
|
-
# did not exist
|
1127
|
-
def getdel(key)
|
1128
|
-
synchronize do |client|
|
1129
|
-
client.call([:getdel, key])
|
1130
|
-
end
|
1131
|
-
end
|
1132
|
-
|
1133
|
-
# Get the value of key and optionally set its expiration. GETEX is similar to
|
1134
|
-
# GET, but is a write command with additional options. When no options are
|
1135
|
-
# provided, GETEX behaves like GET.
|
1136
|
-
#
|
1137
|
-
# @param [String] key
|
1138
|
-
# @param [Hash] options
|
1139
|
-
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
1140
|
-
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
1141
|
-
# - `:exat => true`: Set the specified Unix time at which the key will
|
1142
|
-
# expire, in seconds.
|
1143
|
-
# - `:pxat => true`: Set the specified Unix time at which the key will
|
1144
|
-
# expire, in milliseconds.
|
1145
|
-
# - `:persist => true`: Remove the time to live associated with the key.
|
1146
|
-
# @return [String] The value of key, or nil when key does not exist.
|
1147
|
-
def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
|
1148
|
-
args = [:getex, key]
|
1149
|
-
args << "EX" << ex if ex
|
1150
|
-
args << "PX" << px if px
|
1151
|
-
args << "EXAT" << exat if exat
|
1152
|
-
args << "PXAT" << pxat if pxat
|
1153
|
-
args << "PERSIST" if persist
|
1154
|
-
|
1155
|
-
synchronize do |client|
|
1156
|
-
client.call(args)
|
1157
|
-
end
|
1158
|
-
end
|
1159
|
-
|
1160
|
-
# Get the length of the value stored in a key.
|
1161
|
-
#
|
1162
|
-
# @param [String] key
|
1163
|
-
# @return [Integer] the length of the value stored in the key, or 0
|
1164
|
-
# if the key does not exist
|
1165
|
-
def strlen(key)
|
1166
|
-
synchronize do |client|
|
1167
|
-
client.call([:strlen, key])
|
1168
|
-
end
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
# Get the length of a list.
|
1172
|
-
#
|
1173
|
-
# @param [String] key
|
1174
|
-
# @return [Integer]
|
1175
|
-
def llen(key)
|
1176
|
-
synchronize do |client|
|
1177
|
-
client.call([:llen, key])
|
1178
|
-
end
|
1179
|
-
end
|
1180
|
-
|
1181
|
-
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
1182
|
-
#
|
1183
|
-
# @param [String] source source key
|
1184
|
-
# @param [String] destination destination key
|
1185
|
-
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1186
|
-
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1187
|
-
# @param [String, Symbol] where_destination where to push the element to the source list
|
1188
|
-
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1189
|
-
#
|
1190
|
-
# @return [nil, String] the element, or nil when the source key does not exist
|
1191
|
-
#
|
1192
|
-
# @note This command comes in place of the now deprecated RPOPLPUSH.
|
1193
|
-
# Doing LMOVE RIGHT LEFT is equivalent.
|
1194
|
-
def lmove(source, destination, where_source, where_destination)
|
1195
|
-
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1196
|
-
|
1197
|
-
synchronize do |client|
|
1198
|
-
client.call([:lmove, source, destination, where_source, where_destination])
|
1199
|
-
end
|
1200
|
-
end
|
1201
|
-
|
1202
|
-
# Remove the first/last element in a list and append/prepend it
|
1203
|
-
# to another list and return it, or block until one is available.
|
1204
|
-
#
|
1205
|
-
# @example With timeout
|
1206
|
-
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
|
1207
|
-
# # => nil on timeout
|
1208
|
-
# # => "element" on success
|
1209
|
-
# @example Without timeout
|
1210
|
-
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
|
1211
|
-
# # => "element"
|
1212
|
-
#
|
1213
|
-
# @param [String] source source key
|
1214
|
-
# @param [String] destination destination key
|
1215
|
-
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1216
|
-
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1217
|
-
# @param [String, Symbol] where_destination where to push the element to the source list
|
1218
|
-
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1219
|
-
# @param [Hash] options
|
1220
|
-
# - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
|
1221
|
-
#
|
1222
|
-
# @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
|
1223
|
-
#
|
1224
|
-
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
1225
|
-
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1226
|
-
|
1227
|
-
synchronize do |client|
|
1228
|
-
command = [:blmove, source, destination, where_source, where_destination, timeout]
|
1229
|
-
timeout += client.timeout if timeout > 0
|
1230
|
-
client.call_with_timeout(command, timeout)
|
1231
|
-
end
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1235
|
-
#
|
1236
|
-
# @param [String] key
|
1237
|
-
# @param [String, Array<String>] value string value, or array of string values to push
|
1238
|
-
# @return [Integer] the length of the list after the push operation
|
1239
|
-
def lpush(key, value)
|
1240
|
-
synchronize do |client|
|
1241
|
-
client.call([:lpush, key, value])
|
1242
|
-
end
|
1243
|
-
end
|
1244
|
-
|
1245
|
-
# Prepend a value to a list, only if the list exists.
|
1246
|
-
#
|
1247
|
-
# @param [String] key
|
1248
|
-
# @param [String] value
|
1249
|
-
# @return [Integer] the length of the list after the push operation
|
1250
|
-
def lpushx(key, value)
|
1251
|
-
synchronize do |client|
|
1252
|
-
client.call([:lpushx, key, value])
|
1253
|
-
end
|
1254
|
-
end
|
1255
|
-
|
1256
|
-
# Append one or more values to a list, creating the list if it doesn't exist
|
1257
|
-
#
|
1258
|
-
# @param [String] key
|
1259
|
-
# @param [String, Array<String>] value string value, or array of string values to push
|
1260
|
-
# @return [Integer] the length of the list after the push operation
|
1261
|
-
def rpush(key, value)
|
1262
|
-
synchronize do |client|
|
1263
|
-
client.call([:rpush, key, value])
|
1264
|
-
end
|
1265
|
-
end
|
1266
|
-
|
1267
|
-
# Append a value to a list, only if the list exists.
|
1268
|
-
#
|
1269
|
-
# @param [String] key
|
1270
|
-
# @param [String] value
|
1271
|
-
# @return [Integer] the length of the list after the push operation
|
1272
|
-
def rpushx(key, value)
|
1273
|
-
synchronize do |client|
|
1274
|
-
client.call([:rpushx, key, value])
|
1275
|
-
end
|
1276
|
-
end
|
1277
|
-
|
1278
|
-
# Remove and get the first elements in a list.
|
1279
|
-
#
|
1280
|
-
# @param [String] key
|
1281
|
-
# @param [Integer] count number of elements to remove
|
1282
|
-
# @return [String, Array<String>] the values of the first elements
|
1283
|
-
def lpop(key, count = nil)
|
1284
|
-
synchronize do |client|
|
1285
|
-
command = [:lpop, key]
|
1286
|
-
command << count if count
|
1287
|
-
client.call(command)
|
1288
|
-
end
|
1289
|
-
end
|
1290
|
-
|
1291
|
-
# Remove and get the last elements in a list.
|
1292
|
-
#
|
1293
|
-
# @param [String] key
|
1294
|
-
# @param [Integer] count number of elements to remove
|
1295
|
-
# @return [String, Array<String>] the values of the last elements
|
1296
|
-
def rpop(key, count = nil)
|
1297
|
-
synchronize do |client|
|
1298
|
-
command = [:rpop, key]
|
1299
|
-
command << count if count
|
1300
|
-
client.call(command)
|
1301
|
-
end
|
1302
|
-
end
|
1303
|
-
|
1304
|
-
# Remove the last element in a list, append it to another list and return it.
|
1305
|
-
#
|
1306
|
-
# @param [String] source source key
|
1307
|
-
# @param [String] destination destination key
|
1308
|
-
# @return [nil, String] the element, or nil when the source key does not exist
|
1309
|
-
def rpoplpush(source, destination)
|
1310
|
-
synchronize do |client|
|
1311
|
-
client.call([:rpoplpush, source, destination])
|
1312
|
-
end
|
1313
|
-
end
|
1314
|
-
|
1315
|
-
def _bpop(cmd, args, &blk)
|
1316
|
-
timeout = if args.last.is_a?(Hash)
|
1317
|
-
options = args.pop
|
1318
|
-
options[:timeout]
|
1319
|
-
elsif args.last.respond_to?(:to_int)
|
1320
|
-
# Issue deprecation notice in obnoxious mode...
|
1321
|
-
args.pop.to_int
|
1322
|
-
end
|
1323
|
-
|
1324
|
-
timeout ||= 0
|
1325
|
-
|
1326
|
-
if args.size > 1
|
1327
|
-
# Issue deprecation notice in obnoxious mode...
|
1328
|
-
end
|
1329
|
-
|
1330
|
-
keys = args.flatten
|
1331
|
-
|
1332
|
-
synchronize do |client|
|
1333
|
-
command = [cmd, keys, timeout]
|
1334
|
-
timeout += client.timeout if timeout > 0
|
1335
|
-
client.call_with_timeout(command, timeout, &blk)
|
1336
|
-
end
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
# Remove and get the first element in a list, or block until one is available.
|
1340
|
-
#
|
1341
|
-
# @example With timeout
|
1342
|
-
# list, element = redis.blpop("list", :timeout => 5)
|
1343
|
-
# # => nil on timeout
|
1344
|
-
# # => ["list", "element"] on success
|
1345
|
-
# @example Without timeout
|
1346
|
-
# list, element = redis.blpop("list")
|
1347
|
-
# # => ["list", "element"]
|
1348
|
-
# @example Blocking pop on multiple lists
|
1349
|
-
# list, element = redis.blpop(["list", "another_list"])
|
1350
|
-
# # => ["list", "element"]
|
1351
|
-
#
|
1352
|
-
# @param [String, Array<String>] keys one or more keys to perform the
|
1353
|
-
# blocking pop on
|
1354
|
-
# @param [Hash] options
|
1355
|
-
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1356
|
-
#
|
1357
|
-
# @return [nil, [String, String]]
|
1358
|
-
# - `nil` when the operation timed out
|
1359
|
-
# - tuple of the list that was popped from and element was popped otherwise
|
1360
|
-
def blpop(*args)
|
1361
|
-
_bpop(:blpop, args)
|
1362
|
-
end
|
1363
|
-
|
1364
|
-
# Remove and get the last element in a list, or block until one is available.
|
1365
|
-
#
|
1366
|
-
# @param [String, Array<String>] keys one or more keys to perform the
|
1367
|
-
# blocking pop on
|
1368
|
-
# @param [Hash] options
|
1369
|
-
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1370
|
-
#
|
1371
|
-
# @return [nil, [String, String]]
|
1372
|
-
# - `nil` when the operation timed out
|
1373
|
-
# - tuple of the list that was popped from and element was popped otherwise
|
1374
|
-
#
|
1375
|
-
# @see #blpop
|
1376
|
-
def brpop(*args)
|
1377
|
-
_bpop(:brpop, args)
|
1378
|
-
end
|
1379
|
-
|
1380
|
-
# Pop a value from a list, push it to another list and return it; or block
|
1381
|
-
# until one is available.
|
1382
|
-
#
|
1383
|
-
# @param [String] source source key
|
1384
|
-
# @param [String] destination destination key
|
1385
|
-
# @param [Hash] options
|
1386
|
-
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1387
|
-
#
|
1388
|
-
# @return [nil, String]
|
1389
|
-
# - `nil` when the operation timed out
|
1390
|
-
# - the element was popped and pushed otherwise
|
1391
|
-
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1392
|
-
synchronize do |client|
|
1393
|
-
command = [:brpoplpush, source, destination, timeout]
|
1394
|
-
timeout += client.timeout if timeout > 0
|
1395
|
-
client.call_with_timeout(command, timeout)
|
1396
|
-
end
|
1397
|
-
end
|
1398
|
-
|
1399
|
-
# Get an element from a list by its index.
|
1400
|
-
#
|
1401
|
-
# @param [String] key
|
1402
|
-
# @param [Integer] index
|
1403
|
-
# @return [String]
|
1404
|
-
def lindex(key, index)
|
1405
|
-
synchronize do |client|
|
1406
|
-
client.call([:lindex, key, index])
|
1407
|
-
end
|
1408
|
-
end
|
1409
|
-
|
1410
|
-
# Insert an element before or after another element in a list.
|
1411
|
-
#
|
1412
|
-
# @param [String] key
|
1413
|
-
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1414
|
-
# @param [String] pivot reference element
|
1415
|
-
# @param [String] value
|
1416
|
-
# @return [Integer] length of the list after the insert operation, or `-1`
|
1417
|
-
# when the element `pivot` was not found
|
1418
|
-
def linsert(key, where, pivot, value)
|
1419
|
-
synchronize do |client|
|
1420
|
-
client.call([:linsert, key, where, pivot, value])
|
1421
|
-
end
|
1422
|
-
end
|
1423
|
-
|
1424
|
-
# Get a range of elements from a list.
|
1425
|
-
#
|
1426
|
-
# @param [String] key
|
1427
|
-
# @param [Integer] start start index
|
1428
|
-
# @param [Integer] stop stop index
|
1429
|
-
# @return [Array<String>]
|
1430
|
-
def lrange(key, start, stop)
|
1431
|
-
synchronize do |client|
|
1432
|
-
client.call([:lrange, key, start, stop])
|
1433
|
-
end
|
1434
|
-
end
|
1435
|
-
|
1436
|
-
# Remove elements from a list.
|
1437
|
-
#
|
1438
|
-
# @param [String] key
|
1439
|
-
# @param [Integer] count number of elements to remove. Use a positive
|
1440
|
-
# value to remove the first `count` occurrences of `value`. A negative
|
1441
|
-
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1442
|
-
# remove all occurrences of `value` from the list.
|
1443
|
-
# @param [String] value
|
1444
|
-
# @return [Integer] the number of removed elements
|
1445
|
-
def lrem(key, count, value)
|
1446
|
-
synchronize do |client|
|
1447
|
-
client.call([:lrem, key, count, value])
|
1448
|
-
end
|
1449
|
-
end
|
1450
|
-
|
1451
|
-
# Set the value of an element in a list by its index.
|
1452
|
-
#
|
1453
|
-
# @param [String] key
|
1454
|
-
# @param [Integer] index
|
1455
|
-
# @param [String] value
|
1456
|
-
# @return [String] `OK`
|
1457
|
-
def lset(key, index, value)
|
1458
|
-
synchronize do |client|
|
1459
|
-
client.call([:lset, key, index, value])
|
1460
|
-
end
|
1461
|
-
end
|
1462
|
-
|
1463
|
-
# Trim a list to the specified range.
|
1464
|
-
#
|
1465
|
-
# @param [String] key
|
1466
|
-
# @param [Integer] start start index
|
1467
|
-
# @param [Integer] stop stop index
|
1468
|
-
# @return [String] `OK`
|
1469
|
-
def ltrim(key, start, stop)
|
1470
|
-
synchronize do |client|
|
1471
|
-
client.call([:ltrim, key, start, stop])
|
1472
|
-
end
|
1473
|
-
end
|
1474
|
-
|
1475
|
-
# Get the number of members in a set.
|
1476
|
-
#
|
1477
|
-
# @param [String] key
|
1478
|
-
# @return [Integer]
|
1479
|
-
def scard(key)
|
1480
|
-
synchronize do |client|
|
1481
|
-
client.call([:scard, key])
|
1482
|
-
end
|
1483
|
-
end
|
1484
|
-
|
1485
|
-
# Add one or more members to a set.
|
1486
|
-
#
|
1487
|
-
# @param [String] key
|
1488
|
-
# @param [String, Array<String>] member one member, or array of members
|
1489
|
-
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1490
|
-
# holding whether or not adding the member succeeded, or `Integer` when an
|
1491
|
-
# array of members is specified, holding the number of members that were
|
1492
|
-
# successfully added
|
1493
|
-
def sadd(key, member)
|
1494
|
-
synchronize do |client|
|
1495
|
-
client.call([:sadd, key, member]) do |reply|
|
1496
|
-
if member.is_a? Array
|
1497
|
-
# Variadic: return integer
|
1498
|
-
reply
|
1499
|
-
else
|
1500
|
-
# Single argument: return boolean
|
1501
|
-
Boolify.call(reply)
|
1502
|
-
end
|
1503
|
-
end
|
1504
|
-
end
|
1505
|
-
end
|
1506
|
-
|
1507
|
-
# Remove one or more members from a set.
|
1508
|
-
#
|
1509
|
-
# @param [String] key
|
1510
|
-
# @param [String, Array<String>] member one member, or array of members
|
1511
|
-
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1512
|
-
# holding whether or not removing the member succeeded, or `Integer` when an
|
1513
|
-
# array of members is specified, holding the number of members that were
|
1514
|
-
# successfully removed
|
1515
|
-
def srem(key, member)
|
1516
|
-
synchronize do |client|
|
1517
|
-
client.call([:srem, key, member]) do |reply|
|
1518
|
-
if member.is_a? Array
|
1519
|
-
# Variadic: return integer
|
1520
|
-
reply
|
1521
|
-
else
|
1522
|
-
# Single argument: return boolean
|
1523
|
-
Boolify.call(reply)
|
1524
|
-
end
|
1525
|
-
end
|
1526
|
-
end
|
1527
|
-
end
|
1528
|
-
|
1529
|
-
# Remove and return one or more random member from a set.
|
1530
|
-
#
|
1531
|
-
# @param [String] key
|
1532
|
-
# @return [String]
|
1533
|
-
# @param [Integer] count
|
1534
|
-
def spop(key, count = nil)
|
1535
|
-
synchronize do |client|
|
1536
|
-
if count.nil?
|
1537
|
-
client.call([:spop, key])
|
1538
|
-
else
|
1539
|
-
client.call([:spop, key, count])
|
1540
|
-
end
|
1541
|
-
end
|
1542
|
-
end
|
1543
|
-
|
1544
|
-
# Get one or more random members from a set.
|
1545
|
-
#
|
1546
|
-
# @param [String] key
|
1547
|
-
# @param [Integer] count
|
1548
|
-
# @return [String]
|
1549
|
-
def srandmember(key, count = nil)
|
1550
|
-
synchronize do |client|
|
1551
|
-
if count.nil?
|
1552
|
-
client.call([:srandmember, key])
|
1553
|
-
else
|
1554
|
-
client.call([:srandmember, key, count])
|
1555
|
-
end
|
1556
|
-
end
|
1557
|
-
end
|
1558
|
-
|
1559
|
-
# Move a member from one set to another.
|
1560
|
-
#
|
1561
|
-
# @param [String] source source key
|
1562
|
-
# @param [String] destination destination key
|
1563
|
-
# @param [String] member member to move from `source` to `destination`
|
1564
|
-
# @return [Boolean]
|
1565
|
-
def smove(source, destination, member)
|
1566
|
-
synchronize do |client|
|
1567
|
-
client.call([:smove, source, destination, member], &Boolify)
|
1568
|
-
end
|
1569
|
-
end
|
1570
|
-
|
1571
|
-
# Determine if a given value is a member of a set.
|
1572
|
-
#
|
1573
|
-
# @param [String] key
|
1574
|
-
# @param [String] member
|
1575
|
-
# @return [Boolean]
|
1576
|
-
def sismember(key, member)
|
1577
|
-
synchronize do |client|
|
1578
|
-
client.call([:sismember, key, member], &Boolify)
|
1579
|
-
end
|
1580
|
-
end
|
1581
|
-
|
1582
|
-
# Determine if multiple values are members of a set.
|
1583
|
-
#
|
1584
|
-
# @param [String] key
|
1585
|
-
# @param [String, Array<String>] members
|
1586
|
-
# @return [Array<Boolean>]
|
1587
|
-
def smismember(key, *members)
|
1588
|
-
synchronize do |client|
|
1589
|
-
client.call([:smismember, key, *members]) do |reply|
|
1590
|
-
reply.map(&Boolify)
|
1591
|
-
end
|
1592
|
-
end
|
1593
|
-
end
|
1594
|
-
|
1595
|
-
# Get all the members in a set.
|
1596
|
-
#
|
1597
|
-
# @param [String] key
|
1598
|
-
# @return [Array<String>]
|
1599
|
-
def smembers(key)
|
1600
|
-
synchronize do |client|
|
1601
|
-
client.call([:smembers, key])
|
1602
|
-
end
|
1603
|
-
end
|
1604
|
-
|
1605
|
-
# Subtract multiple sets.
|
1606
|
-
#
|
1607
|
-
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1608
|
-
# @return [Array<String>] members in the difference
|
1609
|
-
def sdiff(*keys)
|
1610
|
-
synchronize do |client|
|
1611
|
-
client.call([:sdiff, *keys])
|
1612
|
-
end
|
1613
|
-
end
|
1614
|
-
|
1615
|
-
# Subtract multiple sets and store the resulting set in a key.
|
1616
|
-
#
|
1617
|
-
# @param [String] destination destination key
|
1618
|
-
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1619
|
-
# @return [Integer] number of elements in the resulting set
|
1620
|
-
def sdiffstore(destination, *keys)
|
1621
|
-
synchronize do |client|
|
1622
|
-
client.call([:sdiffstore, destination, *keys])
|
1623
|
-
end
|
1624
|
-
end
|
1625
|
-
|
1626
|
-
# Intersect multiple sets.
|
1627
|
-
#
|
1628
|
-
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1629
|
-
# @return [Array<String>] members in the intersection
|
1630
|
-
def sinter(*keys)
|
1631
|
-
synchronize do |client|
|
1632
|
-
client.call([:sinter, *keys])
|
1633
|
-
end
|
1634
|
-
end
|
1635
|
-
|
1636
|
-
# Intersect multiple sets and store the resulting set in a key.
|
1637
|
-
#
|
1638
|
-
# @param [String] destination destination key
|
1639
|
-
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1640
|
-
# @return [Integer] number of elements in the resulting set
|
1641
|
-
def sinterstore(destination, *keys)
|
1642
|
-
synchronize do |client|
|
1643
|
-
client.call([:sinterstore, destination, *keys])
|
1644
|
-
end
|
1645
|
-
end
|
1646
|
-
|
1647
|
-
# Add multiple sets.
|
1648
|
-
#
|
1649
|
-
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1650
|
-
# @return [Array<String>] members in the union
|
1651
|
-
def sunion(*keys)
|
1652
|
-
synchronize do |client|
|
1653
|
-
client.call([:sunion, *keys])
|
1654
|
-
end
|
1655
|
-
end
|
1656
|
-
|
1657
|
-
# Add multiple sets and store the resulting set in a key.
|
1658
|
-
#
|
1659
|
-
# @param [String] destination destination key
|
1660
|
-
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1661
|
-
# @return [Integer] number of elements in the resulting set
|
1662
|
-
def sunionstore(destination, *keys)
|
1663
|
-
synchronize do |client|
|
1664
|
-
client.call([:sunionstore, destination, *keys])
|
1665
|
-
end
|
1666
|
-
end
|
1667
|
-
|
1668
|
-
# Get the number of members in a sorted set.
|
1669
|
-
#
|
1670
|
-
# @example
|
1671
|
-
# redis.zcard("zset")
|
1672
|
-
# # => 4
|
1673
|
-
#
|
1674
|
-
# @param [String] key
|
1675
|
-
# @return [Integer]
|
1676
|
-
def zcard(key)
|
1677
|
-
synchronize do |client|
|
1678
|
-
client.call([:zcard, key])
|
1679
|
-
end
|
1680
|
-
end
|
1681
|
-
|
1682
|
-
# Add one or more members to a sorted set, or update the score for members
|
1683
|
-
# that already exist.
|
1684
|
-
#
|
1685
|
-
# @example Add a single `[score, member]` pair to a sorted set
|
1686
|
-
# redis.zadd("zset", 32.0, "member")
|
1687
|
-
# @example Add an array of `[score, member]` pairs to a sorted set
|
1688
|
-
# redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
|
1689
|
-
#
|
1690
|
-
# @param [String] key
|
1691
|
-
# @param [[Float, String], Array<[Float, String]>] args
|
1692
|
-
# - a single `[score, member]` pair
|
1693
|
-
# - an array of `[score, member]` pairs
|
1694
|
-
# @param [Hash] options
|
1695
|
-
# - `:xx => true`: Only update elements that already exist (never
|
1696
|
-
# add elements)
|
1697
|
-
# - `:nx => true`: Don't update already existing elements (always
|
1698
|
-
# add new elements)
|
1699
|
-
# - `:lt => true`: Only update existing elements if the new score
|
1700
|
-
# is less than the current score
|
1701
|
-
# - `:gt => true`: Only update existing elements if the new score
|
1702
|
-
# is greater than the current score
|
1703
|
-
# - `:ch => true`: Modify the return value from the number of new
|
1704
|
-
# elements added, to the total number of elements changed (CH is an
|
1705
|
-
# abbreviation of changed); changed elements are new elements added
|
1706
|
-
# and elements already existing for which the score was updated
|
1707
|
-
# - `:incr => true`: When this option is specified ZADD acts like
|
1708
|
-
# ZINCRBY; only one score-element pair can be specified in this mode
|
1709
|
-
#
|
1710
|
-
# @return [Boolean, Integer, Float]
|
1711
|
-
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1712
|
-
# **added** to the sorted set.
|
1713
|
-
# - `Integer` when an array of pairs is specified, holding the number of
|
1714
|
-
# pairs that were **added** to the sorted set.
|
1715
|
-
# - `Float` when option :incr is specified, holding the score of the member
|
1716
|
-
# after incrementing it.
|
1717
|
-
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
1718
|
-
command = [:zadd, key]
|
1719
|
-
command << "NX" if nx
|
1720
|
-
command << "XX" if xx
|
1721
|
-
command << "LT" if lt
|
1722
|
-
command << "GT" if gt
|
1723
|
-
command << "CH" if ch
|
1724
|
-
command << "INCR" if incr
|
1725
|
-
|
1726
|
-
synchronize do |client|
|
1727
|
-
if args.size == 1 && args[0].is_a?(Array)
|
1728
|
-
# Variadic: return float if INCR, integer if !INCR
|
1729
|
-
client.call(command + args[0], &(incr ? Floatify : nil))
|
1730
|
-
elsif args.size == 2
|
1731
|
-
# Single pair: return float if INCR, boolean if !INCR
|
1732
|
-
client.call(command + args, &(incr ? Floatify : Boolify))
|
1733
|
-
else
|
1734
|
-
raise ArgumentError, "wrong number of arguments"
|
1735
|
-
end
|
1736
|
-
end
|
1737
|
-
end
|
1738
|
-
|
1739
|
-
# Increment the score of a member in a sorted set.
|
1740
|
-
#
|
1741
|
-
# @example
|
1742
|
-
# redis.zincrby("zset", 32.0, "a")
|
1743
|
-
# # => 64.0
|
1744
|
-
#
|
1745
|
-
# @param [String] key
|
1746
|
-
# @param [Float] increment
|
1747
|
-
# @param [String] member
|
1748
|
-
# @return [Float] score of the member after incrementing it
|
1749
|
-
def zincrby(key, increment, member)
|
1750
|
-
synchronize do |client|
|
1751
|
-
client.call([:zincrby, key, increment, member], &Floatify)
|
1752
|
-
end
|
1753
|
-
end
|
1754
|
-
|
1755
|
-
# Remove one or more members from a sorted set.
|
1756
|
-
#
|
1757
|
-
# @example Remove a single member from a sorted set
|
1758
|
-
# redis.zrem("zset", "a")
|
1759
|
-
# @example Remove an array of members from a sorted set
|
1760
|
-
# redis.zrem("zset", ["a", "b"])
|
1761
|
-
#
|
1762
|
-
# @param [String] key
|
1763
|
-
# @param [String, Array<String>] member
|
1764
|
-
# - a single member
|
1765
|
-
# - an array of members
|
1766
|
-
#
|
1767
|
-
# @return [Boolean, Integer]
|
1768
|
-
# - `Boolean` when a single member is specified, holding whether or not it
|
1769
|
-
# was removed from the sorted set
|
1770
|
-
# - `Integer` when an array of pairs is specified, holding the number of
|
1771
|
-
# members that were removed to the sorted set
|
1772
|
-
def zrem(key, member)
|
1773
|
-
synchronize do |client|
|
1774
|
-
client.call([:zrem, key, member]) do |reply|
|
1775
|
-
if member.is_a? Array
|
1776
|
-
# Variadic: return integer
|
1777
|
-
reply
|
1778
|
-
else
|
1779
|
-
# Single argument: return boolean
|
1780
|
-
Boolify.call(reply)
|
1781
|
-
end
|
1782
|
-
end
|
1783
|
-
end
|
1784
|
-
end
|
1785
|
-
|
1786
|
-
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1787
|
-
#
|
1788
|
-
# @example Popping a member
|
1789
|
-
# redis.zpopmax('zset')
|
1790
|
-
# #=> ['b', 2.0]
|
1791
|
-
# @example With count option
|
1792
|
-
# redis.zpopmax('zset', 2)
|
1793
|
-
# #=> [['b', 2.0], ['a', 1.0]]
|
1794
|
-
#
|
1795
|
-
# @params key [String] a key of the sorted set
|
1796
|
-
# @params count [Integer] a number of members
|
1797
|
-
#
|
1798
|
-
# @return [Array<String, Float>] element and score pair if count is not specified
|
1799
|
-
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1800
|
-
def zpopmax(key, count = nil)
|
1801
|
-
synchronize do |client|
|
1802
|
-
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1803
|
-
count.to_i > 1 ? members : members.first
|
1804
|
-
end
|
1805
|
-
end
|
1806
|
-
|
1807
|
-
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1808
|
-
#
|
1809
|
-
# @example Popping a member
|
1810
|
-
# redis.zpopmin('zset')
|
1811
|
-
# #=> ['a', 1.0]
|
1812
|
-
# @example With count option
|
1813
|
-
# redis.zpopmin('zset', 2)
|
1814
|
-
# #=> [['a', 1.0], ['b', 2.0]]
|
1815
|
-
#
|
1816
|
-
# @params key [String] a key of the sorted set
|
1817
|
-
# @params count [Integer] a number of members
|
1818
|
-
#
|
1819
|
-
# @return [Array<String, Float>] element and score pair if count is not specified
|
1820
|
-
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1821
|
-
def zpopmin(key, count = nil)
|
1822
|
-
synchronize do |client|
|
1823
|
-
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1824
|
-
count.to_i > 1 ? members : members.first
|
1825
|
-
end
|
1826
|
-
end
|
1827
|
-
|
1828
|
-
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1829
|
-
# or block until one is available.
|
1830
|
-
#
|
1831
|
-
# @example Popping a member from a sorted set
|
1832
|
-
# redis.bzpopmax('zset', 1)
|
1833
|
-
# #=> ['zset', 'b', 2.0]
|
1834
|
-
# @example Popping a member from multiple sorted sets
|
1835
|
-
# redis.bzpopmax('zset1', 'zset2', 1)
|
1836
|
-
# #=> ['zset1', 'b', 2.0]
|
1837
|
-
#
|
1838
|
-
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1839
|
-
# @params timeout [Integer] the maximum number of seconds to block
|
1840
|
-
#
|
1841
|
-
# @return [Array<String, String, Float>] a touple of key, member and score
|
1842
|
-
# @return [nil] when no element could be popped and the timeout expired
|
1843
|
-
def bzpopmax(*args)
|
1844
|
-
_bpop(:bzpopmax, args) do |reply|
|
1845
|
-
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1846
|
-
end
|
1847
|
-
end
|
1848
|
-
|
1849
|
-
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1850
|
-
# or block until one is available.
|
1851
|
-
#
|
1852
|
-
# @example Popping a member from a sorted set
|
1853
|
-
# redis.bzpopmin('zset', 1)
|
1854
|
-
# #=> ['zset', 'a', 1.0]
|
1855
|
-
# @example Popping a member from multiple sorted sets
|
1856
|
-
# redis.bzpopmin('zset1', 'zset2', 1)
|
1857
|
-
# #=> ['zset1', 'a', 1.0]
|
1858
|
-
#
|
1859
|
-
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1860
|
-
# @params timeout [Integer] the maximum number of seconds to block
|
1861
|
-
#
|
1862
|
-
# @return [Array<String, String, Float>] a touple of key, member and score
|
1863
|
-
# @return [nil] when no element could be popped and the timeout expired
|
1864
|
-
def bzpopmin(*args)
|
1865
|
-
_bpop(:bzpopmin, args) do |reply|
|
1866
|
-
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1867
|
-
end
|
1868
|
-
end
|
1869
|
-
|
1870
|
-
# Get the score associated with the given member in a sorted set.
|
1871
|
-
#
|
1872
|
-
# @example Get the score for member "a"
|
1873
|
-
# redis.zscore("zset", "a")
|
1874
|
-
# # => 32.0
|
1875
|
-
#
|
1876
|
-
# @param [String] key
|
1877
|
-
# @param [String] member
|
1878
|
-
# @return [Float] score of the member
|
1879
|
-
def zscore(key, member)
|
1880
|
-
synchronize do |client|
|
1881
|
-
client.call([:zscore, key, member], &Floatify)
|
1882
|
-
end
|
1883
|
-
end
|
1884
|
-
|
1885
|
-
# Get the scores associated with the given members in a sorted set.
|
1886
|
-
#
|
1887
|
-
# @example Get the scores for members "a" and "b"
|
1888
|
-
# redis.zmscore("zset", "a", "b")
|
1889
|
-
# # => [32.0, 48.0]
|
1890
|
-
#
|
1891
|
-
# @param [String] key
|
1892
|
-
# @param [String, Array<String>] members
|
1893
|
-
# @return [Array<Float>] scores of the members
|
1894
|
-
def zmscore(key, *members)
|
1895
|
-
synchronize do |client|
|
1896
|
-
client.call([:zmscore, key, *members]) do |reply|
|
1897
|
-
reply.map(&Floatify)
|
1898
|
-
end
|
1899
|
-
end
|
1900
|
-
end
|
1901
|
-
|
1902
|
-
# Get one or more random members from a sorted set.
|
1903
|
-
#
|
1904
|
-
# @example Get one random member
|
1905
|
-
# redis.zrandmember("zset")
|
1906
|
-
# # => "a"
|
1907
|
-
# @example Get multiple random members
|
1908
|
-
# redis.zrandmember("zset", 2)
|
1909
|
-
# # => ["a", "b"]
|
1910
|
-
# @example Gem multiple random members with scores
|
1911
|
-
# redis.zrandmember("zset", 2, with_scores: true)
|
1912
|
-
# # => [["a", 2.0], ["b", 3.0]]
|
1913
|
-
#
|
1914
|
-
# @param [String] key
|
1915
|
-
# @param [Integer] count
|
1916
|
-
# @param [Hash] options
|
1917
|
-
# - `:with_scores => true`: include scores in output
|
1918
|
-
#
|
1919
|
-
# @return [nil, String, Array<String>, Array<[String, Float]>]
|
1920
|
-
# - when `key` does not exist or set is empty, `nil`
|
1921
|
-
# - when `count` is not specified, a member
|
1922
|
-
# - when `count` is specified and `:with_scores` is not specified, an array of members
|
1923
|
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1924
|
-
def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
|
1925
|
-
if with_scores && count.nil?
|
1926
|
-
raise ArgumentError, "count argument must be specified"
|
1927
|
-
end
|
1928
|
-
|
1929
|
-
args = [:zrandmember, key]
|
1930
|
-
args << count if count
|
1931
|
-
|
1932
|
-
if with_scores
|
1933
|
-
args << "WITHSCORES"
|
1934
|
-
block = FloatifyPairs
|
1935
|
-
end
|
1936
|
-
|
1937
|
-
synchronize do |client|
|
1938
|
-
client.call(args, &block)
|
1939
|
-
end
|
1940
|
-
end
|
1941
|
-
|
1942
|
-
# Return a range of members in a sorted set, by index.
|
1943
|
-
#
|
1944
|
-
# @example Retrieve all members from a sorted set
|
1945
|
-
# redis.zrange("zset", 0, -1)
|
1946
|
-
# # => ["a", "b"]
|
1947
|
-
# @example Retrieve all members and their scores from a sorted set
|
1948
|
-
# redis.zrange("zset", 0, -1, :with_scores => true)
|
1949
|
-
# # => [["a", 32.0], ["b", 64.0]]
|
1950
|
-
#
|
1951
|
-
# @param [String] key
|
1952
|
-
# @param [Integer] start start index
|
1953
|
-
# @param [Integer] stop stop index
|
1954
|
-
# @param [Hash] options
|
1955
|
-
# - `:with_scores => true`: include scores in output
|
1956
|
-
#
|
1957
|
-
# @return [Array<String>, Array<[String, Float]>]
|
1958
|
-
# - when `:with_scores` is not specified, an array of members
|
1959
|
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1960
|
-
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1961
|
-
args = [:zrange, key, start, stop]
|
1962
|
-
|
1963
|
-
if with_scores
|
1964
|
-
args << "WITHSCORES"
|
1965
|
-
block = FloatifyPairs
|
1966
|
-
end
|
1967
|
-
|
1968
|
-
synchronize do |client|
|
1969
|
-
client.call(args, &block)
|
1970
|
-
end
|
1971
|
-
end
|
1972
|
-
|
1973
|
-
# Return a range of members in a sorted set, by index, with scores ordered
|
1974
|
-
# from high to low.
|
1975
|
-
#
|
1976
|
-
# @example Retrieve all members from a sorted set
|
1977
|
-
# redis.zrevrange("zset", 0, -1)
|
1978
|
-
# # => ["b", "a"]
|
1979
|
-
# @example Retrieve all members and their scores from a sorted set
|
1980
|
-
# redis.zrevrange("zset", 0, -1, :with_scores => true)
|
1981
|
-
# # => [["b", 64.0], ["a", 32.0]]
|
1982
|
-
#
|
1983
|
-
# @see #zrange
|
1984
|
-
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1985
|
-
args = [:zrevrange, key, start, stop]
|
1986
|
-
|
1987
|
-
if with_scores
|
1988
|
-
args << "WITHSCORES"
|
1989
|
-
block = FloatifyPairs
|
1990
|
-
end
|
1991
|
-
|
1992
|
-
synchronize do |client|
|
1993
|
-
client.call(args, &block)
|
1994
|
-
end
|
1995
|
-
end
|
1996
|
-
|
1997
|
-
# Determine the index of a member in a sorted set.
|
1998
|
-
#
|
1999
|
-
# @param [String] key
|
2000
|
-
# @param [String] member
|
2001
|
-
# @return [Integer]
|
2002
|
-
def zrank(key, member)
|
2003
|
-
synchronize do |client|
|
2004
|
-
client.call([:zrank, key, member])
|
2005
|
-
end
|
2006
|
-
end
|
2007
|
-
|
2008
|
-
# Determine the index of a member in a sorted set, with scores ordered from
|
2009
|
-
# high to low.
|
2010
|
-
#
|
2011
|
-
# @param [String] key
|
2012
|
-
# @param [String] member
|
2013
|
-
# @return [Integer]
|
2014
|
-
def zrevrank(key, member)
|
2015
|
-
synchronize do |client|
|
2016
|
-
client.call([:zrevrank, key, member])
|
2017
|
-
end
|
2018
|
-
end
|
2019
|
-
|
2020
|
-
# Remove all members in a sorted set within the given indexes.
|
2021
|
-
#
|
2022
|
-
# @example Remove first 5 members
|
2023
|
-
# redis.zremrangebyrank("zset", 0, 4)
|
2024
|
-
# # => 5
|
2025
|
-
# @example Remove last 5 members
|
2026
|
-
# redis.zremrangebyrank("zset", -5, -1)
|
2027
|
-
# # => 5
|
2028
|
-
#
|
2029
|
-
# @param [String] key
|
2030
|
-
# @param [Integer] start start index
|
2031
|
-
# @param [Integer] stop stop index
|
2032
|
-
# @return [Integer] number of members that were removed
|
2033
|
-
def zremrangebyrank(key, start, stop)
|
2034
|
-
synchronize do |client|
|
2035
|
-
client.call([:zremrangebyrank, key, start, stop])
|
2036
|
-
end
|
2037
|
-
end
|
2038
|
-
|
2039
|
-
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
2040
|
-
#
|
2041
|
-
# @example Count members matching a
|
2042
|
-
# redis.zlexcount("zset", "[a", "[a\xff")
|
2043
|
-
# # => 1
|
2044
|
-
# @example Count members matching a-z
|
2045
|
-
# redis.zlexcount("zset", "[a", "[z\xff")
|
2046
|
-
# # => 26
|
2047
|
-
#
|
2048
|
-
# @param [String] key
|
2049
|
-
# @param [String] min
|
2050
|
-
# - inclusive minimum is specified by prefixing `(`
|
2051
|
-
# - exclusive minimum is specified by prefixing `[`
|
2052
|
-
# @param [String] max
|
2053
|
-
# - inclusive maximum is specified by prefixing `(`
|
2054
|
-
# - exclusive maximum is specified by prefixing `[`
|
2055
|
-
#
|
2056
|
-
# @return [Integer] number of members within the specified lexicographical range
|
2057
|
-
def zlexcount(key, min, max)
|
2058
|
-
synchronize do |client|
|
2059
|
-
client.call([:zlexcount, key, min, max])
|
2060
|
-
end
|
2061
|
-
end
|
2062
|
-
|
2063
|
-
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
2064
|
-
#
|
2065
|
-
# @example Retrieve members matching a
|
2066
|
-
# redis.zrangebylex("zset", "[a", "[a\xff")
|
2067
|
-
# # => ["aaren", "aarika", "abagael", "abby"]
|
2068
|
-
# @example Retrieve the first 2 members matching a
|
2069
|
-
# redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
|
2070
|
-
# # => ["aaren", "aarika"]
|
2071
|
-
#
|
2072
|
-
# @param [String] key
|
2073
|
-
# @param [String] min
|
2074
|
-
# - inclusive minimum is specified by prefixing `(`
|
2075
|
-
# - exclusive minimum is specified by prefixing `[`
|
2076
|
-
# @param [String] max
|
2077
|
-
# - inclusive maximum is specified by prefixing `(`
|
2078
|
-
# - exclusive maximum is specified by prefixing `[`
|
2079
|
-
# @param [Hash] options
|
2080
|
-
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
2081
|
-
# `count` members
|
2082
|
-
#
|
2083
|
-
# @return [Array<String>, Array<[String, Float]>]
|
2084
|
-
def zrangebylex(key, min, max, limit: nil)
|
2085
|
-
args = [:zrangebylex, key, min, max]
|
2086
|
-
|
2087
|
-
if limit
|
2088
|
-
args << "LIMIT"
|
2089
|
-
args.concat(limit)
|
2090
|
-
end
|
2091
|
-
|
2092
|
-
synchronize do |client|
|
2093
|
-
client.call(args)
|
2094
|
-
end
|
2095
|
-
end
|
2096
|
-
|
2097
|
-
# Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
|
2098
|
-
# Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
|
2099
|
-
#
|
2100
|
-
# @example Retrieve members matching a
|
2101
|
-
# redis.zrevrangebylex("zset", "[a", "[a\xff")
|
2102
|
-
# # => ["abbygail", "abby", "abagael", "aaren"]
|
2103
|
-
# @example Retrieve the last 2 members matching a
|
2104
|
-
# redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
|
2105
|
-
# # => ["abbygail", "abby"]
|
2106
|
-
#
|
2107
|
-
# @see #zrangebylex
|
2108
|
-
def zrevrangebylex(key, max, min, limit: nil)
|
2109
|
-
args = [:zrevrangebylex, key, max, min]
|
2110
|
-
|
2111
|
-
if limit
|
2112
|
-
args << "LIMIT"
|
2113
|
-
args.concat(limit)
|
2114
|
-
end
|
2115
|
-
|
2116
|
-
synchronize do |client|
|
2117
|
-
client.call(args)
|
2118
|
-
end
|
2119
|
-
end
|
2120
|
-
|
2121
|
-
# Return a range of members in a sorted set, by score.
|
2122
|
-
#
|
2123
|
-
# @example Retrieve members with score `>= 5` and `< 100`
|
2124
|
-
# redis.zrangebyscore("zset", "5", "(100")
|
2125
|
-
# # => ["a", "b"]
|
2126
|
-
# @example Retrieve the first 2 members with score `>= 0`
|
2127
|
-
# redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
|
2128
|
-
# # => ["a", "b"]
|
2129
|
-
# @example Retrieve members and their scores with scores `> 5`
|
2130
|
-
# redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
|
2131
|
-
# # => [["a", 32.0], ["b", 64.0]]
|
2132
|
-
#
|
2133
|
-
# @param [String] key
|
2134
|
-
# @param [String] min
|
2135
|
-
# - inclusive minimum score is specified verbatim
|
2136
|
-
# - exclusive minimum score is specified by prefixing `(`
|
2137
|
-
# @param [String] max
|
2138
|
-
# - inclusive maximum score is specified verbatim
|
2139
|
-
# - exclusive maximum score is specified by prefixing `(`
|
2140
|
-
# @param [Hash] options
|
2141
|
-
# - `:with_scores => true`: include scores in output
|
2142
|
-
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
2143
|
-
# `count` members
|
2144
|
-
#
|
2145
|
-
# @return [Array<String>, Array<[String, Float]>]
|
2146
|
-
# - when `:with_scores` is not specified, an array of members
|
2147
|
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2148
|
-
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
2149
|
-
args = [:zrangebyscore, key, min, max]
|
2150
|
-
|
2151
|
-
if with_scores
|
2152
|
-
args << "WITHSCORES"
|
2153
|
-
block = FloatifyPairs
|
2154
|
-
end
|
2155
|
-
|
2156
|
-
if limit
|
2157
|
-
args << "LIMIT"
|
2158
|
-
args.concat(limit)
|
2159
|
-
end
|
2160
|
-
|
2161
|
-
synchronize do |client|
|
2162
|
-
client.call(args, &block)
|
2163
|
-
end
|
2164
|
-
end
|
2165
|
-
|
2166
|
-
# Return a range of members in a sorted set, by score, with scores ordered
|
2167
|
-
# from high to low.
|
2168
|
-
#
|
2169
|
-
# @example Retrieve members with score `< 100` and `>= 5`
|
2170
|
-
# redis.zrevrangebyscore("zset", "(100", "5")
|
2171
|
-
# # => ["b", "a"]
|
2172
|
-
# @example Retrieve the first 2 members with score `<= 0`
|
2173
|
-
# redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
|
2174
|
-
# # => ["b", "a"]
|
2175
|
-
# @example Retrieve members and their scores with scores `> 5`
|
2176
|
-
# redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
|
2177
|
-
# # => [["b", 64.0], ["a", 32.0]]
|
2178
|
-
#
|
2179
|
-
# @see #zrangebyscore
|
2180
|
-
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2181
|
-
args = [:zrevrangebyscore, key, max, min]
|
2182
|
-
|
2183
|
-
if with_scores
|
2184
|
-
args << "WITHSCORES"
|
2185
|
-
block = FloatifyPairs
|
2186
|
-
end
|
2187
|
-
|
2188
|
-
if limit
|
2189
|
-
args << "LIMIT"
|
2190
|
-
args.concat(limit)
|
2191
|
-
end
|
2192
|
-
|
2193
|
-
synchronize do |client|
|
2194
|
-
client.call(args, &block)
|
2195
|
-
end
|
2196
|
-
end
|
2197
|
-
|
2198
|
-
# Remove all members in a sorted set within the given scores.
|
2199
|
-
#
|
2200
|
-
# @example Remove members with score `>= 5` and `< 100`
|
2201
|
-
# redis.zremrangebyscore("zset", "5", "(100")
|
2202
|
-
# # => 2
|
2203
|
-
# @example Remove members with scores `> 5`
|
2204
|
-
# redis.zremrangebyscore("zset", "(5", "+inf")
|
2205
|
-
# # => 2
|
2206
|
-
#
|
2207
|
-
# @param [String] key
|
2208
|
-
# @param [String] min
|
2209
|
-
# - inclusive minimum score is specified verbatim
|
2210
|
-
# - exclusive minimum score is specified by prefixing `(`
|
2211
|
-
# @param [String] max
|
2212
|
-
# - inclusive maximum score is specified verbatim
|
2213
|
-
# - exclusive maximum score is specified by prefixing `(`
|
2214
|
-
# @return [Integer] number of members that were removed
|
2215
|
-
def zremrangebyscore(key, min, max)
|
2216
|
-
synchronize do |client|
|
2217
|
-
client.call([:zremrangebyscore, key, min, max])
|
2218
|
-
end
|
2219
|
-
end
|
2220
|
-
|
2221
|
-
# Count the members in a sorted set with scores within the given values.
|
2222
|
-
#
|
2223
|
-
# @example Count members with score `>= 5` and `< 100`
|
2224
|
-
# redis.zcount("zset", "5", "(100")
|
2225
|
-
# # => 2
|
2226
|
-
# @example Count members with scores `> 5`
|
2227
|
-
# redis.zcount("zset", "(5", "+inf")
|
2228
|
-
# # => 2
|
2229
|
-
#
|
2230
|
-
# @param [String] key
|
2231
|
-
# @param [String] min
|
2232
|
-
# - inclusive minimum score is specified verbatim
|
2233
|
-
# - exclusive minimum score is specified by prefixing `(`
|
2234
|
-
# @param [String] max
|
2235
|
-
# - inclusive maximum score is specified verbatim
|
2236
|
-
# - exclusive maximum score is specified by prefixing `(`
|
2237
|
-
# @return [Integer] number of members in within the specified range
|
2238
|
-
def zcount(key, min, max)
|
2239
|
-
synchronize do |client|
|
2240
|
-
client.call([:zcount, key, min, max])
|
2241
|
-
end
|
2242
|
-
end
|
2243
|
-
|
2244
|
-
# Return the intersection of multiple sorted sets
|
2245
|
-
#
|
2246
|
-
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
2247
|
-
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
2248
|
-
# # => ["v1", "v2"]
|
2249
|
-
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
2250
|
-
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
2251
|
-
# # => [["v1", 3.0], ["v2", 6.0]]
|
2252
|
-
#
|
2253
|
-
# @param [String, Array<String>] keys one or more keys to intersect
|
2254
|
-
# @param [Hash] options
|
2255
|
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2256
|
-
# sorted sets
|
2257
|
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2258
|
-
# - `:with_scores => true`: include scores in output
|
2259
|
-
#
|
2260
|
-
# @return [Array<String>, Array<[String, Float]>]
|
2261
|
-
# - when `:with_scores` is not specified, an array of members
|
2262
|
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2263
|
-
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
2264
|
-
args = [:zinter, keys.size, *keys]
|
2265
|
-
|
2266
|
-
if weights
|
2267
|
-
args << "WEIGHTS"
|
2268
|
-
args.concat(weights)
|
2269
|
-
end
|
2270
|
-
|
2271
|
-
args << "AGGREGATE" << aggregate if aggregate
|
2272
|
-
|
2273
|
-
if with_scores
|
2274
|
-
args << "WITHSCORES"
|
2275
|
-
block = FloatifyPairs
|
2276
|
-
end
|
2277
|
-
|
2278
|
-
synchronize do |client|
|
2279
|
-
client.call(args, &block)
|
2280
|
-
end
|
2281
|
-
end
|
2282
|
-
|
2283
|
-
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
2284
|
-
# key.
|
2285
|
-
#
|
2286
|
-
# @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
|
2287
|
-
# redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
|
2288
|
-
# # => 4
|
2289
|
-
#
|
2290
|
-
# @param [String] destination destination key
|
2291
|
-
# @param [Array<String>] keys source keys
|
2292
|
-
# @param [Hash] options
|
2293
|
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2294
|
-
# sorted sets
|
2295
|
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2296
|
-
# @return [Integer] number of elements in the resulting sorted set
|
2297
|
-
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2298
|
-
args = [:zinterstore, destination, keys.size, *keys]
|
2299
|
-
|
2300
|
-
if weights
|
2301
|
-
args << "WEIGHTS"
|
2302
|
-
args.concat(weights)
|
2303
|
-
end
|
2304
|
-
|
2305
|
-
args << "AGGREGATE" << aggregate if aggregate
|
2306
|
-
|
2307
|
-
synchronize do |client|
|
2308
|
-
client.call(args)
|
2309
|
-
end
|
2310
|
-
end
|
2311
|
-
|
2312
|
-
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
2313
|
-
#
|
2314
|
-
# @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
|
2315
|
-
# redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
|
2316
|
-
# # => 8
|
2317
|
-
#
|
2318
|
-
# @param [String] destination destination key
|
2319
|
-
# @param [Array<String>] keys source keys
|
2320
|
-
# @param [Hash] options
|
2321
|
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2322
|
-
# sorted sets
|
2323
|
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2324
|
-
# @return [Integer] number of elements in the resulting sorted set
|
2325
|
-
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2326
|
-
args = [:zunionstore, destination, keys.size, *keys]
|
2327
|
-
|
2328
|
-
if weights
|
2329
|
-
args << "WEIGHTS"
|
2330
|
-
args.concat(weights)
|
2331
|
-
end
|
2332
|
-
|
2333
|
-
args << "AGGREGATE" << aggregate if aggregate
|
2334
|
-
|
2335
|
-
synchronize do |client|
|
2336
|
-
client.call(args)
|
2337
|
-
end
|
2338
|
-
end
|
2339
|
-
|
2340
|
-
# Get the number of fields in a hash.
|
2341
|
-
#
|
2342
|
-
# @param [String] key
|
2343
|
-
# @return [Integer] number of fields in the hash
|
2344
|
-
def hlen(key)
|
2345
|
-
synchronize do |client|
|
2346
|
-
client.call([:hlen, key])
|
2347
|
-
end
|
2348
|
-
end
|
2349
|
-
|
2350
|
-
# Set one or more hash values.
|
2351
|
-
#
|
2352
|
-
# @example
|
2353
|
-
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2354
|
-
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
2355
|
-
#
|
2356
|
-
# @param [String] key
|
2357
|
-
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2358
|
-
# @return [Integer] The number of fields that were added to the hash
|
2359
|
-
def hset(key, *attrs)
|
2360
|
-
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2361
|
-
|
2362
|
-
synchronize do |client|
|
2363
|
-
client.call([:hset, key, *attrs])
|
2364
|
-
end
|
2365
|
-
end
|
2366
|
-
|
2367
|
-
# Set the value of a hash field, only if the field does not exist.
|
2368
|
-
#
|
2369
|
-
# @param [String] key
|
2370
|
-
# @param [String] field
|
2371
|
-
# @param [String] value
|
2372
|
-
# @return [Boolean] whether or not the field was **added** to the hash
|
2373
|
-
def hsetnx(key, field, value)
|
2374
|
-
synchronize do |client|
|
2375
|
-
client.call([:hsetnx, key, field, value], &Boolify)
|
2376
|
-
end
|
2377
|
-
end
|
2378
|
-
|
2379
|
-
# Set one or more hash values.
|
2380
|
-
#
|
2381
|
-
# @example
|
2382
|
-
# redis.hmset("hash", "f1", "v1", "f2", "v2")
|
2383
|
-
# # => "OK"
|
2384
|
-
#
|
2385
|
-
# @param [String] key
|
2386
|
-
# @param [Array<String>] attrs array of fields and values
|
2387
|
-
# @return [String] `"OK"`
|
2388
|
-
#
|
2389
|
-
# @see #mapped_hmset
|
2390
|
-
def hmset(key, *attrs)
|
2391
|
-
synchronize do |client|
|
2392
|
-
client.call([:hmset, key] + attrs)
|
2393
|
-
end
|
2394
|
-
end
|
2395
|
-
|
2396
|
-
# Set one or more hash values.
|
2397
|
-
#
|
2398
|
-
# @example
|
2399
|
-
# redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
|
2400
|
-
# # => "OK"
|
2401
|
-
#
|
2402
|
-
# @param [String] key
|
2403
|
-
# @param [Hash] hash a non-empty hash with fields mapping to values
|
2404
|
-
# @return [String] `"OK"`
|
2405
|
-
#
|
2406
|
-
# @see #hmset
|
2407
|
-
def mapped_hmset(key, hash)
|
2408
|
-
hmset(key, hash.to_a.flatten)
|
2409
|
-
end
|
2410
|
-
|
2411
|
-
# Get the value of a hash field.
|
2412
|
-
#
|
2413
|
-
# @param [String] key
|
2414
|
-
# @param [String] field
|
2415
|
-
# @return [String]
|
2416
|
-
def hget(key, field)
|
2417
|
-
synchronize do |client|
|
2418
|
-
client.call([:hget, key, field])
|
2419
|
-
end
|
2420
|
-
end
|
2421
|
-
|
2422
|
-
# Get the values of all the given hash fields.
|
2423
|
-
#
|
2424
|
-
# @example
|
2425
|
-
# redis.hmget("hash", "f1", "f2")
|
2426
|
-
# # => ["v1", "v2"]
|
2427
|
-
#
|
2428
|
-
# @param [String] key
|
2429
|
-
# @param [Array<String>] fields array of fields
|
2430
|
-
# @return [Array<String>] an array of values for the specified fields
|
2431
|
-
#
|
2432
|
-
# @see #mapped_hmget
|
2433
|
-
def hmget(key, *fields, &blk)
|
2434
|
-
synchronize do |client|
|
2435
|
-
client.call([:hmget, key] + fields, &blk)
|
2436
|
-
end
|
2437
|
-
end
|
2438
|
-
|
2439
|
-
# Get the values of all the given hash fields.
|
2440
|
-
#
|
2441
|
-
# @example
|
2442
|
-
# redis.mapped_hmget("hash", "f1", "f2")
|
2443
|
-
# # => { "f1" => "v1", "f2" => "v2" }
|
2444
|
-
#
|
2445
|
-
# @param [String] key
|
2446
|
-
# @param [Array<String>] fields array of fields
|
2447
|
-
# @return [Hash] a hash mapping the specified fields to their values
|
2448
|
-
#
|
2449
|
-
# @see #hmget
|
2450
|
-
def mapped_hmget(key, *fields)
|
2451
|
-
hmget(key, *fields) do |reply|
|
2452
|
-
if reply.is_a?(Array)
|
2453
|
-
Hash[fields.zip(reply)]
|
2454
|
-
else
|
2455
|
-
reply
|
2456
|
-
end
|
2457
|
-
end
|
2458
|
-
end
|
2459
|
-
|
2460
|
-
# Delete one or more hash fields.
|
2461
|
-
#
|
2462
|
-
# @param [String] key
|
2463
|
-
# @param [String, Array<String>] field
|
2464
|
-
# @return [Integer] the number of fields that were removed from the hash
|
2465
|
-
def hdel(key, *fields)
|
2466
|
-
synchronize do |client|
|
2467
|
-
client.call([:hdel, key, *fields])
|
2468
|
-
end
|
2469
|
-
end
|
2470
|
-
|
2471
|
-
# Determine if a hash field exists.
|
2472
|
-
#
|
2473
|
-
# @param [String] key
|
2474
|
-
# @param [String] field
|
2475
|
-
# @return [Boolean] whether or not the field exists in the hash
|
2476
|
-
def hexists(key, field)
|
2477
|
-
synchronize do |client|
|
2478
|
-
client.call([:hexists, key, field], &Boolify)
|
2479
|
-
end
|
2480
|
-
end
|
2481
|
-
|
2482
|
-
# Increment the integer value of a hash field by the given integer number.
|
2483
|
-
#
|
2484
|
-
# @param [String] key
|
2485
|
-
# @param [String] field
|
2486
|
-
# @param [Integer] increment
|
2487
|
-
# @return [Integer] value of the field after incrementing it
|
2488
|
-
def hincrby(key, field, increment)
|
2489
|
-
synchronize do |client|
|
2490
|
-
client.call([:hincrby, key, field, increment])
|
2491
|
-
end
|
2492
|
-
end
|
2493
|
-
|
2494
|
-
# Increment the numeric value of a hash field by the given float number.
|
2495
|
-
#
|
2496
|
-
# @param [String] key
|
2497
|
-
# @param [String] field
|
2498
|
-
# @param [Float] increment
|
2499
|
-
# @return [Float] value of the field after incrementing it
|
2500
|
-
def hincrbyfloat(key, field, increment)
|
2501
|
-
synchronize do |client|
|
2502
|
-
client.call([:hincrbyfloat, key, field, increment], &Floatify)
|
2503
|
-
end
|
2504
|
-
end
|
2505
|
-
|
2506
|
-
# Get all the fields in a hash.
|
2507
|
-
#
|
2508
|
-
# @param [String] key
|
2509
|
-
# @return [Array<String>]
|
2510
|
-
def hkeys(key)
|
2511
|
-
synchronize do |client|
|
2512
|
-
client.call([:hkeys, key])
|
2513
|
-
end
|
2514
|
-
end
|
2515
|
-
|
2516
|
-
# Get all the values in a hash.
|
2517
|
-
#
|
2518
|
-
# @param [String] key
|
2519
|
-
# @return [Array<String>]
|
2520
|
-
def hvals(key)
|
2521
|
-
synchronize do |client|
|
2522
|
-
client.call([:hvals, key])
|
2523
|
-
end
|
2524
|
-
end
|
2525
|
-
|
2526
|
-
# Get all the fields and values in a hash.
|
2527
|
-
#
|
2528
|
-
# @param [String] key
|
2529
|
-
# @return [Hash<String, String>]
|
2530
|
-
def hgetall(key)
|
2531
|
-
synchronize do |client|
|
2532
|
-
client.call([:hgetall, key], &Hashify)
|
2533
|
-
end
|
2534
|
-
end
|
2535
|
-
|
2536
|
-
# Post a message to a channel.
|
2537
|
-
def publish(channel, message)
|
2538
|
-
synchronize do |client|
|
2539
|
-
client.call([:publish, channel, message])
|
2540
|
-
end
|
2541
|
-
end
|
2542
|
-
|
2543
|
-
def subscribed?
|
2544
|
-
synchronize do |client|
|
2545
|
-
client.is_a? SubscribedClient
|
2546
|
-
end
|
2547
|
-
end
|
2548
|
-
|
2549
|
-
# Listen for messages published to the given channels.
|
2550
|
-
def subscribe(*channels, &block)
|
2551
|
-
synchronize do |_client|
|
2552
|
-
_subscription(:subscribe, 0, channels, block)
|
2553
|
-
end
|
2554
|
-
end
|
2555
|
-
|
2556
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2557
|
-
# if there is no messages for a timeout period.
|
2558
|
-
def subscribe_with_timeout(timeout, *channels, &block)
|
2559
|
-
synchronize do |_client|
|
2560
|
-
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2561
|
-
end
|
2562
|
-
end
|
2563
|
-
|
2564
|
-
# Stop listening for messages posted to the given channels.
|
2565
|
-
def unsubscribe(*channels)
|
2566
|
-
synchronize do |client|
|
2567
|
-
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2568
|
-
|
2569
|
-
client.unsubscribe(*channels)
|
2570
|
-
end
|
2571
|
-
end
|
2572
|
-
|
2573
|
-
# Listen for messages published to channels matching the given patterns.
|
2574
|
-
def psubscribe(*channels, &block)
|
2575
|
-
synchronize do |_client|
|
2576
|
-
_subscription(:psubscribe, 0, channels, block)
|
2577
|
-
end
|
2578
|
-
end
|
2579
|
-
|
2580
|
-
# Listen for messages published to channels matching the given patterns.
|
2581
|
-
# Throw a timeout error if there is no messages for a timeout period.
|
2582
|
-
def psubscribe_with_timeout(timeout, *channels, &block)
|
2583
|
-
synchronize do |_client|
|
2584
|
-
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2585
|
-
end
|
2586
|
-
end
|
2587
|
-
|
2588
|
-
# Stop listening for messages posted to channels matching the given patterns.
|
2589
|
-
def punsubscribe(*channels)
|
2590
|
-
synchronize do |client|
|
2591
|
-
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2592
|
-
|
2593
|
-
client.punsubscribe(*channels)
|
2594
|
-
end
|
2595
|
-
end
|
2596
|
-
|
2597
|
-
# Inspect the state of the Pub/Sub subsystem.
|
2598
|
-
# Possible subcommands: channels, numsub, numpat.
|
2599
|
-
def pubsub(subcommand, *args)
|
2600
|
-
synchronize do |client|
|
2601
|
-
client.call([:pubsub, subcommand] + args)
|
2602
|
-
end
|
2603
|
-
end
|
2604
|
-
|
2605
|
-
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
2606
|
-
#
|
2607
|
-
# Using a block is optional, but is necessary for thread-safety.
|
2608
|
-
#
|
2609
|
-
# An `#unwatch` is automatically issued if an exception is raised within the
|
2610
|
-
# block that is a subclass of StandardError and is not a ConnectionError.
|
2611
|
-
#
|
2612
|
-
# @example With a block
|
2613
|
-
# redis.watch("key") do
|
2614
|
-
# if redis.get("key") == "some value"
|
2615
|
-
# redis.multi do |multi|
|
2616
|
-
# multi.set("key", "other value")
|
2617
|
-
# multi.incr("counter")
|
2618
|
-
# end
|
2619
|
-
# else
|
2620
|
-
# redis.unwatch
|
2621
|
-
# end
|
2622
|
-
# end
|
2623
|
-
# # => ["OK", 6]
|
2624
|
-
#
|
2625
|
-
# @example Without a block
|
2626
|
-
# redis.watch("key")
|
2627
|
-
# # => "OK"
|
2628
|
-
#
|
2629
|
-
# @param [String, Array<String>] keys one or more keys to watch
|
2630
|
-
# @return [Object] if using a block, returns the return value of the block
|
2631
|
-
# @return [String] if not using a block, returns `OK`
|
2632
|
-
#
|
2633
|
-
# @see #unwatch
|
2634
|
-
# @see #multi
|
2635
|
-
def watch(*keys)
|
2636
|
-
synchronize do |client|
|
2637
|
-
res = client.call([:watch, *keys])
|
2638
|
-
|
2639
|
-
if block_given?
|
2640
|
-
begin
|
2641
|
-
yield(self)
|
2642
|
-
rescue ConnectionError
|
2643
|
-
raise
|
2644
|
-
rescue StandardError
|
2645
|
-
unwatch
|
2646
|
-
raise
|
2647
|
-
end
|
2648
|
-
else
|
2649
|
-
res
|
2650
|
-
end
|
2651
|
-
end
|
2652
|
-
end
|
2653
|
-
|
2654
|
-
# Forget about all watched keys.
|
2655
|
-
#
|
2656
|
-
# @return [String] `OK`
|
2657
|
-
#
|
2658
|
-
# @see #watch
|
2659
|
-
# @see #multi
|
2660
|
-
def unwatch
|
2661
|
-
synchronize do |client|
|
2662
|
-
client.call([:unwatch])
|
2663
|
-
end
|
2664
|
-
end
|
2665
|
-
|
2666
|
-
def pipelined
|
2667
|
-
synchronize do |prior_client|
|
2668
|
-
begin
|
2669
|
-
@client = Pipeline.new(prior_client)
|
2670
|
-
yield(self)
|
2671
|
-
prior_client.call_pipeline(@client)
|
2672
|
-
ensure
|
2673
|
-
@client = prior_client
|
2674
|
-
end
|
2675
|
-
end
|
2676
|
-
end
|
2677
|
-
|
2678
|
-
# Mark the start of a transaction block.
|
2679
|
-
#
|
2680
|
-
# Passing a block is optional.
|
2681
|
-
#
|
2682
|
-
# @example With a block
|
2683
|
-
# redis.multi do |multi|
|
2684
|
-
# multi.set("key", "value")
|
2685
|
-
# multi.incr("counter")
|
2686
|
-
# end # => ["OK", 6]
|
2687
|
-
#
|
2688
|
-
# @example Without a block
|
2689
|
-
# redis.multi
|
2690
|
-
# # => "OK"
|
2691
|
-
# redis.set("key", "value")
|
2692
|
-
# # => "QUEUED"
|
2693
|
-
# redis.incr("counter")
|
2694
|
-
# # => "QUEUED"
|
2695
|
-
# redis.exec
|
2696
|
-
# # => ["OK", 6]
|
2697
|
-
#
|
2698
|
-
# @yield [multi] the commands that are called inside this block are cached
|
2699
|
-
# and written to the server upon returning from it
|
2700
|
-
# @yieldparam [Redis] multi `self`
|
2701
|
-
#
|
2702
|
-
# @return [String, Array<...>]
|
2703
|
-
# - when a block is not given, `OK`
|
2704
|
-
# - when a block is given, an array with replies
|
2705
|
-
#
|
2706
|
-
# @see #watch
|
2707
|
-
# @see #unwatch
|
2708
|
-
def multi
|
2709
|
-
synchronize do |prior_client|
|
2710
|
-
if !block_given?
|
2711
|
-
prior_client.call([:multi])
|
2712
|
-
else
|
2713
|
-
begin
|
2714
|
-
@client = Pipeline::Multi.new(prior_client)
|
2715
|
-
yield(self)
|
2716
|
-
prior_client.call_pipeline(@client)
|
2717
|
-
ensure
|
2718
|
-
@client = prior_client
|
2719
|
-
end
|
2720
|
-
end
|
2721
|
-
end
|
2722
|
-
end
|
2723
|
-
|
2724
|
-
# Execute all commands issued after MULTI.
|
2725
|
-
#
|
2726
|
-
# Only call this method when `#multi` was called **without** a block.
|
2727
|
-
#
|
2728
|
-
# @return [nil, Array<...>]
|
2729
|
-
# - when commands were not executed, `nil`
|
2730
|
-
# - when commands were executed, an array with their replies
|
2731
|
-
#
|
2732
|
-
# @see #multi
|
2733
|
-
# @see #discard
|
2734
|
-
def exec
|
2735
|
-
synchronize do |client|
|
2736
|
-
client.call([:exec])
|
2737
|
-
end
|
2738
|
-
end
|
2739
|
-
|
2740
|
-
# Discard all commands issued after MULTI.
|
2741
|
-
#
|
2742
|
-
# Only call this method when `#multi` was called **without** a block.
|
2743
|
-
#
|
2744
|
-
# @return [String] `"OK"`
|
2745
|
-
#
|
2746
|
-
# @see #multi
|
2747
|
-
# @see #exec
|
2748
|
-
def discard
|
2749
|
-
synchronize do |client|
|
2750
|
-
client.call([:discard])
|
2751
|
-
end
|
2752
|
-
end
|
2753
|
-
|
2754
|
-
# Control remote script registry.
|
2755
|
-
#
|
2756
|
-
# @example Load a script
|
2757
|
-
# sha = redis.script(:load, "return 1")
|
2758
|
-
# # => <sha of this script>
|
2759
|
-
# @example Check if a script exists
|
2760
|
-
# redis.script(:exists, sha)
|
2761
|
-
# # => true
|
2762
|
-
# @example Check if multiple scripts exist
|
2763
|
-
# redis.script(:exists, [sha, other_sha])
|
2764
|
-
# # => [true, false]
|
2765
|
-
# @example Flush the script registry
|
2766
|
-
# redis.script(:flush)
|
2767
|
-
# # => "OK"
|
2768
|
-
# @example Kill a running script
|
2769
|
-
# redis.script(:kill)
|
2770
|
-
# # => "OK"
|
2771
|
-
#
|
2772
|
-
# @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
|
2773
|
-
# @param [Array<String>] args depends on subcommand
|
2774
|
-
# @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
|
2775
|
-
#
|
2776
|
-
# @see #eval
|
2777
|
-
# @see #evalsha
|
2778
|
-
def script(subcommand, *args)
|
2779
|
-
subcommand = subcommand.to_s.downcase
|
2780
|
-
|
2781
|
-
if subcommand == "exists"
|
2782
|
-
synchronize do |client|
|
2783
|
-
arg = args.first
|
2784
|
-
|
2785
|
-
client.call([:script, :exists, arg]) do |reply|
|
2786
|
-
reply = reply.map { |r| Boolify.call(r) }
|
2787
|
-
|
2788
|
-
if arg.is_a?(Array)
|
2789
|
-
reply
|
2790
|
-
else
|
2791
|
-
reply.first
|
2792
|
-
end
|
2793
|
-
end
|
2794
|
-
end
|
2795
|
-
else
|
2796
|
-
synchronize do |client|
|
2797
|
-
client.call([:script, subcommand] + args)
|
2798
|
-
end
|
2799
|
-
end
|
2800
|
-
end
|
2801
|
-
|
2802
|
-
def _eval(cmd, args)
|
2803
|
-
script = args.shift
|
2804
|
-
options = args.pop if args.last.is_a?(Hash)
|
2805
|
-
options ||= {}
|
2806
|
-
|
2807
|
-
keys = args.shift || options[:keys] || []
|
2808
|
-
argv = args.shift || options[:argv] || []
|
2809
|
-
|
2810
|
-
synchronize do |client|
|
2811
|
-
client.call([cmd, script, keys.length] + keys + argv)
|
2812
|
-
end
|
2813
|
-
end
|
2814
|
-
|
2815
|
-
# Evaluate Lua script.
|
2816
|
-
#
|
2817
|
-
# @example EVAL without KEYS nor ARGV
|
2818
|
-
# redis.eval("return 1")
|
2819
|
-
# # => 1
|
2820
|
-
# @example EVAL with KEYS and ARGV as array arguments
|
2821
|
-
# redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
|
2822
|
-
# # => [["k1", "k2"], ["a1", "a2"]]
|
2823
|
-
# @example EVAL with KEYS and ARGV in a hash argument
|
2824
|
-
# redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
|
2825
|
-
# # => [["k1", "k2"], ["a1", "a2"]]
|
2826
|
-
#
|
2827
|
-
# @param [Array<String>] keys optional array with keys to pass to the script
|
2828
|
-
# @param [Array<String>] argv optional array with arguments to pass to the script
|
2829
|
-
# @param [Hash] options
|
2830
|
-
# - `:keys => Array<String>`: optional array with keys to pass to the script
|
2831
|
-
# - `:argv => Array<String>`: optional array with arguments to pass to the script
|
2832
|
-
# @return depends on the script
|
2833
|
-
#
|
2834
|
-
# @see #script
|
2835
|
-
# @see #evalsha
|
2836
|
-
def eval(*args)
|
2837
|
-
_eval(:eval, args)
|
2838
|
-
end
|
2839
|
-
|
2840
|
-
# Evaluate Lua script by its SHA.
|
2841
|
-
#
|
2842
|
-
# @example EVALSHA without KEYS nor ARGV
|
2843
|
-
# redis.evalsha(sha)
|
2844
|
-
# # => <depends on script>
|
2845
|
-
# @example EVALSHA with KEYS and ARGV as array arguments
|
2846
|
-
# redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
|
2847
|
-
# # => <depends on script>
|
2848
|
-
# @example EVALSHA with KEYS and ARGV in a hash argument
|
2849
|
-
# redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
|
2850
|
-
# # => <depends on script>
|
2851
|
-
#
|
2852
|
-
# @param [Array<String>] keys optional array with keys to pass to the script
|
2853
|
-
# @param [Array<String>] argv optional array with arguments to pass to the script
|
2854
|
-
# @param [Hash] options
|
2855
|
-
# - `:keys => Array<String>`: optional array with keys to pass to the script
|
2856
|
-
# - `:argv => Array<String>`: optional array with arguments to pass to the script
|
2857
|
-
# @return depends on the script
|
2858
|
-
#
|
2859
|
-
# @see #script
|
2860
|
-
# @see #eval
|
2861
|
-
def evalsha(*args)
|
2862
|
-
_eval(:evalsha, args)
|
2863
|
-
end
|
2864
|
-
|
2865
|
-
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2866
|
-
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2867
|
-
|
2868
|
-
args << cursor
|
2869
|
-
args << "MATCH" << match if match
|
2870
|
-
args << "COUNT" << count if count
|
2871
|
-
args << "TYPE" << type if type
|
2872
|
-
|
2873
|
-
synchronize do |client|
|
2874
|
-
client.call([command] + args, &block)
|
2875
|
-
end
|
2876
|
-
end
|
2877
|
-
|
2878
|
-
# Scan the keyspace
|
2879
|
-
#
|
2880
|
-
# @example Retrieve the first batch of keys
|
2881
|
-
# redis.scan(0)
|
2882
|
-
# # => ["4", ["key:21", "key:47", "key:42"]]
|
2883
|
-
# @example Retrieve a batch of keys matching a pattern
|
2884
|
-
# redis.scan(4, :match => "key:1?")
|
2885
|
-
# # => ["92", ["key:13", "key:18"]]
|
2886
|
-
# @example Retrieve a batch of keys of a certain type
|
2887
|
-
# redis.scan(92, :type => "zset")
|
2888
|
-
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2889
|
-
#
|
2890
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2891
|
-
# @param [Hash] options
|
2892
|
-
# - `:match => String`: only return keys matching the pattern
|
2893
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
-
# - `:type => String`: return keys only of the given type
|
2895
|
-
#
|
2896
|
-
# @return [String, Array<String>] the next cursor and all found keys
|
2897
|
-
def scan(cursor, **options)
|
2898
|
-
_scan(:scan, cursor, [], **options)
|
2899
|
-
end
|
2900
|
-
|
2901
|
-
# Scan the keyspace
|
2902
|
-
#
|
2903
|
-
# @example Retrieve all of the keys (with possible duplicates)
|
2904
|
-
# redis.scan_each.to_a
|
2905
|
-
# # => ["key:21", "key:47", "key:42"]
|
2906
|
-
# @example Execute block for each key matching a pattern
|
2907
|
-
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2908
|
-
# # => key:13
|
2909
|
-
# # => key:18
|
2910
|
-
# @example Execute block for each key of a type
|
2911
|
-
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2912
|
-
# # => "hash"
|
2913
|
-
# # => "hash"
|
2914
|
-
#
|
2915
|
-
# @param [Hash] options
|
2916
|
-
# - `:match => String`: only return keys matching the pattern
|
2917
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
-
# - `:type => String`: return keys only of the given type
|
2919
|
-
#
|
2920
|
-
# @return [Enumerator] an enumerator for all found keys
|
2921
|
-
def scan_each(**options, &block)
|
2922
|
-
return to_enum(:scan_each, **options) unless block_given?
|
2923
|
-
|
2924
|
-
cursor = 0
|
2925
|
-
loop do
|
2926
|
-
cursor, keys = scan(cursor, **options)
|
2927
|
-
keys.each(&block)
|
2928
|
-
break if cursor == "0"
|
2929
|
-
end
|
2930
|
-
end
|
2931
|
-
|
2932
|
-
# Scan a hash
|
2933
|
-
#
|
2934
|
-
# @example Retrieve the first batch of key/value pairs in a hash
|
2935
|
-
# redis.hscan("hash", 0)
|
2936
|
-
#
|
2937
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2938
|
-
# @param [Hash] options
|
2939
|
-
# - `:match => String`: only return keys matching the pattern
|
2940
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2941
|
-
#
|
2942
|
-
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2943
|
-
def hscan(key, cursor, **options)
|
2944
|
-
_scan(:hscan, cursor, [key], **options) do |reply|
|
2945
|
-
[reply[0], reply[1].each_slice(2).to_a]
|
2946
|
-
end
|
2947
|
-
end
|
2948
|
-
|
2949
|
-
# Scan a hash
|
2950
|
-
#
|
2951
|
-
# @example Retrieve all of the key/value pairs in a hash
|
2952
|
-
# redis.hscan_each("hash").to_a
|
2953
|
-
# # => [["key70", "70"], ["key80", "80"]]
|
2954
|
-
#
|
2955
|
-
# @param [Hash] options
|
2956
|
-
# - `:match => String`: only return keys matching the pattern
|
2957
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2958
|
-
#
|
2959
|
-
# @return [Enumerator] an enumerator for all found keys
|
2960
|
-
def hscan_each(key, **options, &block)
|
2961
|
-
return to_enum(:hscan_each, key, **options) unless block_given?
|
2962
|
-
|
2963
|
-
cursor = 0
|
2964
|
-
loop do
|
2965
|
-
cursor, values = hscan(key, cursor, **options)
|
2966
|
-
values.each(&block)
|
2967
|
-
break if cursor == "0"
|
2968
|
-
end
|
2969
|
-
end
|
2970
|
-
|
2971
|
-
# Scan a sorted set
|
2972
|
-
#
|
2973
|
-
# @example Retrieve the first batch of key/value pairs in a hash
|
2974
|
-
# redis.zscan("zset", 0)
|
2975
|
-
#
|
2976
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2977
|
-
# @param [Hash] options
|
2978
|
-
# - `:match => String`: only return keys matching the pattern
|
2979
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2980
|
-
#
|
2981
|
-
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2982
|
-
# members and scores
|
2983
|
-
def zscan(key, cursor, **options)
|
2984
|
-
_scan(:zscan, cursor, [key], **options) do |reply|
|
2985
|
-
[reply[0], FloatifyPairs.call(reply[1])]
|
2986
|
-
end
|
2987
|
-
end
|
2988
|
-
|
2989
|
-
# Scan a sorted set
|
2990
|
-
#
|
2991
|
-
# @example Retrieve all of the members/scores in a sorted set
|
2992
|
-
# redis.zscan_each("zset").to_a
|
2993
|
-
# # => [["key70", "70"], ["key80", "80"]]
|
2994
|
-
#
|
2995
|
-
# @param [Hash] options
|
2996
|
-
# - `:match => String`: only return keys matching the pattern
|
2997
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2998
|
-
#
|
2999
|
-
# @return [Enumerator] an enumerator for all found scores and members
|
3000
|
-
def zscan_each(key, **options, &block)
|
3001
|
-
return to_enum(:zscan_each, key, **options) unless block_given?
|
3002
|
-
|
3003
|
-
cursor = 0
|
3004
|
-
loop do
|
3005
|
-
cursor, values = zscan(key, cursor, **options)
|
3006
|
-
values.each(&block)
|
3007
|
-
break if cursor == "0"
|
3008
|
-
end
|
3009
|
-
end
|
3010
|
-
|
3011
|
-
# Scan a set
|
3012
|
-
#
|
3013
|
-
# @example Retrieve the first batch of keys in a set
|
3014
|
-
# redis.sscan("set", 0)
|
3015
|
-
#
|
3016
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
3017
|
-
# @param [Hash] options
|
3018
|
-
# - `:match => String`: only return keys matching the pattern
|
3019
|
-
# - `:count => Integer`: return count keys at most per iteration
|
3020
|
-
#
|
3021
|
-
# @return [String, Array<String>] the next cursor and all found members
|
3022
|
-
def sscan(key, cursor, **options)
|
3023
|
-
_scan(:sscan, cursor, [key], **options)
|
3024
|
-
end
|
3025
|
-
|
3026
|
-
# Scan a set
|
3027
|
-
#
|
3028
|
-
# @example Retrieve all of the keys in a set
|
3029
|
-
# redis.sscan_each("set").to_a
|
3030
|
-
# # => ["key1", "key2", "key3"]
|
3031
|
-
#
|
3032
|
-
# @param [Hash] options
|
3033
|
-
# - `:match => String`: only return keys matching the pattern
|
3034
|
-
# - `:count => Integer`: return count keys at most per iteration
|
3035
|
-
#
|
3036
|
-
# @return [Enumerator] an enumerator for all keys in the set
|
3037
|
-
def sscan_each(key, **options, &block)
|
3038
|
-
return to_enum(:sscan_each, key, **options) unless block_given?
|
3039
|
-
|
3040
|
-
cursor = 0
|
3041
|
-
loop do
|
3042
|
-
cursor, keys = sscan(key, cursor, **options)
|
3043
|
-
keys.each(&block)
|
3044
|
-
break if cursor == "0"
|
3045
|
-
end
|
3046
|
-
end
|
3047
|
-
|
3048
|
-
# Add one or more members to a HyperLogLog structure.
|
3049
|
-
#
|
3050
|
-
# @param [String] key
|
3051
|
-
# @param [String, Array<String>] member one member, or array of members
|
3052
|
-
# @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
|
3053
|
-
def pfadd(key, member)
|
3054
|
-
synchronize do |client|
|
3055
|
-
client.call([:pfadd, key, member], &Boolify)
|
3056
|
-
end
|
3057
|
-
end
|
3058
|
-
|
3059
|
-
# Get the approximate cardinality of members added to HyperLogLog structure.
|
3060
|
-
#
|
3061
|
-
# If called with multiple keys, returns the approximate cardinality of the
|
3062
|
-
# union of the HyperLogLogs contained in the keys.
|
3063
|
-
#
|
3064
|
-
# @param [String, Array<String>] keys
|
3065
|
-
# @return [Integer]
|
3066
|
-
def pfcount(*keys)
|
3067
|
-
synchronize do |client|
|
3068
|
-
client.call([:pfcount] + keys)
|
3069
|
-
end
|
3070
|
-
end
|
3071
|
-
|
3072
|
-
# Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
|
3073
|
-
# the observed Sets of the source HyperLogLog structures.
|
3074
|
-
#
|
3075
|
-
# @param [String] dest_key destination key
|
3076
|
-
# @param [String, Array<String>] source_key source key, or array of keys
|
3077
|
-
# @return [Boolean]
|
3078
|
-
def pfmerge(dest_key, *source_key)
|
3079
|
-
synchronize do |client|
|
3080
|
-
client.call([:pfmerge, dest_key, *source_key], &BoolifySet)
|
3081
|
-
end
|
3082
|
-
end
|
3083
|
-
|
3084
|
-
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
3085
|
-
#
|
3086
|
-
# @param [String] key
|
3087
|
-
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
3088
|
-
# @return [Integer] number of elements added to the sorted set
|
3089
|
-
def geoadd(key, *member)
|
3090
|
-
synchronize do |client|
|
3091
|
-
client.call([:geoadd, key, *member])
|
3092
|
-
end
|
3093
|
-
end
|
3094
|
-
|
3095
|
-
# Returns geohash string representing position for specified members of the specified key.
|
3096
|
-
#
|
3097
|
-
# @param [String] key
|
3098
|
-
# @param [String, Array<String>] member one member or array of members
|
3099
|
-
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
3100
|
-
def geohash(key, member)
|
3101
|
-
synchronize do |client|
|
3102
|
-
client.call([:geohash, key, member])
|
3103
|
-
end
|
3104
|
-
end
|
3105
|
-
|
3106
|
-
# Query a sorted set representing a geospatial index to fetch members matching a
|
3107
|
-
# given maximum distance from a point
|
3108
|
-
#
|
3109
|
-
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
3110
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
3111
|
-
# or the farthest to the nearest relative to the center
|
3112
|
-
# @param [Integer] count limit the results to the first N matching items
|
3113
|
-
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
3114
|
-
# @return [Array<String>] may be changed with `options`
|
3115
|
-
|
3116
|
-
def georadius(*args, **geoptions)
|
3117
|
-
geoarguments = _geoarguments(*args, **geoptions)
|
3118
|
-
|
3119
|
-
synchronize do |client|
|
3120
|
-
client.call([:georadius, *geoarguments])
|
3121
|
-
end
|
3122
|
-
end
|
3123
|
-
|
3124
|
-
# Query a sorted set representing a geospatial index to fetch members matching a
|
3125
|
-
# given maximum distance from an already existing member
|
3126
|
-
#
|
3127
|
-
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
3128
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
3129
|
-
# to the nearest relative to the center
|
3130
|
-
# @param [Integer] count limit the results to the first N matching items
|
3131
|
-
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
3132
|
-
# @return [Array<String>] may be changed with `options`
|
3133
|
-
|
3134
|
-
def georadiusbymember(*args, **geoptions)
|
3135
|
-
geoarguments = _geoarguments(*args, **geoptions)
|
3136
|
-
|
3137
|
-
synchronize do |client|
|
3138
|
-
client.call([:georadiusbymember, *geoarguments])
|
3139
|
-
end
|
3140
|
-
end
|
3141
|
-
|
3142
|
-
# Returns longitude and latitude of members of a geospatial index
|
3143
|
-
#
|
3144
|
-
# @param [String] key
|
3145
|
-
# @param [String, Array<String>] member one member or array of members
|
3146
|
-
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3147
|
-
# element is either array of longitude and latitude or nil
|
3148
|
-
def geopos(key, member)
|
3149
|
-
synchronize do |client|
|
3150
|
-
client.call([:geopos, key, member])
|
3151
|
-
end
|
3152
|
-
end
|
3153
|
-
|
3154
|
-
# Returns the distance between two members of a geospatial index
|
3155
|
-
#
|
3156
|
-
# @param [String ]key
|
3157
|
-
# @param [Array<String>] members
|
3158
|
-
# @param ['m', 'km', 'mi', 'ft'] unit
|
3159
|
-
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
3160
|
-
def geodist(key, member1, member2, unit = 'm')
|
3161
|
-
synchronize do |client|
|
3162
|
-
client.call([:geodist, key, member1, member2, unit])
|
3163
|
-
end
|
3164
|
-
end
|
3165
|
-
|
3166
|
-
# Returns the stream information each subcommand.
|
3167
|
-
#
|
3168
|
-
# @example stream
|
3169
|
-
# redis.xinfo(:stream, 'mystream')
|
3170
|
-
# @example groups
|
3171
|
-
# redis.xinfo(:groups, 'mystream')
|
3172
|
-
# @example consumers
|
3173
|
-
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
3174
|
-
#
|
3175
|
-
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
3176
|
-
# @param key [String] the stream key
|
3177
|
-
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
3178
|
-
#
|
3179
|
-
# @return [Hash] information of the stream if subcommand is `stream`
|
3180
|
-
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
3181
|
-
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
3182
|
-
def xinfo(subcommand, key, group = nil)
|
3183
|
-
args = [:xinfo, subcommand, key, group].compact
|
3184
|
-
synchronize do |client|
|
3185
|
-
client.call(args) do |reply|
|
3186
|
-
case subcommand.to_s.downcase
|
3187
|
-
when 'stream' then Hashify.call(reply)
|
3188
|
-
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
3189
|
-
else reply
|
3190
|
-
end
|
3191
|
-
end
|
3192
|
-
end
|
3193
|
-
end
|
3194
|
-
|
3195
|
-
# Add new entry to the stream.
|
3196
|
-
#
|
3197
|
-
# @example Without options
|
3198
|
-
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
3199
|
-
# @example With options
|
3200
|
-
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
3201
|
-
#
|
3202
|
-
# @param key [String] the stream key
|
3203
|
-
# @param entry [Hash] one or multiple field-value pairs
|
3204
|
-
# @param opts [Hash] several options for `XADD` command
|
3205
|
-
#
|
3206
|
-
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
3207
|
-
# @option opts [Integer] :maxlen max length of entries
|
3208
|
-
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
3209
|
-
#
|
3210
|
-
# @return [String] the entry id
|
3211
|
-
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
3212
|
-
args = [:xadd, key]
|
3213
|
-
if maxlen
|
3214
|
-
args << "MAXLEN"
|
3215
|
-
args << "~" if approximate
|
3216
|
-
args << maxlen
|
3217
|
-
end
|
3218
|
-
args << id
|
3219
|
-
args.concat(entry.to_a.flatten)
|
3220
|
-
synchronize { |client| client.call(args) }
|
3221
|
-
end
|
3222
|
-
|
3223
|
-
# Trims older entries of the stream if needed.
|
3224
|
-
#
|
3225
|
-
# @example Without options
|
3226
|
-
# redis.xtrim('mystream', 1000)
|
3227
|
-
# @example With options
|
3228
|
-
# redis.xtrim('mystream', 1000, approximate: true)
|
3229
|
-
#
|
3230
|
-
# @param key [String] the stream key
|
3231
|
-
# @param mexlen [Integer] max length of entries
|
3232
|
-
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
3233
|
-
#
|
3234
|
-
# @return [Integer] the number of entries actually deleted
|
3235
|
-
def xtrim(key, maxlen, approximate: false)
|
3236
|
-
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
3237
|
-
synchronize { |client| client.call(args) }
|
3238
|
-
end
|
3239
|
-
|
3240
|
-
# Delete entries by entry ids.
|
3241
|
-
#
|
3242
|
-
# @example With splatted entry ids
|
3243
|
-
# redis.xdel('mystream', '0-1', '0-2')
|
3244
|
-
# @example With arrayed entry ids
|
3245
|
-
# redis.xdel('mystream', ['0-1', '0-2'])
|
3246
|
-
#
|
3247
|
-
# @param key [String] the stream key
|
3248
|
-
# @param ids [Array<String>] one or multiple entry ids
|
3249
|
-
#
|
3250
|
-
# @return [Integer] the number of entries actually deleted
|
3251
|
-
def xdel(key, *ids)
|
3252
|
-
args = [:xdel, key].concat(ids.flatten)
|
3253
|
-
synchronize { |client| client.call(args) }
|
3254
|
-
end
|
3255
|
-
|
3256
|
-
# Fetches entries of the stream in ascending order.
|
3257
|
-
#
|
3258
|
-
# @example Without options
|
3259
|
-
# redis.xrange('mystream')
|
3260
|
-
# @example With a specific start
|
3261
|
-
# redis.xrange('mystream', '0-1')
|
3262
|
-
# @example With a specific start and end
|
3263
|
-
# redis.xrange('mystream', '0-1', '0-3')
|
3264
|
-
# @example With count options
|
3265
|
-
# redis.xrange('mystream', count: 10)
|
3266
|
-
#
|
3267
|
-
# @param key [String] the stream key
|
3268
|
-
# @param start [String] first entry id of range, default value is `-`
|
3269
|
-
# @param end [String] last entry id of range, default value is `+`
|
3270
|
-
# @param count [Integer] the number of entries as limit
|
3271
|
-
#
|
3272
|
-
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3273
|
-
def xrange(key, start = '-', range_end = '+', count: nil)
|
3274
|
-
args = [:xrange, key, start, range_end]
|
3275
|
-
args.concat(['COUNT', count]) if count
|
3276
|
-
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3277
|
-
end
|
3278
|
-
|
3279
|
-
# Fetches entries of the stream in descending order.
|
3280
|
-
#
|
3281
|
-
# @example Without options
|
3282
|
-
# redis.xrevrange('mystream')
|
3283
|
-
# @example With a specific end
|
3284
|
-
# redis.xrevrange('mystream', '0-3')
|
3285
|
-
# @example With a specific end and start
|
3286
|
-
# redis.xrevrange('mystream', '0-3', '0-1')
|
3287
|
-
# @example With count options
|
3288
|
-
# redis.xrevrange('mystream', count: 10)
|
3289
|
-
#
|
3290
|
-
# @param key [String] the stream key
|
3291
|
-
# @param end [String] first entry id of range, default value is `+`
|
3292
|
-
# @param start [String] last entry id of range, default value is `-`
|
3293
|
-
# @params count [Integer] the number of entries as limit
|
3294
|
-
#
|
3295
|
-
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3296
|
-
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3297
|
-
args = [:xrevrange, key, range_end, start]
|
3298
|
-
args.concat(['COUNT', count]) if count
|
3299
|
-
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3300
|
-
end
|
3301
|
-
|
3302
|
-
# Returns the number of entries inside a stream.
|
3303
|
-
#
|
3304
|
-
# @example With key
|
3305
|
-
# redis.xlen('mystream')
|
3306
|
-
#
|
3307
|
-
# @param key [String] the stream key
|
3308
|
-
#
|
3309
|
-
# @return [Integer] the number of entries
|
3310
|
-
def xlen(key)
|
3311
|
-
synchronize { |client| client.call([:xlen, key]) }
|
3312
|
-
end
|
3313
|
-
|
3314
|
-
# Fetches entries from one or multiple streams. Optionally blocking.
|
3315
|
-
#
|
3316
|
-
# @example With a key
|
3317
|
-
# redis.xread('mystream', '0-0')
|
3318
|
-
# @example With multiple keys
|
3319
|
-
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3320
|
-
# @example With count option
|
3321
|
-
# redis.xread('mystream', '0-0', count: 2)
|
3322
|
-
# @example With block option
|
3323
|
-
# redis.xread('mystream', '$', block: 1000)
|
3324
|
-
#
|
3325
|
-
# @param keys [Array<String>] one or multiple stream keys
|
3326
|
-
# @param ids [Array<String>] one or multiple entry ids
|
3327
|
-
# @param count [Integer] the number of entries as limit per stream
|
3328
|
-
# @param block [Integer] the number of milliseconds as blocking timeout
|
3329
|
-
#
|
3330
|
-
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3331
|
-
def xread(keys, ids, count: nil, block: nil)
|
3332
|
-
args = [:xread]
|
3333
|
-
args << 'COUNT' << count if count
|
3334
|
-
args << 'BLOCK' << block.to_i if block
|
3335
|
-
_xread(args, keys, ids, block)
|
3336
|
-
end
|
3337
|
-
|
3338
|
-
# Manages the consumer group of the stream.
|
3339
|
-
#
|
3340
|
-
# @example With `create` subcommand
|
3341
|
-
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3342
|
-
# @example With `setid` subcommand
|
3343
|
-
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3344
|
-
# @example With `destroy` subcommand
|
3345
|
-
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3346
|
-
# @example With `delconsumer` subcommand
|
3347
|
-
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3348
|
-
#
|
3349
|
-
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3350
|
-
# @param key [String] the stream key
|
3351
|
-
# @param group [String] the consumer group name
|
3352
|
-
# @param id_or_consumer [String]
|
3353
|
-
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3354
|
-
# * the consumer name, required if subcommand is `delconsumer`
|
3355
|
-
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3356
|
-
#
|
3357
|
-
# @return [String] `OK` if subcommand is `create` or `setid`
|
3358
|
-
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3359
|
-
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3360
|
-
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3361
|
-
synchronize { |client| client.call(args) }
|
3362
|
-
end
|
3363
|
-
|
3364
|
-
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3365
|
-
# Optionally blocking.
|
3366
|
-
#
|
3367
|
-
# @example With a key
|
3368
|
-
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3369
|
-
# @example With multiple keys
|
3370
|
-
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3371
|
-
# @example With count option
|
3372
|
-
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3373
|
-
# @example With block option
|
3374
|
-
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3375
|
-
# @example With noack option
|
3376
|
-
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3377
|
-
#
|
3378
|
-
# @param group [String] the consumer group name
|
3379
|
-
# @param consumer [String] the consumer name
|
3380
|
-
# @param keys [Array<String>] one or multiple stream keys
|
3381
|
-
# @param ids [Array<String>] one or multiple entry ids
|
3382
|
-
# @param opts [Hash] several options for `XREADGROUP` command
|
3383
|
-
#
|
3384
|
-
# @option opts [Integer] :count the number of entries as limit
|
3385
|
-
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3386
|
-
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3387
|
-
#
|
3388
|
-
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3389
|
-
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3390
|
-
args = [:xreadgroup, 'GROUP', group, consumer]
|
3391
|
-
args << 'COUNT' << count if count
|
3392
|
-
args << 'BLOCK' << block.to_i if block
|
3393
|
-
args << 'NOACK' if noack
|
3394
|
-
_xread(args, keys, ids, block)
|
3395
|
-
end
|
3396
|
-
|
3397
|
-
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3398
|
-
#
|
3399
|
-
# @example With a entry id
|
3400
|
-
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3401
|
-
# @example With splatted entry ids
|
3402
|
-
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3403
|
-
# @example With arrayed entry ids
|
3404
|
-
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3405
|
-
#
|
3406
|
-
# @param key [String] the stream key
|
3407
|
-
# @param group [String] the consumer group name
|
3408
|
-
# @param ids [Array<String>] one or multiple entry ids
|
3409
|
-
#
|
3410
|
-
# @return [Integer] the number of entries successfully acknowledged
|
3411
|
-
def xack(key, group, *ids)
|
3412
|
-
args = [:xack, key, group].concat(ids.flatten)
|
3413
|
-
synchronize { |client| client.call(args) }
|
3414
|
-
end
|
3415
|
-
|
3416
|
-
# Changes the ownership of a pending entry
|
3417
|
-
#
|
3418
|
-
# @example With splatted entry ids
|
3419
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3420
|
-
# @example With arrayed entry ids
|
3421
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3422
|
-
# @example With idle option
|
3423
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3424
|
-
# @example With time option
|
3425
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3426
|
-
# @example With retrycount option
|
3427
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3428
|
-
# @example With force option
|
3429
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3430
|
-
# @example With justid option
|
3431
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3432
|
-
#
|
3433
|
-
# @param key [String] the stream key
|
3434
|
-
# @param group [String] the consumer group name
|
3435
|
-
# @param consumer [String] the consumer name
|
3436
|
-
# @param min_idle_time [Integer] the number of milliseconds
|
3437
|
-
# @param ids [Array<String>] one or multiple entry ids
|
3438
|
-
# @param opts [Hash] several options for `XCLAIM` command
|
3439
|
-
#
|
3440
|
-
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3441
|
-
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3442
|
-
# @option opts [Integer] :retrycount the number of retry counter
|
3443
|
-
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3444
|
-
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3445
|
-
#
|
3446
|
-
# @return [Hash{String => Hash}] the entries successfully claimed
|
3447
|
-
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3448
|
-
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3449
|
-
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3450
|
-
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3451
|
-
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3452
|
-
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3453
|
-
args << 'FORCE' if opts[:force]
|
3454
|
-
args << 'JUSTID' if opts[:justid]
|
3455
|
-
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3456
|
-
synchronize { |client| client.call(args, &blk) }
|
3457
|
-
end
|
3458
|
-
|
3459
|
-
# Transfers ownership of pending stream entries that match the specified criteria.
|
3460
|
-
#
|
3461
|
-
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
3462
|
-
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
3463
|
-
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
3464
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
3465
|
-
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
3466
|
-
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
3467
|
-
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
3468
|
-
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
3469
|
-
#
|
3470
|
-
# @param key [String] the stream key
|
3471
|
-
# @param group [String] the consumer group name
|
3472
|
-
# @param consumer [String] the consumer name
|
3473
|
-
# @param min_idle_time [Integer] the number of milliseconds
|
3474
|
-
# @param start [String] entry id to start scanning from or 0-0 for everything
|
3475
|
-
# @param count [Integer] number of messages to claim (default 1)
|
3476
|
-
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
3477
|
-
# Does not increment retry count when true
|
3478
|
-
#
|
3479
|
-
# @return [Hash{String => Hash}] the entries successfully claimed
|
3480
|
-
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3481
|
-
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
3482
|
-
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
3483
|
-
if count
|
3484
|
-
args << 'COUNT' << count.to_s
|
3485
|
-
end
|
3486
|
-
args << 'JUSTID' if justid
|
3487
|
-
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
3488
|
-
synchronize { |client| client.call(args, &blk) }
|
3489
|
-
end
|
3490
|
-
|
3491
|
-
# Fetches not acknowledging pending entries
|
3492
|
-
#
|
3493
|
-
# @example With key and group
|
3494
|
-
# redis.xpending('mystream', 'mygroup')
|
3495
|
-
# @example With range options
|
3496
|
-
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3497
|
-
# @example With range and consumer options
|
3498
|
-
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3499
|
-
#
|
3500
|
-
# @param key [String] the stream key
|
3501
|
-
# @param group [String] the consumer group name
|
3502
|
-
# @param start [String] start first entry id of range
|
3503
|
-
# @param end [String] end last entry id of range
|
3504
|
-
# @param count [Integer] count the number of entries as limit
|
3505
|
-
# @param consumer [String] the consumer name
|
3506
|
-
#
|
3507
|
-
# @return [Hash] the summary of pending entries
|
3508
|
-
# @return [Array<Hash>] the pending entries details if options were specified
|
3509
|
-
def xpending(key, group, *args)
|
3510
|
-
command_args = [:xpending, key, group]
|
3511
|
-
case args.size
|
3512
|
-
when 0, 3, 4
|
3513
|
-
command_args.concat(args)
|
3514
|
-
else
|
3515
|
-
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3516
|
-
end
|
3517
|
-
|
3518
|
-
summary_needed = args.empty?
|
3519
|
-
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3520
|
-
synchronize { |client| client.call(command_args, &blk) }
|
3521
|
-
end
|
3522
|
-
|
3523
|
-
# Interact with the sentinel command (masters, master, slaves, failover)
|
3524
|
-
#
|
3525
|
-
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
3526
|
-
# @param [Array<String>] args depends on subcommand
|
3527
|
-
# @return [Array<String>, Hash<String, String>, String] depends on subcommand
|
3528
|
-
def sentinel(subcommand, *args)
|
3529
|
-
subcommand = subcommand.to_s.downcase
|
3530
|
-
synchronize do |client|
|
3531
|
-
client.call([:sentinel, subcommand] + args) do |reply|
|
3532
|
-
case subcommand
|
3533
|
-
when "get-master-addr-by-name"
|
3534
|
-
reply
|
3535
|
-
else
|
3536
|
-
if reply.is_a?(Array)
|
3537
|
-
if reply[0].is_a?(Array)
|
3538
|
-
reply.map(&Hashify)
|
3539
|
-
else
|
3540
|
-
Hashify.call(reply)
|
3541
|
-
end
|
3542
|
-
else
|
3543
|
-
reply
|
3544
|
-
end
|
3545
|
-
end
|
3546
|
-
end
|
3547
|
-
end
|
3548
|
-
end
|
3549
|
-
|
3550
|
-
# Sends `CLUSTER *` command to random node and returns its reply.
|
3551
|
-
#
|
3552
|
-
# @see https://redis.io/commands#cluster Reference of cluster command
|
3553
|
-
#
|
3554
|
-
# @param subcommand [String, Symbol] the subcommand of cluster command
|
3555
|
-
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
3556
|
-
#
|
3557
|
-
# @return [Object] depends on the subcommand
|
3558
|
-
def cluster(subcommand, *args)
|
3559
|
-
subcommand = subcommand.to_s.downcase
|
3560
|
-
block = case subcommand
|
3561
|
-
when 'slots'
|
3562
|
-
HashifyClusterSlots
|
3563
|
-
when 'nodes'
|
3564
|
-
HashifyClusterNodes
|
3565
|
-
when 'slaves'
|
3566
|
-
HashifyClusterSlaves
|
3567
|
-
when 'info'
|
3568
|
-
HashifyInfo
|
3569
|
-
else
|
3570
|
-
Noop
|
3571
|
-
end
|
3572
|
-
|
3573
|
-
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3574
|
-
block = Noop unless @cluster_mode
|
3575
|
-
|
3576
|
-
synchronize do |client|
|
3577
|
-
client.call([:cluster, subcommand] + args, &block)
|
3578
|
-
end
|
3579
|
-
end
|
3580
|
-
|
3581
|
-
# Sends `ASKING` command to random node and returns its reply.
|
3582
|
-
#
|
3583
|
-
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
3584
|
-
#
|
3585
|
-
# @return [String] `'OK'`
|
3586
|
-
def asking
|
3587
|
-
synchronize { |client| client.call(%i[asking]) }
|
3588
|
-
end
|
3589
|
-
|
3590
|
-
def id
|
3591
|
-
@original_client.id
|
3592
|
-
end
|
3593
|
-
|
3594
|
-
def inspect
|
3595
|
-
"#<Redis client v#{Redis::VERSION} for #{id}>"
|
3596
|
-
end
|
3597
|
-
|
3598
|
-
def dup
|
3599
|
-
self.class.new(@options)
|
3600
|
-
end
|
3601
|
-
|
3602
|
-
def connection
|
3603
|
-
return @original_client.connection_info if @cluster_mode
|
3604
|
-
|
3605
|
-
{
|
3606
|
-
host: @original_client.host,
|
3607
|
-
port: @original_client.port,
|
3608
|
-
db: @original_client.db,
|
3609
|
-
id: @original_client.id,
|
3610
|
-
location: @original_client.location
|
3611
|
-
}
|
3612
|
-
end
|
3613
|
-
|
3614
|
-
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
3615
|
-
synchronize do |client|
|
3616
|
-
client.call([command] + args)
|
3617
|
-
end
|
3618
|
-
end
|
3619
|
-
|
3620
|
-
private
|
3621
|
-
|
3622
|
-
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
3623
|
-
# where the method call will return nil. Propagate the nil instead of falsely
|
3624
|
-
# returning false.
|
3625
|
-
Boolify = lambda { |value|
|
3626
|
-
case value
|
3627
|
-
when 1
|
3628
|
-
true
|
3629
|
-
when 0
|
3630
|
-
false
|
3631
|
-
else
|
3632
|
-
value
|
3633
|
-
end
|
3634
|
-
}
|
3635
|
-
|
3636
|
-
BoolifySet = lambda { |value|
|
3637
|
-
case value
|
3638
|
-
when "OK"
|
3639
|
-
true
|
3640
|
-
when nil
|
3641
|
-
false
|
3642
|
-
else
|
3643
|
-
value
|
3644
|
-
end
|
3645
|
-
}
|
3646
|
-
|
3647
|
-
Hashify = lambda { |value|
|
3648
|
-
if value.respond_to?(:each_slice)
|
3649
|
-
value.each_slice(2).to_h
|
3650
|
-
else
|
3651
|
-
value
|
3652
|
-
end
|
3653
|
-
}
|
3654
|
-
|
3655
|
-
Floatify = lambda { |value|
|
3656
|
-
case value
|
3657
|
-
when "inf"
|
3658
|
-
Float::INFINITY
|
3659
|
-
when "-inf"
|
3660
|
-
-Float::INFINITY
|
3661
|
-
when String
|
3662
|
-
Float(value)
|
3663
|
-
else
|
3664
|
-
value
|
3665
|
-
end
|
3666
|
-
}
|
3667
|
-
|
3668
|
-
FloatifyPairs = lambda { |value|
|
3669
|
-
return value unless value.respond_to?(:each_slice)
|
3670
|
-
|
3671
|
-
value.each_slice(2).map do |member, score|
|
3672
|
-
[member, Floatify.call(score)]
|
3673
|
-
end
|
3674
|
-
}
|
3675
|
-
|
3676
|
-
HashifyInfo = lambda { |reply|
|
3677
|
-
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3678
|
-
lines.map! { |line| line.split(':', 2) }
|
3679
|
-
lines.compact!
|
3680
|
-
lines.to_h
|
3681
|
-
}
|
3682
|
-
|
3683
|
-
HashifyStreams = lambda { |reply|
|
3684
|
-
case reply
|
3685
|
-
when nil
|
3686
|
-
{}
|
3687
|
-
else
|
3688
|
-
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3689
|
-
end
|
3690
|
-
}
|
3691
|
-
|
3692
|
-
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3693
|
-
private_constant :EMPTY_STREAM_RESPONSE
|
3694
|
-
|
3695
|
-
HashifyStreamEntries = lambda { |reply|
|
3696
|
-
reply.compact.map do |entry_id, values|
|
3697
|
-
[entry_id, values&.each_slice(2)&.to_h]
|
3698
|
-
end
|
3699
|
-
}
|
3700
|
-
|
3701
|
-
HashifyStreamAutoclaim = lambda { |reply|
|
3702
|
-
{
|
3703
|
-
'next' => reply[0],
|
3704
|
-
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
3705
|
-
}
|
3706
|
-
}
|
3707
|
-
|
3708
|
-
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
-
{
|
3710
|
-
'next' => reply[0],
|
3711
|
-
'entries' => reply[1]
|
3712
|
-
}
|
3713
|
-
}
|
3714
|
-
|
3715
|
-
HashifyStreamPendings = lambda { |reply|
|
3716
|
-
{
|
3717
|
-
'size' => reply[0],
|
3718
|
-
'min_entry_id' => reply[1],
|
3719
|
-
'max_entry_id' => reply[2],
|
3720
|
-
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
3721
|
-
}
|
3722
|
-
}
|
3723
|
-
|
3724
|
-
HashifyStreamPendingDetails = lambda { |reply|
|
3725
|
-
reply.map do |arr|
|
3726
|
-
{
|
3727
|
-
'entry_id' => arr[0],
|
3728
|
-
'consumer' => arr[1],
|
3729
|
-
'elapsed' => arr[2],
|
3730
|
-
'count' => arr[3]
|
3731
|
-
}
|
3732
|
-
end
|
3733
|
-
}
|
3734
|
-
|
3735
|
-
HashifyClusterNodeInfo = lambda { |str|
|
3736
|
-
arr = str.split(' ')
|
3737
|
-
{
|
3738
|
-
'node_id' => arr[0],
|
3739
|
-
'ip_port' => arr[1],
|
3740
|
-
'flags' => arr[2].split(','),
|
3741
|
-
'master_node_id' => arr[3],
|
3742
|
-
'ping_sent' => arr[4],
|
3743
|
-
'pong_recv' => arr[5],
|
3744
|
-
'config_epoch' => arr[6],
|
3745
|
-
'link_state' => arr[7],
|
3746
|
-
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3747
|
-
}
|
3748
|
-
}
|
3749
|
-
|
3750
|
-
HashifyClusterSlots = lambda { |reply|
|
3751
|
-
reply.map do |arr|
|
3752
|
-
first_slot, last_slot = arr[0..1]
|
3753
|
-
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3754
|
-
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3755
|
-
{
|
3756
|
-
'start_slot' => first_slot,
|
3757
|
-
'end_slot' => last_slot,
|
3758
|
-
'master' => master,
|
3759
|
-
'replicas' => replicas
|
3760
|
-
}
|
3761
|
-
end
|
3762
|
-
}
|
3763
|
-
|
3764
|
-
HashifyClusterNodes = lambda { |reply|
|
3765
|
-
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3766
|
-
}
|
3767
|
-
|
3768
|
-
HashifyClusterSlaves = lambda { |reply|
|
3769
|
-
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3770
|
-
}
|
3771
|
-
|
3772
|
-
Noop = ->(reply) { reply }
|
3773
|
-
|
3774
|
-
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
3775
|
-
args.push sort if sort
|
3776
|
-
args.push 'count', count if count
|
3777
|
-
args.push options if options
|
3778
|
-
args
|
3779
|
-
end
|
3780
|
-
|
3781
|
-
def _subscription(method, timeout, channels, block)
|
3782
|
-
return @client.call([method] + channels) if subscribed?
|
3783
|
-
|
3784
|
-
begin
|
3785
|
-
original, @client = @client, SubscribedClient.new(@client)
|
3786
|
-
if timeout > 0
|
3787
|
-
@client.send(method, timeout, *channels, &block)
|
3788
|
-
else
|
3789
|
-
@client.send(method, *channels, &block)
|
3790
|
-
end
|
3791
|
-
ensure
|
3792
|
-
@client = original
|
3793
|
-
end
|
3794
|
-
end
|
3795
|
-
|
3796
|
-
def _xread(args, keys, ids, blocking_timeout_msec)
|
3797
|
-
keys = keys.is_a?(Array) ? keys : [keys]
|
3798
|
-
ids = ids.is_a?(Array) ? ids : [ids]
|
3799
|
-
args << 'STREAMS'
|
3800
|
-
args.concat(keys)
|
3801
|
-
args.concat(ids)
|
3802
|
-
|
3803
|
-
synchronize do |client|
|
3804
|
-
if blocking_timeout_msec.nil?
|
3805
|
-
client.call(args, &HashifyStreams)
|
3806
|
-
elsif blocking_timeout_msec.to_f.zero?
|
3807
|
-
client.call_without_timeout(args, &HashifyStreams)
|
3808
|
-
else
|
3809
|
-
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3810
|
-
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3811
|
-
end
|
3812
|
-
end
|
3813
|
-
end
|
3814
|
-
|
3815
|
-
def _normalize_move_wheres(where_source, where_destination)
|
3816
|
-
where_source = where_source.to_s.upcase
|
3817
|
-
where_destination = where_destination.to_s.upcase
|
3818
|
-
|
3819
|
-
if where_source != "LEFT" && where_source != "RIGHT"
|
3820
|
-
raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
|
3821
|
-
end
|
3822
|
-
|
3823
|
-
if where_destination != "LEFT" && where_destination != "RIGHT"
|
3824
|
-
raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
|
3825
|
-
end
|
3826
|
-
|
3827
|
-
[where_source, where_destination]
|
3828
|
-
end
|
3829
287
|
end
|
3830
288
|
|
3831
|
-
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
3835
|
-
|
3836
|
-
|
289
|
+
require "redis/version"
|
290
|
+
require "redis/connection"
|
291
|
+
require "redis/client"
|
292
|
+
require "redis/cluster"
|
293
|
+
require "redis/pipeline"
|
294
|
+
require "redis/subscribe"
|