redis 3.3.5 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +232 -2
- data/README.md +169 -89
- data/lib/redis/client.rb +177 -100
- data/lib/redis/cluster/command.rb +79 -0
- data/lib/redis/cluster/command_loader.rb +33 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +120 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +34 -0
- data/lib/redis/cluster/option.rb +100 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +46 -0
- data/lib/redis/cluster.rb +315 -0
- data/lib/redis/commands/bitmaps.rb +63 -0
- data/lib/redis/commands/cluster.rb +45 -0
- data/lib/redis/commands/connection.rb +58 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +251 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +455 -0
- data/lib/redis/commands/lists.rb +290 -0
- data/lib/redis/commands/pubsub.rb +72 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +223 -0
- data/lib/redis/commands/sorted_sets.rb +812 -0
- data/lib/redis/commands/streams.rb +382 -0
- data/lib/redis/commands/strings.rb +313 -0
- data/lib/redis/commands/transactions.rb +139 -0
- data/lib/redis/commands.rb +240 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +5 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +136 -128
- data/lib/redis/connection/synchrony.rb +24 -9
- data/lib/redis/connection.rb +3 -1
- data/lib/redis/distributed.rb +255 -85
- data/lib/redis/errors.rb +57 -0
- data/lib/redis/hash_ring.rb +30 -73
- data/lib/redis/pipeline.rb +178 -13
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +174 -2661
- metadata +66 -202
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_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 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- 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 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- 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 -66
- 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/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
data/lib/redis.rb
CHANGED
@@ -1,65 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
4
|
require "redis/errors"
|
5
|
+
require "redis/commands"
|
3
6
|
|
4
7
|
class Redis
|
8
|
+
BASE_PATH = __dir__
|
9
|
+
@exists_returns_integer = true
|
10
|
+
@sadd_returns_boolean = true
|
11
|
+
|
12
|
+
Deprecated = Class.new(StandardError)
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_reader :exists_returns_integer
|
16
|
+
attr_accessor :silence_deprecations, :raise_deprecations, :sadd_returns_boolean
|
17
|
+
|
18
|
+
def exists_returns_integer=(value)
|
19
|
+
unless value
|
20
|
+
deprecate!(
|
21
|
+
"`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
22
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
23
|
+
"`exists?` instead."
|
24
|
+
)
|
25
|
+
end
|
5
26
|
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
attr :client
|
27
|
+
@exists_returns_integer = value
|
28
|
+
end
|
11
29
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
30
|
+
def deprecate!(message)
|
31
|
+
unless silence_deprecations
|
32
|
+
if raise_deprecations
|
33
|
+
raise Deprecated, message
|
34
|
+
else
|
35
|
+
::Kernel.warn(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
|
40
|
+
def current
|
41
|
+
deprecate!("`Redis.current` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
|
42
|
+
@current ||= Redis.new
|
43
|
+
end
|
22
44
|
|
23
|
-
|
24
|
-
|
45
|
+
def current=(redis)
|
46
|
+
deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
|
47
|
+
@current = redis
|
48
|
+
end
|
25
49
|
end
|
26
50
|
|
27
|
-
include
|
51
|
+
include Commands
|
28
52
|
|
29
53
|
# Create a new client instance
|
30
54
|
#
|
31
55
|
# @param [Hash] options
|
32
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
56
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
57
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
58
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
33
59
|
# @option options [String] :host ("127.0.0.1") server hostname
|
34
|
-
# @option options [
|
60
|
+
# @option options [Integer] :port (6379) server port
|
35
61
|
# @option options [String] :path path to server socket (overrides host and port)
|
36
62
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
37
63
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
64
|
+
# @option options [String] :username Username to authenticate against server
|
38
65
|
# @option options [String] :password Password to authenticate against server
|
39
|
-
# @option options [
|
66
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
40
67
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
41
|
-
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
42
|
-
#
|
43
|
-
# @option options [
|
68
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
69
|
+
# `CLIENT SETNAME`
|
70
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
71
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
72
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
44
73
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
45
74
|
# @option options [Array] :sentinels List of sentinels to contact
|
46
75
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
76
|
+
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
77
|
+
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
78
|
+
# @option options [String] :fixed_hostname Specify a FQDN if cluster mode enabled and
|
79
|
+
# client has to connect nodes via single endpoint with SSL/TLS
|
80
|
+
# @option options [Class] :connector Class of custom connector
|
47
81
|
#
|
48
82
|
# @return [Redis] a new client instance
|
49
83
|
def initialize(options = {})
|
50
84
|
@options = options.dup
|
51
|
-
@
|
85
|
+
@cluster_mode = options.key?(:cluster)
|
86
|
+
client = @cluster_mode ? Cluster : Client
|
87
|
+
@original_client = @client = client.new(options)
|
52
88
|
@queue = Hash.new { |h, k| h[k] = [] }
|
53
|
-
|
54
|
-
super() # Monitor#initialize
|
55
|
-
end
|
56
|
-
|
57
|
-
def synchronize
|
58
|
-
mon_synchronize { yield(@client) }
|
89
|
+
@monitor = Monitor.new
|
59
90
|
end
|
60
91
|
|
61
92
|
# Run code with the client reconnecting
|
62
|
-
def with_reconnect(val=true, &blk)
|
93
|
+
def with_reconnect(val = true, &blk)
|
63
94
|
synchronize do |client|
|
64
95
|
client.with_reconnect(val, &blk)
|
65
96
|
end
|
@@ -81,2708 +112,190 @@ class Redis
|
|
81
112
|
end
|
82
113
|
alias disconnect! close
|
83
114
|
|
84
|
-
|
85
|
-
|
86
|
-
# Replies are converted to Ruby objects according to the RESP protocol, so
|
87
|
-
# you can expect a Ruby array, integer or nil when Redis sends one. Higher
|
88
|
-
# level transformations, such as converting an array of pairs into a Ruby
|
89
|
-
# hash, are up to consumers.
|
90
|
-
#
|
91
|
-
# Redis error replies are raised as Ruby exceptions.
|
92
|
-
def call(*command)
|
93
|
-
synchronize do |client|
|
94
|
-
client.call(command)
|
95
|
-
end
|
115
|
+
def with
|
116
|
+
yield self
|
96
117
|
end
|
97
118
|
|
98
|
-
# Queues a command for pipelining.
|
119
|
+
# @deprecated Queues a command for pipelining.
|
99
120
|
#
|
100
121
|
# Commands in the queue are executed with the Redis#commit method.
|
101
122
|
#
|
102
123
|
# See http://redis.io/topics/pipelining for more details.
|
103
124
|
#
|
104
125
|
def queue(*command)
|
105
|
-
|
126
|
+
::Redis.deprecate!(
|
127
|
+
"Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
|
128
|
+
"(called from: #{caller(1, 1).first})"
|
129
|
+
)
|
130
|
+
|
131
|
+
synchronize do
|
132
|
+
@queue[Thread.current.object_id] << command
|
133
|
+
end
|
106
134
|
end
|
107
135
|
|
108
|
-
# Sends all commands in the queue.
|
136
|
+
# @deprecated Sends all commands in the queue.
|
109
137
|
#
|
110
138
|
# See http://redis.io/topics/pipelining for more details.
|
111
139
|
#
|
112
140
|
def commit
|
141
|
+
::Redis.deprecate!(
|
142
|
+
"Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
|
143
|
+
"(called from: #{Kernel.caller(1, 1).first})"
|
144
|
+
)
|
145
|
+
|
113
146
|
synchronize do |client|
|
114
147
|
begin
|
115
|
-
|
148
|
+
pipeline = Pipeline.new(client)
|
149
|
+
@queue[Thread.current.object_id].each do |command|
|
150
|
+
pipeline.call(command)
|
151
|
+
end
|
152
|
+
|
153
|
+
client.call_pipelined(pipeline)
|
116
154
|
ensure
|
117
155
|
@queue.delete(Thread.current.object_id)
|
118
156
|
end
|
119
157
|
end
|
120
158
|
end
|
121
159
|
|
122
|
-
|
123
|
-
|
124
|
-
# @param [String] password must match the password specified in the
|
125
|
-
# `requirepass` directive in the configuration file
|
126
|
-
# @return [String] `OK`
|
127
|
-
def auth(password)
|
128
|
-
synchronize do |client|
|
129
|
-
client.call([:auth, password])
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# Change the selected database for the current connection.
|
134
|
-
#
|
135
|
-
# @param [Fixnum] db zero-based index of the DB to use (0 to 15)
|
136
|
-
# @return [String] `OK`
|
137
|
-
def select(db)
|
138
|
-
synchronize do |client|
|
139
|
-
client.db = db
|
140
|
-
client.call([:select, db])
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Ping the server.
|
145
|
-
#
|
146
|
-
# @return [String] `PONG`
|
147
|
-
def ping
|
148
|
-
synchronize do |client|
|
149
|
-
client.call([:ping])
|
150
|
-
end
|
160
|
+
def _client
|
161
|
+
@client
|
151
162
|
end
|
152
163
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
synchronize do |client|
|
159
|
-
client.call([:echo, value])
|
164
|
+
def pipelined(&block)
|
165
|
+
deprecation_displayed = false
|
166
|
+
if block&.arity == 0
|
167
|
+
Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
|
168
|
+
deprecation_displayed = true
|
160
169
|
end
|
161
|
-
end
|
162
170
|
|
163
|
-
|
164
|
-
#
|
165
|
-
# @return [String] `OK`
|
166
|
-
def quit
|
167
|
-
synchronize do |client|
|
171
|
+
synchronize do |prior_client|
|
168
172
|
begin
|
169
|
-
|
170
|
-
|
173
|
+
pipeline = Pipeline.new(prior_client)
|
174
|
+
@client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
|
175
|
+
pipelined_connection = PipelinedConnection.new(pipeline)
|
176
|
+
yield pipelined_connection
|
177
|
+
prior_client.call_pipeline(pipeline)
|
171
178
|
ensure
|
172
|
-
client
|
179
|
+
@client = prior_client
|
173
180
|
end
|
174
181
|
end
|
175
182
|
end
|
176
183
|
|
177
|
-
#
|
178
|
-
#
|
179
|
-
# @return [String] `OK`
|
180
|
-
def bgrewriteaof
|
181
|
-
synchronize do |client|
|
182
|
-
client.call([:bgrewriteaof])
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Asynchronously save the dataset to disk.
|
184
|
+
# Mark the start of a transaction block.
|
187
185
|
#
|
188
|
-
#
|
189
|
-
def bgsave
|
190
|
-
synchronize do |client|
|
191
|
-
client.call([:bgsave])
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Get or set server configuration parameters.
|
186
|
+
# Passing a block is optional.
|
196
187
|
#
|
197
|
-
# @
|
198
|
-
#
|
199
|
-
#
|
200
|
-
|
201
|
-
|
202
|
-
client.call([:config, action] + args) do |reply|
|
203
|
-
if reply.kind_of?(Array) && action == :get
|
204
|
-
Hashify.call(reply)
|
205
|
-
else
|
206
|
-
reply
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# Return the number of keys in the selected database.
|
188
|
+
# @example With a block
|
189
|
+
# redis.multi do |multi|
|
190
|
+
# multi.set("key", "value")
|
191
|
+
# multi.incr("counter")
|
192
|
+
# end # => ["OK", 6]
|
213
193
|
#
|
214
|
-
# @
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
client.call([:debug] + args)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
# Remove all keys from all databases.
|
194
|
+
# @example Without a block
|
195
|
+
# redis.multi
|
196
|
+
# # => "OK"
|
197
|
+
# redis.set("key", "value")
|
198
|
+
# # => "QUEUED"
|
199
|
+
# redis.incr("counter")
|
200
|
+
# # => "QUEUED"
|
201
|
+
# redis.exec
|
202
|
+
# # => ["OK", 6]
|
228
203
|
#
|
229
|
-
# @
|
230
|
-
|
231
|
-
|
232
|
-
client.call([:flushall])
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
# Remove all keys from the current database.
|
204
|
+
# @yield [multi] the commands that are called inside this block are cached
|
205
|
+
# and written to the server upon returning from it
|
206
|
+
# @yieldparam [Redis] multi `self`
|
237
207
|
#
|
238
|
-
# @return [String]
|
239
|
-
|
240
|
-
|
241
|
-
client.call([:flushdb])
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
# Get information and statistics about the server.
|
208
|
+
# @return [String, Array<...>]
|
209
|
+
# - when a block is not given, `OK`
|
210
|
+
# - when a block is given, an array with replies
|
246
211
|
#
|
247
|
-
# @
|
248
|
-
# @
|
249
|
-
def
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
end.compact]
|
256
|
-
|
257
|
-
if cmd && cmd.to_s == "commandstats"
|
258
|
-
# Extract nested hashes for INFO COMMANDSTATS
|
259
|
-
reply = Hash[reply.map do |k, v|
|
260
|
-
v = v.split(",").map { |e| e.split("=") }
|
261
|
-
[k[/^cmdstat_(.*)$/, 1], Hash[v]]
|
262
|
-
end]
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
reply
|
212
|
+
# @see #watch
|
213
|
+
# @see #unwatch
|
214
|
+
def multi(&block)
|
215
|
+
if block_given?
|
216
|
+
deprecation_displayed = false
|
217
|
+
if block&.arity == 0
|
218
|
+
Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
|
219
|
+
deprecation_displayed = true
|
267
220
|
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# Get the UNIX time stamp of the last successful save to disk.
|
272
|
-
#
|
273
|
-
# @return [Fixnum]
|
274
|
-
def lastsave
|
275
|
-
synchronize do |client|
|
276
|
-
client.call([:lastsave])
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
# Listen for all requests received by the server in real time.
|
281
|
-
#
|
282
|
-
# There is no way to interrupt this command.
|
283
|
-
#
|
284
|
-
# @yield a block to be called for every line of output
|
285
|
-
# @yieldparam [String] line timestamp and command that was executed
|
286
|
-
def monitor(&block)
|
287
|
-
synchronize do |client|
|
288
|
-
client.call_loop([:monitor], &block)
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
# Synchronously save the dataset to disk.
|
293
|
-
#
|
294
|
-
# @return [String]
|
295
|
-
def save
|
296
|
-
synchronize do |client|
|
297
|
-
client.call([:save])
|
298
|
-
end
|
299
|
-
end
|
300
221
|
|
301
|
-
|
302
|
-
def shutdown
|
303
|
-
synchronize do |client|
|
304
|
-
client.with_reconnect(false) do
|
222
|
+
synchronize do |prior_client|
|
305
223
|
begin
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
224
|
+
pipeline = Pipeline::Multi.new(prior_client)
|
225
|
+
@client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
|
226
|
+
pipelined_connection = PipelinedConnection.new(pipeline)
|
227
|
+
yield pipelined_connection
|
228
|
+
prior_client.call_pipeline(pipeline)
|
229
|
+
ensure
|
230
|
+
@client = prior_client
|
310
231
|
end
|
311
232
|
end
|
233
|
+
else
|
234
|
+
send_command([:multi])
|
312
235
|
end
|
313
236
|
end
|
314
237
|
|
315
|
-
|
316
|
-
|
317
|
-
synchronize do |client|
|
318
|
-
client.call([:slaveof, host, port])
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
# Interact with the slowlog (get, len, reset)
|
323
|
-
#
|
324
|
-
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
325
|
-
# @param [Fixnum] length maximum number of entries to return
|
326
|
-
# @return [Array<String>, Fixnum, String] depends on subcommand
|
327
|
-
def slowlog(subcommand, length=nil)
|
328
|
-
synchronize do |client|
|
329
|
-
args = [:slowlog, subcommand]
|
330
|
-
args << length if length
|
331
|
-
client.call args
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
# Internal command used for replication.
|
336
|
-
def sync
|
337
|
-
synchronize do |client|
|
338
|
-
client.call([:sync])
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
# Return the server time.
|
343
|
-
#
|
344
|
-
# @example
|
345
|
-
# r.time # => [ 1333093196, 606806 ]
|
346
|
-
#
|
347
|
-
# @return [Array<Fixnum>] tuple of seconds since UNIX epoch and
|
348
|
-
# microseconds in the current second
|
349
|
-
def time
|
350
|
-
synchronize do |client|
|
351
|
-
client.call([:time]) do |reply|
|
352
|
-
reply.map(&:to_i) if reply
|
353
|
-
end
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
# Remove the expiration from a key.
|
358
|
-
#
|
359
|
-
# @param [String] key
|
360
|
-
# @return [Boolean] whether the timeout was removed or not
|
361
|
-
def persist(key)
|
362
|
-
synchronize do |client|
|
363
|
-
client.call([:persist, key], &Boolify)
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
# Set a key's time to live in seconds.
|
368
|
-
#
|
369
|
-
# @param [String] key
|
370
|
-
# @param [Fixnum] seconds time to live
|
371
|
-
# @return [Boolean] whether the timeout was set or not
|
372
|
-
def expire(key, seconds)
|
373
|
-
synchronize do |client|
|
374
|
-
client.call([:expire, key, seconds], &Boolify)
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
# Set the expiration for a key as a UNIX timestamp.
|
379
|
-
#
|
380
|
-
# @param [String] key
|
381
|
-
# @param [Fixnum] unix_time expiry time specified as a UNIX timestamp
|
382
|
-
# @return [Boolean] whether the timeout was set or not
|
383
|
-
def expireat(key, unix_time)
|
384
|
-
synchronize do |client|
|
385
|
-
client.call([:expireat, key, unix_time], &Boolify)
|
386
|
-
end
|
238
|
+
def id
|
239
|
+
@original_client.id
|
387
240
|
end
|
388
241
|
|
389
|
-
|
390
|
-
|
391
|
-
# @param [String] key
|
392
|
-
# @return [Fixnum] remaining time to live in seconds.
|
393
|
-
#
|
394
|
-
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
395
|
-
# the key exist but has no associated expire.
|
396
|
-
#
|
397
|
-
# Starting with Redis 2.8 the return value in case of error changed:
|
398
|
-
#
|
399
|
-
# - The command returns -2 if the key does not exist.
|
400
|
-
# - The command returns -1 if the key exists but has no associated expire.
|
401
|
-
def ttl(key)
|
402
|
-
synchronize do |client|
|
403
|
-
client.call([:ttl, key])
|
404
|
-
end
|
242
|
+
def inspect
|
243
|
+
"#<Redis client v#{Redis::VERSION} for #{id}>"
|
405
244
|
end
|
406
245
|
|
407
|
-
|
408
|
-
|
409
|
-
# @param [String] key
|
410
|
-
# @param [Fixnum] milliseconds time to live
|
411
|
-
# @return [Boolean] whether the timeout was set or not
|
412
|
-
def pexpire(key, milliseconds)
|
413
|
-
synchronize do |client|
|
414
|
-
client.call([:pexpire, key, milliseconds], &Boolify)
|
415
|
-
end
|
246
|
+
def dup
|
247
|
+
self.class.new(@options)
|
416
248
|
end
|
417
249
|
|
418
|
-
|
419
|
-
|
420
|
-
# @param [String] key
|
421
|
-
# @param [Fixnum] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
422
|
-
# @return [Boolean] whether the timeout was set or not
|
423
|
-
def pexpireat(key, ms_unix_time)
|
424
|
-
synchronize do |client|
|
425
|
-
client.call([:pexpireat, key, ms_unix_time], &Boolify)
|
426
|
-
end
|
427
|
-
end
|
250
|
+
def connection
|
251
|
+
return @original_client.connection_info if @cluster_mode
|
428
252
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
# Starting with Redis 2.8 the return value in case of error changed:
|
437
|
-
#
|
438
|
-
# - The command returns -2 if the key does not exist.
|
439
|
-
# - The command returns -1 if the key exists but has no associated expire.
|
440
|
-
def pttl(key)
|
441
|
-
synchronize do |client|
|
442
|
-
client.call([:pttl, key])
|
443
|
-
end
|
253
|
+
{
|
254
|
+
host: @original_client.host,
|
255
|
+
port: @original_client.port,
|
256
|
+
db: @original_client.db,
|
257
|
+
id: @original_client.id,
|
258
|
+
location: @original_client.location
|
259
|
+
}
|
444
260
|
end
|
445
261
|
|
446
|
-
|
447
|
-
#
|
448
|
-
# @param [String] key
|
449
|
-
# @return [String] serialized_value
|
450
|
-
def dump(key)
|
451
|
-
synchronize do |client|
|
452
|
-
client.call([:dump, key])
|
453
|
-
end
|
454
|
-
end
|
262
|
+
private
|
455
263
|
|
456
|
-
|
457
|
-
|
458
|
-
# @param [String] key
|
459
|
-
# @param [String] ttl
|
460
|
-
# @param [String] serialized_value
|
461
|
-
# @return [String] `"OK"`
|
462
|
-
def restore(key, ttl, serialized_value)
|
463
|
-
synchronize do |client|
|
464
|
-
client.call([:restore, key, ttl, serialized_value])
|
465
|
-
end
|
264
|
+
def synchronize
|
265
|
+
@monitor.synchronize { yield(@client) }
|
466
266
|
end
|
467
267
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
# @param [Hash] options
|
472
|
-
# - `:host => String`: host of instance to migrate to
|
473
|
-
# - `:port => Integer`: port of instance to migrate to
|
474
|
-
# - `:db => Integer`: database to migrate to (default: same as source)
|
475
|
-
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
476
|
-
# @return [String] `"OK"`
|
477
|
-
def migrate(key, options)
|
478
|
-
host = options[:host] || raise(RuntimeError, ":host not specified")
|
479
|
-
port = options[:port] || raise(RuntimeError, ":port not specified")
|
480
|
-
db = (options[:db] || client.db).to_i
|
481
|
-
timeout = (options[:timeout] || client.timeout).to_i
|
482
|
-
|
483
|
-
synchronize do |client|
|
484
|
-
client.call([:migrate, host, port, key, db, timeout])
|
268
|
+
def send_command(command, &block)
|
269
|
+
@monitor.synchronize do
|
270
|
+
@client.call(command, &block)
|
485
271
|
end
|
486
272
|
end
|
487
273
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
# @return [Fixnum] number of keys that were deleted
|
492
|
-
def del(*keys)
|
493
|
-
synchronize do |client|
|
494
|
-
client.call([:del] + keys)
|
274
|
+
def send_blocking_command(command, timeout, &block)
|
275
|
+
@monitor.synchronize do
|
276
|
+
@client.call_with_timeout(command, timeout, &block)
|
495
277
|
end
|
496
278
|
end
|
497
279
|
|
498
|
-
|
499
|
-
|
500
|
-
# @param [String] key
|
501
|
-
# @return [Boolean]
|
502
|
-
def exists(key)
|
503
|
-
synchronize do |client|
|
504
|
-
client.call([:exists, key], &Boolify)
|
505
|
-
end
|
506
|
-
end
|
280
|
+
def _subscription(method, timeout, channels, block)
|
281
|
+
return @client.call([method] + channels) if subscribed?
|
507
282
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
client.call([:keys, pattern]) do |reply|
|
515
|
-
if reply.kind_of?(String)
|
516
|
-
reply.split(" ")
|
517
|
-
else
|
518
|
-
reply
|
519
|
-
end
|
283
|
+
begin
|
284
|
+
original, @client = @client, SubscribedClient.new(@client)
|
285
|
+
if timeout > 0
|
286
|
+
@client.send(method, timeout, *channels, &block)
|
287
|
+
else
|
288
|
+
@client.send(method, *channels, &block)
|
520
289
|
end
|
290
|
+
ensure
|
291
|
+
@client = original
|
521
292
|
end
|
522
293
|
end
|
523
|
-
|
524
|
-
# Move a key to another database.
|
525
|
-
#
|
526
|
-
# @example Move a key to another database
|
527
|
-
# redis.set "foo", "bar"
|
528
|
-
# # => "OK"
|
529
|
-
# redis.move "foo", 2
|
530
|
-
# # => true
|
531
|
-
# redis.exists "foo"
|
532
|
-
# # => false
|
533
|
-
# redis.select 2
|
534
|
-
# # => "OK"
|
535
|
-
# redis.exists "foo"
|
536
|
-
# # => true
|
537
|
-
# redis.get "foo"
|
538
|
-
# # => "bar"
|
539
|
-
#
|
540
|
-
# @param [String] key
|
541
|
-
# @param [Fixnum] db
|
542
|
-
# @return [Boolean] whether the key was moved or not
|
543
|
-
def move(key, db)
|
544
|
-
synchronize do |client|
|
545
|
-
client.call([:move, key, db], &Boolify)
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
def object(*args)
|
550
|
-
synchronize do |client|
|
551
|
-
client.call([:object] + args)
|
552
|
-
end
|
553
|
-
end
|
554
|
-
|
555
|
-
# Return a random key from the keyspace.
|
556
|
-
#
|
557
|
-
# @return [String]
|
558
|
-
def randomkey
|
559
|
-
synchronize do |client|
|
560
|
-
client.call([:randomkey])
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
# Rename a key. If the new key already exists it is overwritten.
|
565
|
-
#
|
566
|
-
# @param [String] old_name
|
567
|
-
# @param [String] new_name
|
568
|
-
# @return [String] `OK`
|
569
|
-
def rename(old_name, new_name)
|
570
|
-
synchronize do |client|
|
571
|
-
client.call([:rename, old_name, new_name])
|
572
|
-
end
|
573
|
-
end
|
574
|
-
|
575
|
-
# Rename a key, only if the new key does not exist.
|
576
|
-
#
|
577
|
-
# @param [String] old_name
|
578
|
-
# @param [String] new_name
|
579
|
-
# @return [Boolean] whether the key was renamed or not
|
580
|
-
def renamenx(old_name, new_name)
|
581
|
-
synchronize do |client|
|
582
|
-
client.call([:renamenx, old_name, new_name], &Boolify)
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
# Sort the elements in a list, set or sorted set.
|
587
|
-
#
|
588
|
-
# @example Retrieve the first 2 elements from an alphabetically sorted "list"
|
589
|
-
# redis.sort("list", :order => "alpha", :limit => [0, 2])
|
590
|
-
# # => ["a", "b"]
|
591
|
-
# @example Store an alphabetically descending list in "target"
|
592
|
-
# redis.sort("list", :order => "desc alpha", :store => "target")
|
593
|
-
# # => 26
|
594
|
-
#
|
595
|
-
# @param [String] key
|
596
|
-
# @param [Hash] options
|
597
|
-
# - `:by => String`: use external key to sort elements by
|
598
|
-
# - `:limit => [offset, count]`: skip `offset` elements, return a maximum
|
599
|
-
# of `count` elements
|
600
|
-
# - `:get => [String, Array<String>]`: single key or array of keys to
|
601
|
-
# retrieve per element in the result
|
602
|
-
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
603
|
-
# - `:store => String`: key to store the result at
|
604
|
-
#
|
605
|
-
# @return [Array<String>, Array<Array<String>>, Fixnum]
|
606
|
-
# - when `:get` is not specified, or holds a single element, an array of elements
|
607
|
-
# - when `:get` is specified, and holds more than one element, an array of
|
608
|
-
# elements where every element is an array with the result for every
|
609
|
-
# element specified in `:get`
|
610
|
-
# - when `:store` is specified, the number of elements in the stored result
|
611
|
-
def sort(key, options = {})
|
612
|
-
args = []
|
613
|
-
|
614
|
-
by = options[:by]
|
615
|
-
args.concat(["BY", by]) if by
|
616
|
-
|
617
|
-
limit = options[:limit]
|
618
|
-
args.concat(["LIMIT"] + limit) if limit
|
619
|
-
|
620
|
-
get = Array(options[:get])
|
621
|
-
args.concat(["GET"].product(get).flatten) unless get.empty?
|
622
|
-
|
623
|
-
order = options[:order]
|
624
|
-
args.concat(order.split(" ")) if order
|
625
|
-
|
626
|
-
store = options[:store]
|
627
|
-
args.concat(["STORE", store]) if store
|
628
|
-
|
629
|
-
synchronize do |client|
|
630
|
-
client.call([:sort, key] + args) do |reply|
|
631
|
-
if get.size > 1 && !store
|
632
|
-
if reply
|
633
|
-
reply.each_slice(get.size).to_a
|
634
|
-
end
|
635
|
-
else
|
636
|
-
reply
|
637
|
-
end
|
638
|
-
end
|
639
|
-
end
|
640
|
-
end
|
641
|
-
|
642
|
-
# Determine the type stored at key.
|
643
|
-
#
|
644
|
-
# @param [String] key
|
645
|
-
# @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
|
646
|
-
def type(key)
|
647
|
-
synchronize do |client|
|
648
|
-
client.call([:type, key])
|
649
|
-
end
|
650
|
-
end
|
651
|
-
|
652
|
-
# Decrement the integer value of a key by one.
|
653
|
-
#
|
654
|
-
# @example
|
655
|
-
# redis.decr("value")
|
656
|
-
# # => 4
|
657
|
-
#
|
658
|
-
# @param [String] key
|
659
|
-
# @return [Fixnum] value after decrementing it
|
660
|
-
def decr(key)
|
661
|
-
synchronize do |client|
|
662
|
-
client.call([:decr, key])
|
663
|
-
end
|
664
|
-
end
|
665
|
-
|
666
|
-
# Decrement the integer value of a key by the given number.
|
667
|
-
#
|
668
|
-
# @example
|
669
|
-
# redis.decrby("value", 5)
|
670
|
-
# # => 0
|
671
|
-
#
|
672
|
-
# @param [String] key
|
673
|
-
# @param [Fixnum] decrement
|
674
|
-
# @return [Fixnum] value after decrementing it
|
675
|
-
def decrby(key, decrement)
|
676
|
-
synchronize do |client|
|
677
|
-
client.call([:decrby, key, decrement])
|
678
|
-
end
|
679
|
-
end
|
680
|
-
|
681
|
-
# Increment the integer value of a key by one.
|
682
|
-
#
|
683
|
-
# @example
|
684
|
-
# redis.incr("value")
|
685
|
-
# # => 6
|
686
|
-
#
|
687
|
-
# @param [String] key
|
688
|
-
# @return [Fixnum] value after incrementing it
|
689
|
-
def incr(key)
|
690
|
-
synchronize do |client|
|
691
|
-
client.call([:incr, key])
|
692
|
-
end
|
693
|
-
end
|
694
|
-
|
695
|
-
# Increment the integer value of a key by the given integer number.
|
696
|
-
#
|
697
|
-
# @example
|
698
|
-
# redis.incrby("value", 5)
|
699
|
-
# # => 10
|
700
|
-
#
|
701
|
-
# @param [String] key
|
702
|
-
# @param [Fixnum] increment
|
703
|
-
# @return [Fixnum] value after incrementing it
|
704
|
-
def incrby(key, increment)
|
705
|
-
synchronize do |client|
|
706
|
-
client.call([:incrby, key, increment])
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
# Increment the numeric value of a key by the given float number.
|
711
|
-
#
|
712
|
-
# @example
|
713
|
-
# redis.incrbyfloat("value", 1.23)
|
714
|
-
# # => 1.23
|
715
|
-
#
|
716
|
-
# @param [String] key
|
717
|
-
# @param [Float] increment
|
718
|
-
# @return [Float] value after incrementing it
|
719
|
-
def incrbyfloat(key, increment)
|
720
|
-
synchronize do |client|
|
721
|
-
client.call([:incrbyfloat, key, increment], &Floatify)
|
722
|
-
end
|
723
|
-
end
|
724
|
-
|
725
|
-
# Set the string value of a key.
|
726
|
-
#
|
727
|
-
# @param [String] key
|
728
|
-
# @param [String] value
|
729
|
-
# @param [Hash] options
|
730
|
-
# - `:ex => Fixnum`: Set the specified expire time, in seconds.
|
731
|
-
# - `:px => Fixnum`: Set the specified expire time, in milliseconds.
|
732
|
-
# - `:nx => true`: Only set the key if it does not already exist.
|
733
|
-
# - `:xx => true`: Only set the key if it already exist.
|
734
|
-
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
735
|
-
def set(key, value, options = {})
|
736
|
-
args = []
|
737
|
-
|
738
|
-
ex = options[:ex]
|
739
|
-
args.concat(["EX", ex]) if ex
|
740
|
-
|
741
|
-
px = options[:px]
|
742
|
-
args.concat(["PX", px]) if px
|
743
|
-
|
744
|
-
nx = options[:nx]
|
745
|
-
args.concat(["NX"]) if nx
|
746
|
-
|
747
|
-
xx = options[:xx]
|
748
|
-
args.concat(["XX"]) if xx
|
749
|
-
|
750
|
-
synchronize do |client|
|
751
|
-
if nx || xx
|
752
|
-
client.call([:set, key, value.to_s] + args, &BoolifySet)
|
753
|
-
else
|
754
|
-
client.call([:set, key, value.to_s] + args)
|
755
|
-
end
|
756
|
-
end
|
757
|
-
end
|
758
|
-
|
759
|
-
alias :[]= :set
|
760
|
-
|
761
|
-
# Set the time to live in seconds of a key.
|
762
|
-
#
|
763
|
-
# @param [String] key
|
764
|
-
# @param [Fixnum] ttl
|
765
|
-
# @param [String] value
|
766
|
-
# @return [String] `"OK"`
|
767
|
-
def setex(key, ttl, value)
|
768
|
-
synchronize do |client|
|
769
|
-
client.call([:setex, key, ttl, value.to_s])
|
770
|
-
end
|
771
|
-
end
|
772
|
-
|
773
|
-
# Set the time to live in milliseconds of a key.
|
774
|
-
#
|
775
|
-
# @param [String] key
|
776
|
-
# @param [Fixnum] ttl
|
777
|
-
# @param [String] value
|
778
|
-
# @return [String] `"OK"`
|
779
|
-
def psetex(key, ttl, value)
|
780
|
-
synchronize do |client|
|
781
|
-
client.call([:psetex, key, ttl, value.to_s])
|
782
|
-
end
|
783
|
-
end
|
784
|
-
|
785
|
-
# Set the value of a key, only if the key does not exist.
|
786
|
-
#
|
787
|
-
# @param [String] key
|
788
|
-
# @param [String] value
|
789
|
-
# @return [Boolean] whether the key was set or not
|
790
|
-
def setnx(key, value)
|
791
|
-
synchronize do |client|
|
792
|
-
client.call([:setnx, key, value.to_s], &Boolify)
|
793
|
-
end
|
794
|
-
end
|
795
|
-
|
796
|
-
# Set one or more values.
|
797
|
-
#
|
798
|
-
# @example
|
799
|
-
# redis.mset("key1", "v1", "key2", "v2")
|
800
|
-
# # => "OK"
|
801
|
-
#
|
802
|
-
# @param [Array<String>] args array of keys and values
|
803
|
-
# @return [String] `"OK"`
|
804
|
-
#
|
805
|
-
# @see #mapped_mset
|
806
|
-
def mset(*args)
|
807
|
-
synchronize do |client|
|
808
|
-
client.call([:mset] + args)
|
809
|
-
end
|
810
|
-
end
|
811
|
-
|
812
|
-
# Set one or more values.
|
813
|
-
#
|
814
|
-
# @example
|
815
|
-
# redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
|
816
|
-
# # => "OK"
|
817
|
-
#
|
818
|
-
# @param [Hash] hash keys mapping to values
|
819
|
-
# @return [String] `"OK"`
|
820
|
-
#
|
821
|
-
# @see #mset
|
822
|
-
def mapped_mset(hash)
|
823
|
-
mset(hash.to_a.flatten)
|
824
|
-
end
|
825
|
-
|
826
|
-
# Set one or more values, only if none of the keys exist.
|
827
|
-
#
|
828
|
-
# @example
|
829
|
-
# redis.msetnx("key1", "v1", "key2", "v2")
|
830
|
-
# # => true
|
831
|
-
#
|
832
|
-
# @param [Array<String>] args array of keys and values
|
833
|
-
# @return [Boolean] whether or not all values were set
|
834
|
-
#
|
835
|
-
# @see #mapped_msetnx
|
836
|
-
def msetnx(*args)
|
837
|
-
synchronize do |client|
|
838
|
-
client.call([:msetnx] + args, &Boolify)
|
839
|
-
end
|
840
|
-
end
|
841
|
-
|
842
|
-
# Set one or more values, only if none of the keys exist.
|
843
|
-
#
|
844
|
-
# @example
|
845
|
-
# redis.mapped_msetnx({ "key1" => "v1", "key2" => "v2" })
|
846
|
-
# # => true
|
847
|
-
#
|
848
|
-
# @param [Hash] hash keys mapping to values
|
849
|
-
# @return [Boolean] whether or not all values were set
|
850
|
-
#
|
851
|
-
# @see #msetnx
|
852
|
-
def mapped_msetnx(hash)
|
853
|
-
msetnx(hash.to_a.flatten)
|
854
|
-
end
|
855
|
-
|
856
|
-
# Get the value of a key.
|
857
|
-
#
|
858
|
-
# @param [String] key
|
859
|
-
# @return [String]
|
860
|
-
def get(key)
|
861
|
-
synchronize do |client|
|
862
|
-
client.call([:get, key])
|
863
|
-
end
|
864
|
-
end
|
865
|
-
|
866
|
-
alias :[] :get
|
867
|
-
|
868
|
-
# Get the values of all the given keys.
|
869
|
-
#
|
870
|
-
# @example
|
871
|
-
# redis.mget("key1", "key1")
|
872
|
-
# # => ["v1", "v2"]
|
873
|
-
#
|
874
|
-
# @param [Array<String>] keys
|
875
|
-
# @return [Array<String>] an array of values for the specified keys
|
876
|
-
#
|
877
|
-
# @see #mapped_mget
|
878
|
-
def mget(*keys, &blk)
|
879
|
-
synchronize do |client|
|
880
|
-
client.call([:mget] + keys, &blk)
|
881
|
-
end
|
882
|
-
end
|
883
|
-
|
884
|
-
# Get the values of all the given keys.
|
885
|
-
#
|
886
|
-
# @example
|
887
|
-
# redis.mapped_mget("key1", "key2")
|
888
|
-
# # => { "key1" => "v1", "key2" => "v2" }
|
889
|
-
#
|
890
|
-
# @param [Array<String>] keys array of keys
|
891
|
-
# @return [Hash] a hash mapping the specified keys to their values
|
892
|
-
#
|
893
|
-
# @see #mget
|
894
|
-
def mapped_mget(*keys)
|
895
|
-
mget(*keys) do |reply|
|
896
|
-
if reply.kind_of?(Array)
|
897
|
-
Hash[keys.zip(reply)]
|
898
|
-
else
|
899
|
-
reply
|
900
|
-
end
|
901
|
-
end
|
902
|
-
end
|
903
|
-
|
904
|
-
# Overwrite part of a string at key starting at the specified offset.
|
905
|
-
#
|
906
|
-
# @param [String] key
|
907
|
-
# @param [Fixnum] offset byte offset
|
908
|
-
# @param [String] value
|
909
|
-
# @return [Fixnum] length of the string after it was modified
|
910
|
-
def setrange(key, offset, value)
|
911
|
-
synchronize do |client|
|
912
|
-
client.call([:setrange, key, offset, value.to_s])
|
913
|
-
end
|
914
|
-
end
|
915
|
-
|
916
|
-
# Get a substring of the string stored at a key.
|
917
|
-
#
|
918
|
-
# @param [String] key
|
919
|
-
# @param [Fixnum] start zero-based start offset
|
920
|
-
# @param [Fixnum] stop zero-based end offset. Use -1 for representing
|
921
|
-
# the end of the string
|
922
|
-
# @return [Fixnum] `0` or `1`
|
923
|
-
def getrange(key, start, stop)
|
924
|
-
synchronize do |client|
|
925
|
-
client.call([:getrange, key, start, stop])
|
926
|
-
end
|
927
|
-
end
|
928
|
-
|
929
|
-
# Sets or clears the bit at offset in the string value stored at key.
|
930
|
-
#
|
931
|
-
# @param [String] key
|
932
|
-
# @param [Fixnum] offset bit offset
|
933
|
-
# @param [Fixnum] value bit value `0` or `1`
|
934
|
-
# @return [Fixnum] the original bit value stored at `offset`
|
935
|
-
def setbit(key, offset, value)
|
936
|
-
synchronize do |client|
|
937
|
-
client.call([:setbit, key, offset, value])
|
938
|
-
end
|
939
|
-
end
|
940
|
-
|
941
|
-
# Returns the bit value at offset in the string value stored at key.
|
942
|
-
#
|
943
|
-
# @param [String] key
|
944
|
-
# @param [Fixnum] offset bit offset
|
945
|
-
# @return [Fixnum] `0` or `1`
|
946
|
-
def getbit(key, offset)
|
947
|
-
synchronize do |client|
|
948
|
-
client.call([:getbit, key, offset])
|
949
|
-
end
|
950
|
-
end
|
951
|
-
|
952
|
-
# Append a value to a key.
|
953
|
-
#
|
954
|
-
# @param [String] key
|
955
|
-
# @param [String] value value to append
|
956
|
-
# @return [Fixnum] length of the string after appending
|
957
|
-
def append(key, value)
|
958
|
-
synchronize do |client|
|
959
|
-
client.call([:append, key, value])
|
960
|
-
end
|
961
|
-
end
|
962
|
-
|
963
|
-
# Count the number of set bits in a range of the string value stored at key.
|
964
|
-
#
|
965
|
-
# @param [String] key
|
966
|
-
# @param [Fixnum] start start index
|
967
|
-
# @param [Fixnum] stop stop index
|
968
|
-
# @return [Fixnum] the number of bits set to 1
|
969
|
-
def bitcount(key, start = 0, stop = -1)
|
970
|
-
synchronize do |client|
|
971
|
-
client.call([:bitcount, key, start, stop])
|
972
|
-
end
|
973
|
-
end
|
974
|
-
|
975
|
-
# Perform a bitwise operation between strings and store the resulting string in a key.
|
976
|
-
#
|
977
|
-
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
978
|
-
# @param [String] destkey destination key
|
979
|
-
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
980
|
-
# @return [Fixnum] the length of the string stored in `destkey`
|
981
|
-
def bitop(operation, destkey, *keys)
|
982
|
-
synchronize do |client|
|
983
|
-
client.call([:bitop, operation, destkey] + keys)
|
984
|
-
end
|
985
|
-
end
|
986
|
-
|
987
|
-
# Return the position of the first bit set to 1 or 0 in a string.
|
988
|
-
#
|
989
|
-
# @param [String] key
|
990
|
-
# @param [Fixnum] bit whether to look for the first 1 or 0 bit
|
991
|
-
# @param [Fixnum] start start index
|
992
|
-
# @param [Fixnum] stop stop index
|
993
|
-
# @return [Fixnum] the position of the first 1/0 bit.
|
994
|
-
# -1 if looking for 1 and it is not found or start and stop are given.
|
995
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
996
|
-
if stop and not start
|
997
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
998
|
-
end
|
999
|
-
|
1000
|
-
synchronize do |client|
|
1001
|
-
command = [:bitpos, key, bit]
|
1002
|
-
command << start if start
|
1003
|
-
command << stop if stop
|
1004
|
-
client.call(command)
|
1005
|
-
end
|
1006
|
-
end
|
1007
|
-
|
1008
|
-
# Set the string value of a key and return its old value.
|
1009
|
-
#
|
1010
|
-
# @param [String] key
|
1011
|
-
# @param [String] value value to replace the current value with
|
1012
|
-
# @return [String] the old value stored in the key, or `nil` if the key
|
1013
|
-
# did not exist
|
1014
|
-
def getset(key, value)
|
1015
|
-
synchronize do |client|
|
1016
|
-
client.call([:getset, key, value.to_s])
|
1017
|
-
end
|
1018
|
-
end
|
1019
|
-
|
1020
|
-
# Get the length of the value stored in a key.
|
1021
|
-
#
|
1022
|
-
# @param [String] key
|
1023
|
-
# @return [Fixnum] the length of the value stored in the key, or 0
|
1024
|
-
# if the key does not exist
|
1025
|
-
def strlen(key)
|
1026
|
-
synchronize do |client|
|
1027
|
-
client.call([:strlen, key])
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
|
1031
|
-
# Get the length of a list.
|
1032
|
-
#
|
1033
|
-
# @param [String] key
|
1034
|
-
# @return [Fixnum]
|
1035
|
-
def llen(key)
|
1036
|
-
synchronize do |client|
|
1037
|
-
client.call([:llen, key])
|
1038
|
-
end
|
1039
|
-
end
|
1040
|
-
|
1041
|
-
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1042
|
-
#
|
1043
|
-
# @param [String] key
|
1044
|
-
# @param [String, Array] value string value, or array of string values to push
|
1045
|
-
# @return [Fixnum] the length of the list after the push operation
|
1046
|
-
def lpush(key, value)
|
1047
|
-
synchronize do |client|
|
1048
|
-
client.call([:lpush, key, value])
|
1049
|
-
end
|
1050
|
-
end
|
1051
|
-
|
1052
|
-
# Prepend a value to a list, only if the list exists.
|
1053
|
-
#
|
1054
|
-
# @param [String] key
|
1055
|
-
# @param [String] value
|
1056
|
-
# @return [Fixnum] the length of the list after the push operation
|
1057
|
-
def lpushx(key, value)
|
1058
|
-
synchronize do |client|
|
1059
|
-
client.call([:lpushx, key, value])
|
1060
|
-
end
|
1061
|
-
end
|
1062
|
-
|
1063
|
-
# Append one or more values to a list, creating the list if it doesn't exist
|
1064
|
-
#
|
1065
|
-
# @param [String] key
|
1066
|
-
# @param [String] value
|
1067
|
-
# @return [Fixnum] the length of the list after the push operation
|
1068
|
-
def rpush(key, value)
|
1069
|
-
synchronize do |client|
|
1070
|
-
client.call([:rpush, key, value])
|
1071
|
-
end
|
1072
|
-
end
|
1073
|
-
|
1074
|
-
# Append a value to a list, only if the list exists.
|
1075
|
-
#
|
1076
|
-
# @param [String] key
|
1077
|
-
# @param [String] value
|
1078
|
-
# @return [Fixnum] the length of the list after the push operation
|
1079
|
-
def rpushx(key, value)
|
1080
|
-
synchronize do |client|
|
1081
|
-
client.call([:rpushx, key, value])
|
1082
|
-
end
|
1083
|
-
end
|
1084
|
-
|
1085
|
-
# Remove and get the first element in a list.
|
1086
|
-
#
|
1087
|
-
# @param [String] key
|
1088
|
-
# @return [String]
|
1089
|
-
def lpop(key)
|
1090
|
-
synchronize do |client|
|
1091
|
-
client.call([:lpop, key])
|
1092
|
-
end
|
1093
|
-
end
|
1094
|
-
|
1095
|
-
# Remove and get the last element in a list.
|
1096
|
-
#
|
1097
|
-
# @param [String] key
|
1098
|
-
# @return [String]
|
1099
|
-
def rpop(key)
|
1100
|
-
synchronize do |client|
|
1101
|
-
client.call([:rpop, key])
|
1102
|
-
end
|
1103
|
-
end
|
1104
|
-
|
1105
|
-
# Remove the last element in a list, append it to another list and return it.
|
1106
|
-
#
|
1107
|
-
# @param [String] source source key
|
1108
|
-
# @param [String] destination destination key
|
1109
|
-
# @return [nil, String] the element, or nil when the source key does not exist
|
1110
|
-
def rpoplpush(source, destination)
|
1111
|
-
synchronize do |client|
|
1112
|
-
client.call([:rpoplpush, source, destination])
|
1113
|
-
end
|
1114
|
-
end
|
1115
|
-
|
1116
|
-
def _bpop(cmd, args)
|
1117
|
-
options = {}
|
1118
|
-
|
1119
|
-
case args.last
|
1120
|
-
when Hash
|
1121
|
-
options = args.pop
|
1122
|
-
when Integer
|
1123
|
-
# Issue deprecation notice in obnoxious mode...
|
1124
|
-
options[:timeout] = args.pop
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
if args.size > 1
|
1128
|
-
# Issue deprecation notice in obnoxious mode...
|
1129
|
-
end
|
1130
|
-
|
1131
|
-
keys = args.flatten
|
1132
|
-
timeout = options[:timeout] || 0
|
1133
|
-
|
1134
|
-
synchronize do |client|
|
1135
|
-
command = [cmd, keys, timeout]
|
1136
|
-
timeout += client.timeout if timeout > 0
|
1137
|
-
client.call_with_timeout(command, timeout)
|
1138
|
-
end
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
# Remove and get the first element in a list, or block until one is available.
|
1142
|
-
#
|
1143
|
-
# @example With timeout
|
1144
|
-
# list, element = redis.blpop("list", :timeout => 5)
|
1145
|
-
# # => nil on timeout
|
1146
|
-
# # => ["list", "element"] on success
|
1147
|
-
# @example Without timeout
|
1148
|
-
# list, element = redis.blpop("list")
|
1149
|
-
# # => ["list", "element"]
|
1150
|
-
# @example Blocking pop on multiple lists
|
1151
|
-
# list, element = redis.blpop(["list", "another_list"])
|
1152
|
-
# # => ["list", "element"]
|
1153
|
-
#
|
1154
|
-
# @param [String, Array<String>] keys one or more keys to perform the
|
1155
|
-
# blocking pop on
|
1156
|
-
# @param [Hash] options
|
1157
|
-
# - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
|
1158
|
-
#
|
1159
|
-
# @return [nil, [String, String]]
|
1160
|
-
# - `nil` when the operation timed out
|
1161
|
-
# - tuple of the list that was popped from and element was popped otherwise
|
1162
|
-
def blpop(*args)
|
1163
|
-
_bpop(:blpop, args)
|
1164
|
-
end
|
1165
|
-
|
1166
|
-
# Remove and get the last element in a list, or block until one is available.
|
1167
|
-
#
|
1168
|
-
# @param [String, Array<String>] keys one or more keys to perform the
|
1169
|
-
# blocking pop on
|
1170
|
-
# @param [Hash] options
|
1171
|
-
# - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
|
1172
|
-
#
|
1173
|
-
# @return [nil, [String, String]]
|
1174
|
-
# - `nil` when the operation timed out
|
1175
|
-
# - tuple of the list that was popped from and element was popped otherwise
|
1176
|
-
#
|
1177
|
-
# @see #blpop
|
1178
|
-
def brpop(*args)
|
1179
|
-
_bpop(:brpop, args)
|
1180
|
-
end
|
1181
|
-
|
1182
|
-
# Pop a value from a list, push it to another list and return it; or block
|
1183
|
-
# until one is available.
|
1184
|
-
#
|
1185
|
-
# @param [String] source source key
|
1186
|
-
# @param [String] destination destination key
|
1187
|
-
# @param [Hash] options
|
1188
|
-
# - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
|
1189
|
-
#
|
1190
|
-
# @return [nil, String]
|
1191
|
-
# - `nil` when the operation timed out
|
1192
|
-
# - the element was popped and pushed otherwise
|
1193
|
-
def brpoplpush(source, destination, options = {})
|
1194
|
-
case options
|
1195
|
-
when Integer
|
1196
|
-
# Issue deprecation notice in obnoxious mode...
|
1197
|
-
options = { :timeout => options }
|
1198
|
-
end
|
1199
|
-
|
1200
|
-
timeout = options[:timeout] || 0
|
1201
|
-
|
1202
|
-
synchronize do |client|
|
1203
|
-
command = [:brpoplpush, source, destination, timeout]
|
1204
|
-
timeout += client.timeout if timeout > 0
|
1205
|
-
client.call_with_timeout(command, timeout)
|
1206
|
-
end
|
1207
|
-
end
|
1208
|
-
|
1209
|
-
# Get an element from a list by its index.
|
1210
|
-
#
|
1211
|
-
# @param [String] key
|
1212
|
-
# @param [Fixnum] index
|
1213
|
-
# @return [String]
|
1214
|
-
def lindex(key, index)
|
1215
|
-
synchronize do |client|
|
1216
|
-
client.call([:lindex, key, index])
|
1217
|
-
end
|
1218
|
-
end
|
1219
|
-
|
1220
|
-
# Insert an element before or after another element in a list.
|
1221
|
-
#
|
1222
|
-
# @param [String] key
|
1223
|
-
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1224
|
-
# @param [String] pivot reference element
|
1225
|
-
# @param [String] value
|
1226
|
-
# @return [Fixnum] length of the list after the insert operation, or `-1`
|
1227
|
-
# when the element `pivot` was not found
|
1228
|
-
def linsert(key, where, pivot, value)
|
1229
|
-
synchronize do |client|
|
1230
|
-
client.call([:linsert, key, where, pivot, value])
|
1231
|
-
end
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
# Get a range of elements from a list.
|
1235
|
-
#
|
1236
|
-
# @param [String] key
|
1237
|
-
# @param [Fixnum] start start index
|
1238
|
-
# @param [Fixnum] stop stop index
|
1239
|
-
# @return [Array<String>]
|
1240
|
-
def lrange(key, start, stop)
|
1241
|
-
synchronize do |client|
|
1242
|
-
client.call([:lrange, key, start, stop])
|
1243
|
-
end
|
1244
|
-
end
|
1245
|
-
|
1246
|
-
# Remove elements from a list.
|
1247
|
-
#
|
1248
|
-
# @param [String] key
|
1249
|
-
# @param [Fixnum] count number of elements to remove. Use a positive
|
1250
|
-
# value to remove the first `count` occurrences of `value`. A negative
|
1251
|
-
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1252
|
-
# remove all occurrences of `value` from the list.
|
1253
|
-
# @param [String] value
|
1254
|
-
# @return [Fixnum] the number of removed elements
|
1255
|
-
def lrem(key, count, value)
|
1256
|
-
synchronize do |client|
|
1257
|
-
client.call([:lrem, key, count, value])
|
1258
|
-
end
|
1259
|
-
end
|
1260
|
-
|
1261
|
-
# Set the value of an element in a list by its index.
|
1262
|
-
#
|
1263
|
-
# @param [String] key
|
1264
|
-
# @param [Fixnum] index
|
1265
|
-
# @param [String] value
|
1266
|
-
# @return [String] `OK`
|
1267
|
-
def lset(key, index, value)
|
1268
|
-
synchronize do |client|
|
1269
|
-
client.call([:lset, key, index, value])
|
1270
|
-
end
|
1271
|
-
end
|
1272
|
-
|
1273
|
-
# Trim a list to the specified range.
|
1274
|
-
#
|
1275
|
-
# @param [String] key
|
1276
|
-
# @param [Fixnum] start start index
|
1277
|
-
# @param [Fixnum] stop stop index
|
1278
|
-
# @return [String] `OK`
|
1279
|
-
def ltrim(key, start, stop)
|
1280
|
-
synchronize do |client|
|
1281
|
-
client.call([:ltrim, key, start, stop])
|
1282
|
-
end
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
# Get the number of members in a set.
|
1286
|
-
#
|
1287
|
-
# @param [String] key
|
1288
|
-
# @return [Fixnum]
|
1289
|
-
def scard(key)
|
1290
|
-
synchronize do |client|
|
1291
|
-
client.call([:scard, key])
|
1292
|
-
end
|
1293
|
-
end
|
1294
|
-
|
1295
|
-
# Add one or more members to a set.
|
1296
|
-
#
|
1297
|
-
# @param [String] key
|
1298
|
-
# @param [String, Array<String>] member one member, or array of members
|
1299
|
-
# @return [Boolean, Fixnum] `Boolean` when a single member is specified,
|
1300
|
-
# holding whether or not adding the member succeeded, or `Fixnum` when an
|
1301
|
-
# array of members is specified, holding the number of members that were
|
1302
|
-
# successfully added
|
1303
|
-
def sadd(key, member)
|
1304
|
-
synchronize do |client|
|
1305
|
-
client.call([:sadd, key, member]) do |reply|
|
1306
|
-
if member.is_a? Array
|
1307
|
-
# Variadic: return integer
|
1308
|
-
reply
|
1309
|
-
else
|
1310
|
-
# Single argument: return boolean
|
1311
|
-
Boolify.call(reply)
|
1312
|
-
end
|
1313
|
-
end
|
1314
|
-
end
|
1315
|
-
end
|
1316
|
-
|
1317
|
-
# Remove one or more members from a set.
|
1318
|
-
#
|
1319
|
-
# @param [String] key
|
1320
|
-
# @param [String, Array<String>] member one member, or array of members
|
1321
|
-
# @return [Boolean, Fixnum] `Boolean` when a single member is specified,
|
1322
|
-
# holding whether or not removing the member succeeded, or `Fixnum` when an
|
1323
|
-
# array of members is specified, holding the number of members that were
|
1324
|
-
# successfully removed
|
1325
|
-
def srem(key, member)
|
1326
|
-
synchronize do |client|
|
1327
|
-
client.call([:srem, key, member]) do |reply|
|
1328
|
-
if member.is_a? Array
|
1329
|
-
# Variadic: return integer
|
1330
|
-
reply
|
1331
|
-
else
|
1332
|
-
# Single argument: return boolean
|
1333
|
-
Boolify.call(reply)
|
1334
|
-
end
|
1335
|
-
end
|
1336
|
-
end
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
# Remove and return one or more random member from a set.
|
1340
|
-
#
|
1341
|
-
# @param [String] key
|
1342
|
-
# @return [String]
|
1343
|
-
# @param [Fixnum] count
|
1344
|
-
def spop(key, count = nil)
|
1345
|
-
synchronize do |client|
|
1346
|
-
if count.nil?
|
1347
|
-
client.call([:spop, key])
|
1348
|
-
else
|
1349
|
-
client.call([:spop, key, count])
|
1350
|
-
end
|
1351
|
-
end
|
1352
|
-
end
|
1353
|
-
|
1354
|
-
# Get one or more random members from a set.
|
1355
|
-
#
|
1356
|
-
# @param [String] key
|
1357
|
-
# @param [Fixnum] count
|
1358
|
-
# @return [String]
|
1359
|
-
def srandmember(key, count = nil)
|
1360
|
-
synchronize do |client|
|
1361
|
-
if count.nil?
|
1362
|
-
client.call([:srandmember, key])
|
1363
|
-
else
|
1364
|
-
client.call([:srandmember, key, count])
|
1365
|
-
end
|
1366
|
-
end
|
1367
|
-
end
|
1368
|
-
|
1369
|
-
# Move a member from one set to another.
|
1370
|
-
#
|
1371
|
-
# @param [String] source source key
|
1372
|
-
# @param [String] destination destination key
|
1373
|
-
# @param [String] member member to move from `source` to `destination`
|
1374
|
-
# @return [Boolean]
|
1375
|
-
def smove(source, destination, member)
|
1376
|
-
synchronize do |client|
|
1377
|
-
client.call([:smove, source, destination, member], &Boolify)
|
1378
|
-
end
|
1379
|
-
end
|
1380
|
-
|
1381
|
-
# Determine if a given value is a member of a set.
|
1382
|
-
#
|
1383
|
-
# @param [String] key
|
1384
|
-
# @param [String] member
|
1385
|
-
# @return [Boolean]
|
1386
|
-
def sismember(key, member)
|
1387
|
-
synchronize do |client|
|
1388
|
-
client.call([:sismember, key, member], &Boolify)
|
1389
|
-
end
|
1390
|
-
end
|
1391
|
-
|
1392
|
-
# Get all the members in a set.
|
1393
|
-
#
|
1394
|
-
# @param [String] key
|
1395
|
-
# @return [Array<String>]
|
1396
|
-
def smembers(key)
|
1397
|
-
synchronize do |client|
|
1398
|
-
client.call([:smembers, key])
|
1399
|
-
end
|
1400
|
-
end
|
1401
|
-
|
1402
|
-
# Subtract multiple sets.
|
1403
|
-
#
|
1404
|
-
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1405
|
-
# @return [Array<String>] members in the difference
|
1406
|
-
def sdiff(*keys)
|
1407
|
-
synchronize do |client|
|
1408
|
-
client.call([:sdiff] + keys)
|
1409
|
-
end
|
1410
|
-
end
|
1411
|
-
|
1412
|
-
# Subtract multiple sets and store the resulting set in a key.
|
1413
|
-
#
|
1414
|
-
# @param [String] destination destination key
|
1415
|
-
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1416
|
-
# @return [Fixnum] number of elements in the resulting set
|
1417
|
-
def sdiffstore(destination, *keys)
|
1418
|
-
synchronize do |client|
|
1419
|
-
client.call([:sdiffstore, destination] + keys)
|
1420
|
-
end
|
1421
|
-
end
|
1422
|
-
|
1423
|
-
# Intersect multiple sets.
|
1424
|
-
#
|
1425
|
-
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1426
|
-
# @return [Array<String>] members in the intersection
|
1427
|
-
def sinter(*keys)
|
1428
|
-
synchronize do |client|
|
1429
|
-
client.call([:sinter] + keys)
|
1430
|
-
end
|
1431
|
-
end
|
1432
|
-
|
1433
|
-
# Intersect multiple sets and store the resulting set in a key.
|
1434
|
-
#
|
1435
|
-
# @param [String] destination destination key
|
1436
|
-
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1437
|
-
# @return [Fixnum] number of elements in the resulting set
|
1438
|
-
def sinterstore(destination, *keys)
|
1439
|
-
synchronize do |client|
|
1440
|
-
client.call([:sinterstore, destination] + keys)
|
1441
|
-
end
|
1442
|
-
end
|
1443
|
-
|
1444
|
-
# Add multiple sets.
|
1445
|
-
#
|
1446
|
-
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1447
|
-
# @return [Array<String>] members in the union
|
1448
|
-
def sunion(*keys)
|
1449
|
-
synchronize do |client|
|
1450
|
-
client.call([:sunion] + keys)
|
1451
|
-
end
|
1452
|
-
end
|
1453
|
-
|
1454
|
-
# Add multiple sets and store the resulting set in a key.
|
1455
|
-
#
|
1456
|
-
# @param [String] destination destination key
|
1457
|
-
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1458
|
-
# @return [Fixnum] number of elements in the resulting set
|
1459
|
-
def sunionstore(destination, *keys)
|
1460
|
-
synchronize do |client|
|
1461
|
-
client.call([:sunionstore, destination] + keys)
|
1462
|
-
end
|
1463
|
-
end
|
1464
|
-
|
1465
|
-
# Get the number of members in a sorted set.
|
1466
|
-
#
|
1467
|
-
# @example
|
1468
|
-
# redis.zcard("zset")
|
1469
|
-
# # => 4
|
1470
|
-
#
|
1471
|
-
# @param [String] key
|
1472
|
-
# @return [Fixnum]
|
1473
|
-
def zcard(key)
|
1474
|
-
synchronize do |client|
|
1475
|
-
client.call([:zcard, key])
|
1476
|
-
end
|
1477
|
-
end
|
1478
|
-
|
1479
|
-
# Add one or more members to a sorted set, or update the score for members
|
1480
|
-
# that already exist.
|
1481
|
-
#
|
1482
|
-
# @example Add a single `[score, member]` pair to a sorted set
|
1483
|
-
# redis.zadd("zset", 32.0, "member")
|
1484
|
-
# @example Add an array of `[score, member]` pairs to a sorted set
|
1485
|
-
# redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
|
1486
|
-
#
|
1487
|
-
# @param [String] key
|
1488
|
-
# @param [[Float, String], Array<[Float, String]>] args
|
1489
|
-
# - a single `[score, member]` pair
|
1490
|
-
# - an array of `[score, member]` pairs
|
1491
|
-
# @param [Hash] options
|
1492
|
-
# - `:xx => true`: Only update elements that already exist (never
|
1493
|
-
# add elements)
|
1494
|
-
# - `:nx => true`: Don't update already existing elements (always
|
1495
|
-
# add new elements)
|
1496
|
-
# - `:ch => true`: Modify the return value from the number of new
|
1497
|
-
# elements added, to the total number of elements changed (CH is an
|
1498
|
-
# abbreviation of changed); changed elements are new elements added
|
1499
|
-
# and elements already existing for which the score was updated
|
1500
|
-
# - `:incr => true`: When this option is specified ZADD acts like
|
1501
|
-
# ZINCRBY; only one score-element pair can be specified in this mode
|
1502
|
-
#
|
1503
|
-
# @return [Boolean, Fixnum, Float]
|
1504
|
-
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1505
|
-
# **added** to the sorted set.
|
1506
|
-
# - `Fixnum` when an array of pairs is specified, holding the number of
|
1507
|
-
# pairs that were **added** to the sorted set.
|
1508
|
-
# - `Float` when option :incr is specified, holding the score of the member
|
1509
|
-
# after incrementing it.
|
1510
|
-
def zadd(key, *args) #, options
|
1511
|
-
zadd_options = []
|
1512
|
-
if args.last.is_a?(Hash)
|
1513
|
-
options = args.pop
|
1514
|
-
|
1515
|
-
nx = options[:nx]
|
1516
|
-
zadd_options << "NX" if nx
|
1517
|
-
|
1518
|
-
xx = options[:xx]
|
1519
|
-
zadd_options << "XX" if xx
|
1520
|
-
|
1521
|
-
ch = options[:ch]
|
1522
|
-
zadd_options << "CH" if ch
|
1523
|
-
|
1524
|
-
incr = options[:incr]
|
1525
|
-
zadd_options << "INCR" if incr
|
1526
|
-
end
|
1527
|
-
|
1528
|
-
synchronize do |client|
|
1529
|
-
if args.size == 1 && args[0].is_a?(Array)
|
1530
|
-
# Variadic: return float if INCR, integer if !INCR
|
1531
|
-
client.call([:zadd, key] + zadd_options + args[0], &(incr ? Floatify : nil))
|
1532
|
-
elsif args.size == 2
|
1533
|
-
# Single pair: return float if INCR, boolean if !INCR
|
1534
|
-
client.call([:zadd, key] + zadd_options + args, &(incr ? Floatify : Boolify))
|
1535
|
-
else
|
1536
|
-
raise ArgumentError, "wrong number of arguments"
|
1537
|
-
end
|
1538
|
-
end
|
1539
|
-
end
|
1540
|
-
|
1541
|
-
# Increment the score of a member in a sorted set.
|
1542
|
-
#
|
1543
|
-
# @example
|
1544
|
-
# redis.zincrby("zset", 32.0, "a")
|
1545
|
-
# # => 64.0
|
1546
|
-
#
|
1547
|
-
# @param [String] key
|
1548
|
-
# @param [Float] increment
|
1549
|
-
# @param [String] member
|
1550
|
-
# @return [Float] score of the member after incrementing it
|
1551
|
-
def zincrby(key, increment, member)
|
1552
|
-
synchronize do |client|
|
1553
|
-
client.call([:zincrby, key, increment, member], &Floatify)
|
1554
|
-
end
|
1555
|
-
end
|
1556
|
-
|
1557
|
-
# Remove one or more members from a sorted set.
|
1558
|
-
#
|
1559
|
-
# @example Remove a single member from a sorted set
|
1560
|
-
# redis.zrem("zset", "a")
|
1561
|
-
# @example Remove an array of members from a sorted set
|
1562
|
-
# redis.zrem("zset", ["a", "b"])
|
1563
|
-
#
|
1564
|
-
# @param [String] key
|
1565
|
-
# @param [String, Array<String>] member
|
1566
|
-
# - a single member
|
1567
|
-
# - an array of members
|
1568
|
-
#
|
1569
|
-
# @return [Boolean, Fixnum]
|
1570
|
-
# - `Boolean` when a single member is specified, holding whether or not it
|
1571
|
-
# was removed from the sorted set
|
1572
|
-
# - `Fixnum` when an array of pairs is specified, holding the number of
|
1573
|
-
# members that were removed to the sorted set
|
1574
|
-
def zrem(key, member)
|
1575
|
-
synchronize do |client|
|
1576
|
-
client.call([:zrem, key, member]) do |reply|
|
1577
|
-
if member.is_a? Array
|
1578
|
-
# Variadic: return integer
|
1579
|
-
reply
|
1580
|
-
else
|
1581
|
-
# Single argument: return boolean
|
1582
|
-
Boolify.call(reply)
|
1583
|
-
end
|
1584
|
-
end
|
1585
|
-
end
|
1586
|
-
end
|
1587
|
-
|
1588
|
-
# Get the score associated with the given member in a sorted set.
|
1589
|
-
#
|
1590
|
-
# @example Get the score for member "a"
|
1591
|
-
# redis.zscore("zset", "a")
|
1592
|
-
# # => 32.0
|
1593
|
-
#
|
1594
|
-
# @param [String] key
|
1595
|
-
# @param [String] member
|
1596
|
-
# @return [Float] score of the member
|
1597
|
-
def zscore(key, member)
|
1598
|
-
synchronize do |client|
|
1599
|
-
client.call([:zscore, key, member], &Floatify)
|
1600
|
-
end
|
1601
|
-
end
|
1602
|
-
|
1603
|
-
# Return a range of members in a sorted set, by index.
|
1604
|
-
#
|
1605
|
-
# @example Retrieve all members from a sorted set
|
1606
|
-
# redis.zrange("zset", 0, -1)
|
1607
|
-
# # => ["a", "b"]
|
1608
|
-
# @example Retrieve all members and their scores from a sorted set
|
1609
|
-
# redis.zrange("zset", 0, -1, :with_scores => true)
|
1610
|
-
# # => [["a", 32.0], ["b", 64.0]]
|
1611
|
-
#
|
1612
|
-
# @param [String] key
|
1613
|
-
# @param [Fixnum] start start index
|
1614
|
-
# @param [Fixnum] stop stop index
|
1615
|
-
# @param [Hash] options
|
1616
|
-
# - `:with_scores => true`: include scores in output
|
1617
|
-
#
|
1618
|
-
# @return [Array<String>, Array<[String, Float]>]
|
1619
|
-
# - when `:with_scores` is not specified, an array of members
|
1620
|
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1621
|
-
def zrange(key, start, stop, options = {})
|
1622
|
-
args = []
|
1623
|
-
|
1624
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1625
|
-
|
1626
|
-
if with_scores
|
1627
|
-
args << "WITHSCORES"
|
1628
|
-
block = FloatifyPairs
|
1629
|
-
end
|
1630
|
-
|
1631
|
-
synchronize do |client|
|
1632
|
-
client.call([:zrange, key, start, stop] + args, &block)
|
1633
|
-
end
|
1634
|
-
end
|
1635
|
-
|
1636
|
-
# Return a range of members in a sorted set, by index, with scores ordered
|
1637
|
-
# from high to low.
|
1638
|
-
#
|
1639
|
-
# @example Retrieve all members from a sorted set
|
1640
|
-
# redis.zrevrange("zset", 0, -1)
|
1641
|
-
# # => ["b", "a"]
|
1642
|
-
# @example Retrieve all members and their scores from a sorted set
|
1643
|
-
# redis.zrevrange("zset", 0, -1, :with_scores => true)
|
1644
|
-
# # => [["b", 64.0], ["a", 32.0]]
|
1645
|
-
#
|
1646
|
-
# @see #zrange
|
1647
|
-
def zrevrange(key, start, stop, options = {})
|
1648
|
-
args = []
|
1649
|
-
|
1650
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1651
|
-
|
1652
|
-
if with_scores
|
1653
|
-
args << "WITHSCORES"
|
1654
|
-
block = FloatifyPairs
|
1655
|
-
end
|
1656
|
-
|
1657
|
-
synchronize do |client|
|
1658
|
-
client.call([:zrevrange, key, start, stop] + args, &block)
|
1659
|
-
end
|
1660
|
-
end
|
1661
|
-
|
1662
|
-
# Determine the index of a member in a sorted set.
|
1663
|
-
#
|
1664
|
-
# @param [String] key
|
1665
|
-
# @param [String] member
|
1666
|
-
# @return [Fixnum]
|
1667
|
-
def zrank(key, member)
|
1668
|
-
synchronize do |client|
|
1669
|
-
client.call([:zrank, key, member])
|
1670
|
-
end
|
1671
|
-
end
|
1672
|
-
|
1673
|
-
# Determine the index of a member in a sorted set, with scores ordered from
|
1674
|
-
# high to low.
|
1675
|
-
#
|
1676
|
-
# @param [String] key
|
1677
|
-
# @param [String] member
|
1678
|
-
# @return [Fixnum]
|
1679
|
-
def zrevrank(key, member)
|
1680
|
-
synchronize do |client|
|
1681
|
-
client.call([:zrevrank, key, member])
|
1682
|
-
end
|
1683
|
-
end
|
1684
|
-
|
1685
|
-
# Remove all members in a sorted set within the given indexes.
|
1686
|
-
#
|
1687
|
-
# @example Remove first 5 members
|
1688
|
-
# redis.zremrangebyrank("zset", 0, 4)
|
1689
|
-
# # => 5
|
1690
|
-
# @example Remove last 5 members
|
1691
|
-
# redis.zremrangebyrank("zset", -5, -1)
|
1692
|
-
# # => 5
|
1693
|
-
#
|
1694
|
-
# @param [String] key
|
1695
|
-
# @param [Fixnum] start start index
|
1696
|
-
# @param [Fixnum] stop stop index
|
1697
|
-
# @return [Fixnum] number of members that were removed
|
1698
|
-
def zremrangebyrank(key, start, stop)
|
1699
|
-
synchronize do |client|
|
1700
|
-
client.call([:zremrangebyrank, key, start, stop])
|
1701
|
-
end
|
1702
|
-
end
|
1703
|
-
|
1704
|
-
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1705
|
-
#
|
1706
|
-
# @example Retrieve members matching a
|
1707
|
-
# redis.zrangebylex("zset", "[a", "[a\xff")
|
1708
|
-
# # => ["aaren", "aarika", "abagael", "abby"]
|
1709
|
-
# @example Retrieve the first 2 members matching a
|
1710
|
-
# redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
|
1711
|
-
# # => ["aaren", "aarika"]
|
1712
|
-
#
|
1713
|
-
# @param [String] key
|
1714
|
-
# @param [String] min
|
1715
|
-
# - inclusive minimum is specified by prefixing `(`
|
1716
|
-
# - exclusive minimum is specified by prefixing `[`
|
1717
|
-
# @param [String] max
|
1718
|
-
# - inclusive maximum is specified by prefixing `(`
|
1719
|
-
# - exclusive maximum is specified by prefixing `[`
|
1720
|
-
# @param [Hash] options
|
1721
|
-
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
1722
|
-
# `count` members
|
1723
|
-
#
|
1724
|
-
# @return [Array<String>, Array<[String, Float]>]
|
1725
|
-
def zrangebylex(key, min, max, options = {})
|
1726
|
-
args = []
|
1727
|
-
|
1728
|
-
limit = options[:limit]
|
1729
|
-
args.concat(["LIMIT"] + limit) if limit
|
1730
|
-
|
1731
|
-
synchronize do |client|
|
1732
|
-
client.call([:zrangebylex, key, min, max] + args)
|
1733
|
-
end
|
1734
|
-
end
|
1735
|
-
|
1736
|
-
# Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
|
1737
|
-
# Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
|
1738
|
-
#
|
1739
|
-
# @example Retrieve members matching a
|
1740
|
-
# redis.zrevrangebylex("zset", "[a", "[a\xff")
|
1741
|
-
# # => ["abbygail", "abby", "abagael", "aaren"]
|
1742
|
-
# @example Retrieve the last 2 members matching a
|
1743
|
-
# redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
|
1744
|
-
# # => ["abbygail", "abby"]
|
1745
|
-
#
|
1746
|
-
# @see #zrangebylex
|
1747
|
-
def zrevrangebylex(key, max, min, options = {})
|
1748
|
-
args = []
|
1749
|
-
|
1750
|
-
limit = options[:limit]
|
1751
|
-
args.concat(["LIMIT"] + limit) if limit
|
1752
|
-
|
1753
|
-
synchronize do |client|
|
1754
|
-
client.call([:zrevrangebylex, key, max, min] + args)
|
1755
|
-
end
|
1756
|
-
end
|
1757
|
-
|
1758
|
-
# Return a range of members in a sorted set, by score.
|
1759
|
-
#
|
1760
|
-
# @example Retrieve members with score `>= 5` and `< 100`
|
1761
|
-
# redis.zrangebyscore("zset", "5", "(100")
|
1762
|
-
# # => ["a", "b"]
|
1763
|
-
# @example Retrieve the first 2 members with score `>= 0`
|
1764
|
-
# redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
|
1765
|
-
# # => ["a", "b"]
|
1766
|
-
# @example Retrieve members and their scores with scores `> 5`
|
1767
|
-
# redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
|
1768
|
-
# # => [["a", 32.0], ["b", 64.0]]
|
1769
|
-
#
|
1770
|
-
# @param [String] key
|
1771
|
-
# @param [String] min
|
1772
|
-
# - inclusive minimum score is specified verbatim
|
1773
|
-
# - exclusive minimum score is specified by prefixing `(`
|
1774
|
-
# @param [String] max
|
1775
|
-
# - inclusive maximum score is specified verbatim
|
1776
|
-
# - exclusive maximum score is specified by prefixing `(`
|
1777
|
-
# @param [Hash] options
|
1778
|
-
# - `:with_scores => true`: include scores in output
|
1779
|
-
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
|
1780
|
-
# `count` members
|
1781
|
-
#
|
1782
|
-
# @return [Array<String>, Array<[String, Float]>]
|
1783
|
-
# - when `:with_scores` is not specified, an array of members
|
1784
|
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1785
|
-
def zrangebyscore(key, min, max, options = {})
|
1786
|
-
args = []
|
1787
|
-
|
1788
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1789
|
-
|
1790
|
-
if with_scores
|
1791
|
-
args << "WITHSCORES"
|
1792
|
-
block = FloatifyPairs
|
1793
|
-
end
|
1794
|
-
|
1795
|
-
limit = options[:limit]
|
1796
|
-
args.concat(["LIMIT"] + limit) if limit
|
1797
|
-
|
1798
|
-
synchronize do |client|
|
1799
|
-
client.call([:zrangebyscore, key, min, max] + args, &block)
|
1800
|
-
end
|
1801
|
-
end
|
1802
|
-
|
1803
|
-
# Return a range of members in a sorted set, by score, with scores ordered
|
1804
|
-
# from high to low.
|
1805
|
-
#
|
1806
|
-
# @example Retrieve members with score `< 100` and `>= 5`
|
1807
|
-
# redis.zrevrangebyscore("zset", "(100", "5")
|
1808
|
-
# # => ["b", "a"]
|
1809
|
-
# @example Retrieve the first 2 members with score `<= 0`
|
1810
|
-
# redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
|
1811
|
-
# # => ["b", "a"]
|
1812
|
-
# @example Retrieve members and their scores with scores `> 5`
|
1813
|
-
# redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
|
1814
|
-
# # => [["b", 64.0], ["a", 32.0]]
|
1815
|
-
#
|
1816
|
-
# @see #zrangebyscore
|
1817
|
-
def zrevrangebyscore(key, max, min, options = {})
|
1818
|
-
args = []
|
1819
|
-
|
1820
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1821
|
-
|
1822
|
-
if with_scores
|
1823
|
-
args << ["WITHSCORES"]
|
1824
|
-
block = FloatifyPairs
|
1825
|
-
end
|
1826
|
-
|
1827
|
-
limit = options[:limit]
|
1828
|
-
args.concat(["LIMIT"] + limit) if limit
|
1829
|
-
|
1830
|
-
synchronize do |client|
|
1831
|
-
client.call([:zrevrangebyscore, key, max, min] + args, &block)
|
1832
|
-
end
|
1833
|
-
end
|
1834
|
-
|
1835
|
-
# Remove all members in a sorted set within the given scores.
|
1836
|
-
#
|
1837
|
-
# @example Remove members with score `>= 5` and `< 100`
|
1838
|
-
# redis.zremrangebyscore("zset", "5", "(100")
|
1839
|
-
# # => 2
|
1840
|
-
# @example Remove members with scores `> 5`
|
1841
|
-
# redis.zremrangebyscore("zset", "(5", "+inf")
|
1842
|
-
# # => 2
|
1843
|
-
#
|
1844
|
-
# @param [String] key
|
1845
|
-
# @param [String] min
|
1846
|
-
# - inclusive minimum score is specified verbatim
|
1847
|
-
# - exclusive minimum score is specified by prefixing `(`
|
1848
|
-
# @param [String] max
|
1849
|
-
# - inclusive maximum score is specified verbatim
|
1850
|
-
# - exclusive maximum score is specified by prefixing `(`
|
1851
|
-
# @return [Fixnum] number of members that were removed
|
1852
|
-
def zremrangebyscore(key, min, max)
|
1853
|
-
synchronize do |client|
|
1854
|
-
client.call([:zremrangebyscore, key, min, max])
|
1855
|
-
end
|
1856
|
-
end
|
1857
|
-
|
1858
|
-
# Count the members in a sorted set with scores within the given values.
|
1859
|
-
#
|
1860
|
-
# @example Count members with score `>= 5` and `< 100`
|
1861
|
-
# redis.zcount("zset", "5", "(100")
|
1862
|
-
# # => 2
|
1863
|
-
# @example Count members with scores `> 5`
|
1864
|
-
# redis.zcount("zset", "(5", "+inf")
|
1865
|
-
# # => 2
|
1866
|
-
#
|
1867
|
-
# @param [String] key
|
1868
|
-
# @param [String] min
|
1869
|
-
# - inclusive minimum score is specified verbatim
|
1870
|
-
# - exclusive minimum score is specified by prefixing `(`
|
1871
|
-
# @param [String] max
|
1872
|
-
# - inclusive maximum score is specified verbatim
|
1873
|
-
# - exclusive maximum score is specified by prefixing `(`
|
1874
|
-
# @return [Fixnum] number of members in within the specified range
|
1875
|
-
def zcount(key, min, max)
|
1876
|
-
synchronize do |client|
|
1877
|
-
client.call([:zcount, key, min, max])
|
1878
|
-
end
|
1879
|
-
end
|
1880
|
-
|
1881
|
-
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
1882
|
-
# key.
|
1883
|
-
#
|
1884
|
-
# @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
|
1885
|
-
# redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
|
1886
|
-
# # => 4
|
1887
|
-
#
|
1888
|
-
# @param [String] destination destination key
|
1889
|
-
# @param [Array<String>] keys source keys
|
1890
|
-
# @param [Hash] options
|
1891
|
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1892
|
-
# sorted sets
|
1893
|
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1894
|
-
# @return [Fixnum] number of elements in the resulting sorted set
|
1895
|
-
def zinterstore(destination, keys, options = {})
|
1896
|
-
args = []
|
1897
|
-
|
1898
|
-
weights = options[:weights]
|
1899
|
-
args.concat(["WEIGHTS"] + weights) if weights
|
1900
|
-
|
1901
|
-
aggregate = options[:aggregate]
|
1902
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
1903
|
-
|
1904
|
-
synchronize do |client|
|
1905
|
-
client.call([:zinterstore, destination, keys.size] + keys + args)
|
1906
|
-
end
|
1907
|
-
end
|
1908
|
-
|
1909
|
-
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
1910
|
-
#
|
1911
|
-
# @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
|
1912
|
-
# redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
|
1913
|
-
# # => 8
|
1914
|
-
#
|
1915
|
-
# @param [String] destination destination key
|
1916
|
-
# @param [Array<String>] keys source keys
|
1917
|
-
# @param [Hash] options
|
1918
|
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1919
|
-
# sorted sets
|
1920
|
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1921
|
-
# @return [Fixnum] number of elements in the resulting sorted set
|
1922
|
-
def zunionstore(destination, keys, options = {})
|
1923
|
-
args = []
|
1924
|
-
|
1925
|
-
weights = options[:weights]
|
1926
|
-
args.concat(["WEIGHTS"] + weights) if weights
|
1927
|
-
|
1928
|
-
aggregate = options[:aggregate]
|
1929
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
1930
|
-
|
1931
|
-
synchronize do |client|
|
1932
|
-
client.call([:zunionstore, destination, keys.size] + keys + args)
|
1933
|
-
end
|
1934
|
-
end
|
1935
|
-
|
1936
|
-
# Get the number of fields in a hash.
|
1937
|
-
#
|
1938
|
-
# @param [String] key
|
1939
|
-
# @return [Fixnum] number of fields in the hash
|
1940
|
-
def hlen(key)
|
1941
|
-
synchronize do |client|
|
1942
|
-
client.call([:hlen, key])
|
1943
|
-
end
|
1944
|
-
end
|
1945
|
-
|
1946
|
-
# Set the string value of a hash field.
|
1947
|
-
#
|
1948
|
-
# @param [String] key
|
1949
|
-
# @param [String] field
|
1950
|
-
# @param [String] value
|
1951
|
-
# @return [Boolean] whether or not the field was **added** to the hash
|
1952
|
-
def hset(key, field, value)
|
1953
|
-
synchronize do |client|
|
1954
|
-
client.call([:hset, key, field, value], &Boolify)
|
1955
|
-
end
|
1956
|
-
end
|
1957
|
-
|
1958
|
-
# Set the value of a hash field, only if the field does not exist.
|
1959
|
-
#
|
1960
|
-
# @param [String] key
|
1961
|
-
# @param [String] field
|
1962
|
-
# @param [String] value
|
1963
|
-
# @return [Boolean] whether or not the field was **added** to the hash
|
1964
|
-
def hsetnx(key, field, value)
|
1965
|
-
synchronize do |client|
|
1966
|
-
client.call([:hsetnx, key, field, value], &Boolify)
|
1967
|
-
end
|
1968
|
-
end
|
1969
|
-
|
1970
|
-
# Set one or more hash values.
|
1971
|
-
#
|
1972
|
-
# @example
|
1973
|
-
# redis.hmset("hash", "f1", "v1", "f2", "v2")
|
1974
|
-
# # => "OK"
|
1975
|
-
#
|
1976
|
-
# @param [String] key
|
1977
|
-
# @param [Array<String>] attrs array of fields and values
|
1978
|
-
# @return [String] `"OK"`
|
1979
|
-
#
|
1980
|
-
# @see #mapped_hmset
|
1981
|
-
def hmset(key, *attrs)
|
1982
|
-
synchronize do |client|
|
1983
|
-
client.call([:hmset, key] + attrs)
|
1984
|
-
end
|
1985
|
-
end
|
1986
|
-
|
1987
|
-
# Set one or more hash values.
|
1988
|
-
#
|
1989
|
-
# @example
|
1990
|
-
# redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
|
1991
|
-
# # => "OK"
|
1992
|
-
#
|
1993
|
-
# @param [String] key
|
1994
|
-
# @param [Hash] hash a non-empty hash with fields mapping to values
|
1995
|
-
# @return [String] `"OK"`
|
1996
|
-
#
|
1997
|
-
# @see #hmset
|
1998
|
-
def mapped_hmset(key, hash)
|
1999
|
-
hmset(key, hash.to_a.flatten)
|
2000
|
-
end
|
2001
|
-
|
2002
|
-
# Get the value of a hash field.
|
2003
|
-
#
|
2004
|
-
# @param [String] key
|
2005
|
-
# @param [String] field
|
2006
|
-
# @return [String]
|
2007
|
-
def hget(key, field)
|
2008
|
-
synchronize do |client|
|
2009
|
-
client.call([:hget, key, field])
|
2010
|
-
end
|
2011
|
-
end
|
2012
|
-
|
2013
|
-
# Get the values of all the given hash fields.
|
2014
|
-
#
|
2015
|
-
# @example
|
2016
|
-
# redis.hmget("hash", "f1", "f2")
|
2017
|
-
# # => ["v1", "v2"]
|
2018
|
-
#
|
2019
|
-
# @param [String] key
|
2020
|
-
# @param [Array<String>] fields array of fields
|
2021
|
-
# @return [Array<String>] an array of values for the specified fields
|
2022
|
-
#
|
2023
|
-
# @see #mapped_hmget
|
2024
|
-
def hmget(key, *fields, &blk)
|
2025
|
-
synchronize do |client|
|
2026
|
-
client.call([:hmget, key] + fields, &blk)
|
2027
|
-
end
|
2028
|
-
end
|
2029
|
-
|
2030
|
-
# Get the values of all the given hash fields.
|
2031
|
-
#
|
2032
|
-
# @example
|
2033
|
-
# redis.mapped_hmget("hash", "f1", "f2")
|
2034
|
-
# # => { "f1" => "v1", "f2" => "v2" }
|
2035
|
-
#
|
2036
|
-
# @param [String] key
|
2037
|
-
# @param [Array<String>] fields array of fields
|
2038
|
-
# @return [Hash] a hash mapping the specified fields to their values
|
2039
|
-
#
|
2040
|
-
# @see #hmget
|
2041
|
-
def mapped_hmget(key, *fields)
|
2042
|
-
hmget(key, *fields) do |reply|
|
2043
|
-
if reply.kind_of?(Array)
|
2044
|
-
Hash[fields.zip(reply)]
|
2045
|
-
else
|
2046
|
-
reply
|
2047
|
-
end
|
2048
|
-
end
|
2049
|
-
end
|
2050
|
-
|
2051
|
-
# Delete one or more hash fields.
|
2052
|
-
#
|
2053
|
-
# @param [String] key
|
2054
|
-
# @param [String, Array<String>] field
|
2055
|
-
# @return [Fixnum] the number of fields that were removed from the hash
|
2056
|
-
def hdel(key, field)
|
2057
|
-
synchronize do |client|
|
2058
|
-
client.call([:hdel, key, field])
|
2059
|
-
end
|
2060
|
-
end
|
2061
|
-
|
2062
|
-
# Determine if a hash field exists.
|
2063
|
-
#
|
2064
|
-
# @param [String] key
|
2065
|
-
# @param [String] field
|
2066
|
-
# @return [Boolean] whether or not the field exists in the hash
|
2067
|
-
def hexists(key, field)
|
2068
|
-
synchronize do |client|
|
2069
|
-
client.call([:hexists, key, field], &Boolify)
|
2070
|
-
end
|
2071
|
-
end
|
2072
|
-
|
2073
|
-
# Increment the integer value of a hash field by the given integer number.
|
2074
|
-
#
|
2075
|
-
# @param [String] key
|
2076
|
-
# @param [String] field
|
2077
|
-
# @param [Fixnum] increment
|
2078
|
-
# @return [Fixnum] value of the field after incrementing it
|
2079
|
-
def hincrby(key, field, increment)
|
2080
|
-
synchronize do |client|
|
2081
|
-
client.call([:hincrby, key, field, increment])
|
2082
|
-
end
|
2083
|
-
end
|
2084
|
-
|
2085
|
-
# Increment the numeric value of a hash field by the given float number.
|
2086
|
-
#
|
2087
|
-
# @param [String] key
|
2088
|
-
# @param [String] field
|
2089
|
-
# @param [Float] increment
|
2090
|
-
# @return [Float] value of the field after incrementing it
|
2091
|
-
def hincrbyfloat(key, field, increment)
|
2092
|
-
synchronize do |client|
|
2093
|
-
client.call([:hincrbyfloat, key, field, increment], &Floatify)
|
2094
|
-
end
|
2095
|
-
end
|
2096
|
-
|
2097
|
-
# Get all the fields in a hash.
|
2098
|
-
#
|
2099
|
-
# @param [String] key
|
2100
|
-
# @return [Array<String>]
|
2101
|
-
def hkeys(key)
|
2102
|
-
synchronize do |client|
|
2103
|
-
client.call([:hkeys, key])
|
2104
|
-
end
|
2105
|
-
end
|
2106
|
-
|
2107
|
-
# Get all the values in a hash.
|
2108
|
-
#
|
2109
|
-
# @param [String] key
|
2110
|
-
# @return [Array<String>]
|
2111
|
-
def hvals(key)
|
2112
|
-
synchronize do |client|
|
2113
|
-
client.call([:hvals, key])
|
2114
|
-
end
|
2115
|
-
end
|
2116
|
-
|
2117
|
-
# Get all the fields and values in a hash.
|
2118
|
-
#
|
2119
|
-
# @param [String] key
|
2120
|
-
# @return [Hash<String, String>]
|
2121
|
-
def hgetall(key)
|
2122
|
-
synchronize do |client|
|
2123
|
-
client.call([:hgetall, key], &Hashify)
|
2124
|
-
end
|
2125
|
-
end
|
2126
|
-
|
2127
|
-
# Post a message to a channel.
|
2128
|
-
def publish(channel, message)
|
2129
|
-
synchronize do |client|
|
2130
|
-
client.call([:publish, channel, message])
|
2131
|
-
end
|
2132
|
-
end
|
2133
|
-
|
2134
|
-
def subscribed?
|
2135
|
-
synchronize do |client|
|
2136
|
-
client.kind_of? SubscribedClient
|
2137
|
-
end
|
2138
|
-
end
|
2139
|
-
|
2140
|
-
# Listen for messages published to the given channels.
|
2141
|
-
def subscribe(*channels, &block)
|
2142
|
-
synchronize do |client|
|
2143
|
-
_subscription(:subscribe, 0, channels, block)
|
2144
|
-
end
|
2145
|
-
end
|
2146
|
-
|
2147
|
-
# Listen for messages published to the given channels. Throw a timeout error if there is no messages for a timeout period.
|
2148
|
-
def subscribe_with_timeout(timeout, *channels, &block)
|
2149
|
-
synchronize do |client|
|
2150
|
-
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2151
|
-
end
|
2152
|
-
end
|
2153
|
-
|
2154
|
-
# Stop listening for messages posted to the given channels.
|
2155
|
-
def unsubscribe(*channels)
|
2156
|
-
synchronize do |client|
|
2157
|
-
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
2158
|
-
client.unsubscribe(*channels)
|
2159
|
-
end
|
2160
|
-
end
|
2161
|
-
|
2162
|
-
# Listen for messages published to channels matching the given patterns.
|
2163
|
-
def psubscribe(*channels, &block)
|
2164
|
-
synchronize do |client|
|
2165
|
-
_subscription(:psubscribe, 0, channels, block)
|
2166
|
-
end
|
2167
|
-
end
|
2168
|
-
|
2169
|
-
# Listen for messages published to channels matching the given patterns. Throw a timeout error if there is no messages for a timeout period.
|
2170
|
-
def psubscribe_with_timeout(timeout, *channels, &block)
|
2171
|
-
synchronize do |client|
|
2172
|
-
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2173
|
-
end
|
2174
|
-
end
|
2175
|
-
|
2176
|
-
# Stop listening for messages posted to channels matching the given patterns.
|
2177
|
-
def punsubscribe(*channels)
|
2178
|
-
synchronize do |client|
|
2179
|
-
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
2180
|
-
client.punsubscribe(*channels)
|
2181
|
-
end
|
2182
|
-
end
|
2183
|
-
|
2184
|
-
# Inspect the state of the Pub/Sub subsystem.
|
2185
|
-
# Possible subcommands: channels, numsub, numpat.
|
2186
|
-
def pubsub(subcommand, *args)
|
2187
|
-
synchronize do |client|
|
2188
|
-
client.call([:pubsub, subcommand] + args)
|
2189
|
-
end
|
2190
|
-
end
|
2191
|
-
|
2192
|
-
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
2193
|
-
#
|
2194
|
-
# Using a block is optional, but is necessary for thread-safety.
|
2195
|
-
#
|
2196
|
-
# An `#unwatch` is automatically issued if an exception is raised within the
|
2197
|
-
# block that is a subclass of StandardError and is not a ConnectionError.
|
2198
|
-
#
|
2199
|
-
# @example With a block
|
2200
|
-
# redis.watch("key") do
|
2201
|
-
# if redis.get("key") == "some value"
|
2202
|
-
# redis.multi do |multi|
|
2203
|
-
# multi.set("key", "other value")
|
2204
|
-
# multi.incr("counter")
|
2205
|
-
# end
|
2206
|
-
# else
|
2207
|
-
# redis.unwatch
|
2208
|
-
# end
|
2209
|
-
# end
|
2210
|
-
# # => ["OK", 6]
|
2211
|
-
#
|
2212
|
-
# @example Without a block
|
2213
|
-
# redis.watch("key")
|
2214
|
-
# # => "OK"
|
2215
|
-
#
|
2216
|
-
# @param [String, Array<String>] keys one or more keys to watch
|
2217
|
-
# @return [Object] if using a block, returns the return value of the block
|
2218
|
-
# @return [String] if not using a block, returns `OK`
|
2219
|
-
#
|
2220
|
-
# @see #unwatch
|
2221
|
-
# @see #multi
|
2222
|
-
def watch(*keys)
|
2223
|
-
synchronize do |client|
|
2224
|
-
res = client.call([:watch] + keys)
|
2225
|
-
|
2226
|
-
if block_given?
|
2227
|
-
begin
|
2228
|
-
yield(self)
|
2229
|
-
rescue ConnectionError
|
2230
|
-
raise
|
2231
|
-
rescue StandardError
|
2232
|
-
unwatch
|
2233
|
-
raise
|
2234
|
-
end
|
2235
|
-
else
|
2236
|
-
res
|
2237
|
-
end
|
2238
|
-
end
|
2239
|
-
end
|
2240
|
-
|
2241
|
-
# Forget about all watched keys.
|
2242
|
-
#
|
2243
|
-
# @return [String] `OK`
|
2244
|
-
#
|
2245
|
-
# @see #watch
|
2246
|
-
# @see #multi
|
2247
|
-
def unwatch
|
2248
|
-
synchronize do |client|
|
2249
|
-
client.call([:unwatch])
|
2250
|
-
end
|
2251
|
-
end
|
2252
|
-
|
2253
|
-
def pipelined
|
2254
|
-
synchronize do |client|
|
2255
|
-
begin
|
2256
|
-
original, @client = @client, Pipeline.new
|
2257
|
-
yield(self)
|
2258
|
-
original.call_pipeline(@client)
|
2259
|
-
ensure
|
2260
|
-
@client = original
|
2261
|
-
end
|
2262
|
-
end
|
2263
|
-
end
|
2264
|
-
|
2265
|
-
# Mark the start of a transaction block.
|
2266
|
-
#
|
2267
|
-
# Passing a block is optional.
|
2268
|
-
#
|
2269
|
-
# @example With a block
|
2270
|
-
# redis.multi do |multi|
|
2271
|
-
# multi.set("key", "value")
|
2272
|
-
# multi.incr("counter")
|
2273
|
-
# end # => ["OK", 6]
|
2274
|
-
#
|
2275
|
-
# @example Without a block
|
2276
|
-
# redis.multi
|
2277
|
-
# # => "OK"
|
2278
|
-
# redis.set("key", "value")
|
2279
|
-
# # => "QUEUED"
|
2280
|
-
# redis.incr("counter")
|
2281
|
-
# # => "QUEUED"
|
2282
|
-
# redis.exec
|
2283
|
-
# # => ["OK", 6]
|
2284
|
-
#
|
2285
|
-
# @yield [multi] the commands that are called inside this block are cached
|
2286
|
-
# and written to the server upon returning from it
|
2287
|
-
# @yieldparam [Redis] multi `self`
|
2288
|
-
#
|
2289
|
-
# @return [String, Array<...>]
|
2290
|
-
# - when a block is not given, `OK`
|
2291
|
-
# - when a block is given, an array with replies
|
2292
|
-
#
|
2293
|
-
# @see #watch
|
2294
|
-
# @see #unwatch
|
2295
|
-
def multi
|
2296
|
-
synchronize do |client|
|
2297
|
-
if !block_given?
|
2298
|
-
client.call([:multi])
|
2299
|
-
else
|
2300
|
-
begin
|
2301
|
-
pipeline = Pipeline::Multi.new
|
2302
|
-
original, @client = @client, pipeline
|
2303
|
-
yield(self)
|
2304
|
-
original.call_pipeline(pipeline)
|
2305
|
-
ensure
|
2306
|
-
@client = original
|
2307
|
-
end
|
2308
|
-
end
|
2309
|
-
end
|
2310
|
-
end
|
2311
|
-
|
2312
|
-
# Execute all commands issued after MULTI.
|
2313
|
-
#
|
2314
|
-
# Only call this method when `#multi` was called **without** a block.
|
2315
|
-
#
|
2316
|
-
# @return [nil, Array<...>]
|
2317
|
-
# - when commands were not executed, `nil`
|
2318
|
-
# - when commands were executed, an array with their replies
|
2319
|
-
#
|
2320
|
-
# @see #multi
|
2321
|
-
# @see #discard
|
2322
|
-
def exec
|
2323
|
-
synchronize do |client|
|
2324
|
-
client.call([:exec])
|
2325
|
-
end
|
2326
|
-
end
|
2327
|
-
|
2328
|
-
# Discard all commands issued after MULTI.
|
2329
|
-
#
|
2330
|
-
# Only call this method when `#multi` was called **without** a block.
|
2331
|
-
#
|
2332
|
-
# @return [String] `"OK"`
|
2333
|
-
#
|
2334
|
-
# @see #multi
|
2335
|
-
# @see #exec
|
2336
|
-
def discard
|
2337
|
-
synchronize do |client|
|
2338
|
-
client.call([:discard])
|
2339
|
-
end
|
2340
|
-
end
|
2341
|
-
|
2342
|
-
# Control remote script registry.
|
2343
|
-
#
|
2344
|
-
# @example Load a script
|
2345
|
-
# sha = redis.script(:load, "return 1")
|
2346
|
-
# # => <sha of this script>
|
2347
|
-
# @example Check if a script exists
|
2348
|
-
# redis.script(:exists, sha)
|
2349
|
-
# # => true
|
2350
|
-
# @example Check if multiple scripts exist
|
2351
|
-
# redis.script(:exists, [sha, other_sha])
|
2352
|
-
# # => [true, false]
|
2353
|
-
# @example Flush the script registry
|
2354
|
-
# redis.script(:flush)
|
2355
|
-
# # => "OK"
|
2356
|
-
# @example Kill a running script
|
2357
|
-
# redis.script(:kill)
|
2358
|
-
# # => "OK"
|
2359
|
-
#
|
2360
|
-
# @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
|
2361
|
-
# @param [Array<String>] args depends on subcommand
|
2362
|
-
# @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
|
2363
|
-
#
|
2364
|
-
# @see #eval
|
2365
|
-
# @see #evalsha
|
2366
|
-
def script(subcommand, *args)
|
2367
|
-
subcommand = subcommand.to_s.downcase
|
2368
|
-
|
2369
|
-
if subcommand == "exists"
|
2370
|
-
synchronize do |client|
|
2371
|
-
arg = args.first
|
2372
|
-
|
2373
|
-
client.call([:script, :exists, arg]) do |reply|
|
2374
|
-
reply = reply.map { |r| Boolify.call(r) }
|
2375
|
-
|
2376
|
-
if arg.is_a?(Array)
|
2377
|
-
reply
|
2378
|
-
else
|
2379
|
-
reply.first
|
2380
|
-
end
|
2381
|
-
end
|
2382
|
-
end
|
2383
|
-
else
|
2384
|
-
synchronize do |client|
|
2385
|
-
client.call([:script, subcommand] + args)
|
2386
|
-
end
|
2387
|
-
end
|
2388
|
-
end
|
2389
|
-
|
2390
|
-
def _eval(cmd, args)
|
2391
|
-
script = args.shift
|
2392
|
-
options = args.pop if args.last.is_a?(Hash)
|
2393
|
-
options ||= {}
|
2394
|
-
|
2395
|
-
keys = args.shift || options[:keys] || []
|
2396
|
-
argv = args.shift || options[:argv] || []
|
2397
|
-
|
2398
|
-
synchronize do |client|
|
2399
|
-
client.call([cmd, script, keys.length] + keys + argv)
|
2400
|
-
end
|
2401
|
-
end
|
2402
|
-
|
2403
|
-
# Evaluate Lua script.
|
2404
|
-
#
|
2405
|
-
# @example EVAL without KEYS nor ARGV
|
2406
|
-
# redis.eval("return 1")
|
2407
|
-
# # => 1
|
2408
|
-
# @example EVAL with KEYS and ARGV as array arguments
|
2409
|
-
# redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
|
2410
|
-
# # => [["k1", "k2"], ["a1", "a2"]]
|
2411
|
-
# @example EVAL with KEYS and ARGV in a hash argument
|
2412
|
-
# redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
|
2413
|
-
# # => [["k1", "k2"], ["a1", "a2"]]
|
2414
|
-
#
|
2415
|
-
# @param [Array<String>] keys optional array with keys to pass to the script
|
2416
|
-
# @param [Array<String>] argv optional array with arguments to pass to the script
|
2417
|
-
# @param [Hash] options
|
2418
|
-
# - `:keys => Array<String>`: optional array with keys to pass to the script
|
2419
|
-
# - `:argv => Array<String>`: optional array with arguments to pass to the script
|
2420
|
-
# @return depends on the script
|
2421
|
-
#
|
2422
|
-
# @see #script
|
2423
|
-
# @see #evalsha
|
2424
|
-
def eval(*args)
|
2425
|
-
_eval(:eval, args)
|
2426
|
-
end
|
2427
|
-
|
2428
|
-
# Evaluate Lua script by its SHA.
|
2429
|
-
#
|
2430
|
-
# @example EVALSHA without KEYS nor ARGV
|
2431
|
-
# redis.evalsha(sha)
|
2432
|
-
# # => <depends on script>
|
2433
|
-
# @example EVALSHA with KEYS and ARGV as array arguments
|
2434
|
-
# redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
|
2435
|
-
# # => <depends on script>
|
2436
|
-
# @example EVALSHA with KEYS and ARGV in a hash argument
|
2437
|
-
# redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
|
2438
|
-
# # => <depends on script>
|
2439
|
-
#
|
2440
|
-
# @param [Array<String>] keys optional array with keys to pass to the script
|
2441
|
-
# @param [Array<String>] argv optional array with arguments to pass to the script
|
2442
|
-
# @param [Hash] options
|
2443
|
-
# - `:keys => Array<String>`: optional array with keys to pass to the script
|
2444
|
-
# - `:argv => Array<String>`: optional array with arguments to pass to the script
|
2445
|
-
# @return depends on the script
|
2446
|
-
#
|
2447
|
-
# @see #script
|
2448
|
-
# @see #eval
|
2449
|
-
def evalsha(*args)
|
2450
|
-
_eval(:evalsha, args)
|
2451
|
-
end
|
2452
|
-
|
2453
|
-
def _scan(command, cursor, args, options = {}, &block)
|
2454
|
-
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2455
|
-
|
2456
|
-
args << cursor
|
2457
|
-
|
2458
|
-
if match = options[:match]
|
2459
|
-
args.concat(["MATCH", match])
|
2460
|
-
end
|
2461
|
-
|
2462
|
-
if count = options[:count]
|
2463
|
-
args.concat(["COUNT", count])
|
2464
|
-
end
|
2465
|
-
|
2466
|
-
synchronize do |client|
|
2467
|
-
client.call([command] + args, &block)
|
2468
|
-
end
|
2469
|
-
end
|
2470
|
-
|
2471
|
-
# Scan the keyspace
|
2472
|
-
#
|
2473
|
-
# @example Retrieve the first batch of keys
|
2474
|
-
# redis.scan(0)
|
2475
|
-
# # => ["4", ["key:21", "key:47", "key:42"]]
|
2476
|
-
# @example Retrieve a batch of keys matching a pattern
|
2477
|
-
# redis.scan(4, :match => "key:1?")
|
2478
|
-
# # => ["92", ["key:13", "key:18"]]
|
2479
|
-
#
|
2480
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2481
|
-
# @param [Hash] options
|
2482
|
-
# - `:match => String`: only return keys matching the pattern
|
2483
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2484
|
-
#
|
2485
|
-
# @return [String, Array<String>] the next cursor and all found keys
|
2486
|
-
def scan(cursor, options={})
|
2487
|
-
_scan(:scan, cursor, [], options)
|
2488
|
-
end
|
2489
|
-
|
2490
|
-
# Scan the keyspace
|
2491
|
-
#
|
2492
|
-
# @example Retrieve all of the keys (with possible duplicates)
|
2493
|
-
# redis.scan_each.to_a
|
2494
|
-
# # => ["key:21", "key:47", "key:42"]
|
2495
|
-
# @example Execute block for each key matching a pattern
|
2496
|
-
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2497
|
-
# # => key:13
|
2498
|
-
# # => key:18
|
2499
|
-
#
|
2500
|
-
# @param [Hash] options
|
2501
|
-
# - `:match => String`: only return keys matching the pattern
|
2502
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2503
|
-
#
|
2504
|
-
# @return [Enumerator] an enumerator for all found keys
|
2505
|
-
def scan_each(options={}, &block)
|
2506
|
-
return to_enum(:scan_each, options) unless block_given?
|
2507
|
-
cursor = 0
|
2508
|
-
loop do
|
2509
|
-
cursor, keys = scan(cursor, options)
|
2510
|
-
keys.each(&block)
|
2511
|
-
break if cursor == "0"
|
2512
|
-
end
|
2513
|
-
end
|
2514
|
-
|
2515
|
-
# Scan a hash
|
2516
|
-
#
|
2517
|
-
# @example Retrieve the first batch of key/value pairs in a hash
|
2518
|
-
# redis.hscan("hash", 0)
|
2519
|
-
#
|
2520
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2521
|
-
# @param [Hash] options
|
2522
|
-
# - `:match => String`: only return keys matching the pattern
|
2523
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2524
|
-
#
|
2525
|
-
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2526
|
-
def hscan(key, cursor, options={})
|
2527
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2528
|
-
[reply[0], reply[1].each_slice(2).to_a]
|
2529
|
-
end
|
2530
|
-
end
|
2531
|
-
|
2532
|
-
# Scan a hash
|
2533
|
-
#
|
2534
|
-
# @example Retrieve all of the key/value pairs in a hash
|
2535
|
-
# redis.hscan_each("hash").to_a
|
2536
|
-
# # => [["key70", "70"], ["key80", "80"]]
|
2537
|
-
#
|
2538
|
-
# @param [Hash] options
|
2539
|
-
# - `:match => String`: only return keys matching the pattern
|
2540
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2541
|
-
#
|
2542
|
-
# @return [Enumerator] an enumerator for all found keys
|
2543
|
-
def hscan_each(key, options={}, &block)
|
2544
|
-
return to_enum(:hscan_each, key, options) unless block_given?
|
2545
|
-
cursor = 0
|
2546
|
-
loop do
|
2547
|
-
cursor, values = hscan(key, cursor, options)
|
2548
|
-
values.each(&block)
|
2549
|
-
break if cursor == "0"
|
2550
|
-
end
|
2551
|
-
end
|
2552
|
-
|
2553
|
-
# Scan a sorted set
|
2554
|
-
#
|
2555
|
-
# @example Retrieve the first batch of key/value pairs in a hash
|
2556
|
-
# redis.zscan("zset", 0)
|
2557
|
-
#
|
2558
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2559
|
-
# @param [Hash] options
|
2560
|
-
# - `:match => String`: only return keys matching the pattern
|
2561
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2562
|
-
#
|
2563
|
-
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2564
|
-
# members and scores
|
2565
|
-
def zscan(key, cursor, options={})
|
2566
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2567
|
-
[reply[0], FloatifyPairs.call(reply[1])]
|
2568
|
-
end
|
2569
|
-
end
|
2570
|
-
|
2571
|
-
# Scan a sorted set
|
2572
|
-
#
|
2573
|
-
# @example Retrieve all of the members/scores in a sorted set
|
2574
|
-
# redis.zscan_each("zset").to_a
|
2575
|
-
# # => [["key70", "70"], ["key80", "80"]]
|
2576
|
-
#
|
2577
|
-
# @param [Hash] options
|
2578
|
-
# - `:match => String`: only return keys matching the pattern
|
2579
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2580
|
-
#
|
2581
|
-
# @return [Enumerator] an enumerator for all found scores and members
|
2582
|
-
def zscan_each(key, options={}, &block)
|
2583
|
-
return to_enum(:zscan_each, key, options) unless block_given?
|
2584
|
-
cursor = 0
|
2585
|
-
loop do
|
2586
|
-
cursor, values = zscan(key, cursor, options)
|
2587
|
-
values.each(&block)
|
2588
|
-
break if cursor == "0"
|
2589
|
-
end
|
2590
|
-
end
|
2591
|
-
|
2592
|
-
# Scan a set
|
2593
|
-
#
|
2594
|
-
# @example Retrieve the first batch of keys in a set
|
2595
|
-
# redis.sscan("set", 0)
|
2596
|
-
#
|
2597
|
-
# @param [String, Integer] cursor the cursor of the iteration
|
2598
|
-
# @param [Hash] options
|
2599
|
-
# - `:match => String`: only return keys matching the pattern
|
2600
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2601
|
-
#
|
2602
|
-
# @return [String, Array<String>] the next cursor and all found members
|
2603
|
-
def sscan(key, cursor, options={})
|
2604
|
-
_scan(:sscan, cursor, [key], options)
|
2605
|
-
end
|
2606
|
-
|
2607
|
-
# Scan a set
|
2608
|
-
#
|
2609
|
-
# @example Retrieve all of the keys in a set
|
2610
|
-
# redis.sscan_each("set").to_a
|
2611
|
-
# # => ["key1", "key2", "key3"]
|
2612
|
-
#
|
2613
|
-
# @param [Hash] options
|
2614
|
-
# - `:match => String`: only return keys matching the pattern
|
2615
|
-
# - `:count => Integer`: return count keys at most per iteration
|
2616
|
-
#
|
2617
|
-
# @return [Enumerator] an enumerator for all keys in the set
|
2618
|
-
def sscan_each(key, options={}, &block)
|
2619
|
-
return to_enum(:sscan_each, key, options) unless block_given?
|
2620
|
-
cursor = 0
|
2621
|
-
loop do
|
2622
|
-
cursor, keys = sscan(key, cursor, options)
|
2623
|
-
keys.each(&block)
|
2624
|
-
break if cursor == "0"
|
2625
|
-
end
|
2626
|
-
end
|
2627
|
-
|
2628
|
-
# Add one or more members to a HyperLogLog structure.
|
2629
|
-
#
|
2630
|
-
# @param [String] key
|
2631
|
-
# @param [String, Array<String>] member one member, or array of members
|
2632
|
-
# @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
|
2633
|
-
def pfadd(key, member)
|
2634
|
-
synchronize do |client|
|
2635
|
-
client.call([:pfadd, key, member], &Boolify)
|
2636
|
-
end
|
2637
|
-
end
|
2638
|
-
|
2639
|
-
# Get the approximate cardinality of members added to HyperLogLog structure.
|
2640
|
-
#
|
2641
|
-
# If called with multiple keys, returns the approximate cardinality of the
|
2642
|
-
# union of the HyperLogLogs contained in the keys.
|
2643
|
-
#
|
2644
|
-
# @param [String, Array<String>] keys
|
2645
|
-
# @return [Fixnum]
|
2646
|
-
def pfcount(*keys)
|
2647
|
-
synchronize do |client|
|
2648
|
-
client.call([:pfcount] + keys)
|
2649
|
-
end
|
2650
|
-
end
|
2651
|
-
|
2652
|
-
# Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
|
2653
|
-
# the observed Sets of the source HyperLogLog structures.
|
2654
|
-
#
|
2655
|
-
# @param [String] dest_key destination key
|
2656
|
-
# @param [String, Array<String>] source_key source key, or array of keys
|
2657
|
-
# @return [Boolean]
|
2658
|
-
def pfmerge(dest_key, *source_key)
|
2659
|
-
synchronize do |client|
|
2660
|
-
client.call([:pfmerge, dest_key, *source_key], &BoolifySet)
|
2661
|
-
end
|
2662
|
-
end
|
2663
|
-
|
2664
|
-
# Interact with the sentinel command (masters, master, slaves, failover)
|
2665
|
-
#
|
2666
|
-
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
2667
|
-
# @param [Array<String>] args depends on subcommand
|
2668
|
-
# @return [Array<String>, Hash<String, String>, String] depends on subcommand
|
2669
|
-
def sentinel(subcommand, *args)
|
2670
|
-
subcommand = subcommand.to_s.downcase
|
2671
|
-
synchronize do |client|
|
2672
|
-
client.call([:sentinel, subcommand] + args) do |reply|
|
2673
|
-
case subcommand
|
2674
|
-
when "get-master-addr-by-name"
|
2675
|
-
reply
|
2676
|
-
else
|
2677
|
-
if reply.kind_of?(Array)
|
2678
|
-
if reply[0].kind_of?(Array)
|
2679
|
-
reply.map(&Hashify)
|
2680
|
-
else
|
2681
|
-
Hashify.call(reply)
|
2682
|
-
end
|
2683
|
-
else
|
2684
|
-
reply
|
2685
|
-
end
|
2686
|
-
end
|
2687
|
-
end
|
2688
|
-
end
|
2689
|
-
end
|
2690
|
-
|
2691
|
-
def id
|
2692
|
-
@original_client.id
|
2693
|
-
end
|
2694
|
-
|
2695
|
-
def inspect
|
2696
|
-
"#<Redis client v#{Redis::VERSION} for #{id}>"
|
2697
|
-
end
|
2698
|
-
|
2699
|
-
def dup
|
2700
|
-
self.class.new(@options)
|
2701
|
-
end
|
2702
|
-
|
2703
|
-
def connection
|
2704
|
-
{
|
2705
|
-
:host => @original_client.host,
|
2706
|
-
:port => @original_client.port,
|
2707
|
-
:db => @original_client.db,
|
2708
|
-
:id => @original_client.id,
|
2709
|
-
:location => @original_client.location
|
2710
|
-
}
|
2711
|
-
end
|
2712
|
-
|
2713
|
-
def method_missing(command, *args)
|
2714
|
-
synchronize do |client|
|
2715
|
-
client.call([command] + args)
|
2716
|
-
end
|
2717
|
-
end
|
2718
|
-
|
2719
|
-
private
|
2720
|
-
|
2721
|
-
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2722
|
-
# where the method call will return nil. Propagate the nil instead of falsely
|
2723
|
-
# returning false.
|
2724
|
-
Boolify =
|
2725
|
-
lambda { |value|
|
2726
|
-
value == 1 if value
|
2727
|
-
}
|
2728
|
-
|
2729
|
-
BoolifySet =
|
2730
|
-
lambda { |value|
|
2731
|
-
if value && "OK" == value
|
2732
|
-
true
|
2733
|
-
else
|
2734
|
-
false
|
2735
|
-
end
|
2736
|
-
}
|
2737
|
-
|
2738
|
-
Hashify =
|
2739
|
-
lambda { |array|
|
2740
|
-
hash = Hash.new
|
2741
|
-
array.each_slice(2) do |field, value|
|
2742
|
-
hash[field] = value
|
2743
|
-
end
|
2744
|
-
hash
|
2745
|
-
}
|
2746
|
-
|
2747
|
-
Floatify =
|
2748
|
-
lambda { |str|
|
2749
|
-
if str
|
2750
|
-
if (inf = str.match(/^(-)?inf/i))
|
2751
|
-
(inf[1] ? -1.0 : 1.0) / 0.0
|
2752
|
-
else
|
2753
|
-
Float(str)
|
2754
|
-
end
|
2755
|
-
end
|
2756
|
-
}
|
2757
|
-
|
2758
|
-
FloatifyPairs =
|
2759
|
-
lambda { |array|
|
2760
|
-
if array
|
2761
|
-
array.each_slice(2).map do |member, score|
|
2762
|
-
[member, Floatify.call(score)]
|
2763
|
-
end
|
2764
|
-
end
|
2765
|
-
}
|
2766
|
-
|
2767
|
-
def _subscription(method, timeout, channels, block)
|
2768
|
-
return @client.call([method] + channels) if subscribed?
|
2769
|
-
|
2770
|
-
begin
|
2771
|
-
original, @client = @client, SubscribedClient.new(@client)
|
2772
|
-
if timeout > 0
|
2773
|
-
@client.send(method, timeout, *channels, &block)
|
2774
|
-
else
|
2775
|
-
@client.send(method, *channels, &block)
|
2776
|
-
end
|
2777
|
-
ensure
|
2778
|
-
@client = original
|
2779
|
-
end
|
2780
|
-
end
|
2781
|
-
|
2782
294
|
end
|
2783
295
|
|
2784
296
|
require "redis/version"
|
2785
297
|
require "redis/connection"
|
2786
298
|
require "redis/client"
|
299
|
+
require "redis/cluster"
|
2787
300
|
require "redis/pipeline"
|
2788
301
|
require "redis/subscribe"
|