redis 4.0.3 → 4.5.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 +110 -0
- data/README.md +126 -17
- data/lib/redis/client.rb +130 -82
- data/lib/redis/cluster/command_loader.rb +8 -7
- data/lib/redis/cluster/node.rb +5 -1
- data/lib/redis/cluster/node_key.rb +3 -7
- data/lib/redis/cluster/node_loader.rb +2 -0
- data/lib/redis/cluster/option.rb +31 -14
- data/lib/redis/cluster/slot.rb +30 -13
- data/lib/redis/cluster/slot_loader.rb +6 -4
- data/lib/redis/cluster.rb +23 -17
- data/lib/redis/connection/command_helper.rb +5 -2
- data/lib/redis/connection/hiredis.rb +4 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +139 -106
- data/lib/redis/connection/synchrony.rb +9 -4
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/distributed.rb +171 -70
- data/lib/redis/errors.rb +2 -0
- data/lib/redis/hash_ring.rb +15 -14
- data/lib/redis/pipeline.rb +46 -8
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +1239 -426
- metadata +16 -262
- data/.gitignore +0 -19
- data/.travis/Gemfile +0 -18
- data/.travis.yml +0 -61
- data/.yardopts +0 -3
- data/Gemfile +0 -8
- 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/bin/build +0 -71
- data/bors.toml +0 -14
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- 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 -74
- data/redis.gemspec +0 -43
- data/test/bitpos_test.rb +0 -63
- data/test/blocking_commands_test.rb +0 -40
- data/test/client_test.rb +0 -76
- data/test/cluster_abnormal_state_test.rb +0 -38
- data/test/cluster_blocking_commands_test.rb +0 -15
- data/test/cluster_client_internals_test.rb +0 -77
- data/test/cluster_client_key_hash_tags_test.rb +0 -88
- data/test/cluster_client_options_test.rb +0 -147
- data/test/cluster_client_pipelining_test.rb +0 -59
- data/test/cluster_client_replicas_test.rb +0 -36
- data/test/cluster_client_slots_test.rb +0 -94
- data/test/cluster_client_transactions_test.rb +0 -71
- data/test/cluster_commands_on_cluster_test.rb +0 -165
- data/test/cluster_commands_on_connection_test.rb +0 -40
- data/test/cluster_commands_on_geo_test.rb +0 -74
- data/test/cluster_commands_on_hashes_test.rb +0 -11
- data/test/cluster_commands_on_hyper_log_log_test.rb +0 -17
- data/test/cluster_commands_on_keys_test.rb +0 -134
- data/test/cluster_commands_on_lists_test.rb +0 -15
- data/test/cluster_commands_on_pub_sub_test.rb +0 -101
- data/test/cluster_commands_on_scripting_test.rb +0 -56
- data/test/cluster_commands_on_server_test.rb +0 -221
- data/test/cluster_commands_on_sets_test.rb +0 -39
- data/test/cluster_commands_on_sorted_sets_test.rb +0 -35
- data/test/cluster_commands_on_streams_test.rb +0 -196
- data/test/cluster_commands_on_strings_test.rb +0 -15
- data/test/cluster_commands_on_transactions_test.rb +0 -41
- data/test/cluster_commands_on_value_types_test.rb +0 -14
- data/test/command_map_test.rb +0 -28
- data/test/commands_on_geo_test.rb +0 -116
- data/test/commands_on_hashes_test.rb +0 -7
- data/test/commands_on_hyper_log_log_test.rb +0 -7
- data/test/commands_on_lists_test.rb +0 -7
- data/test/commands_on_sets_test.rb +0 -7
- data/test/commands_on_sorted_sets_test.rb +0 -7
- data/test/commands_on_strings_test.rb +0 -7
- data/test/commands_on_value_types_test.rb +0 -207
- data/test/connection_handling_test.rb +0 -275
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -52
- data/test/distributed_commands_on_hashes_test.rb +0 -21
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -26
- data/test/distributed_commands_on_lists_test.rb +0 -19
- data/test/distributed_commands_on_sets_test.rb +0 -105
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -59
- data/test/distributed_commands_on_strings_test.rb +0 -79
- data/test/distributed_commands_on_value_types_test.rb +0 -129
- data/test/distributed_commands_requiring_clustering_test.rb +0 -162
- data/test/distributed_connection_handling_test.rb +0 -21
- data/test/distributed_internals_test.rb +0 -68
- data/test/distributed_key_tags_test.rb +0 -50
- data/test/distributed_persistence_control_commands_test.rb +0 -24
- data/test/distributed_publish_subscribe_test.rb +0 -90
- data/test/distributed_remote_server_control_commands_test.rb +0 -64
- data/test/distributed_scripting_test.rb +0 -100
- data/test/distributed_sorting_test.rb +0 -18
- data/test/distributed_test.rb +0 -56
- data/test/distributed_transactions_test.rb +0 -30
- 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 -345
- data/test/helper_test.rb +0 -22
- data/test/internals_test.rb +0 -408
- data/test/lint/blocking_commands.rb +0 -174
- data/test/lint/hashes.rb +0 -203
- data/test/lint/hyper_log_log.rb +0 -74
- data/test/lint/lists.rb +0 -159
- data/test/lint/sets.rb +0 -282
- data/test/lint/sorted_sets.rb +0 -497
- data/test/lint/strings.rb +0 -348
- data/test/lint/value_types.rb +0 -130
- data/test/persistence_control_commands_test.rb +0 -24
- data/test/pipelining_commands_test.rb +0 -246
- 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/cluster/orchestrator.rb +0 -199
- 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 -272
- data/test/unknown_commands_test.rb +0 -12
- data/test/url_param_test.rb +0 -136
data/lib/redis.rb
CHANGED
@@ -1,14 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
4
|
require_relative "redis/errors"
|
3
5
|
|
4
6
|
class Redis
|
7
|
+
@exists_returns_integer = true
|
5
8
|
|
6
|
-
|
7
|
-
|
9
|
+
class << self
|
10
|
+
attr_reader :exists_returns_integer
|
11
|
+
|
12
|
+
def exists_returns_integer=(value)
|
13
|
+
unless value
|
14
|
+
message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
15
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
16
|
+
"`exists?` instead."
|
17
|
+
|
18
|
+
::Kernel.warn(message)
|
19
|
+
end
|
20
|
+
|
21
|
+
@exists_returns_integer = value
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_writer :current
|
8
25
|
end
|
9
26
|
|
10
|
-
def self.current
|
11
|
-
@current
|
27
|
+
def self.current
|
28
|
+
@current ||= Redis.new
|
12
29
|
end
|
13
30
|
|
14
31
|
include MonitorMixin
|
@@ -16,18 +33,23 @@ class Redis
|
|
16
33
|
# Create a new client instance
|
17
34
|
#
|
18
35
|
# @param [Hash] options
|
19
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
36
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
37
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
38
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
20
39
|
# @option options [String] :host ("127.0.0.1") server hostname
|
21
|
-
# @option options [
|
40
|
+
# @option options [Integer] :port (6379) server port
|
22
41
|
# @option options [String] :path path to server socket (overrides host and port)
|
23
42
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
24
43
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
44
|
+
# @option options [String] :username Username to authenticate against server
|
25
45
|
# @option options [String] :password Password to authenticate against server
|
26
|
-
# @option options [
|
46
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
27
47
|
# @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 [
|
48
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
49
|
+
# `CLIENT SETNAME`
|
50
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
51
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
52
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
31
53
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
32
54
|
# @option options [Array] :sentinels List of sentinels to contact
|
33
55
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
@@ -51,7 +73,7 @@ class Redis
|
|
51
73
|
end
|
52
74
|
|
53
75
|
# Run code with the client reconnecting
|
54
|
-
def with_reconnect(val=true, &blk)
|
76
|
+
def with_reconnect(val = true, &blk)
|
55
77
|
synchronize do |client|
|
56
78
|
client.with_reconnect(val, &blk)
|
57
79
|
end
|
@@ -94,7 +116,9 @@ class Redis
|
|
94
116
|
# See http://redis.io/topics/pipelining for more details.
|
95
117
|
#
|
96
118
|
def queue(*command)
|
97
|
-
|
119
|
+
synchronize do
|
120
|
+
@queue[Thread.current.object_id] << command
|
121
|
+
end
|
98
122
|
end
|
99
123
|
|
100
124
|
# Sends all commands in the queue.
|
@@ -104,7 +128,12 @@ class Redis
|
|
104
128
|
def commit
|
105
129
|
synchronize do |client|
|
106
130
|
begin
|
107
|
-
|
131
|
+
pipeline = Pipeline.new(client)
|
132
|
+
@queue[Thread.current.object_id].each do |command|
|
133
|
+
pipeline.call(command)
|
134
|
+
end
|
135
|
+
|
136
|
+
client.call_pipelined(pipeline)
|
108
137
|
ensure
|
109
138
|
@queue.delete(Thread.current.object_id)
|
110
139
|
end
|
@@ -117,18 +146,19 @@ class Redis
|
|
117
146
|
|
118
147
|
# Authenticate to the server.
|
119
148
|
#
|
120
|
-
# @param [String]
|
121
|
-
#
|
149
|
+
# @param [Array<String>] args includes both username and password
|
150
|
+
# or only password
|
122
151
|
# @return [String] `OK`
|
123
|
-
|
152
|
+
# @see https://redis.io/commands/auth AUTH command
|
153
|
+
def auth(*args)
|
124
154
|
synchronize do |client|
|
125
|
-
client.call([:auth,
|
155
|
+
client.call([:auth, *args])
|
126
156
|
end
|
127
157
|
end
|
128
158
|
|
129
159
|
# Change the selected database for the current connection.
|
130
160
|
#
|
131
|
-
# @param [
|
161
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
132
162
|
# @return [String] `OK`
|
133
163
|
def select(db)
|
134
164
|
synchronize do |client|
|
@@ -197,7 +227,7 @@ class Redis
|
|
197
227
|
def config(action, *args)
|
198
228
|
synchronize do |client|
|
199
229
|
client.call([:config, action] + args) do |reply|
|
200
|
-
if reply.
|
230
|
+
if reply.is_a?(Array) && action == :get
|
201
231
|
Hashify.call(reply)
|
202
232
|
else
|
203
233
|
reply
|
@@ -227,7 +257,7 @@ class Redis
|
|
227
257
|
|
228
258
|
# Return the number of keys in the selected database.
|
229
259
|
#
|
230
|
-
# @return [
|
260
|
+
# @return [Integer]
|
231
261
|
def dbsize
|
232
262
|
synchronize do |client|
|
233
263
|
client.call([:dbsize])
|
@@ -248,7 +278,7 @@ class Redis
|
|
248
278
|
def flushall(options = nil)
|
249
279
|
synchronize do |client|
|
250
280
|
if options && options[:async]
|
251
|
-
client.call([
|
281
|
+
client.call(%i[flushall async])
|
252
282
|
else
|
253
283
|
client.call([:flushall])
|
254
284
|
end
|
@@ -263,7 +293,7 @@ class Redis
|
|
263
293
|
def flushdb(options = nil)
|
264
294
|
synchronize do |client|
|
265
295
|
if options && options[:async]
|
266
|
-
client.call([
|
296
|
+
client.call(%i[flushdb async])
|
267
297
|
else
|
268
298
|
client.call([:flushdb])
|
269
299
|
end
|
@@ -277,7 +307,7 @@ class Redis
|
|
277
307
|
def info(cmd = nil)
|
278
308
|
synchronize do |client|
|
279
309
|
client.call([:info, cmd].compact) do |reply|
|
280
|
-
if reply.
|
310
|
+
if reply.is_a?(String)
|
281
311
|
reply = HashifyInfo.call(reply)
|
282
312
|
|
283
313
|
if cmd && cmd.to_s == "commandstats"
|
@@ -296,7 +326,7 @@ class Redis
|
|
296
326
|
|
297
327
|
# Get the UNIX time stamp of the last successful save to disk.
|
298
328
|
#
|
299
|
-
# @return [
|
329
|
+
# @return [Integer]
|
300
330
|
def lastsave
|
301
331
|
synchronize do |client|
|
302
332
|
client.call([:lastsave])
|
@@ -348,9 +378,9 @@ class Redis
|
|
348
378
|
# Interact with the slowlog (get, len, reset)
|
349
379
|
#
|
350
380
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
351
|
-
# @param [
|
352
|
-
# @return [Array<String>,
|
353
|
-
def slowlog(subcommand, length=nil)
|
381
|
+
# @param [Integer] length maximum number of entries to return
|
382
|
+
# @return [Array<String>, Integer, String] depends on subcommand
|
383
|
+
def slowlog(subcommand, length = nil)
|
354
384
|
synchronize do |client|
|
355
385
|
args = [:slowlog, subcommand]
|
356
386
|
args << length if length
|
@@ -370,12 +400,12 @@ class Redis
|
|
370
400
|
# @example
|
371
401
|
# r.time # => [ 1333093196, 606806 ]
|
372
402
|
#
|
373
|
-
# @return [Array<
|
403
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
374
404
|
# microseconds in the current second
|
375
405
|
def time
|
376
406
|
synchronize do |client|
|
377
407
|
client.call([:time]) do |reply|
|
378
|
-
reply
|
408
|
+
reply&.map(&:to_i)
|
379
409
|
end
|
380
410
|
end
|
381
411
|
end
|
@@ -393,7 +423,7 @@ class Redis
|
|
393
423
|
# Set a key's time to live in seconds.
|
394
424
|
#
|
395
425
|
# @param [String] key
|
396
|
-
# @param [
|
426
|
+
# @param [Integer] seconds time to live
|
397
427
|
# @return [Boolean] whether the timeout was set or not
|
398
428
|
def expire(key, seconds)
|
399
429
|
synchronize do |client|
|
@@ -404,7 +434,7 @@ class Redis
|
|
404
434
|
# Set the expiration for a key as a UNIX timestamp.
|
405
435
|
#
|
406
436
|
# @param [String] key
|
407
|
-
# @param [
|
437
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
408
438
|
# @return [Boolean] whether the timeout was set or not
|
409
439
|
def expireat(key, unix_time)
|
410
440
|
synchronize do |client|
|
@@ -415,7 +445,7 @@ class Redis
|
|
415
445
|
# Get the time to live (in seconds) for a key.
|
416
446
|
#
|
417
447
|
# @param [String] key
|
418
|
-
# @return [
|
448
|
+
# @return [Integer] remaining time to live in seconds.
|
419
449
|
#
|
420
450
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
421
451
|
# the key exist but has no associated expire.
|
@@ -433,7 +463,7 @@ class Redis
|
|
433
463
|
# Set a key's time to live in milliseconds.
|
434
464
|
#
|
435
465
|
# @param [String] key
|
436
|
-
# @param [
|
466
|
+
# @param [Integer] milliseconds time to live
|
437
467
|
# @return [Boolean] whether the timeout was set or not
|
438
468
|
def pexpire(key, milliseconds)
|
439
469
|
synchronize do |client|
|
@@ -444,7 +474,7 @@ class Redis
|
|
444
474
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
445
475
|
#
|
446
476
|
# @param [String] key
|
447
|
-
# @param [
|
477
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
448
478
|
# @return [Boolean] whether the timeout was set or not
|
449
479
|
def pexpireat(key, ms_unix_time)
|
450
480
|
synchronize do |client|
|
@@ -455,7 +485,7 @@ class Redis
|
|
455
485
|
# Get the time to live (in milliseconds) for a key.
|
456
486
|
#
|
457
487
|
# @param [String] key
|
458
|
-
# @return [
|
488
|
+
# @return [Integer] remaining time to live in milliseconds
|
459
489
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
460
490
|
# the key exist but has no associated expire.
|
461
491
|
#
|
@@ -488,9 +518,9 @@ class Redis
|
|
488
518
|
# - `:replace => Boolean`: if false, raises an error if key already exists
|
489
519
|
# @raise [Redis::CommandError]
|
490
520
|
# @return [String] `"OK"`
|
491
|
-
def restore(key, ttl, serialized_value,
|
521
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
492
522
|
args = [:restore, key, ttl, serialized_value]
|
493
|
-
args << 'REPLACE' if
|
523
|
+
args << 'REPLACE' if replace
|
494
524
|
|
495
525
|
synchronize do |client|
|
496
526
|
client.call(args)
|
@@ -499,29 +529,37 @@ class Redis
|
|
499
529
|
|
500
530
|
# Transfer a key from the connected instance to another instance.
|
501
531
|
#
|
502
|
-
# @param [String] key
|
532
|
+
# @param [String, Array<String>] key
|
503
533
|
# @param [Hash] options
|
504
534
|
# - `:host => String`: host of instance to migrate to
|
505
535
|
# - `:port => Integer`: port of instance to migrate to
|
506
536
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
507
537
|
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
538
|
+
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
539
|
+
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
508
540
|
# @return [String] `"OK"`
|
509
541
|
def migrate(key, options)
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
542
|
+
args = [:migrate]
|
543
|
+
args << (options[:host] || raise(':host not specified'))
|
544
|
+
args << (options[:port] || raise(':port not specified'))
|
545
|
+
args << (key.is_a?(String) ? key : '')
|
546
|
+
args << (options[:db] || @client.db).to_i
|
547
|
+
args << (options[:timeout] || @client.timeout).to_i
|
548
|
+
args << 'COPY' if options[:copy]
|
549
|
+
args << 'REPLACE' if options[:replace]
|
550
|
+
args += ['KEYS', *key] if key.is_a?(Array)
|
514
551
|
|
515
|
-
synchronize
|
516
|
-
client.call([:migrate, host, port, key, db, timeout])
|
517
|
-
end
|
552
|
+
synchronize { |client| client.call(args) }
|
518
553
|
end
|
519
554
|
|
520
555
|
# Delete one or more keys.
|
521
556
|
#
|
522
557
|
# @param [String, Array<String>] keys
|
523
|
-
# @return [
|
558
|
+
# @return [Integer] number of keys that were deleted
|
524
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
525
563
|
synchronize do |client|
|
526
564
|
client.call([:del] + keys)
|
527
565
|
end
|
@@ -530,20 +568,50 @@ class Redis
|
|
530
568
|
# Unlink one or more keys.
|
531
569
|
#
|
532
570
|
# @param [String, Array<String>] keys
|
533
|
-
# @return [
|
571
|
+
# @return [Integer] number of keys that were unlinked
|
534
572
|
def unlink(*keys)
|
535
573
|
synchronize do |client|
|
536
574
|
client.call([:unlink] + keys)
|
537
575
|
end
|
538
576
|
end
|
539
577
|
|
540
|
-
# Determine
|
578
|
+
# Determine how many of the keys exists.
|
541
579
|
#
|
542
|
-
# @param [String]
|
580
|
+
# @param [String, Array<String>] keys
|
581
|
+
# @return [Integer]
|
582
|
+
def exists(*keys)
|
583
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
584
|
+
if Redis.exists_returns_integer.nil?
|
585
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
586
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
587
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
588
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
589
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
590
|
+
|
591
|
+
::Kernel.warn(message)
|
592
|
+
end
|
593
|
+
|
594
|
+
exists?(*keys)
|
595
|
+
else
|
596
|
+
_exists(*keys)
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
def _exists(*keys)
|
601
|
+
synchronize do |client|
|
602
|
+
client.call([:exists, *keys])
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# Determine if any of the keys exists.
|
607
|
+
#
|
608
|
+
# @param [String, Array<String>] keys
|
543
609
|
# @return [Boolean]
|
544
|
-
def exists(
|
610
|
+
def exists?(*keys)
|
545
611
|
synchronize do |client|
|
546
|
-
client.call([:exists,
|
612
|
+
client.call([:exists, *keys]) do |value|
|
613
|
+
value > 0
|
614
|
+
end
|
547
615
|
end
|
548
616
|
end
|
549
617
|
|
@@ -554,7 +622,7 @@ class Redis
|
|
554
622
|
def keys(pattern = "*")
|
555
623
|
synchronize do |client|
|
556
624
|
client.call([:keys, pattern]) do |reply|
|
557
|
-
if reply.
|
625
|
+
if reply.is_a?(String)
|
558
626
|
reply.split(" ")
|
559
627
|
else
|
560
628
|
reply
|
@@ -580,7 +648,7 @@ class Redis
|
|
580
648
|
# # => "bar"
|
581
649
|
#
|
582
650
|
# @param [String] key
|
583
|
-
# @param [
|
651
|
+
# @param [Integer] db
|
584
652
|
# @return [Boolean] whether the key was moved or not
|
585
653
|
def move(key, db)
|
586
654
|
synchronize do |client|
|
@@ -644,36 +712,33 @@ class Redis
|
|
644
712
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
645
713
|
# - `:store => String`: key to store the result at
|
646
714
|
#
|
647
|
-
# @return [Array<String>, Array<Array<String>>,
|
715
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
648
716
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
649
717
|
# - when `:get` is specified, and holds more than one element, an array of
|
650
718
|
# elements where every element is an array with the result for every
|
651
719
|
# element specified in `:get`
|
652
720
|
# - when `:store` is specified, the number of elements in the stored result
|
653
|
-
def sort(key,
|
654
|
-
args = []
|
721
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
722
|
+
args = [:sort, key]
|
723
|
+
args << "BY" << by if by
|
655
724
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
args.concat(["LIMIT"] + limit) if limit
|
725
|
+
if limit
|
726
|
+
args << "LIMIT"
|
727
|
+
args.concat(limit)
|
728
|
+
end
|
661
729
|
|
662
|
-
get = Array(
|
663
|
-
|
730
|
+
get = Array(get)
|
731
|
+
get.each do |item|
|
732
|
+
args << "GET" << item
|
733
|
+
end
|
664
734
|
|
665
|
-
order = options[:order]
|
666
735
|
args.concat(order.split(" ")) if order
|
667
|
-
|
668
|
-
store = options[:store]
|
669
|
-
args.concat(["STORE", store]) if store
|
736
|
+
args << "STORE" << store if store
|
670
737
|
|
671
738
|
synchronize do |client|
|
672
|
-
client.call(
|
739
|
+
client.call(args) do |reply|
|
673
740
|
if get.size > 1 && !store
|
674
|
-
if reply
|
675
|
-
reply.each_slice(get.size).to_a
|
676
|
-
end
|
741
|
+
reply.each_slice(get.size).to_a if reply
|
677
742
|
else
|
678
743
|
reply
|
679
744
|
end
|
@@ -698,7 +763,7 @@ class Redis
|
|
698
763
|
# # => 4
|
699
764
|
#
|
700
765
|
# @param [String] key
|
701
|
-
# @return [
|
766
|
+
# @return [Integer] value after decrementing it
|
702
767
|
def decr(key)
|
703
768
|
synchronize do |client|
|
704
769
|
client.call([:decr, key])
|
@@ -712,8 +777,8 @@ class Redis
|
|
712
777
|
# # => 0
|
713
778
|
#
|
714
779
|
# @param [String] key
|
715
|
-
# @param [
|
716
|
-
# @return [
|
780
|
+
# @param [Integer] decrement
|
781
|
+
# @return [Integer] value after decrementing it
|
717
782
|
def decrby(key, decrement)
|
718
783
|
synchronize do |client|
|
719
784
|
client.call([:decrby, key, decrement])
|
@@ -727,7 +792,7 @@ class Redis
|
|
727
792
|
# # => 6
|
728
793
|
#
|
729
794
|
# @param [String] key
|
730
|
-
# @return [
|
795
|
+
# @return [Integer] value after incrementing it
|
731
796
|
def incr(key)
|
732
797
|
synchronize do |client|
|
733
798
|
client.call([:incr, key])
|
@@ -741,8 +806,8 @@ class Redis
|
|
741
806
|
# # => 10
|
742
807
|
#
|
743
808
|
# @param [String] key
|
744
|
-
# @param [
|
745
|
-
# @return [
|
809
|
+
# @param [Integer] increment
|
810
|
+
# @return [Integer] value after incrementing it
|
746
811
|
def incrby(key, increment)
|
747
812
|
synchronize do |client|
|
748
813
|
client.call([:incrby, key, increment])
|
@@ -769,31 +834,31 @@ class Redis
|
|
769
834
|
# @param [String] key
|
770
835
|
# @param [String] value
|
771
836
|
# @param [Hash] options
|
772
|
-
# - `:ex =>
|
773
|
-
# - `:px =>
|
837
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
838
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
839
|
+
# - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
|
840
|
+
# - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
|
774
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
775
842
|
# - `:xx => true`: Only set the key if it already exist.
|
843
|
+
# - `:keepttl => true`: Retain the time to live associated with the key.
|
844
|
+
# - `:get => true`: Return the old string stored at key, or nil if key did not exist.
|
776
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
777
|
-
def set(key, value,
|
778
|
-
args = []
|
779
|
-
|
780
|
-
|
781
|
-
args
|
782
|
-
|
783
|
-
|
784
|
-
args
|
785
|
-
|
786
|
-
|
787
|
-
args.concat(["NX"]) if nx
|
788
|
-
|
789
|
-
xx = options[:xx]
|
790
|
-
args.concat(["XX"]) if xx
|
846
|
+
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
847
|
+
args = [:set, key, value.to_s]
|
848
|
+
args << "EX" << ex if ex
|
849
|
+
args << "PX" << px if px
|
850
|
+
args << "EXAT" << exat if exat
|
851
|
+
args << "PXAT" << pxat if pxat
|
852
|
+
args << "NX" if nx
|
853
|
+
args << "XX" if xx
|
854
|
+
args << "KEEPTTL" if keepttl
|
855
|
+
args << "GET" if get
|
791
856
|
|
792
857
|
synchronize do |client|
|
793
858
|
if nx || xx
|
794
|
-
client.call(
|
859
|
+
client.call(args, &BoolifySet)
|
795
860
|
else
|
796
|
-
client.call(
|
861
|
+
client.call(args)
|
797
862
|
end
|
798
863
|
end
|
799
864
|
end
|
@@ -801,7 +866,7 @@ class Redis
|
|
801
866
|
# Set the time to live in seconds of a key.
|
802
867
|
#
|
803
868
|
# @param [String] key
|
804
|
-
# @param [
|
869
|
+
# @param [Integer] ttl
|
805
870
|
# @param [String] value
|
806
871
|
# @return [String] `"OK"`
|
807
872
|
def setex(key, ttl, value)
|
@@ -813,7 +878,7 @@ class Redis
|
|
813
878
|
# Set the time to live in milliseconds of a key.
|
814
879
|
#
|
815
880
|
# @param [String] key
|
816
|
-
# @param [
|
881
|
+
# @param [Integer] ttl
|
817
882
|
# @param [String] value
|
818
883
|
# @return [String] `"OK"`
|
819
884
|
def psetex(key, ttl, value)
|
@@ -875,7 +940,7 @@ class Redis
|
|
875
940
|
# @see #mapped_msetnx
|
876
941
|
def msetnx(*args)
|
877
942
|
synchronize do |client|
|
878
|
-
client.call([:msetnx
|
943
|
+
client.call([:msetnx, *args], &Boolify)
|
879
944
|
end
|
880
945
|
end
|
881
946
|
|
@@ -906,7 +971,7 @@ class Redis
|
|
906
971
|
# Get the values of all the given keys.
|
907
972
|
#
|
908
973
|
# @example
|
909
|
-
# redis.mget("key1", "
|
974
|
+
# redis.mget("key1", "key2")
|
910
975
|
# # => ["v1", "v2"]
|
911
976
|
#
|
912
977
|
# @param [Array<String>] keys
|
@@ -915,7 +980,7 @@ class Redis
|
|
915
980
|
# @see #mapped_mget
|
916
981
|
def mget(*keys, &blk)
|
917
982
|
synchronize do |client|
|
918
|
-
client.call([:mget
|
983
|
+
client.call([:mget, *keys], &blk)
|
919
984
|
end
|
920
985
|
end
|
921
986
|
|
@@ -931,7 +996,7 @@ class Redis
|
|
931
996
|
# @see #mget
|
932
997
|
def mapped_mget(*keys)
|
933
998
|
mget(*keys) do |reply|
|
934
|
-
if reply.
|
999
|
+
if reply.is_a?(Array)
|
935
1000
|
Hash[keys.zip(reply)]
|
936
1001
|
else
|
937
1002
|
reply
|
@@ -942,9 +1007,9 @@ class Redis
|
|
942
1007
|
# Overwrite part of a string at key starting at the specified offset.
|
943
1008
|
#
|
944
1009
|
# @param [String] key
|
945
|
-
# @param [
|
1010
|
+
# @param [Integer] offset byte offset
|
946
1011
|
# @param [String] value
|
947
|
-
# @return [
|
1012
|
+
# @return [Integer] length of the string after it was modified
|
948
1013
|
def setrange(key, offset, value)
|
949
1014
|
synchronize do |client|
|
950
1015
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -954,10 +1019,10 @@ class Redis
|
|
954
1019
|
# Get a substring of the string stored at a key.
|
955
1020
|
#
|
956
1021
|
# @param [String] key
|
957
|
-
# @param [
|
958
|
-
# @param [
|
1022
|
+
# @param [Integer] start zero-based start offset
|
1023
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
959
1024
|
# the end of the string
|
960
|
-
# @return [
|
1025
|
+
# @return [Integer] `0` or `1`
|
961
1026
|
def getrange(key, start, stop)
|
962
1027
|
synchronize do |client|
|
963
1028
|
client.call([:getrange, key, start, stop])
|
@@ -967,9 +1032,9 @@ class Redis
|
|
967
1032
|
# Sets or clears the bit at offset in the string value stored at key.
|
968
1033
|
#
|
969
1034
|
# @param [String] key
|
970
|
-
# @param [
|
971
|
-
# @param [
|
972
|
-
# @return [
|
1035
|
+
# @param [Integer] offset bit offset
|
1036
|
+
# @param [Integer] value bit value `0` or `1`
|
1037
|
+
# @return [Integer] the original bit value stored at `offset`
|
973
1038
|
def setbit(key, offset, value)
|
974
1039
|
synchronize do |client|
|
975
1040
|
client.call([:setbit, key, offset, value])
|
@@ -979,8 +1044,8 @@ class Redis
|
|
979
1044
|
# Returns the bit value at offset in the string value stored at key.
|
980
1045
|
#
|
981
1046
|
# @param [String] key
|
982
|
-
# @param [
|
983
|
-
# @return [
|
1047
|
+
# @param [Integer] offset bit offset
|
1048
|
+
# @return [Integer] `0` or `1`
|
984
1049
|
def getbit(key, offset)
|
985
1050
|
synchronize do |client|
|
986
1051
|
client.call([:getbit, key, offset])
|
@@ -991,7 +1056,7 @@ class Redis
|
|
991
1056
|
#
|
992
1057
|
# @param [String] key
|
993
1058
|
# @param [String] value value to append
|
994
|
-
# @return [
|
1059
|
+
# @return [Integer] length of the string after appending
|
995
1060
|
def append(key, value)
|
996
1061
|
synchronize do |client|
|
997
1062
|
client.call([:append, key, value])
|
@@ -1001,9 +1066,9 @@ class Redis
|
|
1001
1066
|
# Count the number of set bits in a range of the string value stored at key.
|
1002
1067
|
#
|
1003
1068
|
# @param [String] key
|
1004
|
-
# @param [
|
1005
|
-
# @param [
|
1006
|
-
# @return [
|
1069
|
+
# @param [Integer] start start index
|
1070
|
+
# @param [Integer] stop stop index
|
1071
|
+
# @return [Integer] the number of bits set to 1
|
1007
1072
|
def bitcount(key, start = 0, stop = -1)
|
1008
1073
|
synchronize do |client|
|
1009
1074
|
client.call([:bitcount, key, start, stop])
|
@@ -1015,25 +1080,23 @@ class Redis
|
|
1015
1080
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
1016
1081
|
# @param [String] destkey destination key
|
1017
1082
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
1018
|
-
# @return [
|
1083
|
+
# @return [Integer] the length of the string stored in `destkey`
|
1019
1084
|
def bitop(operation, destkey, *keys)
|
1020
1085
|
synchronize do |client|
|
1021
|
-
client.call([:bitop, operation, destkey
|
1086
|
+
client.call([:bitop, operation, destkey, *keys])
|
1022
1087
|
end
|
1023
1088
|
end
|
1024
1089
|
|
1025
1090
|
# Return the position of the first bit set to 1 or 0 in a string.
|
1026
1091
|
#
|
1027
1092
|
# @param [String] key
|
1028
|
-
# @param [
|
1029
|
-
# @param [
|
1030
|
-
# @param [
|
1031
|
-
# @return [
|
1093
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1094
|
+
# @param [Integer] start start index
|
1095
|
+
# @param [Integer] stop stop index
|
1096
|
+
# @return [Integer] the position of the first 1/0 bit.
|
1032
1097
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
1033
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
1034
|
-
|
1035
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
1036
|
-
end
|
1098
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1099
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1037
1100
|
|
1038
1101
|
synchronize do |client|
|
1039
1102
|
command = [:bitpos, key, bit]
|
@@ -1055,10 +1118,49 @@ class Redis
|
|
1055
1118
|
end
|
1056
1119
|
end
|
1057
1120
|
|
1121
|
+
# Get the value of key and delete the key. This command is similar to GET,
|
1122
|
+
# except for the fact that it also deletes the key on success.
|
1123
|
+
#
|
1124
|
+
# @param [String] key
|
1125
|
+
# @return [String] the old value stored in the key, or `nil` if the key
|
1126
|
+
# did not exist
|
1127
|
+
def getdel(key)
|
1128
|
+
synchronize do |client|
|
1129
|
+
client.call([:getdel, key])
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Get the value of key and optionally set its expiration. GETEX is similar to
|
1134
|
+
# GET, but is a write command with additional options. When no options are
|
1135
|
+
# provided, GETEX behaves like GET.
|
1136
|
+
#
|
1137
|
+
# @param [String] key
|
1138
|
+
# @param [Hash] options
|
1139
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
1140
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
1141
|
+
# - `:exat => true`: Set the specified Unix time at which the key will
|
1142
|
+
# expire, in seconds.
|
1143
|
+
# - `:pxat => true`: Set the specified Unix time at which the key will
|
1144
|
+
# expire, in milliseconds.
|
1145
|
+
# - `:persist => true`: Remove the time to live associated with the key.
|
1146
|
+
# @return [String] The value of key, or nil when key does not exist.
|
1147
|
+
def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
|
1148
|
+
args = [:getex, key]
|
1149
|
+
args << "EX" << ex if ex
|
1150
|
+
args << "PX" << px if px
|
1151
|
+
args << "EXAT" << exat if exat
|
1152
|
+
args << "PXAT" << pxat if pxat
|
1153
|
+
args << "PERSIST" if persist
|
1154
|
+
|
1155
|
+
synchronize do |client|
|
1156
|
+
client.call(args)
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1058
1160
|
# Get the length of the value stored in a key.
|
1059
1161
|
#
|
1060
1162
|
# @param [String] key
|
1061
|
-
# @return [
|
1163
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1062
1164
|
# if the key does not exist
|
1063
1165
|
def strlen(key)
|
1064
1166
|
synchronize do |client|
|
@@ -1069,18 +1171,71 @@ class Redis
|
|
1069
1171
|
# Get the length of a list.
|
1070
1172
|
#
|
1071
1173
|
# @param [String] key
|
1072
|
-
# @return [
|
1174
|
+
# @return [Integer]
|
1073
1175
|
def llen(key)
|
1074
1176
|
synchronize do |client|
|
1075
1177
|
client.call([:llen, key])
|
1076
1178
|
end
|
1077
1179
|
end
|
1078
1180
|
|
1181
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
1182
|
+
#
|
1183
|
+
# @param [String] source source key
|
1184
|
+
# @param [String] destination destination key
|
1185
|
+
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1186
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1187
|
+
# @param [String, Symbol] where_destination where to push the element to the source list
|
1188
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1189
|
+
#
|
1190
|
+
# @return [nil, String] the element, or nil when the source key does not exist
|
1191
|
+
#
|
1192
|
+
# @note This command comes in place of the now deprecated RPOPLPUSH.
|
1193
|
+
# Doing LMOVE RIGHT LEFT is equivalent.
|
1194
|
+
def lmove(source, destination, where_source, where_destination)
|
1195
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1196
|
+
|
1197
|
+
synchronize do |client|
|
1198
|
+
client.call([:lmove, source, destination, where_source, where_destination])
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
# Remove the first/last element in a list and append/prepend it
|
1203
|
+
# to another list and return it, or block until one is available.
|
1204
|
+
#
|
1205
|
+
# @example With timeout
|
1206
|
+
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
|
1207
|
+
# # => nil on timeout
|
1208
|
+
# # => "element" on success
|
1209
|
+
# @example Without timeout
|
1210
|
+
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
|
1211
|
+
# # => "element"
|
1212
|
+
#
|
1213
|
+
# @param [String] source source key
|
1214
|
+
# @param [String] destination destination key
|
1215
|
+
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1216
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1217
|
+
# @param [String, Symbol] where_destination where to push the element to the source list
|
1218
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1219
|
+
# @param [Hash] options
|
1220
|
+
# - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
|
1221
|
+
#
|
1222
|
+
# @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
|
1223
|
+
#
|
1224
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
1225
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1226
|
+
|
1227
|
+
synchronize do |client|
|
1228
|
+
command = [:blmove, source, destination, where_source, where_destination, timeout]
|
1229
|
+
timeout += client.timeout if timeout > 0
|
1230
|
+
client.call_with_timeout(command, timeout)
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
|
1079
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1080
1235
|
#
|
1081
1236
|
# @param [String] key
|
1082
1237
|
# @param [String, Array<String>] value string value, or array of string values to push
|
1083
|
-
# @return [
|
1238
|
+
# @return [Integer] the length of the list after the push operation
|
1084
1239
|
def lpush(key, value)
|
1085
1240
|
synchronize do |client|
|
1086
1241
|
client.call([:lpush, key, value])
|
@@ -1091,7 +1246,7 @@ class Redis
|
|
1091
1246
|
#
|
1092
1247
|
# @param [String] key
|
1093
1248
|
# @param [String] value
|
1094
|
-
# @return [
|
1249
|
+
# @return [Integer] the length of the list after the push operation
|
1095
1250
|
def lpushx(key, value)
|
1096
1251
|
synchronize do |client|
|
1097
1252
|
client.call([:lpushx, key, value])
|
@@ -1102,7 +1257,7 @@ class Redis
|
|
1102
1257
|
#
|
1103
1258
|
# @param [String] key
|
1104
1259
|
# @param [String, Array<String>] value string value, or array of string values to push
|
1105
|
-
# @return [
|
1260
|
+
# @return [Integer] the length of the list after the push operation
|
1106
1261
|
def rpush(key, value)
|
1107
1262
|
synchronize do |client|
|
1108
1263
|
client.call([:rpush, key, value])
|
@@ -1113,30 +1268,36 @@ class Redis
|
|
1113
1268
|
#
|
1114
1269
|
# @param [String] key
|
1115
1270
|
# @param [String] value
|
1116
|
-
# @return [
|
1271
|
+
# @return [Integer] the length of the list after the push operation
|
1117
1272
|
def rpushx(key, value)
|
1118
1273
|
synchronize do |client|
|
1119
1274
|
client.call([:rpushx, key, value])
|
1120
1275
|
end
|
1121
1276
|
end
|
1122
1277
|
|
1123
|
-
# Remove and get the first
|
1278
|
+
# Remove and get the first elements in a list.
|
1124
1279
|
#
|
1125
1280
|
# @param [String] key
|
1126
|
-
# @
|
1127
|
-
|
1281
|
+
# @param [Integer] count number of elements to remove
|
1282
|
+
# @return [String, Array<String>] the values of the first elements
|
1283
|
+
def lpop(key, count = nil)
|
1128
1284
|
synchronize do |client|
|
1129
|
-
|
1285
|
+
command = [:lpop, key]
|
1286
|
+
command << count if count
|
1287
|
+
client.call(command)
|
1130
1288
|
end
|
1131
1289
|
end
|
1132
1290
|
|
1133
|
-
# Remove and get the last
|
1291
|
+
# Remove and get the last elements in a list.
|
1134
1292
|
#
|
1135
1293
|
# @param [String] key
|
1136
|
-
# @
|
1137
|
-
|
1294
|
+
# @param [Integer] count number of elements to remove
|
1295
|
+
# @return [String, Array<String>] the values of the last elements
|
1296
|
+
def rpop(key, count = nil)
|
1138
1297
|
synchronize do |client|
|
1139
|
-
|
1298
|
+
command = [:rpop, key]
|
1299
|
+
command << count if count
|
1300
|
+
client.call(command)
|
1140
1301
|
end
|
1141
1302
|
end
|
1142
1303
|
|
@@ -1151,28 +1312,27 @@ class Redis
|
|
1151
1312
|
end
|
1152
1313
|
end
|
1153
1314
|
|
1154
|
-
def _bpop(cmd, args)
|
1155
|
-
|
1156
|
-
|
1157
|
-
case args.last
|
1158
|
-
when Hash
|
1315
|
+
def _bpop(cmd, args, &blk)
|
1316
|
+
timeout = if args.last.is_a?(Hash)
|
1159
1317
|
options = args.pop
|
1160
|
-
|
1318
|
+
options[:timeout]
|
1319
|
+
elsif args.last.respond_to?(:to_int)
|
1161
1320
|
# Issue deprecation notice in obnoxious mode...
|
1162
|
-
|
1321
|
+
args.pop.to_int
|
1163
1322
|
end
|
1164
1323
|
|
1324
|
+
timeout ||= 0
|
1325
|
+
|
1165
1326
|
if args.size > 1
|
1166
1327
|
# Issue deprecation notice in obnoxious mode...
|
1167
1328
|
end
|
1168
1329
|
|
1169
1330
|
keys = args.flatten
|
1170
|
-
timeout = options[:timeout] || 0
|
1171
1331
|
|
1172
1332
|
synchronize do |client|
|
1173
1333
|
command = [cmd, keys, timeout]
|
1174
1334
|
timeout += client.timeout if timeout > 0
|
1175
|
-
client.call_with_timeout(command, timeout)
|
1335
|
+
client.call_with_timeout(command, timeout, &blk)
|
1176
1336
|
end
|
1177
1337
|
end
|
1178
1338
|
|
@@ -1192,7 +1352,7 @@ class Redis
|
|
1192
1352
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1193
1353
|
# blocking pop on
|
1194
1354
|
# @param [Hash] options
|
1195
|
-
# - `:timeout =>
|
1355
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1196
1356
|
#
|
1197
1357
|
# @return [nil, [String, String]]
|
1198
1358
|
# - `nil` when the operation timed out
|
@@ -1206,7 +1366,7 @@ class Redis
|
|
1206
1366
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1207
1367
|
# blocking pop on
|
1208
1368
|
# @param [Hash] options
|
1209
|
-
# - `:timeout =>
|
1369
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1210
1370
|
#
|
1211
1371
|
# @return [nil, [String, String]]
|
1212
1372
|
# - `nil` when the operation timed out
|
@@ -1223,20 +1383,12 @@ class Redis
|
|
1223
1383
|
# @param [String] source source key
|
1224
1384
|
# @param [String] destination destination key
|
1225
1385
|
# @param [Hash] options
|
1226
|
-
# - `:timeout =>
|
1386
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1227
1387
|
#
|
1228
1388
|
# @return [nil, String]
|
1229
1389
|
# - `nil` when the operation timed out
|
1230
1390
|
# - the element was popped and pushed otherwise
|
1231
|
-
def brpoplpush(source, destination,
|
1232
|
-
case options
|
1233
|
-
when Integer
|
1234
|
-
# Issue deprecation notice in obnoxious mode...
|
1235
|
-
options = { :timeout => options }
|
1236
|
-
end
|
1237
|
-
|
1238
|
-
timeout = options[:timeout] || 0
|
1239
|
-
|
1391
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1240
1392
|
synchronize do |client|
|
1241
1393
|
command = [:brpoplpush, source, destination, timeout]
|
1242
1394
|
timeout += client.timeout if timeout > 0
|
@@ -1247,7 +1399,7 @@ class Redis
|
|
1247
1399
|
# Get an element from a list by its index.
|
1248
1400
|
#
|
1249
1401
|
# @param [String] key
|
1250
|
-
# @param [
|
1402
|
+
# @param [Integer] index
|
1251
1403
|
# @return [String]
|
1252
1404
|
def lindex(key, index)
|
1253
1405
|
synchronize do |client|
|
@@ -1261,7 +1413,7 @@ class Redis
|
|
1261
1413
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1262
1414
|
# @param [String] pivot reference element
|
1263
1415
|
# @param [String] value
|
1264
|
-
# @return [
|
1416
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1265
1417
|
# when the element `pivot` was not found
|
1266
1418
|
def linsert(key, where, pivot, value)
|
1267
1419
|
synchronize do |client|
|
@@ -1272,8 +1424,8 @@ class Redis
|
|
1272
1424
|
# Get a range of elements from a list.
|
1273
1425
|
#
|
1274
1426
|
# @param [String] key
|
1275
|
-
# @param [
|
1276
|
-
# @param [
|
1427
|
+
# @param [Integer] start start index
|
1428
|
+
# @param [Integer] stop stop index
|
1277
1429
|
# @return [Array<String>]
|
1278
1430
|
def lrange(key, start, stop)
|
1279
1431
|
synchronize do |client|
|
@@ -1284,12 +1436,12 @@ class Redis
|
|
1284
1436
|
# Remove elements from a list.
|
1285
1437
|
#
|
1286
1438
|
# @param [String] key
|
1287
|
-
# @param [
|
1439
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1288
1440
|
# value to remove the first `count` occurrences of `value`. A negative
|
1289
1441
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1290
1442
|
# remove all occurrences of `value` from the list.
|
1291
1443
|
# @param [String] value
|
1292
|
-
# @return [
|
1444
|
+
# @return [Integer] the number of removed elements
|
1293
1445
|
def lrem(key, count, value)
|
1294
1446
|
synchronize do |client|
|
1295
1447
|
client.call([:lrem, key, count, value])
|
@@ -1299,7 +1451,7 @@ class Redis
|
|
1299
1451
|
# Set the value of an element in a list by its index.
|
1300
1452
|
#
|
1301
1453
|
# @param [String] key
|
1302
|
-
# @param [
|
1454
|
+
# @param [Integer] index
|
1303
1455
|
# @param [String] value
|
1304
1456
|
# @return [String] `OK`
|
1305
1457
|
def lset(key, index, value)
|
@@ -1311,8 +1463,8 @@ class Redis
|
|
1311
1463
|
# Trim a list to the specified range.
|
1312
1464
|
#
|
1313
1465
|
# @param [String] key
|
1314
|
-
# @param [
|
1315
|
-
# @param [
|
1466
|
+
# @param [Integer] start start index
|
1467
|
+
# @param [Integer] stop stop index
|
1316
1468
|
# @return [String] `OK`
|
1317
1469
|
def ltrim(key, start, stop)
|
1318
1470
|
synchronize do |client|
|
@@ -1323,7 +1475,7 @@ class Redis
|
|
1323
1475
|
# Get the number of members in a set.
|
1324
1476
|
#
|
1325
1477
|
# @param [String] key
|
1326
|
-
# @return [
|
1478
|
+
# @return [Integer]
|
1327
1479
|
def scard(key)
|
1328
1480
|
synchronize do |client|
|
1329
1481
|
client.call([:scard, key])
|
@@ -1334,8 +1486,8 @@ class Redis
|
|
1334
1486
|
#
|
1335
1487
|
# @param [String] key
|
1336
1488
|
# @param [String, Array<String>] member one member, or array of members
|
1337
|
-
# @return [Boolean,
|
1338
|
-
# holding whether or not adding the member succeeded, or `
|
1489
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1490
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
1339
1491
|
# array of members is specified, holding the number of members that were
|
1340
1492
|
# successfully added
|
1341
1493
|
def sadd(key, member)
|
@@ -1356,8 +1508,8 @@ class Redis
|
|
1356
1508
|
#
|
1357
1509
|
# @param [String] key
|
1358
1510
|
# @param [String, Array<String>] member one member, or array of members
|
1359
|
-
# @return [Boolean,
|
1360
|
-
# holding whether or not removing the member succeeded, or `
|
1511
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1512
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
1361
1513
|
# array of members is specified, holding the number of members that were
|
1362
1514
|
# successfully removed
|
1363
1515
|
def srem(key, member)
|
@@ -1378,7 +1530,7 @@ class Redis
|
|
1378
1530
|
#
|
1379
1531
|
# @param [String] key
|
1380
1532
|
# @return [String]
|
1381
|
-
# @param [
|
1533
|
+
# @param [Integer] count
|
1382
1534
|
def spop(key, count = nil)
|
1383
1535
|
synchronize do |client|
|
1384
1536
|
if count.nil?
|
@@ -1392,7 +1544,7 @@ class Redis
|
|
1392
1544
|
# Get one or more random members from a set.
|
1393
1545
|
#
|
1394
1546
|
# @param [String] key
|
1395
|
-
# @param [
|
1547
|
+
# @param [Integer] count
|
1396
1548
|
# @return [String]
|
1397
1549
|
def srandmember(key, count = nil)
|
1398
1550
|
synchronize do |client|
|
@@ -1427,6 +1579,19 @@ class Redis
|
|
1427
1579
|
end
|
1428
1580
|
end
|
1429
1581
|
|
1582
|
+
# Determine if multiple values are members of a set.
|
1583
|
+
#
|
1584
|
+
# @param [String] key
|
1585
|
+
# @param [String, Array<String>] members
|
1586
|
+
# @return [Array<Boolean>]
|
1587
|
+
def smismember(key, *members)
|
1588
|
+
synchronize do |client|
|
1589
|
+
client.call([:smismember, key, *members]) do |reply|
|
1590
|
+
reply.map(&Boolify)
|
1591
|
+
end
|
1592
|
+
end
|
1593
|
+
end
|
1594
|
+
|
1430
1595
|
# Get all the members in a set.
|
1431
1596
|
#
|
1432
1597
|
# @param [String] key
|
@@ -1443,7 +1608,7 @@ class Redis
|
|
1443
1608
|
# @return [Array<String>] members in the difference
|
1444
1609
|
def sdiff(*keys)
|
1445
1610
|
synchronize do |client|
|
1446
|
-
client.call([:sdiff
|
1611
|
+
client.call([:sdiff, *keys])
|
1447
1612
|
end
|
1448
1613
|
end
|
1449
1614
|
|
@@ -1451,10 +1616,10 @@ class Redis
|
|
1451
1616
|
#
|
1452
1617
|
# @param [String] destination destination key
|
1453
1618
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1454
|
-
# @return [
|
1619
|
+
# @return [Integer] number of elements in the resulting set
|
1455
1620
|
def sdiffstore(destination, *keys)
|
1456
1621
|
synchronize do |client|
|
1457
|
-
client.call([:sdiffstore, destination
|
1622
|
+
client.call([:sdiffstore, destination, *keys])
|
1458
1623
|
end
|
1459
1624
|
end
|
1460
1625
|
|
@@ -1464,7 +1629,7 @@ class Redis
|
|
1464
1629
|
# @return [Array<String>] members in the intersection
|
1465
1630
|
def sinter(*keys)
|
1466
1631
|
synchronize do |client|
|
1467
|
-
client.call([:sinter
|
1632
|
+
client.call([:sinter, *keys])
|
1468
1633
|
end
|
1469
1634
|
end
|
1470
1635
|
|
@@ -1472,10 +1637,10 @@ class Redis
|
|
1472
1637
|
#
|
1473
1638
|
# @param [String] destination destination key
|
1474
1639
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1475
|
-
# @return [
|
1640
|
+
# @return [Integer] number of elements in the resulting set
|
1476
1641
|
def sinterstore(destination, *keys)
|
1477
1642
|
synchronize do |client|
|
1478
|
-
client.call([:sinterstore, destination
|
1643
|
+
client.call([:sinterstore, destination, *keys])
|
1479
1644
|
end
|
1480
1645
|
end
|
1481
1646
|
|
@@ -1485,7 +1650,7 @@ class Redis
|
|
1485
1650
|
# @return [Array<String>] members in the union
|
1486
1651
|
def sunion(*keys)
|
1487
1652
|
synchronize do |client|
|
1488
|
-
client.call([:sunion
|
1653
|
+
client.call([:sunion, *keys])
|
1489
1654
|
end
|
1490
1655
|
end
|
1491
1656
|
|
@@ -1493,10 +1658,10 @@ class Redis
|
|
1493
1658
|
#
|
1494
1659
|
# @param [String] destination destination key
|
1495
1660
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1496
|
-
# @return [
|
1661
|
+
# @return [Integer] number of elements in the resulting set
|
1497
1662
|
def sunionstore(destination, *keys)
|
1498
1663
|
synchronize do |client|
|
1499
|
-
client.call([:sunionstore, destination
|
1664
|
+
client.call([:sunionstore, destination, *keys])
|
1500
1665
|
end
|
1501
1666
|
end
|
1502
1667
|
|
@@ -1507,7 +1672,7 @@ class Redis
|
|
1507
1672
|
# # => 4
|
1508
1673
|
#
|
1509
1674
|
# @param [String] key
|
1510
|
-
# @return [
|
1675
|
+
# @return [Integer]
|
1511
1676
|
def zcard(key)
|
1512
1677
|
synchronize do |client|
|
1513
1678
|
client.call([:zcard, key])
|
@@ -1531,6 +1696,10 @@ class Redis
|
|
1531
1696
|
# add elements)
|
1532
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1533
1698
|
# add new elements)
|
1699
|
+
# - `:lt => true`: Only update existing elements if the new score
|
1700
|
+
# is less than the current score
|
1701
|
+
# - `:gt => true`: Only update existing elements if the new score
|
1702
|
+
# is greater than the current score
|
1534
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1535
1704
|
# elements added, to the total number of elements changed (CH is an
|
1536
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1538,38 +1707,29 @@ class Redis
|
|
1538
1707
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1539
1708
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1540
1709
|
#
|
1541
|
-
# @return [Boolean,
|
1710
|
+
# @return [Boolean, Integer, Float]
|
1542
1711
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1543
1712
|
# **added** to the sorted set.
|
1544
|
-
# - `
|
1713
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1545
1714
|
# pairs that were **added** to the sorted set.
|
1546
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1547
1716
|
# after incrementing it.
|
1548
|
-
def zadd(key, *args
|
1549
|
-
|
1550
|
-
if
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
xx = options[:xx]
|
1557
|
-
zadd_options << "XX" if xx
|
1558
|
-
|
1559
|
-
ch = options[:ch]
|
1560
|
-
zadd_options << "CH" if ch
|
1561
|
-
|
1562
|
-
incr = options[:incr]
|
1563
|
-
zadd_options << "INCR" if incr
|
1564
|
-
end
|
1717
|
+
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
1718
|
+
command = [:zadd, key]
|
1719
|
+
command << "NX" if nx
|
1720
|
+
command << "XX" if xx
|
1721
|
+
command << "LT" if lt
|
1722
|
+
command << "GT" if gt
|
1723
|
+
command << "CH" if ch
|
1724
|
+
command << "INCR" if incr
|
1565
1725
|
|
1566
1726
|
synchronize do |client|
|
1567
1727
|
if args.size == 1 && args[0].is_a?(Array)
|
1568
1728
|
# Variadic: return float if INCR, integer if !INCR
|
1569
|
-
client.call(
|
1729
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1570
1730
|
elsif args.size == 2
|
1571
1731
|
# Single pair: return float if INCR, boolean if !INCR
|
1572
|
-
client.call(
|
1732
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1573
1733
|
else
|
1574
1734
|
raise ArgumentError, "wrong number of arguments"
|
1575
1735
|
end
|
@@ -1604,10 +1764,10 @@ class Redis
|
|
1604
1764
|
# - a single member
|
1605
1765
|
# - an array of members
|
1606
1766
|
#
|
1607
|
-
# @return [Boolean,
|
1767
|
+
# @return [Boolean, Integer]
|
1608
1768
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1609
1769
|
# was removed from the sorted set
|
1610
|
-
# - `
|
1770
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1611
1771
|
# members that were removed to the sorted set
|
1612
1772
|
def zrem(key, member)
|
1613
1773
|
synchronize do |client|
|
@@ -1623,6 +1783,90 @@ class Redis
|
|
1623
1783
|
end
|
1624
1784
|
end
|
1625
1785
|
|
1786
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1787
|
+
#
|
1788
|
+
# @example Popping a member
|
1789
|
+
# redis.zpopmax('zset')
|
1790
|
+
# #=> ['b', 2.0]
|
1791
|
+
# @example With count option
|
1792
|
+
# redis.zpopmax('zset', 2)
|
1793
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
1794
|
+
#
|
1795
|
+
# @params key [String] a key of the sorted set
|
1796
|
+
# @params count [Integer] a number of members
|
1797
|
+
#
|
1798
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1799
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1800
|
+
def zpopmax(key, count = nil)
|
1801
|
+
synchronize do |client|
|
1802
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1803
|
+
count.to_i > 1 ? members : members.first
|
1804
|
+
end
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1808
|
+
#
|
1809
|
+
# @example Popping a member
|
1810
|
+
# redis.zpopmin('zset')
|
1811
|
+
# #=> ['a', 1.0]
|
1812
|
+
# @example With count option
|
1813
|
+
# redis.zpopmin('zset', 2)
|
1814
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
1815
|
+
#
|
1816
|
+
# @params key [String] a key of the sorted set
|
1817
|
+
# @params count [Integer] a number of members
|
1818
|
+
#
|
1819
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1820
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1821
|
+
def zpopmin(key, count = nil)
|
1822
|
+
synchronize do |client|
|
1823
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1824
|
+
count.to_i > 1 ? members : members.first
|
1825
|
+
end
|
1826
|
+
end
|
1827
|
+
|
1828
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1829
|
+
# or block until one is available.
|
1830
|
+
#
|
1831
|
+
# @example Popping a member from a sorted set
|
1832
|
+
# redis.bzpopmax('zset', 1)
|
1833
|
+
# #=> ['zset', 'b', 2.0]
|
1834
|
+
# @example Popping a member from multiple sorted sets
|
1835
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
1836
|
+
# #=> ['zset1', 'b', 2.0]
|
1837
|
+
#
|
1838
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1839
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1840
|
+
#
|
1841
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1842
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1843
|
+
def bzpopmax(*args)
|
1844
|
+
_bpop(:bzpopmax, args) do |reply|
|
1845
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1846
|
+
end
|
1847
|
+
end
|
1848
|
+
|
1849
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1850
|
+
# or block until one is available.
|
1851
|
+
#
|
1852
|
+
# @example Popping a member from a sorted set
|
1853
|
+
# redis.bzpopmin('zset', 1)
|
1854
|
+
# #=> ['zset', 'a', 1.0]
|
1855
|
+
# @example Popping a member from multiple sorted sets
|
1856
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
1857
|
+
# #=> ['zset1', 'a', 1.0]
|
1858
|
+
#
|
1859
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1860
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1861
|
+
#
|
1862
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1863
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1864
|
+
def bzpopmin(*args)
|
1865
|
+
_bpop(:bzpopmin, args) do |reply|
|
1866
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1867
|
+
end
|
1868
|
+
end
|
1869
|
+
|
1626
1870
|
# Get the score associated with the given member in a sorted set.
|
1627
1871
|
#
|
1628
1872
|
# @example Get the score for member "a"
|
@@ -1638,6 +1882,63 @@ class Redis
|
|
1638
1882
|
end
|
1639
1883
|
end
|
1640
1884
|
|
1885
|
+
# Get the scores associated with the given members in a sorted set.
|
1886
|
+
#
|
1887
|
+
# @example Get the scores for members "a" and "b"
|
1888
|
+
# redis.zmscore("zset", "a", "b")
|
1889
|
+
# # => [32.0, 48.0]
|
1890
|
+
#
|
1891
|
+
# @param [String] key
|
1892
|
+
# @param [String, Array<String>] members
|
1893
|
+
# @return [Array<Float>] scores of the members
|
1894
|
+
def zmscore(key, *members)
|
1895
|
+
synchronize do |client|
|
1896
|
+
client.call([:zmscore, key, *members]) do |reply|
|
1897
|
+
reply.map(&Floatify)
|
1898
|
+
end
|
1899
|
+
end
|
1900
|
+
end
|
1901
|
+
|
1902
|
+
# Get one or more random members from a sorted set.
|
1903
|
+
#
|
1904
|
+
# @example Get one random member
|
1905
|
+
# redis.zrandmember("zset")
|
1906
|
+
# # => "a"
|
1907
|
+
# @example Get multiple random members
|
1908
|
+
# redis.zrandmember("zset", 2)
|
1909
|
+
# # => ["a", "b"]
|
1910
|
+
# @example Gem multiple random members with scores
|
1911
|
+
# redis.zrandmember("zset", 2, with_scores: true)
|
1912
|
+
# # => [["a", 2.0], ["b", 3.0]]
|
1913
|
+
#
|
1914
|
+
# @param [String] key
|
1915
|
+
# @param [Integer] count
|
1916
|
+
# @param [Hash] options
|
1917
|
+
# - `:with_scores => true`: include scores in output
|
1918
|
+
#
|
1919
|
+
# @return [nil, String, Array<String>, Array<[String, Float]>]
|
1920
|
+
# - when `key` does not exist or set is empty, `nil`
|
1921
|
+
# - when `count` is not specified, a member
|
1922
|
+
# - when `count` is specified and `:with_scores` is not specified, an array of members
|
1923
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1924
|
+
def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
|
1925
|
+
if with_scores && count.nil?
|
1926
|
+
raise ArgumentError, "count argument must be specified"
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
args = [:zrandmember, key]
|
1930
|
+
args << count if count
|
1931
|
+
|
1932
|
+
if with_scores
|
1933
|
+
args << "WITHSCORES"
|
1934
|
+
block = FloatifyPairs
|
1935
|
+
end
|
1936
|
+
|
1937
|
+
synchronize do |client|
|
1938
|
+
client.call(args, &block)
|
1939
|
+
end
|
1940
|
+
end
|
1941
|
+
|
1641
1942
|
# Return a range of members in a sorted set, by index.
|
1642
1943
|
#
|
1643
1944
|
# @example Retrieve all members from a sorted set
|
@@ -1648,18 +1949,16 @@ class Redis
|
|
1648
1949
|
# # => [["a", 32.0], ["b", 64.0]]
|
1649
1950
|
#
|
1650
1951
|
# @param [String] key
|
1651
|
-
# @param [
|
1652
|
-
# @param [
|
1952
|
+
# @param [Integer] start start index
|
1953
|
+
# @param [Integer] stop stop index
|
1653
1954
|
# @param [Hash] options
|
1654
1955
|
# - `:with_scores => true`: include scores in output
|
1655
1956
|
#
|
1656
1957
|
# @return [Array<String>, Array<[String, Float]>]
|
1657
1958
|
# - when `:with_scores` is not specified, an array of members
|
1658
1959
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1659
|
-
def zrange(key, start, stop,
|
1660
|
-
args = []
|
1661
|
-
|
1662
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1960
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1961
|
+
args = [:zrange, key, start, stop]
|
1663
1962
|
|
1664
1963
|
if with_scores
|
1665
1964
|
args << "WITHSCORES"
|
@@ -1667,7 +1966,7 @@ class Redis
|
|
1667
1966
|
end
|
1668
1967
|
|
1669
1968
|
synchronize do |client|
|
1670
|
-
client.call(
|
1969
|
+
client.call(args, &block)
|
1671
1970
|
end
|
1672
1971
|
end
|
1673
1972
|
|
@@ -1682,10 +1981,8 @@ class Redis
|
|
1682
1981
|
# # => [["b", 64.0], ["a", 32.0]]
|
1683
1982
|
#
|
1684
1983
|
# @see #zrange
|
1685
|
-
def zrevrange(key, start, stop,
|
1686
|
-
args = []
|
1687
|
-
|
1688
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1984
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1985
|
+
args = [:zrevrange, key, start, stop]
|
1689
1986
|
|
1690
1987
|
if with_scores
|
1691
1988
|
args << "WITHSCORES"
|
@@ -1693,7 +1990,7 @@ class Redis
|
|
1693
1990
|
end
|
1694
1991
|
|
1695
1992
|
synchronize do |client|
|
1696
|
-
client.call(
|
1993
|
+
client.call(args, &block)
|
1697
1994
|
end
|
1698
1995
|
end
|
1699
1996
|
|
@@ -1701,7 +1998,7 @@ class Redis
|
|
1701
1998
|
#
|
1702
1999
|
# @param [String] key
|
1703
2000
|
# @param [String] member
|
1704
|
-
# @return [
|
2001
|
+
# @return [Integer]
|
1705
2002
|
def zrank(key, member)
|
1706
2003
|
synchronize do |client|
|
1707
2004
|
client.call([:zrank, key, member])
|
@@ -1713,7 +2010,7 @@ class Redis
|
|
1713
2010
|
#
|
1714
2011
|
# @param [String] key
|
1715
2012
|
# @param [String] member
|
1716
|
-
# @return [
|
2013
|
+
# @return [Integer]
|
1717
2014
|
def zrevrank(key, member)
|
1718
2015
|
synchronize do |client|
|
1719
2016
|
client.call([:zrevrank, key, member])
|
@@ -1730,9 +2027,9 @@ class Redis
|
|
1730
2027
|
# # => 5
|
1731
2028
|
#
|
1732
2029
|
# @param [String] key
|
1733
|
-
# @param [
|
1734
|
-
# @param [
|
1735
|
-
# @return [
|
2030
|
+
# @param [Integer] start start index
|
2031
|
+
# @param [Integer] stop stop index
|
2032
|
+
# @return [Integer] number of members that were removed
|
1736
2033
|
def zremrangebyrank(key, start, stop)
|
1737
2034
|
synchronize do |client|
|
1738
2035
|
client.call([:zremrangebyrank, key, start, stop])
|
@@ -1756,7 +2053,7 @@ class Redis
|
|
1756
2053
|
# - inclusive maximum is specified by prefixing `(`
|
1757
2054
|
# - exclusive maximum is specified by prefixing `[`
|
1758
2055
|
#
|
1759
|
-
# @return [
|
2056
|
+
# @return [Integer] number of members within the specified lexicographical range
|
1760
2057
|
def zlexcount(key, min, max)
|
1761
2058
|
synchronize do |client|
|
1762
2059
|
client.call([:zlexcount, key, min, max])
|
@@ -1784,14 +2081,16 @@ class Redis
|
|
1784
2081
|
# `count` members
|
1785
2082
|
#
|
1786
2083
|
# @return [Array<String>, Array<[String, Float]>]
|
1787
|
-
def zrangebylex(key, min, max,
|
1788
|
-
args = []
|
2084
|
+
def zrangebylex(key, min, max, limit: nil)
|
2085
|
+
args = [:zrangebylex, key, min, max]
|
1789
2086
|
|
1790
|
-
|
1791
|
-
|
2087
|
+
if limit
|
2088
|
+
args << "LIMIT"
|
2089
|
+
args.concat(limit)
|
2090
|
+
end
|
1792
2091
|
|
1793
2092
|
synchronize do |client|
|
1794
|
-
client.call(
|
2093
|
+
client.call(args)
|
1795
2094
|
end
|
1796
2095
|
end
|
1797
2096
|
|
@@ -1806,14 +2105,16 @@ class Redis
|
|
1806
2105
|
# # => ["abbygail", "abby"]
|
1807
2106
|
#
|
1808
2107
|
# @see #zrangebylex
|
1809
|
-
def zrevrangebylex(key, max, min,
|
1810
|
-
args = []
|
2108
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
2109
|
+
args = [:zrevrangebylex, key, max, min]
|
1811
2110
|
|
1812
|
-
|
1813
|
-
|
2111
|
+
if limit
|
2112
|
+
args << "LIMIT"
|
2113
|
+
args.concat(limit)
|
2114
|
+
end
|
1814
2115
|
|
1815
2116
|
synchronize do |client|
|
1816
|
-
client.call(
|
2117
|
+
client.call(args)
|
1817
2118
|
end
|
1818
2119
|
end
|
1819
2120
|
|
@@ -1844,21 +2145,21 @@ class Redis
|
|
1844
2145
|
# @return [Array<String>, Array<[String, Float]>]
|
1845
2146
|
# - when `:with_scores` is not specified, an array of members
|
1846
2147
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1847
|
-
def zrangebyscore(key, min, max,
|
1848
|
-
args = []
|
1849
|
-
|
1850
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2148
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
2149
|
+
args = [:zrangebyscore, key, min, max]
|
1851
2150
|
|
1852
2151
|
if with_scores
|
1853
2152
|
args << "WITHSCORES"
|
1854
2153
|
block = FloatifyPairs
|
1855
2154
|
end
|
1856
2155
|
|
1857
|
-
|
1858
|
-
|
2156
|
+
if limit
|
2157
|
+
args << "LIMIT"
|
2158
|
+
args.concat(limit)
|
2159
|
+
end
|
1859
2160
|
|
1860
2161
|
synchronize do |client|
|
1861
|
-
client.call(
|
2162
|
+
client.call(args, &block)
|
1862
2163
|
end
|
1863
2164
|
end
|
1864
2165
|
|
@@ -1876,21 +2177,21 @@ class Redis
|
|
1876
2177
|
# # => [["b", 64.0], ["a", 32.0]]
|
1877
2178
|
#
|
1878
2179
|
# @see #zrangebyscore
|
1879
|
-
def zrevrangebyscore(key, max, min,
|
1880
|
-
args = []
|
1881
|
-
|
1882
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2180
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2181
|
+
args = [:zrevrangebyscore, key, max, min]
|
1883
2182
|
|
1884
2183
|
if with_scores
|
1885
|
-
args <<
|
2184
|
+
args << "WITHSCORES"
|
1886
2185
|
block = FloatifyPairs
|
1887
2186
|
end
|
1888
2187
|
|
1889
|
-
|
1890
|
-
|
2188
|
+
if limit
|
2189
|
+
args << "LIMIT"
|
2190
|
+
args.concat(limit)
|
2191
|
+
end
|
1891
2192
|
|
1892
2193
|
synchronize do |client|
|
1893
|
-
client.call(
|
2194
|
+
client.call(args, &block)
|
1894
2195
|
end
|
1895
2196
|
end
|
1896
2197
|
|
@@ -1910,7 +2211,7 @@ class Redis
|
|
1910
2211
|
# @param [String] max
|
1911
2212
|
# - inclusive maximum score is specified verbatim
|
1912
2213
|
# - exclusive maximum score is specified by prefixing `(`
|
1913
|
-
# @return [
|
2214
|
+
# @return [Integer] number of members that were removed
|
1914
2215
|
def zremrangebyscore(key, min, max)
|
1915
2216
|
synchronize do |client|
|
1916
2217
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1933,13 +2234,52 @@ class Redis
|
|
1933
2234
|
# @param [String] max
|
1934
2235
|
# - inclusive maximum score is specified verbatim
|
1935
2236
|
# - exclusive maximum score is specified by prefixing `(`
|
1936
|
-
# @return [
|
2237
|
+
# @return [Integer] number of members in within the specified range
|
1937
2238
|
def zcount(key, min, max)
|
1938
2239
|
synchronize do |client|
|
1939
2240
|
client.call([:zcount, key, min, max])
|
1940
2241
|
end
|
1941
2242
|
end
|
1942
2243
|
|
2244
|
+
# Return the intersection of multiple sorted sets
|
2245
|
+
#
|
2246
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
2247
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
2248
|
+
# # => ["v1", "v2"]
|
2249
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
2250
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
2251
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
2252
|
+
#
|
2253
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
2254
|
+
# @param [Hash] options
|
2255
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2256
|
+
# sorted sets
|
2257
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2258
|
+
# - `:with_scores => true`: include scores in output
|
2259
|
+
#
|
2260
|
+
# @return [Array<String>, Array<[String, Float]>]
|
2261
|
+
# - when `:with_scores` is not specified, an array of members
|
2262
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2263
|
+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
2264
|
+
args = [:zinter, keys.size, *keys]
|
2265
|
+
|
2266
|
+
if weights
|
2267
|
+
args << "WEIGHTS"
|
2268
|
+
args.concat(weights)
|
2269
|
+
end
|
2270
|
+
|
2271
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2272
|
+
|
2273
|
+
if with_scores
|
2274
|
+
args << "WITHSCORES"
|
2275
|
+
block = FloatifyPairs
|
2276
|
+
end
|
2277
|
+
|
2278
|
+
synchronize do |client|
|
2279
|
+
client.call(args, &block)
|
2280
|
+
end
|
2281
|
+
end
|
2282
|
+
|
1943
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
1944
2284
|
# key.
|
1945
2285
|
#
|
@@ -1953,18 +2293,19 @@ class Redis
|
|
1953
2293
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1954
2294
|
# sorted sets
|
1955
2295
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1956
|
-
# @return [
|
1957
|
-
def zinterstore(destination, keys,
|
1958
|
-
args = []
|
2296
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2297
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2298
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
1959
2299
|
|
1960
|
-
|
1961
|
-
|
2300
|
+
if weights
|
2301
|
+
args << "WEIGHTS"
|
2302
|
+
args.concat(weights)
|
2303
|
+
end
|
1962
2304
|
|
1963
|
-
aggregate
|
1964
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2305
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1965
2306
|
|
1966
2307
|
synchronize do |client|
|
1967
|
-
client.call(
|
2308
|
+
client.call(args)
|
1968
2309
|
end
|
1969
2310
|
end
|
1970
2311
|
|
@@ -1980,40 +2321,46 @@ class Redis
|
|
1980
2321
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1981
2322
|
# sorted sets
|
1982
2323
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1983
|
-
# @return [
|
1984
|
-
def zunionstore(destination, keys,
|
1985
|
-
args = []
|
2324
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2325
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2326
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
1986
2327
|
|
1987
|
-
|
1988
|
-
|
2328
|
+
if weights
|
2329
|
+
args << "WEIGHTS"
|
2330
|
+
args.concat(weights)
|
2331
|
+
end
|
1989
2332
|
|
1990
|
-
aggregate
|
1991
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2333
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1992
2334
|
|
1993
2335
|
synchronize do |client|
|
1994
|
-
client.call(
|
2336
|
+
client.call(args)
|
1995
2337
|
end
|
1996
2338
|
end
|
1997
2339
|
|
1998
2340
|
# Get the number of fields in a hash.
|
1999
2341
|
#
|
2000
2342
|
# @param [String] key
|
2001
|
-
# @return [
|
2343
|
+
# @return [Integer] number of fields in the hash
|
2002
2344
|
def hlen(key)
|
2003
2345
|
synchronize do |client|
|
2004
2346
|
client.call([:hlen, key])
|
2005
2347
|
end
|
2006
2348
|
end
|
2007
2349
|
|
2008
|
-
# Set
|
2350
|
+
# Set one or more hash values.
|
2351
|
+
#
|
2352
|
+
# @example
|
2353
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2354
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
2009
2355
|
#
|
2010
2356
|
# @param [String] key
|
2011
|
-
# @param [String]
|
2012
|
-
# @
|
2013
|
-
|
2014
|
-
|
2357
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2358
|
+
# @return [Integer] The number of fields that were added to the hash
|
2359
|
+
def hset(key, *attrs)
|
2360
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2361
|
+
|
2015
2362
|
synchronize do |client|
|
2016
|
-
client.call([:hset, key,
|
2363
|
+
client.call([:hset, key, *attrs])
|
2017
2364
|
end
|
2018
2365
|
end
|
2019
2366
|
|
@@ -2102,7 +2449,7 @@ class Redis
|
|
2102
2449
|
# @see #hmget
|
2103
2450
|
def mapped_hmget(key, *fields)
|
2104
2451
|
hmget(key, *fields) do |reply|
|
2105
|
-
if reply.
|
2452
|
+
if reply.is_a?(Array)
|
2106
2453
|
Hash[fields.zip(reply)]
|
2107
2454
|
else
|
2108
2455
|
reply
|
@@ -2114,7 +2461,7 @@ class Redis
|
|
2114
2461
|
#
|
2115
2462
|
# @param [String] key
|
2116
2463
|
# @param [String, Array<String>] field
|
2117
|
-
# @return [
|
2464
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2118
2465
|
def hdel(key, *fields)
|
2119
2466
|
synchronize do |client|
|
2120
2467
|
client.call([:hdel, key, *fields])
|
@@ -2136,8 +2483,8 @@ class Redis
|
|
2136
2483
|
#
|
2137
2484
|
# @param [String] key
|
2138
2485
|
# @param [String] field
|
2139
|
-
# @param [
|
2140
|
-
# @return [
|
2486
|
+
# @param [Integer] increment
|
2487
|
+
# @return [Integer] value of the field after incrementing it
|
2141
2488
|
def hincrby(key, field, increment)
|
2142
2489
|
synchronize do |client|
|
2143
2490
|
client.call([:hincrby, key, field, increment])
|
@@ -2195,20 +2542,21 @@ class Redis
|
|
2195
2542
|
|
2196
2543
|
def subscribed?
|
2197
2544
|
synchronize do |client|
|
2198
|
-
client.
|
2545
|
+
client.is_a? SubscribedClient
|
2199
2546
|
end
|
2200
2547
|
end
|
2201
2548
|
|
2202
2549
|
# Listen for messages published to the given channels.
|
2203
2550
|
def subscribe(*channels, &block)
|
2204
|
-
synchronize do |
|
2551
|
+
synchronize do |_client|
|
2205
2552
|
_subscription(:subscribe, 0, channels, block)
|
2206
2553
|
end
|
2207
2554
|
end
|
2208
2555
|
|
2209
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2556
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2557
|
+
# if there is no messages for a timeout period.
|
2210
2558
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2211
|
-
synchronize do |
|
2559
|
+
synchronize do |_client|
|
2212
2560
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2213
2561
|
end
|
2214
2562
|
end
|
@@ -2216,21 +2564,23 @@ class Redis
|
|
2216
2564
|
# Stop listening for messages posted to the given channels.
|
2217
2565
|
def unsubscribe(*channels)
|
2218
2566
|
synchronize do |client|
|
2219
|
-
raise
|
2567
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2568
|
+
|
2220
2569
|
client.unsubscribe(*channels)
|
2221
2570
|
end
|
2222
2571
|
end
|
2223
2572
|
|
2224
2573
|
# Listen for messages published to channels matching the given patterns.
|
2225
2574
|
def psubscribe(*channels, &block)
|
2226
|
-
synchronize do |
|
2575
|
+
synchronize do |_client|
|
2227
2576
|
_subscription(:psubscribe, 0, channels, block)
|
2228
2577
|
end
|
2229
2578
|
end
|
2230
2579
|
|
2231
|
-
# Listen for messages published to channels matching the given patterns.
|
2580
|
+
# Listen for messages published to channels matching the given patterns.
|
2581
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
2232
2582
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2233
|
-
synchronize do |
|
2583
|
+
synchronize do |_client|
|
2234
2584
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2235
2585
|
end
|
2236
2586
|
end
|
@@ -2238,7 +2588,8 @@ class Redis
|
|
2238
2588
|
# Stop listening for messages posted to channels matching the given patterns.
|
2239
2589
|
def punsubscribe(*channels)
|
2240
2590
|
synchronize do |client|
|
2241
|
-
raise
|
2591
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2592
|
+
|
2242
2593
|
client.punsubscribe(*channels)
|
2243
2594
|
end
|
2244
2595
|
end
|
@@ -2283,7 +2634,7 @@ class Redis
|
|
2283
2634
|
# @see #multi
|
2284
2635
|
def watch(*keys)
|
2285
2636
|
synchronize do |client|
|
2286
|
-
res = client.call([:watch
|
2637
|
+
res = client.call([:watch, *keys])
|
2287
2638
|
|
2288
2639
|
if block_given?
|
2289
2640
|
begin
|
@@ -2313,13 +2664,13 @@ class Redis
|
|
2313
2664
|
end
|
2314
2665
|
|
2315
2666
|
def pipelined
|
2316
|
-
synchronize do |
|
2667
|
+
synchronize do |prior_client|
|
2317
2668
|
begin
|
2318
|
-
|
2669
|
+
@client = Pipeline.new(prior_client)
|
2319
2670
|
yield(self)
|
2320
|
-
|
2671
|
+
prior_client.call_pipeline(@client)
|
2321
2672
|
ensure
|
2322
|
-
@client =
|
2673
|
+
@client = prior_client
|
2323
2674
|
end
|
2324
2675
|
end
|
2325
2676
|
end
|
@@ -2355,17 +2706,16 @@ class Redis
|
|
2355
2706
|
# @see #watch
|
2356
2707
|
# @see #unwatch
|
2357
2708
|
def multi
|
2358
|
-
synchronize do |
|
2709
|
+
synchronize do |prior_client|
|
2359
2710
|
if !block_given?
|
2360
|
-
|
2711
|
+
prior_client.call([:multi])
|
2361
2712
|
else
|
2362
2713
|
begin
|
2363
|
-
|
2364
|
-
original, @client = @client, pipeline
|
2714
|
+
@client = Pipeline::Multi.new(prior_client)
|
2365
2715
|
yield(self)
|
2366
|
-
|
2716
|
+
prior_client.call_pipeline(@client)
|
2367
2717
|
ensure
|
2368
|
-
@client =
|
2718
|
+
@client = prior_client
|
2369
2719
|
end
|
2370
2720
|
end
|
2371
2721
|
end
|
@@ -2512,18 +2862,13 @@ class Redis
|
|
2512
2862
|
_eval(:evalsha, args)
|
2513
2863
|
end
|
2514
2864
|
|
2515
|
-
def _scan(command, cursor, args,
|
2865
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2516
2866
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2517
2867
|
|
2518
2868
|
args << cursor
|
2519
|
-
|
2520
|
-
|
2521
|
-
|
2522
|
-
end
|
2523
|
-
|
2524
|
-
if count = options[:count]
|
2525
|
-
args.concat(["COUNT", count])
|
2526
|
-
end
|
2869
|
+
args << "MATCH" << match if match
|
2870
|
+
args << "COUNT" << count if count
|
2871
|
+
args << "TYPE" << type if type
|
2527
2872
|
|
2528
2873
|
synchronize do |client|
|
2529
2874
|
client.call([command] + args, &block)
|
@@ -2538,15 +2883,19 @@ class Redis
|
|
2538
2883
|
# @example Retrieve a batch of keys matching a pattern
|
2539
2884
|
# redis.scan(4, :match => "key:1?")
|
2540
2885
|
# # => ["92", ["key:13", "key:18"]]
|
2886
|
+
# @example Retrieve a batch of keys of a certain type
|
2887
|
+
# redis.scan(92, :type => "zset")
|
2888
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2541
2889
|
#
|
2542
2890
|
# @param [String, Integer] cursor the cursor of the iteration
|
2543
2891
|
# @param [Hash] options
|
2544
2892
|
# - `:match => String`: only return keys matching the pattern
|
2545
2893
|
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
+
# - `:type => String`: return keys only of the given type
|
2546
2895
|
#
|
2547
2896
|
# @return [String, Array<String>] the next cursor and all found keys
|
2548
|
-
def scan(cursor, options
|
2549
|
-
_scan(:scan, cursor, [], options)
|
2897
|
+
def scan(cursor, **options)
|
2898
|
+
_scan(:scan, cursor, [], **options)
|
2550
2899
|
end
|
2551
2900
|
|
2552
2901
|
# Scan the keyspace
|
@@ -2558,17 +2907,23 @@ class Redis
|
|
2558
2907
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2559
2908
|
# # => key:13
|
2560
2909
|
# # => key:18
|
2910
|
+
# @example Execute block for each key of a type
|
2911
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2912
|
+
# # => "hash"
|
2913
|
+
# # => "hash"
|
2561
2914
|
#
|
2562
2915
|
# @param [Hash] options
|
2563
2916
|
# - `:match => String`: only return keys matching the pattern
|
2564
2917
|
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
+
# - `:type => String`: return keys only of the given type
|
2565
2919
|
#
|
2566
2920
|
# @return [Enumerator] an enumerator for all found keys
|
2567
|
-
def scan_each(options
|
2568
|
-
return to_enum(:scan_each, options) unless block_given?
|
2921
|
+
def scan_each(**options, &block)
|
2922
|
+
return to_enum(:scan_each, **options) unless block_given?
|
2923
|
+
|
2569
2924
|
cursor = 0
|
2570
2925
|
loop do
|
2571
|
-
cursor, keys = scan(cursor, options)
|
2926
|
+
cursor, keys = scan(cursor, **options)
|
2572
2927
|
keys.each(&block)
|
2573
2928
|
break if cursor == "0"
|
2574
2929
|
end
|
@@ -2585,8 +2940,8 @@ class Redis
|
|
2585
2940
|
# - `:count => Integer`: return count keys at most per iteration
|
2586
2941
|
#
|
2587
2942
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2588
|
-
def hscan(key, cursor, options
|
2589
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2943
|
+
def hscan(key, cursor, **options)
|
2944
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2590
2945
|
[reply[0], reply[1].each_slice(2).to_a]
|
2591
2946
|
end
|
2592
2947
|
end
|
@@ -2602,11 +2957,12 @@ class Redis
|
|
2602
2957
|
# - `:count => Integer`: return count keys at most per iteration
|
2603
2958
|
#
|
2604
2959
|
# @return [Enumerator] an enumerator for all found keys
|
2605
|
-
def hscan_each(key, options
|
2606
|
-
return to_enum(:hscan_each, key, options) unless block_given?
|
2960
|
+
def hscan_each(key, **options, &block)
|
2961
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
2962
|
+
|
2607
2963
|
cursor = 0
|
2608
2964
|
loop do
|
2609
|
-
cursor, values = hscan(key, cursor, options)
|
2965
|
+
cursor, values = hscan(key, cursor, **options)
|
2610
2966
|
values.each(&block)
|
2611
2967
|
break if cursor == "0"
|
2612
2968
|
end
|
@@ -2624,8 +2980,8 @@ class Redis
|
|
2624
2980
|
#
|
2625
2981
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2626
2982
|
# members and scores
|
2627
|
-
def zscan(key, cursor, options
|
2628
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2983
|
+
def zscan(key, cursor, **options)
|
2984
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2629
2985
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2630
2986
|
end
|
2631
2987
|
end
|
@@ -2641,11 +2997,12 @@ class Redis
|
|
2641
2997
|
# - `:count => Integer`: return count keys at most per iteration
|
2642
2998
|
#
|
2643
2999
|
# @return [Enumerator] an enumerator for all found scores and members
|
2644
|
-
def zscan_each(key, options
|
2645
|
-
return to_enum(:zscan_each, key, options) unless block_given?
|
3000
|
+
def zscan_each(key, **options, &block)
|
3001
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
3002
|
+
|
2646
3003
|
cursor = 0
|
2647
3004
|
loop do
|
2648
|
-
cursor, values = zscan(key, cursor, options)
|
3005
|
+
cursor, values = zscan(key, cursor, **options)
|
2649
3006
|
values.each(&block)
|
2650
3007
|
break if cursor == "0"
|
2651
3008
|
end
|
@@ -2662,8 +3019,8 @@ class Redis
|
|
2662
3019
|
# - `:count => Integer`: return count keys at most per iteration
|
2663
3020
|
#
|
2664
3021
|
# @return [String, Array<String>] the next cursor and all found members
|
2665
|
-
def sscan(key, cursor, options
|
2666
|
-
_scan(:sscan, cursor, [key], options)
|
3022
|
+
def sscan(key, cursor, **options)
|
3023
|
+
_scan(:sscan, cursor, [key], **options)
|
2667
3024
|
end
|
2668
3025
|
|
2669
3026
|
# Scan a set
|
@@ -2677,11 +3034,12 @@ class Redis
|
|
2677
3034
|
# - `:count => Integer`: return count keys at most per iteration
|
2678
3035
|
#
|
2679
3036
|
# @return [Enumerator] an enumerator for all keys in the set
|
2680
|
-
def sscan_each(key, options
|
2681
|
-
return to_enum(:sscan_each, key, options) unless block_given?
|
3037
|
+
def sscan_each(key, **options, &block)
|
3038
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
3039
|
+
|
2682
3040
|
cursor = 0
|
2683
3041
|
loop do
|
2684
|
-
cursor, keys = sscan(key, cursor, options)
|
3042
|
+
cursor, keys = sscan(key, cursor, **options)
|
2685
3043
|
keys.each(&block)
|
2686
3044
|
break if cursor == "0"
|
2687
3045
|
end
|
@@ -2704,7 +3062,7 @@ class Redis
|
|
2704
3062
|
# union of the HyperLogLogs contained in the keys.
|
2705
3063
|
#
|
2706
3064
|
# @param [String, Array<String>] keys
|
2707
|
-
# @return [
|
3065
|
+
# @return [Integer]
|
2708
3066
|
def pfcount(*keys)
|
2709
3067
|
synchronize do |client|
|
2710
3068
|
client.call([:pfcount] + keys)
|
@@ -2727,10 +3085,10 @@ class Redis
|
|
2727
3085
|
#
|
2728
3086
|
# @param [String] key
|
2729
3087
|
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
2730
|
-
# @return [
|
3088
|
+
# @return [Integer] number of elements added to the sorted set
|
2731
3089
|
def geoadd(key, *member)
|
2732
3090
|
synchronize do |client|
|
2733
|
-
client.call([:geoadd, key, member])
|
3091
|
+
client.call([:geoadd, key, *member])
|
2734
3092
|
end
|
2735
3093
|
end
|
2736
3094
|
|
@@ -2745,12 +3103,12 @@ class Redis
|
|
2745
3103
|
end
|
2746
3104
|
end
|
2747
3105
|
|
2748
|
-
|
2749
3106
|
# Query a sorted set representing a geospatial index to fetch members matching a
|
2750
3107
|
# given maximum distance from a point
|
2751
3108
|
#
|
2752
3109
|
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2753
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
3110
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
3111
|
+
# or the farthest to the nearest relative to the center
|
2754
3112
|
# @param [Integer] count limit the results to the first N matching items
|
2755
3113
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2756
3114
|
# @return [Array<String>] may be changed with `options`
|
@@ -2767,7 +3125,8 @@ class Redis
|
|
2767
3125
|
# given maximum distance from an already existing member
|
2768
3126
|
#
|
2769
3127
|
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2770
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
3128
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
3129
|
+
# to the nearest relative to the center
|
2771
3130
|
# @param [Integer] count limit the results to the first N matching items
|
2772
3131
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2773
3132
|
# @return [Array<String>] may be changed with `options`
|
@@ -2784,7 +3143,8 @@ class Redis
|
|
2784
3143
|
#
|
2785
3144
|
# @param [String] key
|
2786
3145
|
# @param [String, Array<String>] member one member or array of members
|
2787
|
-
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3146
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3147
|
+
# element is either array of longitude and latitude or nil
|
2788
3148
|
def geopos(key, member)
|
2789
3149
|
synchronize do |client|
|
2790
3150
|
client.call([:geopos, key, member])
|
@@ -2803,6 +3163,363 @@ class Redis
|
|
2803
3163
|
end
|
2804
3164
|
end
|
2805
3165
|
|
3166
|
+
# Returns the stream information each subcommand.
|
3167
|
+
#
|
3168
|
+
# @example stream
|
3169
|
+
# redis.xinfo(:stream, 'mystream')
|
3170
|
+
# @example groups
|
3171
|
+
# redis.xinfo(:groups, 'mystream')
|
3172
|
+
# @example consumers
|
3173
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
3174
|
+
#
|
3175
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
3176
|
+
# @param key [String] the stream key
|
3177
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
3178
|
+
#
|
3179
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
3180
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
3181
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
3182
|
+
def xinfo(subcommand, key, group = nil)
|
3183
|
+
args = [:xinfo, subcommand, key, group].compact
|
3184
|
+
synchronize do |client|
|
3185
|
+
client.call(args) do |reply|
|
3186
|
+
case subcommand.to_s.downcase
|
3187
|
+
when 'stream' then Hashify.call(reply)
|
3188
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
3189
|
+
else reply
|
3190
|
+
end
|
3191
|
+
end
|
3192
|
+
end
|
3193
|
+
end
|
3194
|
+
|
3195
|
+
# Add new entry to the stream.
|
3196
|
+
#
|
3197
|
+
# @example Without options
|
3198
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
3199
|
+
# @example With options
|
3200
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
3201
|
+
#
|
3202
|
+
# @param key [String] the stream key
|
3203
|
+
# @param entry [Hash] one or multiple field-value pairs
|
3204
|
+
# @param opts [Hash] several options for `XADD` command
|
3205
|
+
#
|
3206
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
3207
|
+
# @option opts [Integer] :maxlen max length of entries
|
3208
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
3209
|
+
#
|
3210
|
+
# @return [String] the entry id
|
3211
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
3212
|
+
args = [:xadd, key]
|
3213
|
+
if maxlen
|
3214
|
+
args << "MAXLEN"
|
3215
|
+
args << "~" if approximate
|
3216
|
+
args << maxlen
|
3217
|
+
end
|
3218
|
+
args << id
|
3219
|
+
args.concat(entry.to_a.flatten)
|
3220
|
+
synchronize { |client| client.call(args) }
|
3221
|
+
end
|
3222
|
+
|
3223
|
+
# Trims older entries of the stream if needed.
|
3224
|
+
#
|
3225
|
+
# @example Without options
|
3226
|
+
# redis.xtrim('mystream', 1000)
|
3227
|
+
# @example With options
|
3228
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
3229
|
+
#
|
3230
|
+
# @param key [String] the stream key
|
3231
|
+
# @param mexlen [Integer] max length of entries
|
3232
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
3233
|
+
#
|
3234
|
+
# @return [Integer] the number of entries actually deleted
|
3235
|
+
def xtrim(key, maxlen, approximate: false)
|
3236
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
3237
|
+
synchronize { |client| client.call(args) }
|
3238
|
+
end
|
3239
|
+
|
3240
|
+
# Delete entries by entry ids.
|
3241
|
+
#
|
3242
|
+
# @example With splatted entry ids
|
3243
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
3244
|
+
# @example With arrayed entry ids
|
3245
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
3246
|
+
#
|
3247
|
+
# @param key [String] the stream key
|
3248
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3249
|
+
#
|
3250
|
+
# @return [Integer] the number of entries actually deleted
|
3251
|
+
def xdel(key, *ids)
|
3252
|
+
args = [:xdel, key].concat(ids.flatten)
|
3253
|
+
synchronize { |client| client.call(args) }
|
3254
|
+
end
|
3255
|
+
|
3256
|
+
# Fetches entries of the stream in ascending order.
|
3257
|
+
#
|
3258
|
+
# @example Without options
|
3259
|
+
# redis.xrange('mystream')
|
3260
|
+
# @example With a specific start
|
3261
|
+
# redis.xrange('mystream', '0-1')
|
3262
|
+
# @example With a specific start and end
|
3263
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
3264
|
+
# @example With count options
|
3265
|
+
# redis.xrange('mystream', count: 10)
|
3266
|
+
#
|
3267
|
+
# @param key [String] the stream key
|
3268
|
+
# @param start [String] first entry id of range, default value is `-`
|
3269
|
+
# @param end [String] last entry id of range, default value is `+`
|
3270
|
+
# @param count [Integer] the number of entries as limit
|
3271
|
+
#
|
3272
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3273
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3274
|
+
args = [:xrange, key, start, range_end]
|
3275
|
+
args.concat(['COUNT', count]) if count
|
3276
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3277
|
+
end
|
3278
|
+
|
3279
|
+
# Fetches entries of the stream in descending order.
|
3280
|
+
#
|
3281
|
+
# @example Without options
|
3282
|
+
# redis.xrevrange('mystream')
|
3283
|
+
# @example With a specific end
|
3284
|
+
# redis.xrevrange('mystream', '0-3')
|
3285
|
+
# @example With a specific end and start
|
3286
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
3287
|
+
# @example With count options
|
3288
|
+
# redis.xrevrange('mystream', count: 10)
|
3289
|
+
#
|
3290
|
+
# @param key [String] the stream key
|
3291
|
+
# @param end [String] first entry id of range, default value is `+`
|
3292
|
+
# @param start [String] last entry id of range, default value is `-`
|
3293
|
+
# @params count [Integer] the number of entries as limit
|
3294
|
+
#
|
3295
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3296
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3297
|
+
args = [:xrevrange, key, range_end, start]
|
3298
|
+
args.concat(['COUNT', count]) if count
|
3299
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3300
|
+
end
|
3301
|
+
|
3302
|
+
# Returns the number of entries inside a stream.
|
3303
|
+
#
|
3304
|
+
# @example With key
|
3305
|
+
# redis.xlen('mystream')
|
3306
|
+
#
|
3307
|
+
# @param key [String] the stream key
|
3308
|
+
#
|
3309
|
+
# @return [Integer] the number of entries
|
3310
|
+
def xlen(key)
|
3311
|
+
synchronize { |client| client.call([:xlen, key]) }
|
3312
|
+
end
|
3313
|
+
|
3314
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
3315
|
+
#
|
3316
|
+
# @example With a key
|
3317
|
+
# redis.xread('mystream', '0-0')
|
3318
|
+
# @example With multiple keys
|
3319
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3320
|
+
# @example With count option
|
3321
|
+
# redis.xread('mystream', '0-0', count: 2)
|
3322
|
+
# @example With block option
|
3323
|
+
# redis.xread('mystream', '$', block: 1000)
|
3324
|
+
#
|
3325
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3326
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3327
|
+
# @param count [Integer] the number of entries as limit per stream
|
3328
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
3329
|
+
#
|
3330
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3331
|
+
def xread(keys, ids, count: nil, block: nil)
|
3332
|
+
args = [:xread]
|
3333
|
+
args << 'COUNT' << count if count
|
3334
|
+
args << 'BLOCK' << block.to_i if block
|
3335
|
+
_xread(args, keys, ids, block)
|
3336
|
+
end
|
3337
|
+
|
3338
|
+
# Manages the consumer group of the stream.
|
3339
|
+
#
|
3340
|
+
# @example With `create` subcommand
|
3341
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3342
|
+
# @example With `setid` subcommand
|
3343
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3344
|
+
# @example With `destroy` subcommand
|
3345
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3346
|
+
# @example With `delconsumer` subcommand
|
3347
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3348
|
+
#
|
3349
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3350
|
+
# @param key [String] the stream key
|
3351
|
+
# @param group [String] the consumer group name
|
3352
|
+
# @param id_or_consumer [String]
|
3353
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3354
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
3355
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3356
|
+
#
|
3357
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
3358
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3359
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3360
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3361
|
+
synchronize { |client| client.call(args) }
|
3362
|
+
end
|
3363
|
+
|
3364
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3365
|
+
# Optionally blocking.
|
3366
|
+
#
|
3367
|
+
# @example With a key
|
3368
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3369
|
+
# @example With multiple keys
|
3370
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3371
|
+
# @example With count option
|
3372
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3373
|
+
# @example With block option
|
3374
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3375
|
+
# @example With noack option
|
3376
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3377
|
+
#
|
3378
|
+
# @param group [String] the consumer group name
|
3379
|
+
# @param consumer [String] the consumer name
|
3380
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3381
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3382
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
3383
|
+
#
|
3384
|
+
# @option opts [Integer] :count the number of entries as limit
|
3385
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3386
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3387
|
+
#
|
3388
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3389
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3390
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
3391
|
+
args << 'COUNT' << count if count
|
3392
|
+
args << 'BLOCK' << block.to_i if block
|
3393
|
+
args << 'NOACK' if noack
|
3394
|
+
_xread(args, keys, ids, block)
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3398
|
+
#
|
3399
|
+
# @example With a entry id
|
3400
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3401
|
+
# @example With splatted entry ids
|
3402
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3403
|
+
# @example With arrayed entry ids
|
3404
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3405
|
+
#
|
3406
|
+
# @param key [String] the stream key
|
3407
|
+
# @param group [String] the consumer group name
|
3408
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3409
|
+
#
|
3410
|
+
# @return [Integer] the number of entries successfully acknowledged
|
3411
|
+
def xack(key, group, *ids)
|
3412
|
+
args = [:xack, key, group].concat(ids.flatten)
|
3413
|
+
synchronize { |client| client.call(args) }
|
3414
|
+
end
|
3415
|
+
|
3416
|
+
# Changes the ownership of a pending entry
|
3417
|
+
#
|
3418
|
+
# @example With splatted entry ids
|
3419
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3420
|
+
# @example With arrayed entry ids
|
3421
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3422
|
+
# @example With idle option
|
3423
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3424
|
+
# @example With time option
|
3425
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3426
|
+
# @example With retrycount option
|
3427
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3428
|
+
# @example With force option
|
3429
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3430
|
+
# @example With justid option
|
3431
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3432
|
+
#
|
3433
|
+
# @param key [String] the stream key
|
3434
|
+
# @param group [String] the consumer group name
|
3435
|
+
# @param consumer [String] the consumer name
|
3436
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3437
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3438
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
3439
|
+
#
|
3440
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3441
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3442
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
3443
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3444
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3445
|
+
#
|
3446
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3447
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3448
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3449
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3450
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3451
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3452
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3453
|
+
args << 'FORCE' if opts[:force]
|
3454
|
+
args << 'JUSTID' if opts[:justid]
|
3455
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3456
|
+
synchronize { |client| client.call(args, &blk) }
|
3457
|
+
end
|
3458
|
+
|
3459
|
+
# Transfers ownership of pending stream entries that match the specified criteria.
|
3460
|
+
#
|
3461
|
+
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
3462
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
3463
|
+
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
3464
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
3465
|
+
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
3466
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
3467
|
+
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
3468
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
3469
|
+
#
|
3470
|
+
# @param key [String] the stream key
|
3471
|
+
# @param group [String] the consumer group name
|
3472
|
+
# @param consumer [String] the consumer name
|
3473
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3474
|
+
# @param start [String] entry id to start scanning from or 0-0 for everything
|
3475
|
+
# @param count [Integer] number of messages to claim (default 1)
|
3476
|
+
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
3477
|
+
# Does not increment retry count when true
|
3478
|
+
#
|
3479
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3480
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3481
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
3482
|
+
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
3483
|
+
if count
|
3484
|
+
args << 'COUNT' << count.to_s
|
3485
|
+
end
|
3486
|
+
args << 'JUSTID' if justid
|
3487
|
+
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
3488
|
+
synchronize { |client| client.call(args, &blk) }
|
3489
|
+
end
|
3490
|
+
|
3491
|
+
# Fetches not acknowledging pending entries
|
3492
|
+
#
|
3493
|
+
# @example With key and group
|
3494
|
+
# redis.xpending('mystream', 'mygroup')
|
3495
|
+
# @example With range options
|
3496
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3497
|
+
# @example With range and consumer options
|
3498
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3499
|
+
#
|
3500
|
+
# @param key [String] the stream key
|
3501
|
+
# @param group [String] the consumer group name
|
3502
|
+
# @param start [String] start first entry id of range
|
3503
|
+
# @param end [String] end last entry id of range
|
3504
|
+
# @param count [Integer] count the number of entries as limit
|
3505
|
+
# @param consumer [String] the consumer name
|
3506
|
+
#
|
3507
|
+
# @return [Hash] the summary of pending entries
|
3508
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
3509
|
+
def xpending(key, group, *args)
|
3510
|
+
command_args = [:xpending, key, group]
|
3511
|
+
case args.size
|
3512
|
+
when 0, 3, 4
|
3513
|
+
command_args.concat(args)
|
3514
|
+
else
|
3515
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3516
|
+
end
|
3517
|
+
|
3518
|
+
summary_needed = args.empty?
|
3519
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3520
|
+
synchronize { |client| client.call(command_args, &blk) }
|
3521
|
+
end
|
3522
|
+
|
2806
3523
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2807
3524
|
#
|
2808
3525
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2816,8 +3533,8 @@ class Redis
|
|
2816
3533
|
when "get-master-addr-by-name"
|
2817
3534
|
reply
|
2818
3535
|
else
|
2819
|
-
if reply.
|
2820
|
-
if reply[0].
|
3536
|
+
if reply.is_a?(Array)
|
3537
|
+
if reply[0].is_a?(Array)
|
2821
3538
|
reply.map(&Hashify)
|
2822
3539
|
else
|
2823
3540
|
Hashify.call(reply)
|
@@ -2841,12 +3558,17 @@ class Redis
|
|
2841
3558
|
def cluster(subcommand, *args)
|
2842
3559
|
subcommand = subcommand.to_s.downcase
|
2843
3560
|
block = case subcommand
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
3561
|
+
when 'slots'
|
3562
|
+
HashifyClusterSlots
|
3563
|
+
when 'nodes'
|
3564
|
+
HashifyClusterNodes
|
3565
|
+
when 'slaves'
|
3566
|
+
HashifyClusterSlaves
|
3567
|
+
when 'info'
|
3568
|
+
HashifyInfo
|
3569
|
+
else
|
3570
|
+
Noop
|
3571
|
+
end
|
2850
3572
|
|
2851
3573
|
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
2852
3574
|
block = Noop unless @cluster_mode
|
@@ -2881,113 +3603,171 @@ class Redis
|
|
2881
3603
|
return @original_client.connection_info if @cluster_mode
|
2882
3604
|
|
2883
3605
|
{
|
2884
|
-
host:
|
2885
|
-
port:
|
2886
|
-
db:
|
2887
|
-
id:
|
3606
|
+
host: @original_client.host,
|
3607
|
+
port: @original_client.port,
|
3608
|
+
db: @original_client.db,
|
3609
|
+
id: @original_client.id,
|
2888
3610
|
location: @original_client.location
|
2889
3611
|
}
|
2890
3612
|
end
|
2891
3613
|
|
2892
|
-
def method_missing(command, *args)
|
3614
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
2893
3615
|
synchronize do |client|
|
2894
3616
|
client.call([command] + args)
|
2895
3617
|
end
|
2896
3618
|
end
|
2897
3619
|
|
2898
|
-
private
|
3620
|
+
private
|
2899
3621
|
|
2900
3622
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2901
3623
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2902
3624
|
# returning false.
|
2903
|
-
Boolify =
|
2904
|
-
|
2905
|
-
|
2906
|
-
|
3625
|
+
Boolify = lambda { |value|
|
3626
|
+
case value
|
3627
|
+
when 1
|
3628
|
+
true
|
3629
|
+
when 0
|
3630
|
+
false
|
3631
|
+
else
|
3632
|
+
value
|
3633
|
+
end
|
3634
|
+
}
|
2907
3635
|
|
2908
|
-
BoolifySet =
|
2909
|
-
|
2910
|
-
|
2911
|
-
|
2912
|
-
|
2913
|
-
|
2914
|
-
|
2915
|
-
|
3636
|
+
BoolifySet = lambda { |value|
|
3637
|
+
case value
|
3638
|
+
when "OK"
|
3639
|
+
true
|
3640
|
+
when nil
|
3641
|
+
false
|
3642
|
+
else
|
3643
|
+
value
|
3644
|
+
end
|
3645
|
+
}
|
2916
3646
|
|
2917
|
-
Hashify =
|
2918
|
-
|
2919
|
-
|
2920
|
-
|
2921
|
-
|
2922
|
-
|
2923
|
-
|
2924
|
-
|
3647
|
+
Hashify = lambda { |value|
|
3648
|
+
if value.respond_to?(:each_slice)
|
3649
|
+
value.each_slice(2).to_h
|
3650
|
+
else
|
3651
|
+
value
|
3652
|
+
end
|
3653
|
+
}
|
3654
|
+
|
3655
|
+
Floatify = lambda { |value|
|
3656
|
+
case value
|
3657
|
+
when "inf"
|
3658
|
+
Float::INFINITY
|
3659
|
+
when "-inf"
|
3660
|
+
-Float::INFINITY
|
3661
|
+
when String
|
3662
|
+
Float(value)
|
3663
|
+
else
|
3664
|
+
value
|
3665
|
+
end
|
3666
|
+
}
|
2925
3667
|
|
2926
|
-
|
2927
|
-
|
2928
|
-
|
2929
|
-
|
2930
|
-
|
2931
|
-
|
2932
|
-
|
2933
|
-
|
2934
|
-
|
3668
|
+
FloatifyPairs = lambda { |value|
|
3669
|
+
return value unless value.respond_to?(:each_slice)
|
3670
|
+
|
3671
|
+
value.each_slice(2).map do |member, score|
|
3672
|
+
[member, Floatify.call(score)]
|
3673
|
+
end
|
3674
|
+
}
|
3675
|
+
|
3676
|
+
HashifyInfo = lambda { |reply|
|
3677
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3678
|
+
lines.map! { |line| line.split(':', 2) }
|
3679
|
+
lines.compact!
|
3680
|
+
lines.to_h
|
3681
|
+
}
|
3682
|
+
|
3683
|
+
HashifyStreams = lambda { |reply|
|
3684
|
+
case reply
|
3685
|
+
when nil
|
3686
|
+
{}
|
3687
|
+
else
|
3688
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3689
|
+
end
|
3690
|
+
}
|
3691
|
+
|
3692
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3693
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3694
|
+
|
3695
|
+
HashifyStreamEntries = lambda { |reply|
|
3696
|
+
reply.compact.map do |entry_id, values|
|
3697
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
3698
|
+
end
|
3699
|
+
}
|
3700
|
+
|
3701
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
3702
|
+
{
|
3703
|
+
'next' => reply[0],
|
3704
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
2935
3705
|
}
|
3706
|
+
}
|
2936
3707
|
|
2937
|
-
|
2938
|
-
|
2939
|
-
|
2940
|
-
|
2941
|
-
end
|
3708
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
+
{
|
3710
|
+
'next' => reply[0],
|
3711
|
+
'entries' => reply[1]
|
2942
3712
|
}
|
3713
|
+
}
|
2943
3714
|
|
2944
|
-
|
2945
|
-
|
2946
|
-
|
2947
|
-
|
2948
|
-
|
3715
|
+
HashifyStreamPendings = lambda { |reply|
|
3716
|
+
{
|
3717
|
+
'size' => reply[0],
|
3718
|
+
'min_entry_id' => reply[1],
|
3719
|
+
'max_entry_id' => reply[2],
|
3720
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
2949
3721
|
}
|
3722
|
+
}
|
2950
3723
|
|
2951
|
-
|
2952
|
-
|
2953
|
-
arr = str.split(' ')
|
3724
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3725
|
+
reply.map do |arr|
|
2954
3726
|
{
|
2955
|
-
'
|
2956
|
-
'
|
2957
|
-
'
|
2958
|
-
'
|
2959
|
-
'ping_sent' => arr[4],
|
2960
|
-
'pong_recv' => arr[5],
|
2961
|
-
'config_epoch' => arr[6],
|
2962
|
-
'link_state' => arr[7],
|
2963
|
-
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3727
|
+
'entry_id' => arr[0],
|
3728
|
+
'consumer' => arr[1],
|
3729
|
+
'elapsed' => arr[2],
|
3730
|
+
'count' => arr[3]
|
2964
3731
|
}
|
2965
|
-
|
3732
|
+
end
|
3733
|
+
}
|
2966
3734
|
|
2967
|
-
|
2968
|
-
|
2969
|
-
|
2970
|
-
|
2971
|
-
|
2972
|
-
|
2973
|
-
|
2974
|
-
|
2975
|
-
|
2976
|
-
|
2977
|
-
|
2978
|
-
|
2979
|
-
end
|
3735
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3736
|
+
arr = str.split(' ')
|
3737
|
+
{
|
3738
|
+
'node_id' => arr[0],
|
3739
|
+
'ip_port' => arr[1],
|
3740
|
+
'flags' => arr[2].split(','),
|
3741
|
+
'master_node_id' => arr[3],
|
3742
|
+
'ping_sent' => arr[4],
|
3743
|
+
'pong_recv' => arr[5],
|
3744
|
+
'config_epoch' => arr[6],
|
3745
|
+
'link_state' => arr[7],
|
3746
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
2980
3747
|
}
|
3748
|
+
}
|
2981
3749
|
|
2982
|
-
|
2983
|
-
|
2984
|
-
|
2985
|
-
|
3750
|
+
HashifyClusterSlots = lambda { |reply|
|
3751
|
+
reply.map do |arr|
|
3752
|
+
first_slot, last_slot = arr[0..1]
|
3753
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3754
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3755
|
+
{
|
3756
|
+
'start_slot' => first_slot,
|
3757
|
+
'end_slot' => last_slot,
|
3758
|
+
'master' => master,
|
3759
|
+
'replicas' => replicas
|
3760
|
+
}
|
3761
|
+
end
|
3762
|
+
}
|
2986
3763
|
|
2987
|
-
|
2988
|
-
|
2989
|
-
|
2990
|
-
|
3764
|
+
HashifyClusterNodes = lambda { |reply|
|
3765
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3766
|
+
}
|
3767
|
+
|
3768
|
+
HashifyClusterSlaves = lambda { |reply|
|
3769
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3770
|
+
}
|
2991
3771
|
|
2992
3772
|
Noop = ->(reply) { reply }
|
2993
3773
|
|
@@ -2995,8 +3775,7 @@ private
|
|
2995
3775
|
args.push sort if sort
|
2996
3776
|
args.push 'count', count if count
|
2997
3777
|
args.push options if options
|
2998
|
-
|
2999
|
-
args.uniq
|
3778
|
+
args
|
3000
3779
|
end
|
3001
3780
|
|
3002
3781
|
def _subscription(method, timeout, channels, block)
|
@@ -3013,6 +3792,40 @@ private
|
|
3013
3792
|
@client = original
|
3014
3793
|
end
|
3015
3794
|
end
|
3795
|
+
|
3796
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
3797
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
3798
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
3799
|
+
args << 'STREAMS'
|
3800
|
+
args.concat(keys)
|
3801
|
+
args.concat(ids)
|
3802
|
+
|
3803
|
+
synchronize do |client|
|
3804
|
+
if blocking_timeout_msec.nil?
|
3805
|
+
client.call(args, &HashifyStreams)
|
3806
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3807
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3808
|
+
else
|
3809
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3810
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3811
|
+
end
|
3812
|
+
end
|
3813
|
+
end
|
3814
|
+
|
3815
|
+
def _normalize_move_wheres(where_source, where_destination)
|
3816
|
+
where_source = where_source.to_s.upcase
|
3817
|
+
where_destination = where_destination.to_s.upcase
|
3818
|
+
|
3819
|
+
if where_source != "LEFT" && where_source != "RIGHT"
|
3820
|
+
raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
|
3821
|
+
end
|
3822
|
+
|
3823
|
+
if where_destination != "LEFT" && where_destination != "RIGHT"
|
3824
|
+
raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
|
3825
|
+
end
|
3826
|
+
|
3827
|
+
[where_source, where_destination]
|
3828
|
+
end
|
3016
3829
|
end
|
3017
3830
|
|
3018
3831
|
require_relative "redis/version"
|