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