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