redis 4.0.0.rc1 → 4.4.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 +5 -5
- data/CHANGELOG.md +143 -3
- data/README.md +127 -18
- data/lib/redis/client.rb +150 -93
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +34 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/cluster.rb +291 -0
- data/lib/redis/connection/command_helper.rb +3 -2
- data/lib/redis/connection/hiredis.rb +4 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +123 -105
- data/lib/redis/connection/synchrony.rb +18 -5
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/distributed.rb +955 -0
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +89 -0
- data/lib/redis/pipeline.rb +55 -9
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +1242 -381
- metadata +34 -141
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -71
- data/.yardopts +0 -3
- data/Gemfile +0 -3
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/makefile +0 -42
- data/redis.gemspec +0 -40
- data/test/bitpos_test.rb +0 -63
- data/test/blocking_commands_test.rb +0 -183
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -28
- data/test/commands_on_hashes_test.rb +0 -174
- data/test/commands_on_hyper_log_log_test.rb +0 -70
- data/test/commands_on_lists_test.rb +0 -154
- data/test/commands_on_sets_test.rb +0 -208
- data/test/commands_on_sorted_sets_test.rb +0 -444
- data/test/commands_on_strings_test.rb +0 -338
- data/test/commands_on_value_types_test.rb +0 -246
- data/test/connection_handling_test.rb +0 -275
- data/test/db/.gitkeep +0 -0
- data/test/encoding_test.rb +0 -14
- data/test/error_replies_test.rb +0 -57
- data/test/fork_safety_test.rb +0 -60
- data/test/helper.rb +0 -179
- data/test/helper_test.rb +0 -22
- data/test/internals_test.rb +0 -435
- data/test/persistence_control_commands_test.rb +0 -24
- data/test/pipelining_commands_test.rb +0 -238
- data/test/publish_subscribe_test.rb +0 -280
- data/test/remote_server_control_commands_test.rb +0 -175
- data/test/scanning_test.rb +0 -407
- data/test/scripting_test.rb +0 -76
- data/test/sentinel_command_test.rb +0 -78
- data/test/sentinel_test.rb +0 -253
- data/test/sorting_test.rb +0 -57
- data/test/ssl_test.rb +0 -69
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -85
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -60
- data/test/transactions_test.rb +0 -262
- data/test/unknown_commands_test.rb +0 -12
- data/test/url_param_test.rb +0 -136
data/lib/redis.rb
CHANGED
@@ -1,14 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
4
|
require_relative "redis/errors"
|
3
5
|
|
4
6
|
class Redis
|
7
|
+
class << self
|
8
|
+
attr_reader :exists_returns_integer
|
5
9
|
|
6
|
-
|
7
|
-
|
10
|
+
def exists_returns_integer=(value)
|
11
|
+
unless value
|
12
|
+
message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
13
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
14
|
+
"`exists?` instead."
|
15
|
+
|
16
|
+
::Kernel.warn(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
@exists_returns_integer = value
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_writer :current
|
8
23
|
end
|
9
24
|
|
10
|
-
def self.current
|
11
|
-
@current
|
25
|
+
def self.current
|
26
|
+
@current ||= Redis.new
|
12
27
|
end
|
13
28
|
|
14
29
|
include MonitorMixin
|
@@ -16,26 +31,36 @@ class Redis
|
|
16
31
|
# Create a new client instance
|
17
32
|
#
|
18
33
|
# @param [Hash] options
|
19
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
34
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
35
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
36
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
20
37
|
# @option options [String] :host ("127.0.0.1") server hostname
|
21
|
-
# @option options [
|
38
|
+
# @option options [Integer] :port (6379) server port
|
22
39
|
# @option options [String] :path path to server socket (overrides host and port)
|
23
40
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
24
41
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
42
|
+
# @option options [String] :username Username to authenticate against server
|
25
43
|
# @option options [String] :password Password to authenticate against server
|
26
|
-
# @option options [
|
44
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
27
45
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
28
|
-
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
29
|
-
#
|
30
|
-
# @option options [
|
46
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
47
|
+
# `CLIENT SETNAME`
|
48
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
49
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
50
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
31
51
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
32
52
|
# @option options [Array] :sentinels List of sentinels to contact
|
33
53
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
54
|
+
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
55
|
+
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
56
|
+
# @option options [Class] :connector Class of custom connector
|
34
57
|
#
|
35
58
|
# @return [Redis] a new client instance
|
36
59
|
def initialize(options = {})
|
37
60
|
@options = options.dup
|
38
|
-
@
|
61
|
+
@cluster_mode = options.key?(:cluster)
|
62
|
+
client = @cluster_mode ? Cluster : Client
|
63
|
+
@original_client = @client = client.new(options)
|
39
64
|
@queue = Hash.new { |h, k| h[k] = [] }
|
40
65
|
|
41
66
|
super() # Monitor#initialize
|
@@ -46,7 +71,7 @@ class Redis
|
|
46
71
|
end
|
47
72
|
|
48
73
|
# Run code with the client reconnecting
|
49
|
-
def with_reconnect(val=true, &blk)
|
74
|
+
def with_reconnect(val = true, &blk)
|
50
75
|
synchronize do |client|
|
51
76
|
client.with_reconnect(val, &blk)
|
52
77
|
end
|
@@ -89,7 +114,9 @@ class Redis
|
|
89
114
|
# See http://redis.io/topics/pipelining for more details.
|
90
115
|
#
|
91
116
|
def queue(*command)
|
92
|
-
|
117
|
+
synchronize do
|
118
|
+
@queue[Thread.current.object_id] << command
|
119
|
+
end
|
93
120
|
end
|
94
121
|
|
95
122
|
# Sends all commands in the queue.
|
@@ -99,7 +126,12 @@ class Redis
|
|
99
126
|
def commit
|
100
127
|
synchronize do |client|
|
101
128
|
begin
|
102
|
-
|
129
|
+
pipeline = Pipeline.new(client)
|
130
|
+
@queue[Thread.current.object_id].each do |command|
|
131
|
+
pipeline.call(command)
|
132
|
+
end
|
133
|
+
|
134
|
+
client.call_pipelined(pipeline)
|
103
135
|
ensure
|
104
136
|
@queue.delete(Thread.current.object_id)
|
105
137
|
end
|
@@ -112,18 +144,19 @@ class Redis
|
|
112
144
|
|
113
145
|
# Authenticate to the server.
|
114
146
|
#
|
115
|
-
# @param [String]
|
116
|
-
#
|
147
|
+
# @param [Array<String>] args includes both username and password
|
148
|
+
# or only password
|
117
149
|
# @return [String] `OK`
|
118
|
-
|
150
|
+
# @see https://redis.io/commands/auth AUTH command
|
151
|
+
def auth(*args)
|
119
152
|
synchronize do |client|
|
120
|
-
client.call([:auth,
|
153
|
+
client.call([:auth, *args])
|
121
154
|
end
|
122
155
|
end
|
123
156
|
|
124
157
|
# Change the selected database for the current connection.
|
125
158
|
#
|
126
|
-
# @param [
|
159
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
127
160
|
# @return [String] `OK`
|
128
161
|
def select(db)
|
129
162
|
synchronize do |client|
|
@@ -134,10 +167,11 @@ class Redis
|
|
134
167
|
|
135
168
|
# Ping the server.
|
136
169
|
#
|
170
|
+
# @param [optional, String] message
|
137
171
|
# @return [String] `PONG`
|
138
|
-
def ping
|
172
|
+
def ping(message = nil)
|
139
173
|
synchronize do |client|
|
140
|
-
client.call([:ping])
|
174
|
+
client.call([:ping, message].compact)
|
141
175
|
end
|
142
176
|
end
|
143
177
|
|
@@ -191,7 +225,7 @@ class Redis
|
|
191
225
|
def config(action, *args)
|
192
226
|
synchronize do |client|
|
193
227
|
client.call([:config, action] + args) do |reply|
|
194
|
-
if reply.
|
228
|
+
if reply.is_a?(Array) && action == :get
|
195
229
|
Hashify.call(reply)
|
196
230
|
else
|
197
231
|
reply
|
@@ -221,7 +255,7 @@ class Redis
|
|
221
255
|
|
222
256
|
# Return the number of keys in the selected database.
|
223
257
|
#
|
224
|
-
# @return [
|
258
|
+
# @return [Integer]
|
225
259
|
def dbsize
|
226
260
|
synchronize do |client|
|
227
261
|
client.call([:dbsize])
|
@@ -236,19 +270,31 @@ class Redis
|
|
236
270
|
|
237
271
|
# Remove all keys from all databases.
|
238
272
|
#
|
273
|
+
# @param [Hash] options
|
274
|
+
# - `:async => Boolean`: async flush (default: false)
|
239
275
|
# @return [String] `OK`
|
240
|
-
def flushall
|
276
|
+
def flushall(options = nil)
|
241
277
|
synchronize do |client|
|
242
|
-
|
278
|
+
if options && options[:async]
|
279
|
+
client.call(%i[flushall async])
|
280
|
+
else
|
281
|
+
client.call([:flushall])
|
282
|
+
end
|
243
283
|
end
|
244
284
|
end
|
245
285
|
|
246
286
|
# Remove all keys from the current database.
|
247
287
|
#
|
288
|
+
# @param [Hash] options
|
289
|
+
# - `:async => Boolean`: async flush (default: false)
|
248
290
|
# @return [String] `OK`
|
249
|
-
def flushdb
|
291
|
+
def flushdb(options = nil)
|
250
292
|
synchronize do |client|
|
251
|
-
|
293
|
+
if options && options[:async]
|
294
|
+
client.call(%i[flushdb async])
|
295
|
+
else
|
296
|
+
client.call([:flushdb])
|
297
|
+
end
|
252
298
|
end
|
253
299
|
end
|
254
300
|
|
@@ -259,10 +305,8 @@ class Redis
|
|
259
305
|
def info(cmd = nil)
|
260
306
|
synchronize do |client|
|
261
307
|
client.call([:info, cmd].compact) do |reply|
|
262
|
-
if reply.
|
263
|
-
reply =
|
264
|
-
line.split(":", 2) unless line =~ /^(#|$)/
|
265
|
-
end.compact]
|
308
|
+
if reply.is_a?(String)
|
309
|
+
reply = HashifyInfo.call(reply)
|
266
310
|
|
267
311
|
if cmd && cmd.to_s == "commandstats"
|
268
312
|
# Extract nested hashes for INFO COMMANDSTATS
|
@@ -280,7 +324,7 @@ class Redis
|
|
280
324
|
|
281
325
|
# Get the UNIX time stamp of the last successful save to disk.
|
282
326
|
#
|
283
|
-
# @return [
|
327
|
+
# @return [Integer]
|
284
328
|
def lastsave
|
285
329
|
synchronize do |client|
|
286
330
|
client.call([:lastsave])
|
@@ -332,9 +376,9 @@ class Redis
|
|
332
376
|
# Interact with the slowlog (get, len, reset)
|
333
377
|
#
|
334
378
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
335
|
-
# @param [
|
336
|
-
# @return [Array<String>,
|
337
|
-
def slowlog(subcommand, length=nil)
|
379
|
+
# @param [Integer] length maximum number of entries to return
|
380
|
+
# @return [Array<String>, Integer, String] depends on subcommand
|
381
|
+
def slowlog(subcommand, length = nil)
|
338
382
|
synchronize do |client|
|
339
383
|
args = [:slowlog, subcommand]
|
340
384
|
args << length if length
|
@@ -354,12 +398,12 @@ class Redis
|
|
354
398
|
# @example
|
355
399
|
# r.time # => [ 1333093196, 606806 ]
|
356
400
|
#
|
357
|
-
# @return [Array<
|
401
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
358
402
|
# microseconds in the current second
|
359
403
|
def time
|
360
404
|
synchronize do |client|
|
361
405
|
client.call([:time]) do |reply|
|
362
|
-
reply
|
406
|
+
reply&.map(&:to_i)
|
363
407
|
end
|
364
408
|
end
|
365
409
|
end
|
@@ -377,7 +421,7 @@ class Redis
|
|
377
421
|
# Set a key's time to live in seconds.
|
378
422
|
#
|
379
423
|
# @param [String] key
|
380
|
-
# @param [
|
424
|
+
# @param [Integer] seconds time to live
|
381
425
|
# @return [Boolean] whether the timeout was set or not
|
382
426
|
def expire(key, seconds)
|
383
427
|
synchronize do |client|
|
@@ -388,7 +432,7 @@ class Redis
|
|
388
432
|
# Set the expiration for a key as a UNIX timestamp.
|
389
433
|
#
|
390
434
|
# @param [String] key
|
391
|
-
# @param [
|
435
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
392
436
|
# @return [Boolean] whether the timeout was set or not
|
393
437
|
def expireat(key, unix_time)
|
394
438
|
synchronize do |client|
|
@@ -399,7 +443,7 @@ class Redis
|
|
399
443
|
# Get the time to live (in seconds) for a key.
|
400
444
|
#
|
401
445
|
# @param [String] key
|
402
|
-
# @return [
|
446
|
+
# @return [Integer] remaining time to live in seconds.
|
403
447
|
#
|
404
448
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
405
449
|
# the key exist but has no associated expire.
|
@@ -417,7 +461,7 @@ class Redis
|
|
417
461
|
# Set a key's time to live in milliseconds.
|
418
462
|
#
|
419
463
|
# @param [String] key
|
420
|
-
# @param [
|
464
|
+
# @param [Integer] milliseconds time to live
|
421
465
|
# @return [Boolean] whether the timeout was set or not
|
422
466
|
def pexpire(key, milliseconds)
|
423
467
|
synchronize do |client|
|
@@ -428,7 +472,7 @@ class Redis
|
|
428
472
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
429
473
|
#
|
430
474
|
# @param [String] key
|
431
|
-
# @param [
|
475
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
432
476
|
# @return [Boolean] whether the timeout was set or not
|
433
477
|
def pexpireat(key, ms_unix_time)
|
434
478
|
synchronize do |client|
|
@@ -439,7 +483,7 @@ class Redis
|
|
439
483
|
# Get the time to live (in milliseconds) for a key.
|
440
484
|
#
|
441
485
|
# @param [String] key
|
442
|
-
# @return [
|
486
|
+
# @return [Integer] remaining time to live in milliseconds
|
443
487
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
444
488
|
# the key exist but has no associated expire.
|
445
489
|
#
|
@@ -468,50 +512,104 @@ class Redis
|
|
468
512
|
# @param [String] key
|
469
513
|
# @param [String] ttl
|
470
514
|
# @param [String] serialized_value
|
515
|
+
# @param [Hash] options
|
516
|
+
# - `:replace => Boolean`: if false, raises an error if key already exists
|
517
|
+
# @raise [Redis::CommandError]
|
471
518
|
# @return [String] `"OK"`
|
472
|
-
def restore(key, ttl, serialized_value)
|
519
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
520
|
+
args = [:restore, key, ttl, serialized_value]
|
521
|
+
args << 'REPLACE' if replace
|
522
|
+
|
473
523
|
synchronize do |client|
|
474
|
-
client.call(
|
524
|
+
client.call(args)
|
475
525
|
end
|
476
526
|
end
|
477
527
|
|
478
528
|
# Transfer a key from the connected instance to another instance.
|
479
529
|
#
|
480
|
-
# @param [String] key
|
530
|
+
# @param [String, Array<String>] key
|
481
531
|
# @param [Hash] options
|
482
532
|
# - `:host => String`: host of instance to migrate to
|
483
533
|
# - `:port => Integer`: port of instance to migrate to
|
484
534
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
485
535
|
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
536
|
+
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
537
|
+
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
486
538
|
# @return [String] `"OK"`
|
487
539
|
def migrate(key, options)
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
540
|
+
args = [:migrate]
|
541
|
+
args << (options[:host] || raise(':host not specified'))
|
542
|
+
args << (options[:port] || raise(':port not specified'))
|
543
|
+
args << (key.is_a?(String) ? key : '')
|
544
|
+
args << (options[:db] || @client.db).to_i
|
545
|
+
args << (options[:timeout] || @client.timeout).to_i
|
546
|
+
args << 'COPY' if options[:copy]
|
547
|
+
args << 'REPLACE' if options[:replace]
|
548
|
+
args += ['KEYS', *key] if key.is_a?(Array)
|
492
549
|
|
493
|
-
synchronize
|
494
|
-
client.call([:migrate, host, port, key, db, timeout])
|
495
|
-
end
|
550
|
+
synchronize { |client| client.call(args) }
|
496
551
|
end
|
497
552
|
|
498
553
|
# Delete one or more keys.
|
499
554
|
#
|
500
555
|
# @param [String, Array<String>] keys
|
501
|
-
# @return [
|
556
|
+
# @return [Integer] number of keys that were deleted
|
502
557
|
def del(*keys)
|
558
|
+
keys.flatten!(1)
|
559
|
+
return 0 if keys.empty?
|
560
|
+
|
503
561
|
synchronize do |client|
|
504
562
|
client.call([:del] + keys)
|
505
563
|
end
|
506
564
|
end
|
507
565
|
|
508
|
-
#
|
566
|
+
# Unlink one or more keys.
|
509
567
|
#
|
510
|
-
# @param [String]
|
568
|
+
# @param [String, Array<String>] keys
|
569
|
+
# @return [Integer] number of keys that were unlinked
|
570
|
+
def unlink(*keys)
|
571
|
+
synchronize do |client|
|
572
|
+
client.call([:unlink] + keys)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
# Determine how many of the keys exists.
|
577
|
+
#
|
578
|
+
# @param [String, Array<String>] keys
|
579
|
+
# @return [Integer]
|
580
|
+
def exists(*keys)
|
581
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
582
|
+
if Redis.exists_returns_integer.nil?
|
583
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
584
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
585
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
586
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
587
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
588
|
+
|
589
|
+
::Kernel.warn(message)
|
590
|
+
end
|
591
|
+
|
592
|
+
exists?(*keys)
|
593
|
+
else
|
594
|
+
_exists(*keys)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
def _exists(*keys)
|
599
|
+
synchronize do |client|
|
600
|
+
client.call([:exists, *keys])
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
# Determine if any of the keys exists.
|
605
|
+
#
|
606
|
+
# @param [String, Array<String>] keys
|
511
607
|
# @return [Boolean]
|
512
|
-
def exists(
|
608
|
+
def exists?(*keys)
|
513
609
|
synchronize do |client|
|
514
|
-
client.call([:exists,
|
610
|
+
client.call([:exists, *keys]) do |value|
|
611
|
+
value > 0
|
612
|
+
end
|
515
613
|
end
|
516
614
|
end
|
517
615
|
|
@@ -522,7 +620,7 @@ class Redis
|
|
522
620
|
def keys(pattern = "*")
|
523
621
|
synchronize do |client|
|
524
622
|
client.call([:keys, pattern]) do |reply|
|
525
|
-
if reply.
|
623
|
+
if reply.is_a?(String)
|
526
624
|
reply.split(" ")
|
527
625
|
else
|
528
626
|
reply
|
@@ -548,7 +646,7 @@ class Redis
|
|
548
646
|
# # => "bar"
|
549
647
|
#
|
550
648
|
# @param [String] key
|
551
|
-
# @param [
|
649
|
+
# @param [Integer] db
|
552
650
|
# @return [Boolean] whether the key was moved or not
|
553
651
|
def move(key, db)
|
554
652
|
synchronize do |client|
|
@@ -612,36 +710,33 @@ class Redis
|
|
612
710
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
613
711
|
# - `:store => String`: key to store the result at
|
614
712
|
#
|
615
|
-
# @return [Array<String>, Array<Array<String>>,
|
713
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
616
714
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
617
715
|
# - when `:get` is specified, and holds more than one element, an array of
|
618
716
|
# elements where every element is an array with the result for every
|
619
717
|
# element specified in `:get`
|
620
718
|
# - when `:store` is specified, the number of elements in the stored result
|
621
|
-
def sort(key,
|
622
|
-
args = []
|
623
|
-
|
624
|
-
by = options[:by]
|
625
|
-
args.concat(["BY", by]) if by
|
719
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
720
|
+
args = [:sort, key]
|
721
|
+
args << "BY" << by if by
|
626
722
|
|
627
|
-
|
628
|
-
|
723
|
+
if limit
|
724
|
+
args << "LIMIT"
|
725
|
+
args.concat(limit)
|
726
|
+
end
|
629
727
|
|
630
|
-
get = Array(
|
631
|
-
|
728
|
+
get = Array(get)
|
729
|
+
get.each do |item|
|
730
|
+
args << "GET" << item
|
731
|
+
end
|
632
732
|
|
633
|
-
order = options[:order]
|
634
733
|
args.concat(order.split(" ")) if order
|
635
|
-
|
636
|
-
store = options[:store]
|
637
|
-
args.concat(["STORE", store]) if store
|
734
|
+
args << "STORE" << store if store
|
638
735
|
|
639
736
|
synchronize do |client|
|
640
|
-
client.call(
|
737
|
+
client.call(args) do |reply|
|
641
738
|
if get.size > 1 && !store
|
642
|
-
if reply
|
643
|
-
reply.each_slice(get.size).to_a
|
644
|
-
end
|
739
|
+
reply.each_slice(get.size).to_a if reply
|
645
740
|
else
|
646
741
|
reply
|
647
742
|
end
|
@@ -666,7 +761,7 @@ class Redis
|
|
666
761
|
# # => 4
|
667
762
|
#
|
668
763
|
# @param [String] key
|
669
|
-
# @return [
|
764
|
+
# @return [Integer] value after decrementing it
|
670
765
|
def decr(key)
|
671
766
|
synchronize do |client|
|
672
767
|
client.call([:decr, key])
|
@@ -680,8 +775,8 @@ class Redis
|
|
680
775
|
# # => 0
|
681
776
|
#
|
682
777
|
# @param [String] key
|
683
|
-
# @param [
|
684
|
-
# @return [
|
778
|
+
# @param [Integer] decrement
|
779
|
+
# @return [Integer] value after decrementing it
|
685
780
|
def decrby(key, decrement)
|
686
781
|
synchronize do |client|
|
687
782
|
client.call([:decrby, key, decrement])
|
@@ -695,7 +790,7 @@ class Redis
|
|
695
790
|
# # => 6
|
696
791
|
#
|
697
792
|
# @param [String] key
|
698
|
-
# @return [
|
793
|
+
# @return [Integer] value after incrementing it
|
699
794
|
def incr(key)
|
700
795
|
synchronize do |client|
|
701
796
|
client.call([:incr, key])
|
@@ -709,8 +804,8 @@ class Redis
|
|
709
804
|
# # => 10
|
710
805
|
#
|
711
806
|
# @param [String] key
|
712
|
-
# @param [
|
713
|
-
# @return [
|
807
|
+
# @param [Integer] increment
|
808
|
+
# @return [Integer] value after incrementing it
|
714
809
|
def incrby(key, increment)
|
715
810
|
synchronize do |client|
|
716
811
|
client.call([:incrby, key, increment])
|
@@ -737,31 +832,25 @@ class Redis
|
|
737
832
|
# @param [String] key
|
738
833
|
# @param [String] value
|
739
834
|
# @param [Hash] options
|
740
|
-
# - `:ex =>
|
741
|
-
# - `:px =>
|
835
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
836
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
742
837
|
# - `:nx => true`: Only set the key if it does not already exist.
|
743
838
|
# - `:xx => true`: Only set the key if it already exist.
|
839
|
+
# - `:keepttl => true`: Retain the time to live associated with the key.
|
744
840
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
745
|
-
def set(key, value,
|
746
|
-
args = []
|
747
|
-
|
748
|
-
|
749
|
-
args
|
750
|
-
|
751
|
-
|
752
|
-
args.concat(["PX", px]) if px
|
753
|
-
|
754
|
-
nx = options[:nx]
|
755
|
-
args.concat(["NX"]) if nx
|
756
|
-
|
757
|
-
xx = options[:xx]
|
758
|
-
args.concat(["XX"]) if xx
|
841
|
+
def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
|
842
|
+
args = [:set, key, value.to_s]
|
843
|
+
args << "EX" << ex if ex
|
844
|
+
args << "PX" << px if px
|
845
|
+
args << "NX" if nx
|
846
|
+
args << "XX" if xx
|
847
|
+
args << "KEEPTTL" if keepttl
|
759
848
|
|
760
849
|
synchronize do |client|
|
761
850
|
if nx || xx
|
762
|
-
client.call(
|
851
|
+
client.call(args, &BoolifySet)
|
763
852
|
else
|
764
|
-
client.call(
|
853
|
+
client.call(args)
|
765
854
|
end
|
766
855
|
end
|
767
856
|
end
|
@@ -769,7 +858,7 @@ class Redis
|
|
769
858
|
# Set the time to live in seconds of a key.
|
770
859
|
#
|
771
860
|
# @param [String] key
|
772
|
-
# @param [
|
861
|
+
# @param [Integer] ttl
|
773
862
|
# @param [String] value
|
774
863
|
# @return [String] `"OK"`
|
775
864
|
def setex(key, ttl, value)
|
@@ -781,7 +870,7 @@ class Redis
|
|
781
870
|
# Set the time to live in milliseconds of a key.
|
782
871
|
#
|
783
872
|
# @param [String] key
|
784
|
-
# @param [
|
873
|
+
# @param [Integer] ttl
|
785
874
|
# @param [String] value
|
786
875
|
# @return [String] `"OK"`
|
787
876
|
def psetex(key, ttl, value)
|
@@ -843,7 +932,7 @@ class Redis
|
|
843
932
|
# @see #mapped_msetnx
|
844
933
|
def msetnx(*args)
|
845
934
|
synchronize do |client|
|
846
|
-
client.call([:msetnx
|
935
|
+
client.call([:msetnx, *args], &Boolify)
|
847
936
|
end
|
848
937
|
end
|
849
938
|
|
@@ -874,7 +963,7 @@ class Redis
|
|
874
963
|
# Get the values of all the given keys.
|
875
964
|
#
|
876
965
|
# @example
|
877
|
-
# redis.mget("key1", "
|
966
|
+
# redis.mget("key1", "key2")
|
878
967
|
# # => ["v1", "v2"]
|
879
968
|
#
|
880
969
|
# @param [Array<String>] keys
|
@@ -883,7 +972,7 @@ class Redis
|
|
883
972
|
# @see #mapped_mget
|
884
973
|
def mget(*keys, &blk)
|
885
974
|
synchronize do |client|
|
886
|
-
client.call([:mget
|
975
|
+
client.call([:mget, *keys], &blk)
|
887
976
|
end
|
888
977
|
end
|
889
978
|
|
@@ -899,7 +988,7 @@ class Redis
|
|
899
988
|
# @see #mget
|
900
989
|
def mapped_mget(*keys)
|
901
990
|
mget(*keys) do |reply|
|
902
|
-
if reply.
|
991
|
+
if reply.is_a?(Array)
|
903
992
|
Hash[keys.zip(reply)]
|
904
993
|
else
|
905
994
|
reply
|
@@ -910,9 +999,9 @@ class Redis
|
|
910
999
|
# Overwrite part of a string at key starting at the specified offset.
|
911
1000
|
#
|
912
1001
|
# @param [String] key
|
913
|
-
# @param [
|
1002
|
+
# @param [Integer] offset byte offset
|
914
1003
|
# @param [String] value
|
915
|
-
# @return [
|
1004
|
+
# @return [Integer] length of the string after it was modified
|
916
1005
|
def setrange(key, offset, value)
|
917
1006
|
synchronize do |client|
|
918
1007
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -922,10 +1011,10 @@ class Redis
|
|
922
1011
|
# Get a substring of the string stored at a key.
|
923
1012
|
#
|
924
1013
|
# @param [String] key
|
925
|
-
# @param [
|
926
|
-
# @param [
|
1014
|
+
# @param [Integer] start zero-based start offset
|
1015
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
927
1016
|
# the end of the string
|
928
|
-
# @return [
|
1017
|
+
# @return [Integer] `0` or `1`
|
929
1018
|
def getrange(key, start, stop)
|
930
1019
|
synchronize do |client|
|
931
1020
|
client.call([:getrange, key, start, stop])
|
@@ -935,9 +1024,9 @@ class Redis
|
|
935
1024
|
# Sets or clears the bit at offset in the string value stored at key.
|
936
1025
|
#
|
937
1026
|
# @param [String] key
|
938
|
-
# @param [
|
939
|
-
# @param [
|
940
|
-
# @return [
|
1027
|
+
# @param [Integer] offset bit offset
|
1028
|
+
# @param [Integer] value bit value `0` or `1`
|
1029
|
+
# @return [Integer] the original bit value stored at `offset`
|
941
1030
|
def setbit(key, offset, value)
|
942
1031
|
synchronize do |client|
|
943
1032
|
client.call([:setbit, key, offset, value])
|
@@ -947,8 +1036,8 @@ class Redis
|
|
947
1036
|
# Returns the bit value at offset in the string value stored at key.
|
948
1037
|
#
|
949
1038
|
# @param [String] key
|
950
|
-
# @param [
|
951
|
-
# @return [
|
1039
|
+
# @param [Integer] offset bit offset
|
1040
|
+
# @return [Integer] `0` or `1`
|
952
1041
|
def getbit(key, offset)
|
953
1042
|
synchronize do |client|
|
954
1043
|
client.call([:getbit, key, offset])
|
@@ -959,7 +1048,7 @@ class Redis
|
|
959
1048
|
#
|
960
1049
|
# @param [String] key
|
961
1050
|
# @param [String] value value to append
|
962
|
-
# @return [
|
1051
|
+
# @return [Integer] length of the string after appending
|
963
1052
|
def append(key, value)
|
964
1053
|
synchronize do |client|
|
965
1054
|
client.call([:append, key, value])
|
@@ -969,9 +1058,9 @@ class Redis
|
|
969
1058
|
# Count the number of set bits in a range of the string value stored at key.
|
970
1059
|
#
|
971
1060
|
# @param [String] key
|
972
|
-
# @param [
|
973
|
-
# @param [
|
974
|
-
# @return [
|
1061
|
+
# @param [Integer] start start index
|
1062
|
+
# @param [Integer] stop stop index
|
1063
|
+
# @return [Integer] the number of bits set to 1
|
975
1064
|
def bitcount(key, start = 0, stop = -1)
|
976
1065
|
synchronize do |client|
|
977
1066
|
client.call([:bitcount, key, start, stop])
|
@@ -983,25 +1072,23 @@ class Redis
|
|
983
1072
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
984
1073
|
# @param [String] destkey destination key
|
985
1074
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
986
|
-
# @return [
|
1075
|
+
# @return [Integer] the length of the string stored in `destkey`
|
987
1076
|
def bitop(operation, destkey, *keys)
|
988
1077
|
synchronize do |client|
|
989
|
-
client.call([:bitop, operation, destkey
|
1078
|
+
client.call([:bitop, operation, destkey, *keys])
|
990
1079
|
end
|
991
1080
|
end
|
992
1081
|
|
993
1082
|
# Return the position of the first bit set to 1 or 0 in a string.
|
994
1083
|
#
|
995
1084
|
# @param [String] key
|
996
|
-
# @param [
|
997
|
-
# @param [
|
998
|
-
# @param [
|
999
|
-
# @return [
|
1085
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1086
|
+
# @param [Integer] start start index
|
1087
|
+
# @param [Integer] stop stop index
|
1088
|
+
# @return [Integer] the position of the first 1/0 bit.
|
1000
1089
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
1001
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
1002
|
-
|
1003
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
1004
|
-
end
|
1090
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1091
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1005
1092
|
|
1006
1093
|
synchronize do |client|
|
1007
1094
|
command = [:bitpos, key, bit]
|
@@ -1026,7 +1113,7 @@ class Redis
|
|
1026
1113
|
# Get the length of the value stored in a key.
|
1027
1114
|
#
|
1028
1115
|
# @param [String] key
|
1029
|
-
# @return [
|
1116
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1030
1117
|
# if the key does not exist
|
1031
1118
|
def strlen(key)
|
1032
1119
|
synchronize do |client|
|
@@ -1037,7 +1124,7 @@ class Redis
|
|
1037
1124
|
# Get the length of a list.
|
1038
1125
|
#
|
1039
1126
|
# @param [String] key
|
1040
|
-
# @return [
|
1127
|
+
# @return [Integer]
|
1041
1128
|
def llen(key)
|
1042
1129
|
synchronize do |client|
|
1043
1130
|
client.call([:llen, key])
|
@@ -1047,8 +1134,8 @@ class Redis
|
|
1047
1134
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1048
1135
|
#
|
1049
1136
|
# @param [String] key
|
1050
|
-
# @param [String, Array] value string value, or array of string values to push
|
1051
|
-
# @return [
|
1137
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1138
|
+
# @return [Integer] the length of the list after the push operation
|
1052
1139
|
def lpush(key, value)
|
1053
1140
|
synchronize do |client|
|
1054
1141
|
client.call([:lpush, key, value])
|
@@ -1059,7 +1146,7 @@ class Redis
|
|
1059
1146
|
#
|
1060
1147
|
# @param [String] key
|
1061
1148
|
# @param [String] value
|
1062
|
-
# @return [
|
1149
|
+
# @return [Integer] the length of the list after the push operation
|
1063
1150
|
def lpushx(key, value)
|
1064
1151
|
synchronize do |client|
|
1065
1152
|
client.call([:lpushx, key, value])
|
@@ -1069,8 +1156,8 @@ class Redis
|
|
1069
1156
|
# Append one or more values to a list, creating the list if it doesn't exist
|
1070
1157
|
#
|
1071
1158
|
# @param [String] key
|
1072
|
-
# @param [String] value
|
1073
|
-
# @return [
|
1159
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1160
|
+
# @return [Integer] the length of the list after the push operation
|
1074
1161
|
def rpush(key, value)
|
1075
1162
|
synchronize do |client|
|
1076
1163
|
client.call([:rpush, key, value])
|
@@ -1081,30 +1168,36 @@ class Redis
|
|
1081
1168
|
#
|
1082
1169
|
# @param [String] key
|
1083
1170
|
# @param [String] value
|
1084
|
-
# @return [
|
1171
|
+
# @return [Integer] the length of the list after the push operation
|
1085
1172
|
def rpushx(key, value)
|
1086
1173
|
synchronize do |client|
|
1087
1174
|
client.call([:rpushx, key, value])
|
1088
1175
|
end
|
1089
1176
|
end
|
1090
1177
|
|
1091
|
-
# Remove and get the first
|
1178
|
+
# Remove and get the first elements in a list.
|
1092
1179
|
#
|
1093
1180
|
# @param [String] key
|
1094
|
-
# @
|
1095
|
-
|
1181
|
+
# @param [Integer] count number of elements to remove
|
1182
|
+
# @return [String, Array<String>] the values of the first elements
|
1183
|
+
def lpop(key, count = nil)
|
1096
1184
|
synchronize do |client|
|
1097
|
-
|
1185
|
+
command = [:lpop, key]
|
1186
|
+
command << count if count
|
1187
|
+
client.call(command)
|
1098
1188
|
end
|
1099
1189
|
end
|
1100
1190
|
|
1101
|
-
# Remove and get the last
|
1191
|
+
# Remove and get the last elements in a list.
|
1102
1192
|
#
|
1103
1193
|
# @param [String] key
|
1104
|
-
# @
|
1105
|
-
|
1194
|
+
# @param [Integer] count number of elements to remove
|
1195
|
+
# @return [String, Array<String>] the values of the last elements
|
1196
|
+
def rpop(key, count = nil)
|
1106
1197
|
synchronize do |client|
|
1107
|
-
|
1198
|
+
command = [:rpop, key]
|
1199
|
+
command << count if count
|
1200
|
+
client.call(command)
|
1108
1201
|
end
|
1109
1202
|
end
|
1110
1203
|
|
@@ -1119,28 +1212,27 @@ class Redis
|
|
1119
1212
|
end
|
1120
1213
|
end
|
1121
1214
|
|
1122
|
-
def _bpop(cmd, args)
|
1123
|
-
|
1124
|
-
|
1125
|
-
case args.last
|
1126
|
-
when Hash
|
1215
|
+
def _bpop(cmd, args, &blk)
|
1216
|
+
timeout = if args.last.is_a?(Hash)
|
1127
1217
|
options = args.pop
|
1128
|
-
|
1218
|
+
options[:timeout]
|
1219
|
+
elsif args.last.respond_to?(:to_int)
|
1129
1220
|
# Issue deprecation notice in obnoxious mode...
|
1130
|
-
|
1221
|
+
args.pop.to_int
|
1131
1222
|
end
|
1132
1223
|
|
1224
|
+
timeout ||= 0
|
1225
|
+
|
1133
1226
|
if args.size > 1
|
1134
1227
|
# Issue deprecation notice in obnoxious mode...
|
1135
1228
|
end
|
1136
1229
|
|
1137
1230
|
keys = args.flatten
|
1138
|
-
timeout = options[:timeout] || 0
|
1139
1231
|
|
1140
1232
|
synchronize do |client|
|
1141
1233
|
command = [cmd, keys, timeout]
|
1142
1234
|
timeout += client.timeout if timeout > 0
|
1143
|
-
client.call_with_timeout(command, timeout)
|
1235
|
+
client.call_with_timeout(command, timeout, &blk)
|
1144
1236
|
end
|
1145
1237
|
end
|
1146
1238
|
|
@@ -1160,7 +1252,7 @@ class Redis
|
|
1160
1252
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1161
1253
|
# blocking pop on
|
1162
1254
|
# @param [Hash] options
|
1163
|
-
# - `:timeout =>
|
1255
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1164
1256
|
#
|
1165
1257
|
# @return [nil, [String, String]]
|
1166
1258
|
# - `nil` when the operation timed out
|
@@ -1174,7 +1266,7 @@ class Redis
|
|
1174
1266
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1175
1267
|
# blocking pop on
|
1176
1268
|
# @param [Hash] options
|
1177
|
-
# - `:timeout =>
|
1269
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1178
1270
|
#
|
1179
1271
|
# @return [nil, [String, String]]
|
1180
1272
|
# - `nil` when the operation timed out
|
@@ -1191,20 +1283,12 @@ class Redis
|
|
1191
1283
|
# @param [String] source source key
|
1192
1284
|
# @param [String] destination destination key
|
1193
1285
|
# @param [Hash] options
|
1194
|
-
# - `:timeout =>
|
1286
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1195
1287
|
#
|
1196
1288
|
# @return [nil, String]
|
1197
1289
|
# - `nil` when the operation timed out
|
1198
1290
|
# - the element was popped and pushed otherwise
|
1199
|
-
def brpoplpush(source, destination,
|
1200
|
-
case options
|
1201
|
-
when Integer
|
1202
|
-
# Issue deprecation notice in obnoxious mode...
|
1203
|
-
options = { :timeout => options }
|
1204
|
-
end
|
1205
|
-
|
1206
|
-
timeout = options[:timeout] || 0
|
1207
|
-
|
1291
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1208
1292
|
synchronize do |client|
|
1209
1293
|
command = [:brpoplpush, source, destination, timeout]
|
1210
1294
|
timeout += client.timeout if timeout > 0
|
@@ -1215,7 +1299,7 @@ class Redis
|
|
1215
1299
|
# Get an element from a list by its index.
|
1216
1300
|
#
|
1217
1301
|
# @param [String] key
|
1218
|
-
# @param [
|
1302
|
+
# @param [Integer] index
|
1219
1303
|
# @return [String]
|
1220
1304
|
def lindex(key, index)
|
1221
1305
|
synchronize do |client|
|
@@ -1229,7 +1313,7 @@ class Redis
|
|
1229
1313
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1230
1314
|
# @param [String] pivot reference element
|
1231
1315
|
# @param [String] value
|
1232
|
-
# @return [
|
1316
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1233
1317
|
# when the element `pivot` was not found
|
1234
1318
|
def linsert(key, where, pivot, value)
|
1235
1319
|
synchronize do |client|
|
@@ -1240,8 +1324,8 @@ class Redis
|
|
1240
1324
|
# Get a range of elements from a list.
|
1241
1325
|
#
|
1242
1326
|
# @param [String] key
|
1243
|
-
# @param [
|
1244
|
-
# @param [
|
1327
|
+
# @param [Integer] start start index
|
1328
|
+
# @param [Integer] stop stop index
|
1245
1329
|
# @return [Array<String>]
|
1246
1330
|
def lrange(key, start, stop)
|
1247
1331
|
synchronize do |client|
|
@@ -1252,12 +1336,12 @@ class Redis
|
|
1252
1336
|
# Remove elements from a list.
|
1253
1337
|
#
|
1254
1338
|
# @param [String] key
|
1255
|
-
# @param [
|
1339
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1256
1340
|
# value to remove the first `count` occurrences of `value`. A negative
|
1257
1341
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1258
1342
|
# remove all occurrences of `value` from the list.
|
1259
1343
|
# @param [String] value
|
1260
|
-
# @return [
|
1344
|
+
# @return [Integer] the number of removed elements
|
1261
1345
|
def lrem(key, count, value)
|
1262
1346
|
synchronize do |client|
|
1263
1347
|
client.call([:lrem, key, count, value])
|
@@ -1267,7 +1351,7 @@ class Redis
|
|
1267
1351
|
# Set the value of an element in a list by its index.
|
1268
1352
|
#
|
1269
1353
|
# @param [String] key
|
1270
|
-
# @param [
|
1354
|
+
# @param [Integer] index
|
1271
1355
|
# @param [String] value
|
1272
1356
|
# @return [String] `OK`
|
1273
1357
|
def lset(key, index, value)
|
@@ -1279,8 +1363,8 @@ class Redis
|
|
1279
1363
|
# Trim a list to the specified range.
|
1280
1364
|
#
|
1281
1365
|
# @param [String] key
|
1282
|
-
# @param [
|
1283
|
-
# @param [
|
1366
|
+
# @param [Integer] start start index
|
1367
|
+
# @param [Integer] stop stop index
|
1284
1368
|
# @return [String] `OK`
|
1285
1369
|
def ltrim(key, start, stop)
|
1286
1370
|
synchronize do |client|
|
@@ -1291,7 +1375,7 @@ class Redis
|
|
1291
1375
|
# Get the number of members in a set.
|
1292
1376
|
#
|
1293
1377
|
# @param [String] key
|
1294
|
-
# @return [
|
1378
|
+
# @return [Integer]
|
1295
1379
|
def scard(key)
|
1296
1380
|
synchronize do |client|
|
1297
1381
|
client.call([:scard, key])
|
@@ -1302,8 +1386,8 @@ class Redis
|
|
1302
1386
|
#
|
1303
1387
|
# @param [String] key
|
1304
1388
|
# @param [String, Array<String>] member one member, or array of members
|
1305
|
-
# @return [Boolean,
|
1306
|
-
# holding whether or not adding the member succeeded, or `
|
1389
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1390
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
1307
1391
|
# array of members is specified, holding the number of members that were
|
1308
1392
|
# successfully added
|
1309
1393
|
def sadd(key, member)
|
@@ -1324,8 +1408,8 @@ class Redis
|
|
1324
1408
|
#
|
1325
1409
|
# @param [String] key
|
1326
1410
|
# @param [String, Array<String>] member one member, or array of members
|
1327
|
-
# @return [Boolean,
|
1328
|
-
# holding whether or not removing the member succeeded, or `
|
1411
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1412
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
1329
1413
|
# array of members is specified, holding the number of members that were
|
1330
1414
|
# successfully removed
|
1331
1415
|
def srem(key, member)
|
@@ -1346,7 +1430,7 @@ class Redis
|
|
1346
1430
|
#
|
1347
1431
|
# @param [String] key
|
1348
1432
|
# @return [String]
|
1349
|
-
# @param [
|
1433
|
+
# @param [Integer] count
|
1350
1434
|
def spop(key, count = nil)
|
1351
1435
|
synchronize do |client|
|
1352
1436
|
if count.nil?
|
@@ -1360,7 +1444,7 @@ class Redis
|
|
1360
1444
|
# Get one or more random members from a set.
|
1361
1445
|
#
|
1362
1446
|
# @param [String] key
|
1363
|
-
# @param [
|
1447
|
+
# @param [Integer] count
|
1364
1448
|
# @return [String]
|
1365
1449
|
def srandmember(key, count = nil)
|
1366
1450
|
synchronize do |client|
|
@@ -1411,7 +1495,7 @@ class Redis
|
|
1411
1495
|
# @return [Array<String>] members in the difference
|
1412
1496
|
def sdiff(*keys)
|
1413
1497
|
synchronize do |client|
|
1414
|
-
client.call([:sdiff
|
1498
|
+
client.call([:sdiff, *keys])
|
1415
1499
|
end
|
1416
1500
|
end
|
1417
1501
|
|
@@ -1419,10 +1503,10 @@ class Redis
|
|
1419
1503
|
#
|
1420
1504
|
# @param [String] destination destination key
|
1421
1505
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1422
|
-
# @return [
|
1506
|
+
# @return [Integer] number of elements in the resulting set
|
1423
1507
|
def sdiffstore(destination, *keys)
|
1424
1508
|
synchronize do |client|
|
1425
|
-
client.call([:sdiffstore, destination
|
1509
|
+
client.call([:sdiffstore, destination, *keys])
|
1426
1510
|
end
|
1427
1511
|
end
|
1428
1512
|
|
@@ -1432,7 +1516,7 @@ class Redis
|
|
1432
1516
|
# @return [Array<String>] members in the intersection
|
1433
1517
|
def sinter(*keys)
|
1434
1518
|
synchronize do |client|
|
1435
|
-
client.call([:sinter
|
1519
|
+
client.call([:sinter, *keys])
|
1436
1520
|
end
|
1437
1521
|
end
|
1438
1522
|
|
@@ -1440,10 +1524,10 @@ class Redis
|
|
1440
1524
|
#
|
1441
1525
|
# @param [String] destination destination key
|
1442
1526
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1443
|
-
# @return [
|
1527
|
+
# @return [Integer] number of elements in the resulting set
|
1444
1528
|
def sinterstore(destination, *keys)
|
1445
1529
|
synchronize do |client|
|
1446
|
-
client.call([:sinterstore, destination
|
1530
|
+
client.call([:sinterstore, destination, *keys])
|
1447
1531
|
end
|
1448
1532
|
end
|
1449
1533
|
|
@@ -1453,7 +1537,7 @@ class Redis
|
|
1453
1537
|
# @return [Array<String>] members in the union
|
1454
1538
|
def sunion(*keys)
|
1455
1539
|
synchronize do |client|
|
1456
|
-
client.call([:sunion
|
1540
|
+
client.call([:sunion, *keys])
|
1457
1541
|
end
|
1458
1542
|
end
|
1459
1543
|
|
@@ -1461,10 +1545,10 @@ class Redis
|
|
1461
1545
|
#
|
1462
1546
|
# @param [String] destination destination key
|
1463
1547
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1464
|
-
# @return [
|
1548
|
+
# @return [Integer] number of elements in the resulting set
|
1465
1549
|
def sunionstore(destination, *keys)
|
1466
1550
|
synchronize do |client|
|
1467
|
-
client.call([:sunionstore, destination
|
1551
|
+
client.call([:sunionstore, destination, *keys])
|
1468
1552
|
end
|
1469
1553
|
end
|
1470
1554
|
|
@@ -1475,7 +1559,7 @@ class Redis
|
|
1475
1559
|
# # => 4
|
1476
1560
|
#
|
1477
1561
|
# @param [String] key
|
1478
|
-
# @return [
|
1562
|
+
# @return [Integer]
|
1479
1563
|
def zcard(key)
|
1480
1564
|
synchronize do |client|
|
1481
1565
|
client.call([:zcard, key])
|
@@ -1506,38 +1590,27 @@ class Redis
|
|
1506
1590
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1507
1591
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1508
1592
|
#
|
1509
|
-
# @return [Boolean,
|
1593
|
+
# @return [Boolean, Integer, Float]
|
1510
1594
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1511
1595
|
# **added** to the sorted set.
|
1512
|
-
# - `
|
1596
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1513
1597
|
# pairs that were **added** to the sorted set.
|
1514
1598
|
# - `Float` when option :incr is specified, holding the score of the member
|
1515
1599
|
# after incrementing it.
|
1516
|
-
def zadd(key, *args
|
1517
|
-
|
1518
|
-
if
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
zadd_options << "NX" if nx
|
1523
|
-
|
1524
|
-
xx = options[:xx]
|
1525
|
-
zadd_options << "XX" if xx
|
1526
|
-
|
1527
|
-
ch = options[:ch]
|
1528
|
-
zadd_options << "CH" if ch
|
1529
|
-
|
1530
|
-
incr = options[:incr]
|
1531
|
-
zadd_options << "INCR" if incr
|
1532
|
-
end
|
1600
|
+
def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
|
1601
|
+
command = [:zadd, key]
|
1602
|
+
command << "NX" if nx
|
1603
|
+
command << "XX" if xx
|
1604
|
+
command << "CH" if ch
|
1605
|
+
command << "INCR" if incr
|
1533
1606
|
|
1534
1607
|
synchronize do |client|
|
1535
1608
|
if args.size == 1 && args[0].is_a?(Array)
|
1536
1609
|
# Variadic: return float if INCR, integer if !INCR
|
1537
|
-
client.call(
|
1610
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1538
1611
|
elsif args.size == 2
|
1539
1612
|
# Single pair: return float if INCR, boolean if !INCR
|
1540
|
-
client.call(
|
1613
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1541
1614
|
else
|
1542
1615
|
raise ArgumentError, "wrong number of arguments"
|
1543
1616
|
end
|
@@ -1572,10 +1645,10 @@ class Redis
|
|
1572
1645
|
# - a single member
|
1573
1646
|
# - an array of members
|
1574
1647
|
#
|
1575
|
-
# @return [Boolean,
|
1648
|
+
# @return [Boolean, Integer]
|
1576
1649
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1577
1650
|
# was removed from the sorted set
|
1578
|
-
# - `
|
1651
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1579
1652
|
# members that were removed to the sorted set
|
1580
1653
|
def zrem(key, member)
|
1581
1654
|
synchronize do |client|
|
@@ -1591,6 +1664,90 @@ class Redis
|
|
1591
1664
|
end
|
1592
1665
|
end
|
1593
1666
|
|
1667
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1668
|
+
#
|
1669
|
+
# @example Popping a member
|
1670
|
+
# redis.zpopmax('zset')
|
1671
|
+
# #=> ['b', 2.0]
|
1672
|
+
# @example With count option
|
1673
|
+
# redis.zpopmax('zset', 2)
|
1674
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
1675
|
+
#
|
1676
|
+
# @params key [String] a key of the sorted set
|
1677
|
+
# @params count [Integer] a number of members
|
1678
|
+
#
|
1679
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1680
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1681
|
+
def zpopmax(key, count = nil)
|
1682
|
+
synchronize do |client|
|
1683
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1684
|
+
count.to_i > 1 ? members : members.first
|
1685
|
+
end
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1689
|
+
#
|
1690
|
+
# @example Popping a member
|
1691
|
+
# redis.zpopmin('zset')
|
1692
|
+
# #=> ['a', 1.0]
|
1693
|
+
# @example With count option
|
1694
|
+
# redis.zpopmin('zset', 2)
|
1695
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
1696
|
+
#
|
1697
|
+
# @params key [String] a key of the sorted set
|
1698
|
+
# @params count [Integer] a number of members
|
1699
|
+
#
|
1700
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1701
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1702
|
+
def zpopmin(key, count = nil)
|
1703
|
+
synchronize do |client|
|
1704
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1705
|
+
count.to_i > 1 ? members : members.first
|
1706
|
+
end
|
1707
|
+
end
|
1708
|
+
|
1709
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1710
|
+
# or block until one is available.
|
1711
|
+
#
|
1712
|
+
# @example Popping a member from a sorted set
|
1713
|
+
# redis.bzpopmax('zset', 1)
|
1714
|
+
# #=> ['zset', 'b', 2.0]
|
1715
|
+
# @example Popping a member from multiple sorted sets
|
1716
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
1717
|
+
# #=> ['zset1', 'b', 2.0]
|
1718
|
+
#
|
1719
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1720
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1721
|
+
#
|
1722
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1723
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1724
|
+
def bzpopmax(*args)
|
1725
|
+
_bpop(:bzpopmax, args) do |reply|
|
1726
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1727
|
+
end
|
1728
|
+
end
|
1729
|
+
|
1730
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1731
|
+
# or block until one is available.
|
1732
|
+
#
|
1733
|
+
# @example Popping a member from a sorted set
|
1734
|
+
# redis.bzpopmin('zset', 1)
|
1735
|
+
# #=> ['zset', 'a', 1.0]
|
1736
|
+
# @example Popping a member from multiple sorted sets
|
1737
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
1738
|
+
# #=> ['zset1', 'a', 1.0]
|
1739
|
+
#
|
1740
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1741
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1742
|
+
#
|
1743
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1744
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1745
|
+
def bzpopmin(*args)
|
1746
|
+
_bpop(:bzpopmin, args) do |reply|
|
1747
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1748
|
+
end
|
1749
|
+
end
|
1750
|
+
|
1594
1751
|
# Get the score associated with the given member in a sorted set.
|
1595
1752
|
#
|
1596
1753
|
# @example Get the score for member "a"
|
@@ -1616,18 +1773,16 @@ class Redis
|
|
1616
1773
|
# # => [["a", 32.0], ["b", 64.0]]
|
1617
1774
|
#
|
1618
1775
|
# @param [String] key
|
1619
|
-
# @param [
|
1620
|
-
# @param [
|
1776
|
+
# @param [Integer] start start index
|
1777
|
+
# @param [Integer] stop stop index
|
1621
1778
|
# @param [Hash] options
|
1622
1779
|
# - `:with_scores => true`: include scores in output
|
1623
1780
|
#
|
1624
1781
|
# @return [Array<String>, Array<[String, Float]>]
|
1625
1782
|
# - when `:with_scores` is not specified, an array of members
|
1626
1783
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1627
|
-
def zrange(key, start, stop,
|
1628
|
-
args = []
|
1629
|
-
|
1630
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1784
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1785
|
+
args = [:zrange, key, start, stop]
|
1631
1786
|
|
1632
1787
|
if with_scores
|
1633
1788
|
args << "WITHSCORES"
|
@@ -1635,7 +1790,7 @@ class Redis
|
|
1635
1790
|
end
|
1636
1791
|
|
1637
1792
|
synchronize do |client|
|
1638
|
-
client.call(
|
1793
|
+
client.call(args, &block)
|
1639
1794
|
end
|
1640
1795
|
end
|
1641
1796
|
|
@@ -1650,10 +1805,8 @@ class Redis
|
|
1650
1805
|
# # => [["b", 64.0], ["a", 32.0]]
|
1651
1806
|
#
|
1652
1807
|
# @see #zrange
|
1653
|
-
def zrevrange(key, start, stop,
|
1654
|
-
args = []
|
1655
|
-
|
1656
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1808
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1809
|
+
args = [:zrevrange, key, start, stop]
|
1657
1810
|
|
1658
1811
|
if with_scores
|
1659
1812
|
args << "WITHSCORES"
|
@@ -1661,7 +1814,7 @@ class Redis
|
|
1661
1814
|
end
|
1662
1815
|
|
1663
1816
|
synchronize do |client|
|
1664
|
-
client.call(
|
1817
|
+
client.call(args, &block)
|
1665
1818
|
end
|
1666
1819
|
end
|
1667
1820
|
|
@@ -1669,7 +1822,7 @@ class Redis
|
|
1669
1822
|
#
|
1670
1823
|
# @param [String] key
|
1671
1824
|
# @param [String] member
|
1672
|
-
# @return [
|
1825
|
+
# @return [Integer]
|
1673
1826
|
def zrank(key, member)
|
1674
1827
|
synchronize do |client|
|
1675
1828
|
client.call([:zrank, key, member])
|
@@ -1681,7 +1834,7 @@ class Redis
|
|
1681
1834
|
#
|
1682
1835
|
# @param [String] key
|
1683
1836
|
# @param [String] member
|
1684
|
-
# @return [
|
1837
|
+
# @return [Integer]
|
1685
1838
|
def zrevrank(key, member)
|
1686
1839
|
synchronize do |client|
|
1687
1840
|
client.call([:zrevrank, key, member])
|
@@ -1698,15 +1851,39 @@ class Redis
|
|
1698
1851
|
# # => 5
|
1699
1852
|
#
|
1700
1853
|
# @param [String] key
|
1701
|
-
# @param [
|
1702
|
-
# @param [
|
1703
|
-
# @return [
|
1854
|
+
# @param [Integer] start start index
|
1855
|
+
# @param [Integer] stop stop index
|
1856
|
+
# @return [Integer] number of members that were removed
|
1704
1857
|
def zremrangebyrank(key, start, stop)
|
1705
1858
|
synchronize do |client|
|
1706
1859
|
client.call([:zremrangebyrank, key, start, stop])
|
1707
1860
|
end
|
1708
1861
|
end
|
1709
1862
|
|
1863
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
1864
|
+
#
|
1865
|
+
# @example Count members matching a
|
1866
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
1867
|
+
# # => 1
|
1868
|
+
# @example Count members matching a-z
|
1869
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
1870
|
+
# # => 26
|
1871
|
+
#
|
1872
|
+
# @param [String] key
|
1873
|
+
# @param [String] min
|
1874
|
+
# - inclusive minimum is specified by prefixing `(`
|
1875
|
+
# - exclusive minimum is specified by prefixing `[`
|
1876
|
+
# @param [String] max
|
1877
|
+
# - inclusive maximum is specified by prefixing `(`
|
1878
|
+
# - exclusive maximum is specified by prefixing `[`
|
1879
|
+
#
|
1880
|
+
# @return [Integer] number of members within the specified lexicographical range
|
1881
|
+
def zlexcount(key, min, max)
|
1882
|
+
synchronize do |client|
|
1883
|
+
client.call([:zlexcount, key, min, max])
|
1884
|
+
end
|
1885
|
+
end
|
1886
|
+
|
1710
1887
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1711
1888
|
#
|
1712
1889
|
# @example Retrieve members matching a
|
@@ -1728,14 +1905,16 @@ class Redis
|
|
1728
1905
|
# `count` members
|
1729
1906
|
#
|
1730
1907
|
# @return [Array<String>, Array<[String, Float]>]
|
1731
|
-
def zrangebylex(key, min, max,
|
1732
|
-
args = []
|
1908
|
+
def zrangebylex(key, min, max, limit: nil)
|
1909
|
+
args = [:zrangebylex, key, min, max]
|
1733
1910
|
|
1734
|
-
|
1735
|
-
|
1911
|
+
if limit
|
1912
|
+
args << "LIMIT"
|
1913
|
+
args.concat(limit)
|
1914
|
+
end
|
1736
1915
|
|
1737
1916
|
synchronize do |client|
|
1738
|
-
client.call(
|
1917
|
+
client.call(args)
|
1739
1918
|
end
|
1740
1919
|
end
|
1741
1920
|
|
@@ -1750,14 +1929,16 @@ class Redis
|
|
1750
1929
|
# # => ["abbygail", "abby"]
|
1751
1930
|
#
|
1752
1931
|
# @see #zrangebylex
|
1753
|
-
def zrevrangebylex(key, max, min,
|
1754
|
-
args = []
|
1932
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
1933
|
+
args = [:zrevrangebylex, key, max, min]
|
1755
1934
|
|
1756
|
-
|
1757
|
-
|
1935
|
+
if limit
|
1936
|
+
args << "LIMIT"
|
1937
|
+
args.concat(limit)
|
1938
|
+
end
|
1758
1939
|
|
1759
1940
|
synchronize do |client|
|
1760
|
-
client.call(
|
1941
|
+
client.call(args)
|
1761
1942
|
end
|
1762
1943
|
end
|
1763
1944
|
|
@@ -1788,21 +1969,21 @@ class Redis
|
|
1788
1969
|
# @return [Array<String>, Array<[String, Float]>]
|
1789
1970
|
# - when `:with_scores` is not specified, an array of members
|
1790
1971
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1791
|
-
def zrangebyscore(key, min, max,
|
1792
|
-
args = []
|
1793
|
-
|
1794
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1972
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
1973
|
+
args = [:zrangebyscore, key, min, max]
|
1795
1974
|
|
1796
1975
|
if with_scores
|
1797
1976
|
args << "WITHSCORES"
|
1798
1977
|
block = FloatifyPairs
|
1799
1978
|
end
|
1800
1979
|
|
1801
|
-
|
1802
|
-
|
1980
|
+
if limit
|
1981
|
+
args << "LIMIT"
|
1982
|
+
args.concat(limit)
|
1983
|
+
end
|
1803
1984
|
|
1804
1985
|
synchronize do |client|
|
1805
|
-
client.call(
|
1986
|
+
client.call(args, &block)
|
1806
1987
|
end
|
1807
1988
|
end
|
1808
1989
|
|
@@ -1820,21 +2001,21 @@ class Redis
|
|
1820
2001
|
# # => [["b", 64.0], ["a", 32.0]]
|
1821
2002
|
#
|
1822
2003
|
# @see #zrangebyscore
|
1823
|
-
def zrevrangebyscore(key, max, min,
|
1824
|
-
args = []
|
1825
|
-
|
1826
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2004
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2005
|
+
args = [:zrevrangebyscore, key, max, min]
|
1827
2006
|
|
1828
2007
|
if with_scores
|
1829
|
-
args <<
|
2008
|
+
args << "WITHSCORES"
|
1830
2009
|
block = FloatifyPairs
|
1831
2010
|
end
|
1832
2011
|
|
1833
|
-
|
1834
|
-
|
2012
|
+
if limit
|
2013
|
+
args << "LIMIT"
|
2014
|
+
args.concat(limit)
|
2015
|
+
end
|
1835
2016
|
|
1836
2017
|
synchronize do |client|
|
1837
|
-
client.call(
|
2018
|
+
client.call(args, &block)
|
1838
2019
|
end
|
1839
2020
|
end
|
1840
2021
|
|
@@ -1854,7 +2035,7 @@ class Redis
|
|
1854
2035
|
# @param [String] max
|
1855
2036
|
# - inclusive maximum score is specified verbatim
|
1856
2037
|
# - exclusive maximum score is specified by prefixing `(`
|
1857
|
-
# @return [
|
2038
|
+
# @return [Integer] number of members that were removed
|
1858
2039
|
def zremrangebyscore(key, min, max)
|
1859
2040
|
synchronize do |client|
|
1860
2041
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1877,13 +2058,52 @@ class Redis
|
|
1877
2058
|
# @param [String] max
|
1878
2059
|
# - inclusive maximum score is specified verbatim
|
1879
2060
|
# - exclusive maximum score is specified by prefixing `(`
|
1880
|
-
# @return [
|
2061
|
+
# @return [Integer] number of members in within the specified range
|
1881
2062
|
def zcount(key, min, max)
|
1882
2063
|
synchronize do |client|
|
1883
2064
|
client.call([:zcount, key, min, max])
|
1884
2065
|
end
|
1885
2066
|
end
|
1886
2067
|
|
2068
|
+
# Return the intersection of multiple sorted sets
|
2069
|
+
#
|
2070
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
2071
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
2072
|
+
# # => ["v1", "v2"]
|
2073
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
2074
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
2075
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
2076
|
+
#
|
2077
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
2078
|
+
# @param [Hash] options
|
2079
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2080
|
+
# sorted sets
|
2081
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2082
|
+
# - `:with_scores => true`: include scores in output
|
2083
|
+
#
|
2084
|
+
# @return [Array<String>, Array<[String, Float]>]
|
2085
|
+
# - when `:with_scores` is not specified, an array of members
|
2086
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2087
|
+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
2088
|
+
args = [:zinter, keys.size, *keys]
|
2089
|
+
|
2090
|
+
if weights
|
2091
|
+
args << "WEIGHTS"
|
2092
|
+
args.concat(weights)
|
2093
|
+
end
|
2094
|
+
|
2095
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2096
|
+
|
2097
|
+
if with_scores
|
2098
|
+
args << "WITHSCORES"
|
2099
|
+
block = FloatifyPairs
|
2100
|
+
end
|
2101
|
+
|
2102
|
+
synchronize do |client|
|
2103
|
+
client.call(args, &block)
|
2104
|
+
end
|
2105
|
+
end
|
2106
|
+
|
1887
2107
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
1888
2108
|
# key.
|
1889
2109
|
#
|
@@ -1897,18 +2117,19 @@ class Redis
|
|
1897
2117
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1898
2118
|
# sorted sets
|
1899
2119
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1900
|
-
# @return [
|
1901
|
-
def zinterstore(destination, keys,
|
1902
|
-
args = []
|
2120
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2121
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2122
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
1903
2123
|
|
1904
|
-
|
1905
|
-
|
2124
|
+
if weights
|
2125
|
+
args << "WEIGHTS"
|
2126
|
+
args.concat(weights)
|
2127
|
+
end
|
1906
2128
|
|
1907
|
-
aggregate
|
1908
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2129
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1909
2130
|
|
1910
2131
|
synchronize do |client|
|
1911
|
-
client.call(
|
2132
|
+
client.call(args)
|
1912
2133
|
end
|
1913
2134
|
end
|
1914
2135
|
|
@@ -1924,40 +2145,46 @@ class Redis
|
|
1924
2145
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1925
2146
|
# sorted sets
|
1926
2147
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1927
|
-
# @return [
|
1928
|
-
def zunionstore(destination, keys,
|
1929
|
-
args = []
|
2148
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2149
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2150
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
1930
2151
|
|
1931
|
-
|
1932
|
-
|
2152
|
+
if weights
|
2153
|
+
args << "WEIGHTS"
|
2154
|
+
args.concat(weights)
|
2155
|
+
end
|
1933
2156
|
|
1934
|
-
aggregate
|
1935
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2157
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1936
2158
|
|
1937
2159
|
synchronize do |client|
|
1938
|
-
client.call(
|
2160
|
+
client.call(args)
|
1939
2161
|
end
|
1940
2162
|
end
|
1941
2163
|
|
1942
2164
|
# Get the number of fields in a hash.
|
1943
2165
|
#
|
1944
2166
|
# @param [String] key
|
1945
|
-
# @return [
|
2167
|
+
# @return [Integer] number of fields in the hash
|
1946
2168
|
def hlen(key)
|
1947
2169
|
synchronize do |client|
|
1948
2170
|
client.call([:hlen, key])
|
1949
2171
|
end
|
1950
2172
|
end
|
1951
2173
|
|
1952
|
-
# Set
|
2174
|
+
# Set one or more hash values.
|
2175
|
+
#
|
2176
|
+
# @example
|
2177
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2178
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
1953
2179
|
#
|
1954
2180
|
# @param [String] key
|
1955
|
-
# @param [String]
|
1956
|
-
# @
|
1957
|
-
|
1958
|
-
|
2181
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2182
|
+
# @return [Integer] The number of fields that were added to the hash
|
2183
|
+
def hset(key, *attrs)
|
2184
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2185
|
+
|
1959
2186
|
synchronize do |client|
|
1960
|
-
client.call([:hset, key,
|
2187
|
+
client.call([:hset, key, *attrs])
|
1961
2188
|
end
|
1962
2189
|
end
|
1963
2190
|
|
@@ -2046,7 +2273,7 @@ class Redis
|
|
2046
2273
|
# @see #hmget
|
2047
2274
|
def mapped_hmget(key, *fields)
|
2048
2275
|
hmget(key, *fields) do |reply|
|
2049
|
-
if reply.
|
2276
|
+
if reply.is_a?(Array)
|
2050
2277
|
Hash[fields.zip(reply)]
|
2051
2278
|
else
|
2052
2279
|
reply
|
@@ -2058,10 +2285,10 @@ class Redis
|
|
2058
2285
|
#
|
2059
2286
|
# @param [String] key
|
2060
2287
|
# @param [String, Array<String>] field
|
2061
|
-
# @return [
|
2062
|
-
def hdel(key,
|
2288
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2289
|
+
def hdel(key, *fields)
|
2063
2290
|
synchronize do |client|
|
2064
|
-
client.call([:hdel, key,
|
2291
|
+
client.call([:hdel, key, *fields])
|
2065
2292
|
end
|
2066
2293
|
end
|
2067
2294
|
|
@@ -2080,8 +2307,8 @@ class Redis
|
|
2080
2307
|
#
|
2081
2308
|
# @param [String] key
|
2082
2309
|
# @param [String] field
|
2083
|
-
# @param [
|
2084
|
-
# @return [
|
2310
|
+
# @param [Integer] increment
|
2311
|
+
# @return [Integer] value of the field after incrementing it
|
2085
2312
|
def hincrby(key, field, increment)
|
2086
2313
|
synchronize do |client|
|
2087
2314
|
client.call([:hincrby, key, field, increment])
|
@@ -2139,20 +2366,21 @@ class Redis
|
|
2139
2366
|
|
2140
2367
|
def subscribed?
|
2141
2368
|
synchronize do |client|
|
2142
|
-
client.
|
2369
|
+
client.is_a? SubscribedClient
|
2143
2370
|
end
|
2144
2371
|
end
|
2145
2372
|
|
2146
2373
|
# Listen for messages published to the given channels.
|
2147
2374
|
def subscribe(*channels, &block)
|
2148
|
-
synchronize do |
|
2375
|
+
synchronize do |_client|
|
2149
2376
|
_subscription(:subscribe, 0, channels, block)
|
2150
2377
|
end
|
2151
2378
|
end
|
2152
2379
|
|
2153
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2380
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2381
|
+
# if there is no messages for a timeout period.
|
2154
2382
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2155
|
-
synchronize do |
|
2383
|
+
synchronize do |_client|
|
2156
2384
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2157
2385
|
end
|
2158
2386
|
end
|
@@ -2160,21 +2388,23 @@ class Redis
|
|
2160
2388
|
# Stop listening for messages posted to the given channels.
|
2161
2389
|
def unsubscribe(*channels)
|
2162
2390
|
synchronize do |client|
|
2163
|
-
raise
|
2391
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2392
|
+
|
2164
2393
|
client.unsubscribe(*channels)
|
2165
2394
|
end
|
2166
2395
|
end
|
2167
2396
|
|
2168
2397
|
# Listen for messages published to channels matching the given patterns.
|
2169
2398
|
def psubscribe(*channels, &block)
|
2170
|
-
synchronize do |
|
2399
|
+
synchronize do |_client|
|
2171
2400
|
_subscription(:psubscribe, 0, channels, block)
|
2172
2401
|
end
|
2173
2402
|
end
|
2174
2403
|
|
2175
|
-
# Listen for messages published to channels matching the given patterns.
|
2404
|
+
# Listen for messages published to channels matching the given patterns.
|
2405
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
2176
2406
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2177
|
-
synchronize do |
|
2407
|
+
synchronize do |_client|
|
2178
2408
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2179
2409
|
end
|
2180
2410
|
end
|
@@ -2182,7 +2412,8 @@ class Redis
|
|
2182
2412
|
# Stop listening for messages posted to channels matching the given patterns.
|
2183
2413
|
def punsubscribe(*channels)
|
2184
2414
|
synchronize do |client|
|
2185
|
-
raise
|
2415
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2416
|
+
|
2186
2417
|
client.punsubscribe(*channels)
|
2187
2418
|
end
|
2188
2419
|
end
|
@@ -2227,7 +2458,7 @@ class Redis
|
|
2227
2458
|
# @see #multi
|
2228
2459
|
def watch(*keys)
|
2229
2460
|
synchronize do |client|
|
2230
|
-
res = client.call([:watch
|
2461
|
+
res = client.call([:watch, *keys])
|
2231
2462
|
|
2232
2463
|
if block_given?
|
2233
2464
|
begin
|
@@ -2257,13 +2488,13 @@ class Redis
|
|
2257
2488
|
end
|
2258
2489
|
|
2259
2490
|
def pipelined
|
2260
|
-
synchronize do |
|
2491
|
+
synchronize do |prior_client|
|
2261
2492
|
begin
|
2262
|
-
|
2493
|
+
@client = Pipeline.new(prior_client)
|
2263
2494
|
yield(self)
|
2264
|
-
|
2495
|
+
prior_client.call_pipeline(@client)
|
2265
2496
|
ensure
|
2266
|
-
@client =
|
2497
|
+
@client = prior_client
|
2267
2498
|
end
|
2268
2499
|
end
|
2269
2500
|
end
|
@@ -2299,17 +2530,16 @@ class Redis
|
|
2299
2530
|
# @see #watch
|
2300
2531
|
# @see #unwatch
|
2301
2532
|
def multi
|
2302
|
-
synchronize do |
|
2533
|
+
synchronize do |prior_client|
|
2303
2534
|
if !block_given?
|
2304
|
-
|
2535
|
+
prior_client.call([:multi])
|
2305
2536
|
else
|
2306
2537
|
begin
|
2307
|
-
|
2308
|
-
original, @client = @client, pipeline
|
2538
|
+
@client = Pipeline::Multi.new(prior_client)
|
2309
2539
|
yield(self)
|
2310
|
-
|
2540
|
+
prior_client.call_pipeline(@client)
|
2311
2541
|
ensure
|
2312
|
-
@client =
|
2542
|
+
@client = prior_client
|
2313
2543
|
end
|
2314
2544
|
end
|
2315
2545
|
end
|
@@ -2456,18 +2686,13 @@ class Redis
|
|
2456
2686
|
_eval(:evalsha, args)
|
2457
2687
|
end
|
2458
2688
|
|
2459
|
-
def _scan(command, cursor, args,
|
2689
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2460
2690
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2461
2691
|
|
2462
2692
|
args << cursor
|
2463
|
-
|
2464
|
-
|
2465
|
-
|
2466
|
-
end
|
2467
|
-
|
2468
|
-
if count = options[:count]
|
2469
|
-
args.concat(["COUNT", count])
|
2470
|
-
end
|
2693
|
+
args << "MATCH" << match if match
|
2694
|
+
args << "COUNT" << count if count
|
2695
|
+
args << "TYPE" << type if type
|
2471
2696
|
|
2472
2697
|
synchronize do |client|
|
2473
2698
|
client.call([command] + args, &block)
|
@@ -2482,15 +2707,19 @@ class Redis
|
|
2482
2707
|
# @example Retrieve a batch of keys matching a pattern
|
2483
2708
|
# redis.scan(4, :match => "key:1?")
|
2484
2709
|
# # => ["92", ["key:13", "key:18"]]
|
2710
|
+
# @example Retrieve a batch of keys of a certain type
|
2711
|
+
# redis.scan(92, :type => "zset")
|
2712
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2485
2713
|
#
|
2486
2714
|
# @param [String, Integer] cursor the cursor of the iteration
|
2487
2715
|
# @param [Hash] options
|
2488
2716
|
# - `:match => String`: only return keys matching the pattern
|
2489
2717
|
# - `:count => Integer`: return count keys at most per iteration
|
2718
|
+
# - `:type => String`: return keys only of the given type
|
2490
2719
|
#
|
2491
2720
|
# @return [String, Array<String>] the next cursor and all found keys
|
2492
|
-
def scan(cursor, options
|
2493
|
-
_scan(:scan, cursor, [], options)
|
2721
|
+
def scan(cursor, **options)
|
2722
|
+
_scan(:scan, cursor, [], **options)
|
2494
2723
|
end
|
2495
2724
|
|
2496
2725
|
# Scan the keyspace
|
@@ -2502,17 +2731,23 @@ class Redis
|
|
2502
2731
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2503
2732
|
# # => key:13
|
2504
2733
|
# # => key:18
|
2734
|
+
# @example Execute block for each key of a type
|
2735
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2736
|
+
# # => "hash"
|
2737
|
+
# # => "hash"
|
2505
2738
|
#
|
2506
2739
|
# @param [Hash] options
|
2507
2740
|
# - `:match => String`: only return keys matching the pattern
|
2508
2741
|
# - `:count => Integer`: return count keys at most per iteration
|
2742
|
+
# - `:type => String`: return keys only of the given type
|
2509
2743
|
#
|
2510
2744
|
# @return [Enumerator] an enumerator for all found keys
|
2511
|
-
def scan_each(options
|
2512
|
-
return to_enum(:scan_each, options) unless block_given?
|
2745
|
+
def scan_each(**options, &block)
|
2746
|
+
return to_enum(:scan_each, **options) unless block_given?
|
2747
|
+
|
2513
2748
|
cursor = 0
|
2514
2749
|
loop do
|
2515
|
-
cursor, keys = scan(cursor, options)
|
2750
|
+
cursor, keys = scan(cursor, **options)
|
2516
2751
|
keys.each(&block)
|
2517
2752
|
break if cursor == "0"
|
2518
2753
|
end
|
@@ -2529,8 +2764,8 @@ class Redis
|
|
2529
2764
|
# - `:count => Integer`: return count keys at most per iteration
|
2530
2765
|
#
|
2531
2766
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2532
|
-
def hscan(key, cursor, options
|
2533
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2767
|
+
def hscan(key, cursor, **options)
|
2768
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2534
2769
|
[reply[0], reply[1].each_slice(2).to_a]
|
2535
2770
|
end
|
2536
2771
|
end
|
@@ -2546,11 +2781,12 @@ class Redis
|
|
2546
2781
|
# - `:count => Integer`: return count keys at most per iteration
|
2547
2782
|
#
|
2548
2783
|
# @return [Enumerator] an enumerator for all found keys
|
2549
|
-
def hscan_each(key, options
|
2550
|
-
return to_enum(:hscan_each, key, options) unless block_given?
|
2784
|
+
def hscan_each(key, **options, &block)
|
2785
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
2786
|
+
|
2551
2787
|
cursor = 0
|
2552
2788
|
loop do
|
2553
|
-
cursor, values = hscan(key, cursor, options)
|
2789
|
+
cursor, values = hscan(key, cursor, **options)
|
2554
2790
|
values.each(&block)
|
2555
2791
|
break if cursor == "0"
|
2556
2792
|
end
|
@@ -2568,8 +2804,8 @@ class Redis
|
|
2568
2804
|
#
|
2569
2805
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2570
2806
|
# members and scores
|
2571
|
-
def zscan(key, cursor, options
|
2572
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2807
|
+
def zscan(key, cursor, **options)
|
2808
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2573
2809
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2574
2810
|
end
|
2575
2811
|
end
|
@@ -2585,11 +2821,12 @@ class Redis
|
|
2585
2821
|
# - `:count => Integer`: return count keys at most per iteration
|
2586
2822
|
#
|
2587
2823
|
# @return [Enumerator] an enumerator for all found scores and members
|
2588
|
-
def zscan_each(key, options
|
2589
|
-
return to_enum(:zscan_each, key, options) unless block_given?
|
2824
|
+
def zscan_each(key, **options, &block)
|
2825
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
2826
|
+
|
2590
2827
|
cursor = 0
|
2591
2828
|
loop do
|
2592
|
-
cursor, values = zscan(key, cursor, options)
|
2829
|
+
cursor, values = zscan(key, cursor, **options)
|
2593
2830
|
values.each(&block)
|
2594
2831
|
break if cursor == "0"
|
2595
2832
|
end
|
@@ -2606,8 +2843,8 @@ class Redis
|
|
2606
2843
|
# - `:count => Integer`: return count keys at most per iteration
|
2607
2844
|
#
|
2608
2845
|
# @return [String, Array<String>] the next cursor and all found members
|
2609
|
-
def sscan(key, cursor, options
|
2610
|
-
_scan(:sscan, cursor, [key], options)
|
2846
|
+
def sscan(key, cursor, **options)
|
2847
|
+
_scan(:sscan, cursor, [key], **options)
|
2611
2848
|
end
|
2612
2849
|
|
2613
2850
|
# Scan a set
|
@@ -2621,11 +2858,12 @@ class Redis
|
|
2621
2858
|
# - `:count => Integer`: return count keys at most per iteration
|
2622
2859
|
#
|
2623
2860
|
# @return [Enumerator] an enumerator for all keys in the set
|
2624
|
-
def sscan_each(key, options
|
2625
|
-
return to_enum(:sscan_each, key, options) unless block_given?
|
2861
|
+
def sscan_each(key, **options, &block)
|
2862
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
2863
|
+
|
2626
2864
|
cursor = 0
|
2627
2865
|
loop do
|
2628
|
-
cursor, keys = sscan(key, cursor, options)
|
2866
|
+
cursor, keys = sscan(key, cursor, **options)
|
2629
2867
|
keys.each(&block)
|
2630
2868
|
break if cursor == "0"
|
2631
2869
|
end
|
@@ -2648,7 +2886,7 @@ class Redis
|
|
2648
2886
|
# union of the HyperLogLogs contained in the keys.
|
2649
2887
|
#
|
2650
2888
|
# @param [String, Array<String>] keys
|
2651
|
-
# @return [
|
2889
|
+
# @return [Integer]
|
2652
2890
|
def pfcount(*keys)
|
2653
2891
|
synchronize do |client|
|
2654
2892
|
client.call([:pfcount] + keys)
|
@@ -2667,6 +2905,445 @@ class Redis
|
|
2667
2905
|
end
|
2668
2906
|
end
|
2669
2907
|
|
2908
|
+
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
2909
|
+
#
|
2910
|
+
# @param [String] key
|
2911
|
+
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
2912
|
+
# @return [Integer] number of elements added to the sorted set
|
2913
|
+
def geoadd(key, *member)
|
2914
|
+
synchronize do |client|
|
2915
|
+
client.call([:geoadd, key, *member])
|
2916
|
+
end
|
2917
|
+
end
|
2918
|
+
|
2919
|
+
# Returns geohash string representing position for specified members of the specified key.
|
2920
|
+
#
|
2921
|
+
# @param [String] key
|
2922
|
+
# @param [String, Array<String>] member one member or array of members
|
2923
|
+
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
2924
|
+
def geohash(key, member)
|
2925
|
+
synchronize do |client|
|
2926
|
+
client.call([:geohash, key, member])
|
2927
|
+
end
|
2928
|
+
end
|
2929
|
+
|
2930
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
2931
|
+
# given maximum distance from a point
|
2932
|
+
#
|
2933
|
+
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2934
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
2935
|
+
# or the farthest to the nearest relative to the center
|
2936
|
+
# @param [Integer] count limit the results to the first N matching items
|
2937
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2938
|
+
# @return [Array<String>] may be changed with `options`
|
2939
|
+
|
2940
|
+
def georadius(*args, **geoptions)
|
2941
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
2942
|
+
|
2943
|
+
synchronize do |client|
|
2944
|
+
client.call([:georadius, *geoarguments])
|
2945
|
+
end
|
2946
|
+
end
|
2947
|
+
|
2948
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
2949
|
+
# given maximum distance from an already existing member
|
2950
|
+
#
|
2951
|
+
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2952
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
2953
|
+
# to the nearest relative to the center
|
2954
|
+
# @param [Integer] count limit the results to the first N matching items
|
2955
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2956
|
+
# @return [Array<String>] may be changed with `options`
|
2957
|
+
|
2958
|
+
def georadiusbymember(*args, **geoptions)
|
2959
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
2960
|
+
|
2961
|
+
synchronize do |client|
|
2962
|
+
client.call([:georadiusbymember, *geoarguments])
|
2963
|
+
end
|
2964
|
+
end
|
2965
|
+
|
2966
|
+
# Returns longitude and latitude of members of a geospatial index
|
2967
|
+
#
|
2968
|
+
# @param [String] key
|
2969
|
+
# @param [String, Array<String>] member one member or array of members
|
2970
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
2971
|
+
# element is either array of longitude and latitude or nil
|
2972
|
+
def geopos(key, member)
|
2973
|
+
synchronize do |client|
|
2974
|
+
client.call([:geopos, key, member])
|
2975
|
+
end
|
2976
|
+
end
|
2977
|
+
|
2978
|
+
# Returns the distance between two members of a geospatial index
|
2979
|
+
#
|
2980
|
+
# @param [String ]key
|
2981
|
+
# @param [Array<String>] members
|
2982
|
+
# @param ['m', 'km', 'mi', 'ft'] unit
|
2983
|
+
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
2984
|
+
def geodist(key, member1, member2, unit = 'm')
|
2985
|
+
synchronize do |client|
|
2986
|
+
client.call([:geodist, key, member1, member2, unit])
|
2987
|
+
end
|
2988
|
+
end
|
2989
|
+
|
2990
|
+
# Returns the stream information each subcommand.
|
2991
|
+
#
|
2992
|
+
# @example stream
|
2993
|
+
# redis.xinfo(:stream, 'mystream')
|
2994
|
+
# @example groups
|
2995
|
+
# redis.xinfo(:groups, 'mystream')
|
2996
|
+
# @example consumers
|
2997
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
2998
|
+
#
|
2999
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
3000
|
+
# @param key [String] the stream key
|
3001
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
3002
|
+
#
|
3003
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
3004
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
3005
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
3006
|
+
def xinfo(subcommand, key, group = nil)
|
3007
|
+
args = [:xinfo, subcommand, key, group].compact
|
3008
|
+
synchronize do |client|
|
3009
|
+
client.call(args) do |reply|
|
3010
|
+
case subcommand.to_s.downcase
|
3011
|
+
when 'stream' then Hashify.call(reply)
|
3012
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
3013
|
+
else reply
|
3014
|
+
end
|
3015
|
+
end
|
3016
|
+
end
|
3017
|
+
end
|
3018
|
+
|
3019
|
+
# Add new entry to the stream.
|
3020
|
+
#
|
3021
|
+
# @example Without options
|
3022
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
3023
|
+
# @example With options
|
3024
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
3025
|
+
#
|
3026
|
+
# @param key [String] the stream key
|
3027
|
+
# @param entry [Hash] one or multiple field-value pairs
|
3028
|
+
# @param opts [Hash] several options for `XADD` command
|
3029
|
+
#
|
3030
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
3031
|
+
# @option opts [Integer] :maxlen max length of entries
|
3032
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
3033
|
+
#
|
3034
|
+
# @return [String] the entry id
|
3035
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
3036
|
+
args = [:xadd, key]
|
3037
|
+
if maxlen
|
3038
|
+
args << "MAXLEN"
|
3039
|
+
args << "~" if approximate
|
3040
|
+
args << maxlen
|
3041
|
+
end
|
3042
|
+
args << id
|
3043
|
+
args.concat(entry.to_a.flatten)
|
3044
|
+
synchronize { |client| client.call(args) }
|
3045
|
+
end
|
3046
|
+
|
3047
|
+
# Trims older entries of the stream if needed.
|
3048
|
+
#
|
3049
|
+
# @example Without options
|
3050
|
+
# redis.xtrim('mystream', 1000)
|
3051
|
+
# @example With options
|
3052
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
3053
|
+
#
|
3054
|
+
# @param key [String] the stream key
|
3055
|
+
# @param mexlen [Integer] max length of entries
|
3056
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
3057
|
+
#
|
3058
|
+
# @return [Integer] the number of entries actually deleted
|
3059
|
+
def xtrim(key, maxlen, approximate: false)
|
3060
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
3061
|
+
synchronize { |client| client.call(args) }
|
3062
|
+
end
|
3063
|
+
|
3064
|
+
# Delete entries by entry ids.
|
3065
|
+
#
|
3066
|
+
# @example With splatted entry ids
|
3067
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
3068
|
+
# @example With arrayed entry ids
|
3069
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
3070
|
+
#
|
3071
|
+
# @param key [String] the stream key
|
3072
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3073
|
+
#
|
3074
|
+
# @return [Integer] the number of entries actually deleted
|
3075
|
+
def xdel(key, *ids)
|
3076
|
+
args = [:xdel, key].concat(ids.flatten)
|
3077
|
+
synchronize { |client| client.call(args) }
|
3078
|
+
end
|
3079
|
+
|
3080
|
+
# Fetches entries of the stream in ascending order.
|
3081
|
+
#
|
3082
|
+
# @example Without options
|
3083
|
+
# redis.xrange('mystream')
|
3084
|
+
# @example With a specific start
|
3085
|
+
# redis.xrange('mystream', '0-1')
|
3086
|
+
# @example With a specific start and end
|
3087
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
3088
|
+
# @example With count options
|
3089
|
+
# redis.xrange('mystream', count: 10)
|
3090
|
+
#
|
3091
|
+
# @param key [String] the stream key
|
3092
|
+
# @param start [String] first entry id of range, default value is `-`
|
3093
|
+
# @param end [String] last entry id of range, default value is `+`
|
3094
|
+
# @param count [Integer] the number of entries as limit
|
3095
|
+
#
|
3096
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3097
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3098
|
+
args = [:xrange, key, start, range_end]
|
3099
|
+
args.concat(['COUNT', count]) if count
|
3100
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3101
|
+
end
|
3102
|
+
|
3103
|
+
# Fetches entries of the stream in descending order.
|
3104
|
+
#
|
3105
|
+
# @example Without options
|
3106
|
+
# redis.xrevrange('mystream')
|
3107
|
+
# @example With a specific end
|
3108
|
+
# redis.xrevrange('mystream', '0-3')
|
3109
|
+
# @example With a specific end and start
|
3110
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
3111
|
+
# @example With count options
|
3112
|
+
# redis.xrevrange('mystream', count: 10)
|
3113
|
+
#
|
3114
|
+
# @param key [String] the stream key
|
3115
|
+
# @param end [String] first entry id of range, default value is `+`
|
3116
|
+
# @param start [String] last entry id of range, default value is `-`
|
3117
|
+
# @params count [Integer] the number of entries as limit
|
3118
|
+
#
|
3119
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3120
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3121
|
+
args = [:xrevrange, key, range_end, start]
|
3122
|
+
args.concat(['COUNT', count]) if count
|
3123
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3124
|
+
end
|
3125
|
+
|
3126
|
+
# Returns the number of entries inside a stream.
|
3127
|
+
#
|
3128
|
+
# @example With key
|
3129
|
+
# redis.xlen('mystream')
|
3130
|
+
#
|
3131
|
+
# @param key [String] the stream key
|
3132
|
+
#
|
3133
|
+
# @return [Integer] the number of entries
|
3134
|
+
def xlen(key)
|
3135
|
+
synchronize { |client| client.call([:xlen, key]) }
|
3136
|
+
end
|
3137
|
+
|
3138
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
3139
|
+
#
|
3140
|
+
# @example With a key
|
3141
|
+
# redis.xread('mystream', '0-0')
|
3142
|
+
# @example With multiple keys
|
3143
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3144
|
+
# @example With count option
|
3145
|
+
# redis.xread('mystream', '0-0', count: 2)
|
3146
|
+
# @example With block option
|
3147
|
+
# redis.xread('mystream', '$', block: 1000)
|
3148
|
+
#
|
3149
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3150
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3151
|
+
# @param count [Integer] the number of entries as limit per stream
|
3152
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
3153
|
+
#
|
3154
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3155
|
+
def xread(keys, ids, count: nil, block: nil)
|
3156
|
+
args = [:xread]
|
3157
|
+
args << 'COUNT' << count if count
|
3158
|
+
args << 'BLOCK' << block.to_i if block
|
3159
|
+
_xread(args, keys, ids, block)
|
3160
|
+
end
|
3161
|
+
|
3162
|
+
# Manages the consumer group of the stream.
|
3163
|
+
#
|
3164
|
+
# @example With `create` subcommand
|
3165
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3166
|
+
# @example With `setid` subcommand
|
3167
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3168
|
+
# @example With `destroy` subcommand
|
3169
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3170
|
+
# @example With `delconsumer` subcommand
|
3171
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3172
|
+
#
|
3173
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3174
|
+
# @param key [String] the stream key
|
3175
|
+
# @param group [String] the consumer group name
|
3176
|
+
# @param id_or_consumer [String]
|
3177
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3178
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
3179
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3180
|
+
#
|
3181
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
3182
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3183
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3184
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3185
|
+
synchronize { |client| client.call(args) }
|
3186
|
+
end
|
3187
|
+
|
3188
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3189
|
+
# Optionally blocking.
|
3190
|
+
#
|
3191
|
+
# @example With a key
|
3192
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3193
|
+
# @example With multiple keys
|
3194
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3195
|
+
# @example With count option
|
3196
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3197
|
+
# @example With block option
|
3198
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3199
|
+
# @example With noack option
|
3200
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3201
|
+
#
|
3202
|
+
# @param group [String] the consumer group name
|
3203
|
+
# @param consumer [String] the consumer name
|
3204
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3205
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3206
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
3207
|
+
#
|
3208
|
+
# @option opts [Integer] :count the number of entries as limit
|
3209
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3210
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3211
|
+
#
|
3212
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3213
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3214
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
3215
|
+
args << 'COUNT' << count if count
|
3216
|
+
args << 'BLOCK' << block.to_i if block
|
3217
|
+
args << 'NOACK' if noack
|
3218
|
+
_xread(args, keys, ids, block)
|
3219
|
+
end
|
3220
|
+
|
3221
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3222
|
+
#
|
3223
|
+
# @example With a entry id
|
3224
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3225
|
+
# @example With splatted entry ids
|
3226
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3227
|
+
# @example With arrayed entry ids
|
3228
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3229
|
+
#
|
3230
|
+
# @param key [String] the stream key
|
3231
|
+
# @param group [String] the consumer group name
|
3232
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3233
|
+
#
|
3234
|
+
# @return [Integer] the number of entries successfully acknowledged
|
3235
|
+
def xack(key, group, *ids)
|
3236
|
+
args = [:xack, key, group].concat(ids.flatten)
|
3237
|
+
synchronize { |client| client.call(args) }
|
3238
|
+
end
|
3239
|
+
|
3240
|
+
# Changes the ownership of a pending entry
|
3241
|
+
#
|
3242
|
+
# @example With splatted entry ids
|
3243
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3244
|
+
# @example With arrayed entry ids
|
3245
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3246
|
+
# @example With idle option
|
3247
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3248
|
+
# @example With time option
|
3249
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3250
|
+
# @example With retrycount option
|
3251
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3252
|
+
# @example With force option
|
3253
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3254
|
+
# @example With justid option
|
3255
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3256
|
+
#
|
3257
|
+
# @param key [String] the stream key
|
3258
|
+
# @param group [String] the consumer group name
|
3259
|
+
# @param consumer [String] the consumer name
|
3260
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3261
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3262
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
3263
|
+
#
|
3264
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3265
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3266
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
3267
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3268
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3269
|
+
#
|
3270
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3271
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3272
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3273
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3274
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3275
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3276
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3277
|
+
args << 'FORCE' if opts[:force]
|
3278
|
+
args << 'JUSTID' if opts[:justid]
|
3279
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3280
|
+
synchronize { |client| client.call(args, &blk) }
|
3281
|
+
end
|
3282
|
+
|
3283
|
+
# Transfers ownership of pending stream entries that match the specified criteria.
|
3284
|
+
#
|
3285
|
+
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
3286
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
3287
|
+
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
3288
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
3289
|
+
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
3290
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
3291
|
+
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
3292
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
3293
|
+
#
|
3294
|
+
# @param key [String] the stream key
|
3295
|
+
# @param group [String] the consumer group name
|
3296
|
+
# @param consumer [String] the consumer name
|
3297
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3298
|
+
# @param start [String] entry id to start scanning from or 0-0 for everything
|
3299
|
+
# @param count [Integer] number of messages to claim (default 1)
|
3300
|
+
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
3301
|
+
# Does not increment retry count when true
|
3302
|
+
#
|
3303
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3304
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3305
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
3306
|
+
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
3307
|
+
if count
|
3308
|
+
args << 'COUNT' << count.to_s
|
3309
|
+
end
|
3310
|
+
args << 'JUSTID' if justid
|
3311
|
+
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
3312
|
+
synchronize { |client| client.call(args, &blk) }
|
3313
|
+
end
|
3314
|
+
|
3315
|
+
# Fetches not acknowledging pending entries
|
3316
|
+
#
|
3317
|
+
# @example With key and group
|
3318
|
+
# redis.xpending('mystream', 'mygroup')
|
3319
|
+
# @example With range options
|
3320
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3321
|
+
# @example With range and consumer options
|
3322
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3323
|
+
#
|
3324
|
+
# @param key [String] the stream key
|
3325
|
+
# @param group [String] the consumer group name
|
3326
|
+
# @param start [String] start first entry id of range
|
3327
|
+
# @param end [String] end last entry id of range
|
3328
|
+
# @param count [Integer] count the number of entries as limit
|
3329
|
+
# @param consumer [String] the consumer name
|
3330
|
+
#
|
3331
|
+
# @return [Hash] the summary of pending entries
|
3332
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
3333
|
+
def xpending(key, group, *args)
|
3334
|
+
command_args = [:xpending, key, group]
|
3335
|
+
case args.size
|
3336
|
+
when 0, 3, 4
|
3337
|
+
command_args.concat(args)
|
3338
|
+
else
|
3339
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3340
|
+
end
|
3341
|
+
|
3342
|
+
summary_needed = args.empty?
|
3343
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3344
|
+
synchronize { |client| client.call(command_args, &blk) }
|
3345
|
+
end
|
3346
|
+
|
2670
3347
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2671
3348
|
#
|
2672
3349
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2680,8 +3357,8 @@ class Redis
|
|
2680
3357
|
when "get-master-addr-by-name"
|
2681
3358
|
reply
|
2682
3359
|
else
|
2683
|
-
if reply.
|
2684
|
-
if reply[0].
|
3360
|
+
if reply.is_a?(Array)
|
3361
|
+
if reply[0].is_a?(Array)
|
2685
3362
|
reply.map(&Hashify)
|
2686
3363
|
else
|
2687
3364
|
Hashify.call(reply)
|
@@ -2694,6 +3371,46 @@ class Redis
|
|
2694
3371
|
end
|
2695
3372
|
end
|
2696
3373
|
|
3374
|
+
# Sends `CLUSTER *` command to random node and returns its reply.
|
3375
|
+
#
|
3376
|
+
# @see https://redis.io/commands#cluster Reference of cluster command
|
3377
|
+
#
|
3378
|
+
# @param subcommand [String, Symbol] the subcommand of cluster command
|
3379
|
+
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
3380
|
+
#
|
3381
|
+
# @return [Object] depends on the subcommand
|
3382
|
+
def cluster(subcommand, *args)
|
3383
|
+
subcommand = subcommand.to_s.downcase
|
3384
|
+
block = case subcommand
|
3385
|
+
when 'slots'
|
3386
|
+
HashifyClusterSlots
|
3387
|
+
when 'nodes'
|
3388
|
+
HashifyClusterNodes
|
3389
|
+
when 'slaves'
|
3390
|
+
HashifyClusterSlaves
|
3391
|
+
when 'info'
|
3392
|
+
HashifyInfo
|
3393
|
+
else
|
3394
|
+
Noop
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3398
|
+
block = Noop unless @cluster_mode
|
3399
|
+
|
3400
|
+
synchronize do |client|
|
3401
|
+
client.call([:cluster, subcommand] + args, &block)
|
3402
|
+
end
|
3403
|
+
end
|
3404
|
+
|
3405
|
+
# Sends `ASKING` command to random node and returns its reply.
|
3406
|
+
#
|
3407
|
+
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
3408
|
+
#
|
3409
|
+
# @return [String] `'OK'`
|
3410
|
+
def asking
|
3411
|
+
synchronize { |client| client.call(%i[asking]) }
|
3412
|
+
end
|
3413
|
+
|
2697
3414
|
def id
|
2698
3415
|
@original_client.id
|
2699
3416
|
end
|
@@ -2706,59 +3423,184 @@ class Redis
|
|
2706
3423
|
self.class.new(@options)
|
2707
3424
|
end
|
2708
3425
|
|
2709
|
-
def
|
3426
|
+
def connection
|
3427
|
+
return @original_client.connection_info if @cluster_mode
|
3428
|
+
|
3429
|
+
{
|
3430
|
+
host: @original_client.host,
|
3431
|
+
port: @original_client.port,
|
3432
|
+
db: @original_client.db,
|
3433
|
+
id: @original_client.id,
|
3434
|
+
location: @original_client.location
|
3435
|
+
}
|
3436
|
+
end
|
3437
|
+
|
3438
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
2710
3439
|
synchronize do |client|
|
2711
3440
|
client.call([command] + args)
|
2712
3441
|
end
|
2713
3442
|
end
|
2714
3443
|
|
2715
|
-
private
|
3444
|
+
private
|
2716
3445
|
|
2717
3446
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2718
3447
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2719
3448
|
# returning false.
|
2720
|
-
Boolify =
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
3449
|
+
Boolify = lambda { |value|
|
3450
|
+
case value
|
3451
|
+
when 1
|
3452
|
+
true
|
3453
|
+
when 0
|
3454
|
+
false
|
3455
|
+
else
|
3456
|
+
value
|
3457
|
+
end
|
3458
|
+
}
|
2724
3459
|
|
2725
|
-
BoolifySet =
|
2726
|
-
|
2727
|
-
|
2728
|
-
|
2729
|
-
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
3460
|
+
BoolifySet = lambda { |value|
|
3461
|
+
case value
|
3462
|
+
when "OK"
|
3463
|
+
true
|
3464
|
+
when nil
|
3465
|
+
false
|
3466
|
+
else
|
3467
|
+
value
|
3468
|
+
end
|
3469
|
+
}
|
2733
3470
|
|
2734
|
-
Hashify =
|
2735
|
-
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
3471
|
+
Hashify = lambda { |value|
|
3472
|
+
if value.respond_to?(:each_slice)
|
3473
|
+
value.each_slice(2).to_h
|
3474
|
+
else
|
3475
|
+
value
|
3476
|
+
end
|
3477
|
+
}
|
3478
|
+
|
3479
|
+
Floatify = lambda { |value|
|
3480
|
+
case value
|
3481
|
+
when "inf"
|
3482
|
+
Float::INFINITY
|
3483
|
+
when "-inf"
|
3484
|
+
-Float::INFINITY
|
3485
|
+
when String
|
3486
|
+
Float(value)
|
3487
|
+
else
|
3488
|
+
value
|
3489
|
+
end
|
3490
|
+
}
|
2742
3491
|
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
2746
|
-
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
3492
|
+
FloatifyPairs = lambda { |value|
|
3493
|
+
return value unless value.respond_to?(:each_slice)
|
3494
|
+
|
3495
|
+
value.each_slice(2).map do |member, score|
|
3496
|
+
[member, Floatify.call(score)]
|
3497
|
+
end
|
3498
|
+
}
|
3499
|
+
|
3500
|
+
HashifyInfo = lambda { |reply|
|
3501
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3502
|
+
lines.map! { |line| line.split(':', 2) }
|
3503
|
+
lines.compact!
|
3504
|
+
lines.to_h
|
3505
|
+
}
|
3506
|
+
|
3507
|
+
HashifyStreams = lambda { |reply|
|
3508
|
+
case reply
|
3509
|
+
when nil
|
3510
|
+
{}
|
3511
|
+
else
|
3512
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3513
|
+
end
|
3514
|
+
}
|
3515
|
+
|
3516
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3517
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3518
|
+
|
3519
|
+
HashifyStreamEntries = lambda { |reply|
|
3520
|
+
reply.compact.map do |entry_id, values|
|
3521
|
+
[entry_id, values.each_slice(2).to_h]
|
3522
|
+
end
|
3523
|
+
}
|
3524
|
+
|
3525
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
3526
|
+
{
|
3527
|
+
'next' => reply[0],
|
3528
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
2752
3529
|
}
|
3530
|
+
}
|
2753
3531
|
|
2754
|
-
|
2755
|
-
|
2756
|
-
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
3532
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3533
|
+
{
|
3534
|
+
'next' => reply[0],
|
3535
|
+
'entries' => reply[1]
|
3536
|
+
}
|
3537
|
+
}
|
3538
|
+
|
3539
|
+
HashifyStreamPendings = lambda { |reply|
|
3540
|
+
{
|
3541
|
+
'size' => reply[0],
|
3542
|
+
'min_entry_id' => reply[1],
|
3543
|
+
'max_entry_id' => reply[2],
|
3544
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
2761
3545
|
}
|
3546
|
+
}
|
3547
|
+
|
3548
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3549
|
+
reply.map do |arr|
|
3550
|
+
{
|
3551
|
+
'entry_id' => arr[0],
|
3552
|
+
'consumer' => arr[1],
|
3553
|
+
'elapsed' => arr[2],
|
3554
|
+
'count' => arr[3]
|
3555
|
+
}
|
3556
|
+
end
|
3557
|
+
}
|
3558
|
+
|
3559
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3560
|
+
arr = str.split(' ')
|
3561
|
+
{
|
3562
|
+
'node_id' => arr[0],
|
3563
|
+
'ip_port' => arr[1],
|
3564
|
+
'flags' => arr[2].split(','),
|
3565
|
+
'master_node_id' => arr[3],
|
3566
|
+
'ping_sent' => arr[4],
|
3567
|
+
'pong_recv' => arr[5],
|
3568
|
+
'config_epoch' => arr[6],
|
3569
|
+
'link_state' => arr[7],
|
3570
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3571
|
+
}
|
3572
|
+
}
|
3573
|
+
|
3574
|
+
HashifyClusterSlots = lambda { |reply|
|
3575
|
+
reply.map do |arr|
|
3576
|
+
first_slot, last_slot = arr[0..1]
|
3577
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3578
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3579
|
+
{
|
3580
|
+
'start_slot' => first_slot,
|
3581
|
+
'end_slot' => last_slot,
|
3582
|
+
'master' => master,
|
3583
|
+
'replicas' => replicas
|
3584
|
+
}
|
3585
|
+
end
|
3586
|
+
}
|
3587
|
+
|
3588
|
+
HashifyClusterNodes = lambda { |reply|
|
3589
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3590
|
+
}
|
3591
|
+
|
3592
|
+
HashifyClusterSlaves = lambda { |reply|
|
3593
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3594
|
+
}
|
3595
|
+
|
3596
|
+
Noop = ->(reply) { reply }
|
3597
|
+
|
3598
|
+
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
3599
|
+
args.push sort if sort
|
3600
|
+
args.push 'count', count if count
|
3601
|
+
args.push options if options
|
3602
|
+
args
|
3603
|
+
end
|
2762
3604
|
|
2763
3605
|
def _subscription(method, timeout, channels, block)
|
2764
3606
|
return @client.call([method] + channels) if subscribed?
|
@@ -2775,10 +3617,29 @@ private
|
|
2775
3617
|
end
|
2776
3618
|
end
|
2777
3619
|
|
3620
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
3621
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
3622
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
3623
|
+
args << 'STREAMS'
|
3624
|
+
args.concat(keys)
|
3625
|
+
args.concat(ids)
|
3626
|
+
|
3627
|
+
synchronize do |client|
|
3628
|
+
if blocking_timeout_msec.nil?
|
3629
|
+
client.call(args, &HashifyStreams)
|
3630
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3631
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3632
|
+
else
|
3633
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3634
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3635
|
+
end
|
3636
|
+
end
|
3637
|
+
end
|
2778
3638
|
end
|
2779
3639
|
|
2780
3640
|
require_relative "redis/version"
|
2781
3641
|
require_relative "redis/connection"
|
2782
3642
|
require_relative "redis/client"
|
3643
|
+
require_relative "redis/cluster"
|
2783
3644
|
require_relative "redis/pipeline"
|
2784
3645
|
require_relative "redis/subscribe"
|