redis 4.1.0 → 4.5.1
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 +107 -0
- data/README.md +81 -17
- data/lib/redis/client.rb +137 -82
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +5 -1
- data/lib/redis/cluster/node_key.rb +3 -7
- data/lib/redis/cluster/option.rb +30 -14
- data/lib/redis/cluster/slot.rb +30 -13
- data/lib/redis/cluster/slot_loader.rb +4 -4
- data/lib/redis/cluster.rb +22 -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 +121 -105
- data/lib/redis/connection/synchrony.rb +9 -4
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/distributed.rb +170 -68
- 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 +808 -476
- metadata +15 -25
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)
|
@@ -525,8 +555,11 @@ class Redis
|
|
525
555
|
# Delete one or more keys.
|
526
556
|
#
|
527
557
|
# @param [String, Array<String>] keys
|
528
|
-
# @return [
|
558
|
+
# @return [Integer] number of keys that were deleted
|
529
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
530
563
|
synchronize do |client|
|
531
564
|
client.call([:del] + keys)
|
532
565
|
end
|
@@ -535,20 +568,50 @@ class Redis
|
|
535
568
|
# Unlink one or more keys.
|
536
569
|
#
|
537
570
|
# @param [String, Array<String>] keys
|
538
|
-
# @return [
|
571
|
+
# @return [Integer] number of keys that were unlinked
|
539
572
|
def unlink(*keys)
|
540
573
|
synchronize do |client|
|
541
574
|
client.call([:unlink] + keys)
|
542
575
|
end
|
543
576
|
end
|
544
577
|
|
545
|
-
# Determine
|
578
|
+
# Determine how many of the keys exists.
|
546
579
|
#
|
547
|
-
# @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
|
548
609
|
# @return [Boolean]
|
549
|
-
def exists(
|
610
|
+
def exists?(*keys)
|
550
611
|
synchronize do |client|
|
551
|
-
client.call([:exists,
|
612
|
+
client.call([:exists, *keys]) do |value|
|
613
|
+
value > 0
|
614
|
+
end
|
552
615
|
end
|
553
616
|
end
|
554
617
|
|
@@ -559,7 +622,7 @@ class Redis
|
|
559
622
|
def keys(pattern = "*")
|
560
623
|
synchronize do |client|
|
561
624
|
client.call([:keys, pattern]) do |reply|
|
562
|
-
if reply.
|
625
|
+
if reply.is_a?(String)
|
563
626
|
reply.split(" ")
|
564
627
|
else
|
565
628
|
reply
|
@@ -585,7 +648,7 @@ class Redis
|
|
585
648
|
# # => "bar"
|
586
649
|
#
|
587
650
|
# @param [String] key
|
588
|
-
# @param [
|
651
|
+
# @param [Integer] db
|
589
652
|
# @return [Boolean] whether the key was moved or not
|
590
653
|
def move(key, db)
|
591
654
|
synchronize do |client|
|
@@ -649,36 +712,33 @@ class Redis
|
|
649
712
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
650
713
|
# - `:store => String`: key to store the result at
|
651
714
|
#
|
652
|
-
# @return [Array<String>, Array<Array<String>>,
|
715
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
653
716
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
654
717
|
# - when `:get` is specified, and holds more than one element, an array of
|
655
718
|
# elements where every element is an array with the result for every
|
656
719
|
# element specified in `:get`
|
657
720
|
# - when `:store` is specified, the number of elements in the stored result
|
658
|
-
def sort(key,
|
659
|
-
args = []
|
660
|
-
|
661
|
-
by = options[:by]
|
662
|
-
args.concat(["BY", by]) if by
|
721
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
722
|
+
args = [:sort, key]
|
723
|
+
args << "BY" << by if by
|
663
724
|
|
664
|
-
|
665
|
-
|
725
|
+
if limit
|
726
|
+
args << "LIMIT"
|
727
|
+
args.concat(limit)
|
728
|
+
end
|
666
729
|
|
667
|
-
get = Array(
|
668
|
-
|
730
|
+
get = Array(get)
|
731
|
+
get.each do |item|
|
732
|
+
args << "GET" << item
|
733
|
+
end
|
669
734
|
|
670
|
-
order = options[:order]
|
671
735
|
args.concat(order.split(" ")) if order
|
672
|
-
|
673
|
-
store = options[:store]
|
674
|
-
args.concat(["STORE", store]) if store
|
736
|
+
args << "STORE" << store if store
|
675
737
|
|
676
738
|
synchronize do |client|
|
677
|
-
client.call(
|
739
|
+
client.call(args) do |reply|
|
678
740
|
if get.size > 1 && !store
|
679
|
-
if reply
|
680
|
-
reply.each_slice(get.size).to_a
|
681
|
-
end
|
741
|
+
reply.each_slice(get.size).to_a if reply
|
682
742
|
else
|
683
743
|
reply
|
684
744
|
end
|
@@ -703,7 +763,7 @@ class Redis
|
|
703
763
|
# # => 4
|
704
764
|
#
|
705
765
|
# @param [String] key
|
706
|
-
# @return [
|
766
|
+
# @return [Integer] value after decrementing it
|
707
767
|
def decr(key)
|
708
768
|
synchronize do |client|
|
709
769
|
client.call([:decr, key])
|
@@ -717,8 +777,8 @@ class Redis
|
|
717
777
|
# # => 0
|
718
778
|
#
|
719
779
|
# @param [String] key
|
720
|
-
# @param [
|
721
|
-
# @return [
|
780
|
+
# @param [Integer] decrement
|
781
|
+
# @return [Integer] value after decrementing it
|
722
782
|
def decrby(key, decrement)
|
723
783
|
synchronize do |client|
|
724
784
|
client.call([:decrby, key, decrement])
|
@@ -732,7 +792,7 @@ class Redis
|
|
732
792
|
# # => 6
|
733
793
|
#
|
734
794
|
# @param [String] key
|
735
|
-
# @return [
|
795
|
+
# @return [Integer] value after incrementing it
|
736
796
|
def incr(key)
|
737
797
|
synchronize do |client|
|
738
798
|
client.call([:incr, key])
|
@@ -746,8 +806,8 @@ class Redis
|
|
746
806
|
# # => 10
|
747
807
|
#
|
748
808
|
# @param [String] key
|
749
|
-
# @param [
|
750
|
-
# @return [
|
809
|
+
# @param [Integer] increment
|
810
|
+
# @return [Integer] value after incrementing it
|
751
811
|
def incrby(key, increment)
|
752
812
|
synchronize do |client|
|
753
813
|
client.call([:incrby, key, increment])
|
@@ -774,31 +834,31 @@ class Redis
|
|
774
834
|
# @param [String] key
|
775
835
|
# @param [String] value
|
776
836
|
# @param [Hash] options
|
777
|
-
# - `:ex =>
|
778
|
-
# - `: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.
|
779
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
780
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.
|
781
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
782
|
-
def set(key, value,
|
783
|
-
args = []
|
784
|
-
|
785
|
-
|
786
|
-
args
|
787
|
-
|
788
|
-
|
789
|
-
args
|
790
|
-
|
791
|
-
|
792
|
-
args.concat(["NX"]) if nx
|
793
|
-
|
794
|
-
xx = options[:xx]
|
795
|
-
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
|
796
856
|
|
797
857
|
synchronize do |client|
|
798
858
|
if nx || xx
|
799
|
-
client.call(
|
859
|
+
client.call(args, &BoolifySet)
|
800
860
|
else
|
801
|
-
client.call(
|
861
|
+
client.call(args)
|
802
862
|
end
|
803
863
|
end
|
804
864
|
end
|
@@ -806,7 +866,7 @@ class Redis
|
|
806
866
|
# Set the time to live in seconds of a key.
|
807
867
|
#
|
808
868
|
# @param [String] key
|
809
|
-
# @param [
|
869
|
+
# @param [Integer] ttl
|
810
870
|
# @param [String] value
|
811
871
|
# @return [String] `"OK"`
|
812
872
|
def setex(key, ttl, value)
|
@@ -818,7 +878,7 @@ class Redis
|
|
818
878
|
# Set the time to live in milliseconds of a key.
|
819
879
|
#
|
820
880
|
# @param [String] key
|
821
|
-
# @param [
|
881
|
+
# @param [Integer] ttl
|
822
882
|
# @param [String] value
|
823
883
|
# @return [String] `"OK"`
|
824
884
|
def psetex(key, ttl, value)
|
@@ -880,7 +940,7 @@ class Redis
|
|
880
940
|
# @see #mapped_msetnx
|
881
941
|
def msetnx(*args)
|
882
942
|
synchronize do |client|
|
883
|
-
client.call([:msetnx
|
943
|
+
client.call([:msetnx, *args], &Boolify)
|
884
944
|
end
|
885
945
|
end
|
886
946
|
|
@@ -911,7 +971,7 @@ class Redis
|
|
911
971
|
# Get the values of all the given keys.
|
912
972
|
#
|
913
973
|
# @example
|
914
|
-
# redis.mget("key1", "
|
974
|
+
# redis.mget("key1", "key2")
|
915
975
|
# # => ["v1", "v2"]
|
916
976
|
#
|
917
977
|
# @param [Array<String>] keys
|
@@ -920,7 +980,7 @@ class Redis
|
|
920
980
|
# @see #mapped_mget
|
921
981
|
def mget(*keys, &blk)
|
922
982
|
synchronize do |client|
|
923
|
-
client.call([:mget
|
983
|
+
client.call([:mget, *keys], &blk)
|
924
984
|
end
|
925
985
|
end
|
926
986
|
|
@@ -936,7 +996,7 @@ class Redis
|
|
936
996
|
# @see #mget
|
937
997
|
def mapped_mget(*keys)
|
938
998
|
mget(*keys) do |reply|
|
939
|
-
if reply.
|
999
|
+
if reply.is_a?(Array)
|
940
1000
|
Hash[keys.zip(reply)]
|
941
1001
|
else
|
942
1002
|
reply
|
@@ -947,9 +1007,9 @@ class Redis
|
|
947
1007
|
# Overwrite part of a string at key starting at the specified offset.
|
948
1008
|
#
|
949
1009
|
# @param [String] key
|
950
|
-
# @param [
|
1010
|
+
# @param [Integer] offset byte offset
|
951
1011
|
# @param [String] value
|
952
|
-
# @return [
|
1012
|
+
# @return [Integer] length of the string after it was modified
|
953
1013
|
def setrange(key, offset, value)
|
954
1014
|
synchronize do |client|
|
955
1015
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -959,10 +1019,10 @@ class Redis
|
|
959
1019
|
# Get a substring of the string stored at a key.
|
960
1020
|
#
|
961
1021
|
# @param [String] key
|
962
|
-
# @param [
|
963
|
-
# @param [
|
1022
|
+
# @param [Integer] start zero-based start offset
|
1023
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
964
1024
|
# the end of the string
|
965
|
-
# @return [
|
1025
|
+
# @return [Integer] `0` or `1`
|
966
1026
|
def getrange(key, start, stop)
|
967
1027
|
synchronize do |client|
|
968
1028
|
client.call([:getrange, key, start, stop])
|
@@ -972,9 +1032,9 @@ class Redis
|
|
972
1032
|
# Sets or clears the bit at offset in the string value stored at key.
|
973
1033
|
#
|
974
1034
|
# @param [String] key
|
975
|
-
# @param [
|
976
|
-
# @param [
|
977
|
-
# @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`
|
978
1038
|
def setbit(key, offset, value)
|
979
1039
|
synchronize do |client|
|
980
1040
|
client.call([:setbit, key, offset, value])
|
@@ -984,8 +1044,8 @@ class Redis
|
|
984
1044
|
# Returns the bit value at offset in the string value stored at key.
|
985
1045
|
#
|
986
1046
|
# @param [String] key
|
987
|
-
# @param [
|
988
|
-
# @return [
|
1047
|
+
# @param [Integer] offset bit offset
|
1048
|
+
# @return [Integer] `0` or `1`
|
989
1049
|
def getbit(key, offset)
|
990
1050
|
synchronize do |client|
|
991
1051
|
client.call([:getbit, key, offset])
|
@@ -996,7 +1056,7 @@ class Redis
|
|
996
1056
|
#
|
997
1057
|
# @param [String] key
|
998
1058
|
# @param [String] value value to append
|
999
|
-
# @return [
|
1059
|
+
# @return [Integer] length of the string after appending
|
1000
1060
|
def append(key, value)
|
1001
1061
|
synchronize do |client|
|
1002
1062
|
client.call([:append, key, value])
|
@@ -1006,9 +1066,9 @@ class Redis
|
|
1006
1066
|
# Count the number of set bits in a range of the string value stored at key.
|
1007
1067
|
#
|
1008
1068
|
# @param [String] key
|
1009
|
-
# @param [
|
1010
|
-
# @param [
|
1011
|
-
# @return [
|
1069
|
+
# @param [Integer] start start index
|
1070
|
+
# @param [Integer] stop stop index
|
1071
|
+
# @return [Integer] the number of bits set to 1
|
1012
1072
|
def bitcount(key, start = 0, stop = -1)
|
1013
1073
|
synchronize do |client|
|
1014
1074
|
client.call([:bitcount, key, start, stop])
|
@@ -1020,25 +1080,23 @@ class Redis
|
|
1020
1080
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
1021
1081
|
# @param [String] destkey destination key
|
1022
1082
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
1023
|
-
# @return [
|
1083
|
+
# @return [Integer] the length of the string stored in `destkey`
|
1024
1084
|
def bitop(operation, destkey, *keys)
|
1025
1085
|
synchronize do |client|
|
1026
|
-
client.call([:bitop, operation, destkey
|
1086
|
+
client.call([:bitop, operation, destkey, *keys])
|
1027
1087
|
end
|
1028
1088
|
end
|
1029
1089
|
|
1030
1090
|
# Return the position of the first bit set to 1 or 0 in a string.
|
1031
1091
|
#
|
1032
1092
|
# @param [String] key
|
1033
|
-
# @param [
|
1034
|
-
# @param [
|
1035
|
-
# @param [
|
1036
|
-
# @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.
|
1037
1097
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
1038
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
1039
|
-
|
1040
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
1041
|
-
end
|
1098
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1099
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1042
1100
|
|
1043
1101
|
synchronize do |client|
|
1044
1102
|
command = [:bitpos, key, bit]
|
@@ -1060,10 +1118,49 @@ class Redis
|
|
1060
1118
|
end
|
1061
1119
|
end
|
1062
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
|
+
|
1063
1160
|
# Get the length of the value stored in a key.
|
1064
1161
|
#
|
1065
1162
|
# @param [String] key
|
1066
|
-
# @return [
|
1163
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1067
1164
|
# if the key does not exist
|
1068
1165
|
def strlen(key)
|
1069
1166
|
synchronize do |client|
|
@@ -1074,18 +1171,71 @@ class Redis
|
|
1074
1171
|
# Get the length of a list.
|
1075
1172
|
#
|
1076
1173
|
# @param [String] key
|
1077
|
-
# @return [
|
1174
|
+
# @return [Integer]
|
1078
1175
|
def llen(key)
|
1079
1176
|
synchronize do |client|
|
1080
1177
|
client.call([:llen, key])
|
1081
1178
|
end
|
1082
1179
|
end
|
1083
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
|
+
|
1084
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1085
1235
|
#
|
1086
1236
|
# @param [String] key
|
1087
1237
|
# @param [String, Array<String>] value string value, or array of string values to push
|
1088
|
-
# @return [
|
1238
|
+
# @return [Integer] the length of the list after the push operation
|
1089
1239
|
def lpush(key, value)
|
1090
1240
|
synchronize do |client|
|
1091
1241
|
client.call([:lpush, key, value])
|
@@ -1096,7 +1246,7 @@ class Redis
|
|
1096
1246
|
#
|
1097
1247
|
# @param [String] key
|
1098
1248
|
# @param [String] value
|
1099
|
-
# @return [
|
1249
|
+
# @return [Integer] the length of the list after the push operation
|
1100
1250
|
def lpushx(key, value)
|
1101
1251
|
synchronize do |client|
|
1102
1252
|
client.call([:lpushx, key, value])
|
@@ -1107,7 +1257,7 @@ class Redis
|
|
1107
1257
|
#
|
1108
1258
|
# @param [String] key
|
1109
1259
|
# @param [String, Array<String>] value string value, or array of string values to push
|
1110
|
-
# @return [
|
1260
|
+
# @return [Integer] the length of the list after the push operation
|
1111
1261
|
def rpush(key, value)
|
1112
1262
|
synchronize do |client|
|
1113
1263
|
client.call([:rpush, key, value])
|
@@ -1118,30 +1268,36 @@ class Redis
|
|
1118
1268
|
#
|
1119
1269
|
# @param [String] key
|
1120
1270
|
# @param [String] value
|
1121
|
-
# @return [
|
1271
|
+
# @return [Integer] the length of the list after the push operation
|
1122
1272
|
def rpushx(key, value)
|
1123
1273
|
synchronize do |client|
|
1124
1274
|
client.call([:rpushx, key, value])
|
1125
1275
|
end
|
1126
1276
|
end
|
1127
1277
|
|
1128
|
-
# Remove and get the first
|
1278
|
+
# Remove and get the first elements in a list.
|
1129
1279
|
#
|
1130
1280
|
# @param [String] key
|
1131
|
-
# @
|
1132
|
-
|
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)
|
1133
1284
|
synchronize do |client|
|
1134
|
-
|
1285
|
+
command = [:lpop, key]
|
1286
|
+
command << count if count
|
1287
|
+
client.call(command)
|
1135
1288
|
end
|
1136
1289
|
end
|
1137
1290
|
|
1138
|
-
# Remove and get the last
|
1291
|
+
# Remove and get the last elements in a list.
|
1139
1292
|
#
|
1140
1293
|
# @param [String] key
|
1141
|
-
# @
|
1142
|
-
|
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)
|
1143
1297
|
synchronize do |client|
|
1144
|
-
|
1298
|
+
command = [:rpop, key]
|
1299
|
+
command << count if count
|
1300
|
+
client.call(command)
|
1145
1301
|
end
|
1146
1302
|
end
|
1147
1303
|
|
@@ -1157,21 +1313,21 @@ class Redis
|
|
1157
1313
|
end
|
1158
1314
|
|
1159
1315
|
def _bpop(cmd, args, &blk)
|
1160
|
-
|
1161
|
-
|
1162
|
-
if args.last.is_a?(Hash)
|
1316
|
+
timeout = if args.last.is_a?(Hash)
|
1163
1317
|
options = args.pop
|
1318
|
+
options[:timeout]
|
1164
1319
|
elsif args.last.respond_to?(:to_int)
|
1165
1320
|
# Issue deprecation notice in obnoxious mode...
|
1166
|
-
|
1321
|
+
args.pop.to_int
|
1167
1322
|
end
|
1168
1323
|
|
1324
|
+
timeout ||= 0
|
1325
|
+
|
1169
1326
|
if args.size > 1
|
1170
1327
|
# Issue deprecation notice in obnoxious mode...
|
1171
1328
|
end
|
1172
1329
|
|
1173
1330
|
keys = args.flatten
|
1174
|
-
timeout = options[:timeout] || 0
|
1175
1331
|
|
1176
1332
|
synchronize do |client|
|
1177
1333
|
command = [cmd, keys, timeout]
|
@@ -1196,7 +1352,7 @@ class Redis
|
|
1196
1352
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1197
1353
|
# blocking pop on
|
1198
1354
|
# @param [Hash] options
|
1199
|
-
# - `:timeout =>
|
1355
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1200
1356
|
#
|
1201
1357
|
# @return [nil, [String, String]]
|
1202
1358
|
# - `nil` when the operation timed out
|
@@ -1210,7 +1366,7 @@ class Redis
|
|
1210
1366
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1211
1367
|
# blocking pop on
|
1212
1368
|
# @param [Hash] options
|
1213
|
-
# - `:timeout =>
|
1369
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1214
1370
|
#
|
1215
1371
|
# @return [nil, [String, String]]
|
1216
1372
|
# - `nil` when the operation timed out
|
@@ -1227,20 +1383,12 @@ class Redis
|
|
1227
1383
|
# @param [String] source source key
|
1228
1384
|
# @param [String] destination destination key
|
1229
1385
|
# @param [Hash] options
|
1230
|
-
# - `:timeout =>
|
1386
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1231
1387
|
#
|
1232
1388
|
# @return [nil, String]
|
1233
1389
|
# - `nil` when the operation timed out
|
1234
1390
|
# - the element was popped and pushed otherwise
|
1235
|
-
def brpoplpush(source, destination,
|
1236
|
-
case options
|
1237
|
-
when Integer
|
1238
|
-
# Issue deprecation notice in obnoxious mode...
|
1239
|
-
options = { :timeout => options }
|
1240
|
-
end
|
1241
|
-
|
1242
|
-
timeout = options[:timeout] || 0
|
1243
|
-
|
1391
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1244
1392
|
synchronize do |client|
|
1245
1393
|
command = [:brpoplpush, source, destination, timeout]
|
1246
1394
|
timeout += client.timeout if timeout > 0
|
@@ -1251,7 +1399,7 @@ class Redis
|
|
1251
1399
|
# Get an element from a list by its index.
|
1252
1400
|
#
|
1253
1401
|
# @param [String] key
|
1254
|
-
# @param [
|
1402
|
+
# @param [Integer] index
|
1255
1403
|
# @return [String]
|
1256
1404
|
def lindex(key, index)
|
1257
1405
|
synchronize do |client|
|
@@ -1265,7 +1413,7 @@ class Redis
|
|
1265
1413
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1266
1414
|
# @param [String] pivot reference element
|
1267
1415
|
# @param [String] value
|
1268
|
-
# @return [
|
1416
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1269
1417
|
# when the element `pivot` was not found
|
1270
1418
|
def linsert(key, where, pivot, value)
|
1271
1419
|
synchronize do |client|
|
@@ -1276,8 +1424,8 @@ class Redis
|
|
1276
1424
|
# Get a range of elements from a list.
|
1277
1425
|
#
|
1278
1426
|
# @param [String] key
|
1279
|
-
# @param [
|
1280
|
-
# @param [
|
1427
|
+
# @param [Integer] start start index
|
1428
|
+
# @param [Integer] stop stop index
|
1281
1429
|
# @return [Array<String>]
|
1282
1430
|
def lrange(key, start, stop)
|
1283
1431
|
synchronize do |client|
|
@@ -1288,12 +1436,12 @@ class Redis
|
|
1288
1436
|
# Remove elements from a list.
|
1289
1437
|
#
|
1290
1438
|
# @param [String] key
|
1291
|
-
# @param [
|
1439
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1292
1440
|
# value to remove the first `count` occurrences of `value`. A negative
|
1293
1441
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1294
1442
|
# remove all occurrences of `value` from the list.
|
1295
1443
|
# @param [String] value
|
1296
|
-
# @return [
|
1444
|
+
# @return [Integer] the number of removed elements
|
1297
1445
|
def lrem(key, count, value)
|
1298
1446
|
synchronize do |client|
|
1299
1447
|
client.call([:lrem, key, count, value])
|
@@ -1303,7 +1451,7 @@ class Redis
|
|
1303
1451
|
# Set the value of an element in a list by its index.
|
1304
1452
|
#
|
1305
1453
|
# @param [String] key
|
1306
|
-
# @param [
|
1454
|
+
# @param [Integer] index
|
1307
1455
|
# @param [String] value
|
1308
1456
|
# @return [String] `OK`
|
1309
1457
|
def lset(key, index, value)
|
@@ -1315,8 +1463,8 @@ class Redis
|
|
1315
1463
|
# Trim a list to the specified range.
|
1316
1464
|
#
|
1317
1465
|
# @param [String] key
|
1318
|
-
# @param [
|
1319
|
-
# @param [
|
1466
|
+
# @param [Integer] start start index
|
1467
|
+
# @param [Integer] stop stop index
|
1320
1468
|
# @return [String] `OK`
|
1321
1469
|
def ltrim(key, start, stop)
|
1322
1470
|
synchronize do |client|
|
@@ -1327,7 +1475,7 @@ class Redis
|
|
1327
1475
|
# Get the number of members in a set.
|
1328
1476
|
#
|
1329
1477
|
# @param [String] key
|
1330
|
-
# @return [
|
1478
|
+
# @return [Integer]
|
1331
1479
|
def scard(key)
|
1332
1480
|
synchronize do |client|
|
1333
1481
|
client.call([:scard, key])
|
@@ -1338,8 +1486,8 @@ class Redis
|
|
1338
1486
|
#
|
1339
1487
|
# @param [String] key
|
1340
1488
|
# @param [String, Array<String>] member one member, or array of members
|
1341
|
-
# @return [Boolean,
|
1342
|
-
# 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
|
1343
1491
|
# array of members is specified, holding the number of members that were
|
1344
1492
|
# successfully added
|
1345
1493
|
def sadd(key, member)
|
@@ -1360,8 +1508,8 @@ class Redis
|
|
1360
1508
|
#
|
1361
1509
|
# @param [String] key
|
1362
1510
|
# @param [String, Array<String>] member one member, or array of members
|
1363
|
-
# @return [Boolean,
|
1364
|
-
# 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
|
1365
1513
|
# array of members is specified, holding the number of members that were
|
1366
1514
|
# successfully removed
|
1367
1515
|
def srem(key, member)
|
@@ -1382,7 +1530,7 @@ class Redis
|
|
1382
1530
|
#
|
1383
1531
|
# @param [String] key
|
1384
1532
|
# @return [String]
|
1385
|
-
# @param [
|
1533
|
+
# @param [Integer] count
|
1386
1534
|
def spop(key, count = nil)
|
1387
1535
|
synchronize do |client|
|
1388
1536
|
if count.nil?
|
@@ -1396,7 +1544,7 @@ class Redis
|
|
1396
1544
|
# Get one or more random members from a set.
|
1397
1545
|
#
|
1398
1546
|
# @param [String] key
|
1399
|
-
# @param [
|
1547
|
+
# @param [Integer] count
|
1400
1548
|
# @return [String]
|
1401
1549
|
def srandmember(key, count = nil)
|
1402
1550
|
synchronize do |client|
|
@@ -1431,6 +1579,19 @@ class Redis
|
|
1431
1579
|
end
|
1432
1580
|
end
|
1433
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
|
+
|
1434
1595
|
# Get all the members in a set.
|
1435
1596
|
#
|
1436
1597
|
# @param [String] key
|
@@ -1447,7 +1608,7 @@ class Redis
|
|
1447
1608
|
# @return [Array<String>] members in the difference
|
1448
1609
|
def sdiff(*keys)
|
1449
1610
|
synchronize do |client|
|
1450
|
-
client.call([:sdiff
|
1611
|
+
client.call([:sdiff, *keys])
|
1451
1612
|
end
|
1452
1613
|
end
|
1453
1614
|
|
@@ -1455,10 +1616,10 @@ class Redis
|
|
1455
1616
|
#
|
1456
1617
|
# @param [String] destination destination key
|
1457
1618
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1458
|
-
# @return [
|
1619
|
+
# @return [Integer] number of elements in the resulting set
|
1459
1620
|
def sdiffstore(destination, *keys)
|
1460
1621
|
synchronize do |client|
|
1461
|
-
client.call([:sdiffstore, destination
|
1622
|
+
client.call([:sdiffstore, destination, *keys])
|
1462
1623
|
end
|
1463
1624
|
end
|
1464
1625
|
|
@@ -1468,7 +1629,7 @@ class Redis
|
|
1468
1629
|
# @return [Array<String>] members in the intersection
|
1469
1630
|
def sinter(*keys)
|
1470
1631
|
synchronize do |client|
|
1471
|
-
client.call([:sinter
|
1632
|
+
client.call([:sinter, *keys])
|
1472
1633
|
end
|
1473
1634
|
end
|
1474
1635
|
|
@@ -1476,10 +1637,10 @@ class Redis
|
|
1476
1637
|
#
|
1477
1638
|
# @param [String] destination destination key
|
1478
1639
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1479
|
-
# @return [
|
1640
|
+
# @return [Integer] number of elements in the resulting set
|
1480
1641
|
def sinterstore(destination, *keys)
|
1481
1642
|
synchronize do |client|
|
1482
|
-
client.call([:sinterstore, destination
|
1643
|
+
client.call([:sinterstore, destination, *keys])
|
1483
1644
|
end
|
1484
1645
|
end
|
1485
1646
|
|
@@ -1489,7 +1650,7 @@ class Redis
|
|
1489
1650
|
# @return [Array<String>] members in the union
|
1490
1651
|
def sunion(*keys)
|
1491
1652
|
synchronize do |client|
|
1492
|
-
client.call([:sunion
|
1653
|
+
client.call([:sunion, *keys])
|
1493
1654
|
end
|
1494
1655
|
end
|
1495
1656
|
|
@@ -1497,10 +1658,10 @@ class Redis
|
|
1497
1658
|
#
|
1498
1659
|
# @param [String] destination destination key
|
1499
1660
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1500
|
-
# @return [
|
1661
|
+
# @return [Integer] number of elements in the resulting set
|
1501
1662
|
def sunionstore(destination, *keys)
|
1502
1663
|
synchronize do |client|
|
1503
|
-
client.call([:sunionstore, destination
|
1664
|
+
client.call([:sunionstore, destination, *keys])
|
1504
1665
|
end
|
1505
1666
|
end
|
1506
1667
|
|
@@ -1511,7 +1672,7 @@ class Redis
|
|
1511
1672
|
# # => 4
|
1512
1673
|
#
|
1513
1674
|
# @param [String] key
|
1514
|
-
# @return [
|
1675
|
+
# @return [Integer]
|
1515
1676
|
def zcard(key)
|
1516
1677
|
synchronize do |client|
|
1517
1678
|
client.call([:zcard, key])
|
@@ -1535,6 +1696,10 @@ class Redis
|
|
1535
1696
|
# add elements)
|
1536
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1537
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
|
1538
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1539
1704
|
# elements added, to the total number of elements changed (CH is an
|
1540
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1542,38 +1707,29 @@ class Redis
|
|
1542
1707
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1543
1708
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1544
1709
|
#
|
1545
|
-
# @return [Boolean,
|
1710
|
+
# @return [Boolean, Integer, Float]
|
1546
1711
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1547
1712
|
# **added** to the sorted set.
|
1548
|
-
# - `
|
1713
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1549
1714
|
# pairs that were **added** to the sorted set.
|
1550
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1551
1716
|
# after incrementing it.
|
1552
|
-
def zadd(key, *args
|
1553
|
-
|
1554
|
-
if
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
xx = options[:xx]
|
1561
|
-
zadd_options << "XX" if xx
|
1562
|
-
|
1563
|
-
ch = options[:ch]
|
1564
|
-
zadd_options << "CH" if ch
|
1565
|
-
|
1566
|
-
incr = options[:incr]
|
1567
|
-
zadd_options << "INCR" if incr
|
1568
|
-
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
|
1569
1725
|
|
1570
1726
|
synchronize do |client|
|
1571
1727
|
if args.size == 1 && args[0].is_a?(Array)
|
1572
1728
|
# Variadic: return float if INCR, integer if !INCR
|
1573
|
-
client.call(
|
1729
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1574
1730
|
elsif args.size == 2
|
1575
1731
|
# Single pair: return float if INCR, boolean if !INCR
|
1576
|
-
client.call(
|
1732
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1577
1733
|
else
|
1578
1734
|
raise ArgumentError, "wrong number of arguments"
|
1579
1735
|
end
|
@@ -1608,10 +1764,10 @@ class Redis
|
|
1608
1764
|
# - a single member
|
1609
1765
|
# - an array of members
|
1610
1766
|
#
|
1611
|
-
# @return [Boolean,
|
1767
|
+
# @return [Boolean, Integer]
|
1612
1768
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1613
1769
|
# was removed from the sorted set
|
1614
|
-
# - `
|
1770
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1615
1771
|
# members that were removed to the sorted set
|
1616
1772
|
def zrem(key, member)
|
1617
1773
|
synchronize do |client|
|
@@ -1726,6 +1882,63 @@ class Redis
|
|
1726
1882
|
end
|
1727
1883
|
end
|
1728
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
|
+
|
1729
1942
|
# Return a range of members in a sorted set, by index.
|
1730
1943
|
#
|
1731
1944
|
# @example Retrieve all members from a sorted set
|
@@ -1736,18 +1949,16 @@ class Redis
|
|
1736
1949
|
# # => [["a", 32.0], ["b", 64.0]]
|
1737
1950
|
#
|
1738
1951
|
# @param [String] key
|
1739
|
-
# @param [
|
1740
|
-
# @param [
|
1952
|
+
# @param [Integer] start start index
|
1953
|
+
# @param [Integer] stop stop index
|
1741
1954
|
# @param [Hash] options
|
1742
1955
|
# - `:with_scores => true`: include scores in output
|
1743
1956
|
#
|
1744
1957
|
# @return [Array<String>, Array<[String, Float]>]
|
1745
1958
|
# - when `:with_scores` is not specified, an array of members
|
1746
1959
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1747
|
-
def zrange(key, start, stop,
|
1748
|
-
args = []
|
1749
|
-
|
1750
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1960
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1961
|
+
args = [:zrange, key, start, stop]
|
1751
1962
|
|
1752
1963
|
if with_scores
|
1753
1964
|
args << "WITHSCORES"
|
@@ -1755,7 +1966,7 @@ class Redis
|
|
1755
1966
|
end
|
1756
1967
|
|
1757
1968
|
synchronize do |client|
|
1758
|
-
client.call(
|
1969
|
+
client.call(args, &block)
|
1759
1970
|
end
|
1760
1971
|
end
|
1761
1972
|
|
@@ -1770,10 +1981,8 @@ class Redis
|
|
1770
1981
|
# # => [["b", 64.0], ["a", 32.0]]
|
1771
1982
|
#
|
1772
1983
|
# @see #zrange
|
1773
|
-
def zrevrange(key, start, stop,
|
1774
|
-
args = []
|
1775
|
-
|
1776
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1984
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1985
|
+
args = [:zrevrange, key, start, stop]
|
1777
1986
|
|
1778
1987
|
if with_scores
|
1779
1988
|
args << "WITHSCORES"
|
@@ -1781,7 +1990,7 @@ class Redis
|
|
1781
1990
|
end
|
1782
1991
|
|
1783
1992
|
synchronize do |client|
|
1784
|
-
client.call(
|
1993
|
+
client.call(args, &block)
|
1785
1994
|
end
|
1786
1995
|
end
|
1787
1996
|
|
@@ -1789,7 +1998,7 @@ class Redis
|
|
1789
1998
|
#
|
1790
1999
|
# @param [String] key
|
1791
2000
|
# @param [String] member
|
1792
|
-
# @return [
|
2001
|
+
# @return [Integer]
|
1793
2002
|
def zrank(key, member)
|
1794
2003
|
synchronize do |client|
|
1795
2004
|
client.call([:zrank, key, member])
|
@@ -1801,7 +2010,7 @@ class Redis
|
|
1801
2010
|
#
|
1802
2011
|
# @param [String] key
|
1803
2012
|
# @param [String] member
|
1804
|
-
# @return [
|
2013
|
+
# @return [Integer]
|
1805
2014
|
def zrevrank(key, member)
|
1806
2015
|
synchronize do |client|
|
1807
2016
|
client.call([:zrevrank, key, member])
|
@@ -1818,9 +2027,9 @@ class Redis
|
|
1818
2027
|
# # => 5
|
1819
2028
|
#
|
1820
2029
|
# @param [String] key
|
1821
|
-
# @param [
|
1822
|
-
# @param [
|
1823
|
-
# @return [
|
2030
|
+
# @param [Integer] start start index
|
2031
|
+
# @param [Integer] stop stop index
|
2032
|
+
# @return [Integer] number of members that were removed
|
1824
2033
|
def zremrangebyrank(key, start, stop)
|
1825
2034
|
synchronize do |client|
|
1826
2035
|
client.call([:zremrangebyrank, key, start, stop])
|
@@ -1844,7 +2053,7 @@ class Redis
|
|
1844
2053
|
# - inclusive maximum is specified by prefixing `(`
|
1845
2054
|
# - exclusive maximum is specified by prefixing `[`
|
1846
2055
|
#
|
1847
|
-
# @return [
|
2056
|
+
# @return [Integer] number of members within the specified lexicographical range
|
1848
2057
|
def zlexcount(key, min, max)
|
1849
2058
|
synchronize do |client|
|
1850
2059
|
client.call([:zlexcount, key, min, max])
|
@@ -1872,14 +2081,16 @@ class Redis
|
|
1872
2081
|
# `count` members
|
1873
2082
|
#
|
1874
2083
|
# @return [Array<String>, Array<[String, Float]>]
|
1875
|
-
def zrangebylex(key, min, max,
|
1876
|
-
args = []
|
2084
|
+
def zrangebylex(key, min, max, limit: nil)
|
2085
|
+
args = [:zrangebylex, key, min, max]
|
1877
2086
|
|
1878
|
-
|
1879
|
-
|
2087
|
+
if limit
|
2088
|
+
args << "LIMIT"
|
2089
|
+
args.concat(limit)
|
2090
|
+
end
|
1880
2091
|
|
1881
2092
|
synchronize do |client|
|
1882
|
-
client.call(
|
2093
|
+
client.call(args)
|
1883
2094
|
end
|
1884
2095
|
end
|
1885
2096
|
|
@@ -1894,14 +2105,16 @@ class Redis
|
|
1894
2105
|
# # => ["abbygail", "abby"]
|
1895
2106
|
#
|
1896
2107
|
# @see #zrangebylex
|
1897
|
-
def zrevrangebylex(key, max, min,
|
1898
|
-
args = []
|
2108
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
2109
|
+
args = [:zrevrangebylex, key, max, min]
|
1899
2110
|
|
1900
|
-
|
1901
|
-
|
2111
|
+
if limit
|
2112
|
+
args << "LIMIT"
|
2113
|
+
args.concat(limit)
|
2114
|
+
end
|
1902
2115
|
|
1903
2116
|
synchronize do |client|
|
1904
|
-
client.call(
|
2117
|
+
client.call(args)
|
1905
2118
|
end
|
1906
2119
|
end
|
1907
2120
|
|
@@ -1932,21 +2145,21 @@ class Redis
|
|
1932
2145
|
# @return [Array<String>, Array<[String, Float]>]
|
1933
2146
|
# - when `:with_scores` is not specified, an array of members
|
1934
2147
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1935
|
-
def zrangebyscore(key, min, max,
|
1936
|
-
args = []
|
1937
|
-
|
1938
|
-
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]
|
1939
2150
|
|
1940
2151
|
if with_scores
|
1941
2152
|
args << "WITHSCORES"
|
1942
2153
|
block = FloatifyPairs
|
1943
2154
|
end
|
1944
2155
|
|
1945
|
-
|
1946
|
-
|
2156
|
+
if limit
|
2157
|
+
args << "LIMIT"
|
2158
|
+
args.concat(limit)
|
2159
|
+
end
|
1947
2160
|
|
1948
2161
|
synchronize do |client|
|
1949
|
-
client.call(
|
2162
|
+
client.call(args, &block)
|
1950
2163
|
end
|
1951
2164
|
end
|
1952
2165
|
|
@@ -1964,21 +2177,21 @@ class Redis
|
|
1964
2177
|
# # => [["b", 64.0], ["a", 32.0]]
|
1965
2178
|
#
|
1966
2179
|
# @see #zrangebyscore
|
1967
|
-
def zrevrangebyscore(key, max, min,
|
1968
|
-
args = []
|
1969
|
-
|
1970
|
-
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]
|
1971
2182
|
|
1972
2183
|
if with_scores
|
1973
|
-
args <<
|
2184
|
+
args << "WITHSCORES"
|
1974
2185
|
block = FloatifyPairs
|
1975
2186
|
end
|
1976
2187
|
|
1977
|
-
|
1978
|
-
|
2188
|
+
if limit
|
2189
|
+
args << "LIMIT"
|
2190
|
+
args.concat(limit)
|
2191
|
+
end
|
1979
2192
|
|
1980
2193
|
synchronize do |client|
|
1981
|
-
client.call(
|
2194
|
+
client.call(args, &block)
|
1982
2195
|
end
|
1983
2196
|
end
|
1984
2197
|
|
@@ -1998,7 +2211,7 @@ class Redis
|
|
1998
2211
|
# @param [String] max
|
1999
2212
|
# - inclusive maximum score is specified verbatim
|
2000
2213
|
# - exclusive maximum score is specified by prefixing `(`
|
2001
|
-
# @return [
|
2214
|
+
# @return [Integer] number of members that were removed
|
2002
2215
|
def zremrangebyscore(key, min, max)
|
2003
2216
|
synchronize do |client|
|
2004
2217
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -2021,13 +2234,52 @@ class Redis
|
|
2021
2234
|
# @param [String] max
|
2022
2235
|
# - inclusive maximum score is specified verbatim
|
2023
2236
|
# - exclusive maximum score is specified by prefixing `(`
|
2024
|
-
# @return [
|
2237
|
+
# @return [Integer] number of members in within the specified range
|
2025
2238
|
def zcount(key, min, max)
|
2026
2239
|
synchronize do |client|
|
2027
2240
|
client.call([:zcount, key, min, max])
|
2028
2241
|
end
|
2029
2242
|
end
|
2030
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
|
+
|
2031
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
2032
2284
|
# key.
|
2033
2285
|
#
|
@@ -2041,18 +2293,19 @@ class Redis
|
|
2041
2293
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2042
2294
|
# sorted sets
|
2043
2295
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2044
|
-
# @return [
|
2045
|
-
def zinterstore(destination, keys,
|
2046
|
-
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]
|
2047
2299
|
|
2048
|
-
|
2049
|
-
|
2300
|
+
if weights
|
2301
|
+
args << "WEIGHTS"
|
2302
|
+
args.concat(weights)
|
2303
|
+
end
|
2050
2304
|
|
2051
|
-
aggregate
|
2052
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2305
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2053
2306
|
|
2054
2307
|
synchronize do |client|
|
2055
|
-
client.call(
|
2308
|
+
client.call(args)
|
2056
2309
|
end
|
2057
2310
|
end
|
2058
2311
|
|
@@ -2068,40 +2321,46 @@ class Redis
|
|
2068
2321
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2069
2322
|
# sorted sets
|
2070
2323
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2071
|
-
# @return [
|
2072
|
-
def zunionstore(destination, keys,
|
2073
|
-
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]
|
2074
2327
|
|
2075
|
-
|
2076
|
-
|
2328
|
+
if weights
|
2329
|
+
args << "WEIGHTS"
|
2330
|
+
args.concat(weights)
|
2331
|
+
end
|
2077
2332
|
|
2078
|
-
aggregate
|
2079
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2333
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2080
2334
|
|
2081
2335
|
synchronize do |client|
|
2082
|
-
client.call(
|
2336
|
+
client.call(args)
|
2083
2337
|
end
|
2084
2338
|
end
|
2085
2339
|
|
2086
2340
|
# Get the number of fields in a hash.
|
2087
2341
|
#
|
2088
2342
|
# @param [String] key
|
2089
|
-
# @return [
|
2343
|
+
# @return [Integer] number of fields in the hash
|
2090
2344
|
def hlen(key)
|
2091
2345
|
synchronize do |client|
|
2092
2346
|
client.call([:hlen, key])
|
2093
2347
|
end
|
2094
2348
|
end
|
2095
2349
|
|
2096
|
-
# 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
|
2097
2355
|
#
|
2098
2356
|
# @param [String] key
|
2099
|
-
# @param [String]
|
2100
|
-
# @
|
2101
|
-
|
2102
|
-
|
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
|
+
|
2103
2362
|
synchronize do |client|
|
2104
|
-
client.call([:hset, key,
|
2363
|
+
client.call([:hset, key, *attrs])
|
2105
2364
|
end
|
2106
2365
|
end
|
2107
2366
|
|
@@ -2190,7 +2449,7 @@ class Redis
|
|
2190
2449
|
# @see #hmget
|
2191
2450
|
def mapped_hmget(key, *fields)
|
2192
2451
|
hmget(key, *fields) do |reply|
|
2193
|
-
if reply.
|
2452
|
+
if reply.is_a?(Array)
|
2194
2453
|
Hash[fields.zip(reply)]
|
2195
2454
|
else
|
2196
2455
|
reply
|
@@ -2202,7 +2461,7 @@ class Redis
|
|
2202
2461
|
#
|
2203
2462
|
# @param [String] key
|
2204
2463
|
# @param [String, Array<String>] field
|
2205
|
-
# @return [
|
2464
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2206
2465
|
def hdel(key, *fields)
|
2207
2466
|
synchronize do |client|
|
2208
2467
|
client.call([:hdel, key, *fields])
|
@@ -2224,8 +2483,8 @@ class Redis
|
|
2224
2483
|
#
|
2225
2484
|
# @param [String] key
|
2226
2485
|
# @param [String] field
|
2227
|
-
# @param [
|
2228
|
-
# @return [
|
2486
|
+
# @param [Integer] increment
|
2487
|
+
# @return [Integer] value of the field after incrementing it
|
2229
2488
|
def hincrby(key, field, increment)
|
2230
2489
|
synchronize do |client|
|
2231
2490
|
client.call([:hincrby, key, field, increment])
|
@@ -2283,20 +2542,21 @@ class Redis
|
|
2283
2542
|
|
2284
2543
|
def subscribed?
|
2285
2544
|
synchronize do |client|
|
2286
|
-
client.
|
2545
|
+
client.is_a? SubscribedClient
|
2287
2546
|
end
|
2288
2547
|
end
|
2289
2548
|
|
2290
2549
|
# Listen for messages published to the given channels.
|
2291
2550
|
def subscribe(*channels, &block)
|
2292
|
-
synchronize do |
|
2551
|
+
synchronize do |_client|
|
2293
2552
|
_subscription(:subscribe, 0, channels, block)
|
2294
2553
|
end
|
2295
2554
|
end
|
2296
2555
|
|
2297
|
-
# 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.
|
2298
2558
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2299
|
-
synchronize do |
|
2559
|
+
synchronize do |_client|
|
2300
2560
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2301
2561
|
end
|
2302
2562
|
end
|
@@ -2304,21 +2564,23 @@ class Redis
|
|
2304
2564
|
# Stop listening for messages posted to the given channels.
|
2305
2565
|
def unsubscribe(*channels)
|
2306
2566
|
synchronize do |client|
|
2307
|
-
raise
|
2567
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2568
|
+
|
2308
2569
|
client.unsubscribe(*channels)
|
2309
2570
|
end
|
2310
2571
|
end
|
2311
2572
|
|
2312
2573
|
# Listen for messages published to channels matching the given patterns.
|
2313
2574
|
def psubscribe(*channels, &block)
|
2314
|
-
synchronize do |
|
2575
|
+
synchronize do |_client|
|
2315
2576
|
_subscription(:psubscribe, 0, channels, block)
|
2316
2577
|
end
|
2317
2578
|
end
|
2318
2579
|
|
2319
|
-
# 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.
|
2320
2582
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2321
|
-
synchronize do |
|
2583
|
+
synchronize do |_client|
|
2322
2584
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2323
2585
|
end
|
2324
2586
|
end
|
@@ -2326,7 +2588,8 @@ class Redis
|
|
2326
2588
|
# Stop listening for messages posted to channels matching the given patterns.
|
2327
2589
|
def punsubscribe(*channels)
|
2328
2590
|
synchronize do |client|
|
2329
|
-
raise
|
2591
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2592
|
+
|
2330
2593
|
client.punsubscribe(*channels)
|
2331
2594
|
end
|
2332
2595
|
end
|
@@ -2371,7 +2634,7 @@ class Redis
|
|
2371
2634
|
# @see #multi
|
2372
2635
|
def watch(*keys)
|
2373
2636
|
synchronize do |client|
|
2374
|
-
res = client.call([:watch
|
2637
|
+
res = client.call([:watch, *keys])
|
2375
2638
|
|
2376
2639
|
if block_given?
|
2377
2640
|
begin
|
@@ -2401,13 +2664,13 @@ class Redis
|
|
2401
2664
|
end
|
2402
2665
|
|
2403
2666
|
def pipelined
|
2404
|
-
synchronize do |
|
2667
|
+
synchronize do |prior_client|
|
2405
2668
|
begin
|
2406
|
-
|
2669
|
+
@client = Pipeline.new(prior_client)
|
2407
2670
|
yield(self)
|
2408
|
-
|
2671
|
+
prior_client.call_pipeline(@client)
|
2409
2672
|
ensure
|
2410
|
-
@client =
|
2673
|
+
@client = prior_client
|
2411
2674
|
end
|
2412
2675
|
end
|
2413
2676
|
end
|
@@ -2443,17 +2706,16 @@ class Redis
|
|
2443
2706
|
# @see #watch
|
2444
2707
|
# @see #unwatch
|
2445
2708
|
def multi
|
2446
|
-
synchronize do |
|
2709
|
+
synchronize do |prior_client|
|
2447
2710
|
if !block_given?
|
2448
|
-
|
2711
|
+
prior_client.call([:multi])
|
2449
2712
|
else
|
2450
2713
|
begin
|
2451
|
-
|
2452
|
-
original, @client = @client, pipeline
|
2714
|
+
@client = Pipeline::Multi.new(prior_client)
|
2453
2715
|
yield(self)
|
2454
|
-
|
2716
|
+
prior_client.call_pipeline(@client)
|
2455
2717
|
ensure
|
2456
|
-
@client =
|
2718
|
+
@client = prior_client
|
2457
2719
|
end
|
2458
2720
|
end
|
2459
2721
|
end
|
@@ -2600,18 +2862,13 @@ class Redis
|
|
2600
2862
|
_eval(:evalsha, args)
|
2601
2863
|
end
|
2602
2864
|
|
2603
|
-
def _scan(command, cursor, args,
|
2865
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2604
2866
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2605
2867
|
|
2606
2868
|
args << cursor
|
2607
|
-
|
2608
|
-
|
2609
|
-
|
2610
|
-
end
|
2611
|
-
|
2612
|
-
if count = options[:count]
|
2613
|
-
args.concat(["COUNT", count])
|
2614
|
-
end
|
2869
|
+
args << "MATCH" << match if match
|
2870
|
+
args << "COUNT" << count if count
|
2871
|
+
args << "TYPE" << type if type
|
2615
2872
|
|
2616
2873
|
synchronize do |client|
|
2617
2874
|
client.call([command] + args, &block)
|
@@ -2626,15 +2883,19 @@ class Redis
|
|
2626
2883
|
# @example Retrieve a batch of keys matching a pattern
|
2627
2884
|
# redis.scan(4, :match => "key:1?")
|
2628
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"]]
|
2629
2889
|
#
|
2630
2890
|
# @param [String, Integer] cursor the cursor of the iteration
|
2631
2891
|
# @param [Hash] options
|
2632
2892
|
# - `:match => String`: only return keys matching the pattern
|
2633
2893
|
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
+
# - `:type => String`: return keys only of the given type
|
2634
2895
|
#
|
2635
2896
|
# @return [String, Array<String>] the next cursor and all found keys
|
2636
|
-
def scan(cursor, options
|
2637
|
-
_scan(:scan, cursor, [], options)
|
2897
|
+
def scan(cursor, **options)
|
2898
|
+
_scan(:scan, cursor, [], **options)
|
2638
2899
|
end
|
2639
2900
|
|
2640
2901
|
# Scan the keyspace
|
@@ -2646,17 +2907,23 @@ class Redis
|
|
2646
2907
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2647
2908
|
# # => key:13
|
2648
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"
|
2649
2914
|
#
|
2650
2915
|
# @param [Hash] options
|
2651
2916
|
# - `:match => String`: only return keys matching the pattern
|
2652
2917
|
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
+
# - `:type => String`: return keys only of the given type
|
2653
2919
|
#
|
2654
2920
|
# @return [Enumerator] an enumerator for all found keys
|
2655
|
-
def scan_each(options
|
2656
|
-
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
|
+
|
2657
2924
|
cursor = 0
|
2658
2925
|
loop do
|
2659
|
-
cursor, keys = scan(cursor, options)
|
2926
|
+
cursor, keys = scan(cursor, **options)
|
2660
2927
|
keys.each(&block)
|
2661
2928
|
break if cursor == "0"
|
2662
2929
|
end
|
@@ -2673,8 +2940,8 @@ class Redis
|
|
2673
2940
|
# - `:count => Integer`: return count keys at most per iteration
|
2674
2941
|
#
|
2675
2942
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2676
|
-
def hscan(key, cursor, options
|
2677
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2943
|
+
def hscan(key, cursor, **options)
|
2944
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2678
2945
|
[reply[0], reply[1].each_slice(2).to_a]
|
2679
2946
|
end
|
2680
2947
|
end
|
@@ -2690,11 +2957,12 @@ class Redis
|
|
2690
2957
|
# - `:count => Integer`: return count keys at most per iteration
|
2691
2958
|
#
|
2692
2959
|
# @return [Enumerator] an enumerator for all found keys
|
2693
|
-
def hscan_each(key, options
|
2694
|
-
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
|
+
|
2695
2963
|
cursor = 0
|
2696
2964
|
loop do
|
2697
|
-
cursor, values = hscan(key, cursor, options)
|
2965
|
+
cursor, values = hscan(key, cursor, **options)
|
2698
2966
|
values.each(&block)
|
2699
2967
|
break if cursor == "0"
|
2700
2968
|
end
|
@@ -2712,8 +2980,8 @@ class Redis
|
|
2712
2980
|
#
|
2713
2981
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2714
2982
|
# members and scores
|
2715
|
-
def zscan(key, cursor, options
|
2716
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2983
|
+
def zscan(key, cursor, **options)
|
2984
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2717
2985
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2718
2986
|
end
|
2719
2987
|
end
|
@@ -2729,11 +2997,12 @@ class Redis
|
|
2729
2997
|
# - `:count => Integer`: return count keys at most per iteration
|
2730
2998
|
#
|
2731
2999
|
# @return [Enumerator] an enumerator for all found scores and members
|
2732
|
-
def zscan_each(key, options
|
2733
|
-
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
|
+
|
2734
3003
|
cursor = 0
|
2735
3004
|
loop do
|
2736
|
-
cursor, values = zscan(key, cursor, options)
|
3005
|
+
cursor, values = zscan(key, cursor, **options)
|
2737
3006
|
values.each(&block)
|
2738
3007
|
break if cursor == "0"
|
2739
3008
|
end
|
@@ -2750,8 +3019,8 @@ class Redis
|
|
2750
3019
|
# - `:count => Integer`: return count keys at most per iteration
|
2751
3020
|
#
|
2752
3021
|
# @return [String, Array<String>] the next cursor and all found members
|
2753
|
-
def sscan(key, cursor, options
|
2754
|
-
_scan(:sscan, cursor, [key], options)
|
3022
|
+
def sscan(key, cursor, **options)
|
3023
|
+
_scan(:sscan, cursor, [key], **options)
|
2755
3024
|
end
|
2756
3025
|
|
2757
3026
|
# Scan a set
|
@@ -2765,11 +3034,12 @@ class Redis
|
|
2765
3034
|
# - `:count => Integer`: return count keys at most per iteration
|
2766
3035
|
#
|
2767
3036
|
# @return [Enumerator] an enumerator for all keys in the set
|
2768
|
-
def sscan_each(key, options
|
2769
|
-
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
|
+
|
2770
3040
|
cursor = 0
|
2771
3041
|
loop do
|
2772
|
-
cursor, keys = sscan(key, cursor, options)
|
3042
|
+
cursor, keys = sscan(key, cursor, **options)
|
2773
3043
|
keys.each(&block)
|
2774
3044
|
break if cursor == "0"
|
2775
3045
|
end
|
@@ -2792,7 +3062,7 @@ class Redis
|
|
2792
3062
|
# union of the HyperLogLogs contained in the keys.
|
2793
3063
|
#
|
2794
3064
|
# @param [String, Array<String>] keys
|
2795
|
-
# @return [
|
3065
|
+
# @return [Integer]
|
2796
3066
|
def pfcount(*keys)
|
2797
3067
|
synchronize do |client|
|
2798
3068
|
client.call([:pfcount] + keys)
|
@@ -2815,10 +3085,10 @@ class Redis
|
|
2815
3085
|
#
|
2816
3086
|
# @param [String] key
|
2817
3087
|
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
2818
|
-
# @return [
|
3088
|
+
# @return [Integer] number of elements added to the sorted set
|
2819
3089
|
def geoadd(key, *member)
|
2820
3090
|
synchronize do |client|
|
2821
|
-
client.call([:geoadd, key, member])
|
3091
|
+
client.call([:geoadd, key, *member])
|
2822
3092
|
end
|
2823
3093
|
end
|
2824
3094
|
|
@@ -2833,12 +3103,12 @@ class Redis
|
|
2833
3103
|
end
|
2834
3104
|
end
|
2835
3105
|
|
2836
|
-
|
2837
3106
|
# Query a sorted set representing a geospatial index to fetch members matching a
|
2838
3107
|
# given maximum distance from a point
|
2839
3108
|
#
|
2840
3109
|
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2841
|
-
# @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
|
2842
3112
|
# @param [Integer] count limit the results to the first N matching items
|
2843
3113
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2844
3114
|
# @return [Array<String>] may be changed with `options`
|
@@ -2855,7 +3125,8 @@ class Redis
|
|
2855
3125
|
# given maximum distance from an already existing member
|
2856
3126
|
#
|
2857
3127
|
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2858
|
-
# @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
|
2859
3130
|
# @param [Integer] count limit the results to the first N matching items
|
2860
3131
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2861
3132
|
# @return [Array<String>] may be changed with `options`
|
@@ -2872,7 +3143,8 @@ class Redis
|
|
2872
3143
|
#
|
2873
3144
|
# @param [String] key
|
2874
3145
|
# @param [String, Array<String>] member one member or array of members
|
2875
|
-
# @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
|
2876
3148
|
def geopos(key, member)
|
2877
3149
|
synchronize do |client|
|
2878
3150
|
client.call([:geopos, key, member])
|
@@ -2936,10 +3208,14 @@ class Redis
|
|
2936
3208
|
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
2937
3209
|
#
|
2938
3210
|
# @return [String] the entry id
|
2939
|
-
def xadd(key, entry,
|
3211
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
2940
3212
|
args = [:xadd, key]
|
2941
|
-
|
2942
|
-
|
3213
|
+
if maxlen
|
3214
|
+
args << "MAXLEN"
|
3215
|
+
args << "~" if approximate
|
3216
|
+
args << maxlen
|
3217
|
+
end
|
3218
|
+
args << id
|
2943
3219
|
args.concat(entry.to_a.flatten)
|
2944
3220
|
synchronize { |client| client.call(args) }
|
2945
3221
|
end
|
@@ -2977,24 +3253,6 @@ class Redis
|
|
2977
3253
|
synchronize { |client| client.call(args) }
|
2978
3254
|
end
|
2979
3255
|
|
2980
|
-
# Fetches entries of the stream.
|
2981
|
-
#
|
2982
|
-
# @example Without options
|
2983
|
-
# redis.xrange('mystream')
|
2984
|
-
# @example With first entry id option
|
2985
|
-
# redis.xrange('mystream', first: '0-1')
|
2986
|
-
# @example With first and last entry id options
|
2987
|
-
# redis.xrange('mystream', first: '0-1', last: '0-3')
|
2988
|
-
# @example With count options
|
2989
|
-
# redis.xrange('mystream', count: 10)
|
2990
|
-
#
|
2991
|
-
# @param key [String] the stream key
|
2992
|
-
# @param start [String] first entry id of range, default value is `+`
|
2993
|
-
# @param end [String] last entry id of range, default value is `-`
|
2994
|
-
# @param count [Integer] the number of entries as limit
|
2995
|
-
#
|
2996
|
-
# @return [Hash{String => Hash}] the entries
|
2997
|
-
|
2998
3256
|
# Fetches entries of the stream in ascending order.
|
2999
3257
|
#
|
3000
3258
|
# @example Without options
|
@@ -3012,8 +3270,8 @@ class Redis
|
|
3012
3270
|
# @param count [Integer] the number of entries as limit
|
3013
3271
|
#
|
3014
3272
|
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3015
|
-
def xrange(key, start = '-',
|
3016
|
-
args = [:xrange, key, start,
|
3273
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3274
|
+
args = [:xrange, key, start, range_end]
|
3017
3275
|
args.concat(['COUNT', count]) if count
|
3018
3276
|
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3019
3277
|
end
|
@@ -3035,8 +3293,8 @@ class Redis
|
|
3035
3293
|
# @params count [Integer] the number of entries as limit
|
3036
3294
|
#
|
3037
3295
|
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3038
|
-
def xrevrange(key,
|
3039
|
-
args = [:xrevrange, key,
|
3296
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3297
|
+
args = [:xrevrange, key, range_end, start]
|
3040
3298
|
args.concat(['COUNT', count]) if count
|
3041
3299
|
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3042
3300
|
end
|
@@ -3128,12 +3386,12 @@ class Redis
|
|
3128
3386
|
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3129
3387
|
#
|
3130
3388
|
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3131
|
-
def xreadgroup(group, consumer, keys, ids,
|
3389
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3132
3390
|
args = [:xreadgroup, 'GROUP', group, consumer]
|
3133
|
-
args << 'COUNT' <<
|
3134
|
-
args << 'BLOCK' <<
|
3135
|
-
args << 'NOACK' if
|
3136
|
-
_xread(args, keys, ids,
|
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)
|
3137
3395
|
end
|
3138
3396
|
|
3139
3397
|
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
@@ -3198,6 +3456,38 @@ class Redis
|
|
3198
3456
|
synchronize { |client| client.call(args, &blk) }
|
3199
3457
|
end
|
3200
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
|
+
|
3201
3491
|
# Fetches not acknowledging pending entries
|
3202
3492
|
#
|
3203
3493
|
# @example With key and group
|
@@ -3243,8 +3533,8 @@ class Redis
|
|
3243
3533
|
when "get-master-addr-by-name"
|
3244
3534
|
reply
|
3245
3535
|
else
|
3246
|
-
if reply.
|
3247
|
-
if reply[0].
|
3536
|
+
if reply.is_a?(Array)
|
3537
|
+
if reply[0].is_a?(Array)
|
3248
3538
|
reply.map(&Hashify)
|
3249
3539
|
else
|
3250
3540
|
Hashify.call(reply)
|
@@ -3268,12 +3558,17 @@ class Redis
|
|
3268
3558
|
def cluster(subcommand, *args)
|
3269
3559
|
subcommand = subcommand.to_s.downcase
|
3270
3560
|
block = case subcommand
|
3271
|
-
|
3272
|
-
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3276
|
-
|
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
|
3277
3572
|
|
3278
3573
|
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3279
3574
|
block = Noop unless @cluster_mode
|
@@ -3308,150 +3603,171 @@ class Redis
|
|
3308
3603
|
return @original_client.connection_info if @cluster_mode
|
3309
3604
|
|
3310
3605
|
{
|
3311
|
-
host:
|
3312
|
-
port:
|
3313
|
-
db:
|
3314
|
-
id:
|
3606
|
+
host: @original_client.host,
|
3607
|
+
port: @original_client.port,
|
3608
|
+
db: @original_client.db,
|
3609
|
+
id: @original_client.id,
|
3315
3610
|
location: @original_client.location
|
3316
3611
|
}
|
3317
3612
|
end
|
3318
3613
|
|
3319
|
-
def method_missing(command, *args)
|
3614
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
3320
3615
|
synchronize do |client|
|
3321
3616
|
client.call([command] + args)
|
3322
3617
|
end
|
3323
3618
|
end
|
3324
3619
|
|
3325
|
-
private
|
3620
|
+
private
|
3326
3621
|
|
3327
3622
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
3328
3623
|
# where the method call will return nil. Propagate the nil instead of falsely
|
3329
3624
|
# returning false.
|
3330
|
-
Boolify =
|
3331
|
-
|
3332
|
-
|
3333
|
-
|
3625
|
+
Boolify = lambda { |value|
|
3626
|
+
case value
|
3627
|
+
when 1
|
3628
|
+
true
|
3629
|
+
when 0
|
3630
|
+
false
|
3631
|
+
else
|
3632
|
+
value
|
3633
|
+
end
|
3634
|
+
}
|
3334
3635
|
|
3335
|
-
BoolifySet =
|
3336
|
-
|
3337
|
-
|
3338
|
-
|
3339
|
-
|
3340
|
-
|
3341
|
-
|
3342
|
-
|
3636
|
+
BoolifySet = lambda { |value|
|
3637
|
+
case value
|
3638
|
+
when "OK"
|
3639
|
+
true
|
3640
|
+
when nil
|
3641
|
+
false
|
3642
|
+
else
|
3643
|
+
value
|
3644
|
+
end
|
3645
|
+
}
|
3343
3646
|
|
3344
|
-
Hashify =
|
3345
|
-
|
3346
|
-
|
3347
|
-
|
3348
|
-
|
3349
|
-
|
3350
|
-
|
3351
|
-
|
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
|
+
}
|
3352
3667
|
|
3353
|
-
|
3354
|
-
|
3355
|
-
if str
|
3356
|
-
if (inf = str.match(/^(-)?inf/i))
|
3357
|
-
(inf[1] ? -1.0 : 1.0) / 0.0
|
3358
|
-
else
|
3359
|
-
Float(str)
|
3360
|
-
end
|
3361
|
-
end
|
3362
|
-
}
|
3668
|
+
FloatifyPairs = lambda { |value|
|
3669
|
+
return value unless value.respond_to?(:each_slice)
|
3363
3670
|
|
3364
|
-
|
3365
|
-
|
3366
|
-
|
3367
|
-
|
3368
|
-
|
3369
|
-
|
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
|
+
}
|
3370
3691
|
|
3371
|
-
|
3372
|
-
|
3373
|
-
|
3374
|
-
|
3375
|
-
|
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] }
|
3376
3705
|
}
|
3706
|
+
}
|
3377
3707
|
|
3378
|
-
|
3379
|
-
|
3380
|
-
|
3381
|
-
|
3382
|
-
[stream_key, HashifyStreamEntries.call(entries)]
|
3383
|
-
end.to_h
|
3708
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
+
{
|
3710
|
+
'next' => reply[0],
|
3711
|
+
'entries' => reply[1]
|
3384
3712
|
}
|
3713
|
+
}
|
3385
3714
|
|
3386
|
-
|
3387
|
-
|
3388
|
-
|
3389
|
-
|
3390
|
-
|
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
|
3391
3721
|
}
|
3722
|
+
}
|
3392
3723
|
|
3393
|
-
|
3394
|
-
|
3724
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3725
|
+
reply.map do |arr|
|
3395
3726
|
{
|
3396
|
-
'
|
3397
|
-
'
|
3398
|
-
'
|
3399
|
-
'
|
3727
|
+
'entry_id' => arr[0],
|
3728
|
+
'consumer' => arr[1],
|
3729
|
+
'elapsed' => arr[2],
|
3730
|
+
'count' => arr[3]
|
3400
3731
|
}
|
3401
|
-
|
3732
|
+
end
|
3733
|
+
}
|
3402
3734
|
|
3403
|
-
|
3404
|
-
|
3405
|
-
|
3406
|
-
|
3407
|
-
|
3408
|
-
|
3409
|
-
|
3410
|
-
|
3411
|
-
|
3412
|
-
|
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('-'))
|
3413
3747
|
}
|
3748
|
+
}
|
3414
3749
|
|
3415
|
-
|
3416
|
-
|
3417
|
-
|
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] } }
|
3418
3755
|
{
|
3419
|
-
'
|
3420
|
-
'
|
3421
|
-
'
|
3422
|
-
'
|
3423
|
-
'ping_sent' => arr[4],
|
3424
|
-
'pong_recv' => arr[5],
|
3425
|
-
'config_epoch' => arr[6],
|
3426
|
-
'link_state' => arr[7],
|
3427
|
-
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3756
|
+
'start_slot' => first_slot,
|
3757
|
+
'end_slot' => last_slot,
|
3758
|
+
'master' => master,
|
3759
|
+
'replicas' => replicas
|
3428
3760
|
}
|
3429
|
-
|
3430
|
-
|
3431
|
-
HashifyClusterSlots =
|
3432
|
-
lambda { |reply|
|
3433
|
-
reply.map do |arr|
|
3434
|
-
first_slot, last_slot = arr[0..1]
|
3435
|
-
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3436
|
-
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3437
|
-
{
|
3438
|
-
'start_slot' => first_slot,
|
3439
|
-
'end_slot' => last_slot,
|
3440
|
-
'master' => master,
|
3441
|
-
'replicas' => replicas
|
3442
|
-
}
|
3443
|
-
end
|
3444
|
-
}
|
3761
|
+
end
|
3762
|
+
}
|
3445
3763
|
|
3446
|
-
HashifyClusterNodes =
|
3447
|
-
|
3448
|
-
|
3449
|
-
}
|
3764
|
+
HashifyClusterNodes = lambda { |reply|
|
3765
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3766
|
+
}
|
3450
3767
|
|
3451
|
-
HashifyClusterSlaves =
|
3452
|
-
|
3453
|
-
|
3454
|
-
}
|
3768
|
+
HashifyClusterSlaves = lambda { |reply|
|
3769
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3770
|
+
}
|
3455
3771
|
|
3456
3772
|
Noop = ->(reply) { reply }
|
3457
3773
|
|
@@ -3459,8 +3775,7 @@ private
|
|
3459
3775
|
args.push sort if sort
|
3460
3776
|
args.push 'count', count if count
|
3461
3777
|
args.push options if options
|
3462
|
-
|
3463
|
-
args.uniq
|
3778
|
+
args
|
3464
3779
|
end
|
3465
3780
|
|
3466
3781
|
def _subscription(method, timeout, channels, block)
|
@@ -3488,12 +3803,29 @@ private
|
|
3488
3803
|
synchronize do |client|
|
3489
3804
|
if blocking_timeout_msec.nil?
|
3490
3805
|
client.call(args, &HashifyStreams)
|
3806
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3807
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3491
3808
|
else
|
3492
3809
|
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3493
3810
|
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3494
3811
|
end
|
3495
3812
|
end
|
3496
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
|
3497
3829
|
end
|
3498
3830
|
|
3499
3831
|
require_relative "redis/version"
|