redis 4.3.1 → 4.6.0

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