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