redis 4.8.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +138 -1
  3. data/README.md +285 -169
  4. data/lib/redis/client.rb +116 -611
  5. data/lib/redis/commands/bitmaps.rb +14 -4
  6. data/lib/redis/commands/cluster.rb +1 -18
  7. data/lib/redis/commands/connection.rb +5 -10
  8. data/lib/redis/commands/geo.rb +109 -7
  9. data/lib/redis/commands/hashes.rb +179 -8
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +32 -24
  12. data/lib/redis/commands/lists.rb +167 -25
  13. data/lib/redis/commands/modules/json.rb +530 -0
  14. data/lib/redis/commands/modules/search/aggregation.rb +418 -0
  15. data/lib/redis/commands/modules/search/dialect.rb +14 -0
  16. data/lib/redis/commands/modules/search/field.rb +306 -0
  17. data/lib/redis/commands/modules/search/hybrid.rb +359 -0
  18. data/lib/redis/commands/modules/search/index.rb +351 -0
  19. data/lib/redis/commands/modules/search/index_definition.rb +114 -0
  20. data/lib/redis/commands/modules/search/miscellaneous.rb +607 -0
  21. data/lib/redis/commands/modules/search/query.rb +738 -0
  22. data/lib/redis/commands/modules/search/result.rb +488 -0
  23. data/lib/redis/commands/modules/search/schema.rb +211 -0
  24. data/lib/redis/commands/modules/search.rb +19 -0
  25. data/lib/redis/commands/pubsub.rb +34 -25
  26. data/lib/redis/commands/server.rb +15 -15
  27. data/lib/redis/commands/sets.rb +76 -40
  28. data/lib/redis/commands/sorted_sets.rb +128 -19
  29. data/lib/redis/commands/streams.rb +75 -28
  30. data/lib/redis/commands/strings.rb +18 -17
  31. data/lib/redis/commands/transactions.rb +7 -31
  32. data/lib/redis/commands.rb +39 -20
  33. data/lib/redis/distributed.rb +407 -73
  34. data/lib/redis/errors.rb +20 -50
  35. data/lib/redis/hash_ring.rb +26 -26
  36. data/lib/redis/lib_identity.rb +105 -0
  37. data/lib/redis/pipeline.rb +47 -222
  38. data/lib/redis/subscribe.rb +51 -15
  39. data/lib/redis/version.rb +1 -1
  40. data/lib/redis.rb +213 -188
  41. metadata +25 -59
  42. data/lib/redis/cluster/command.rb +0 -79
  43. data/lib/redis/cluster/command_loader.rb +0 -33
  44. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  45. data/lib/redis/cluster/node.rb +0 -120
  46. data/lib/redis/cluster/node_key.rb +0 -31
  47. data/lib/redis/cluster/node_loader.rb +0 -34
  48. data/lib/redis/cluster/option.rb +0 -100
  49. data/lib/redis/cluster/slot.rb +0 -86
  50. data/lib/redis/cluster/slot_loader.rb +0 -46
  51. data/lib/redis/cluster.rb +0 -315
  52. data/lib/redis/connection/command_helper.rb +0 -41
  53. data/lib/redis/connection/hiredis.rb +0 -68
  54. data/lib/redis/connection/registry.rb +0 -13
  55. data/lib/redis/connection/ruby.rb +0 -437
  56. data/lib/redis/connection/synchrony.rb +0 -148
  57. data/lib/redis/connection.rb +0 -11
@@ -20,8 +20,12 @@ class Redis
20
20
  def initialize(node_configs, options = {})
21
21
  @tag = options[:tag] || /^\{(.+?)\}/
22
22
  @ring = options[:ring] || HashRing.new
23
- @node_configs = node_configs.dup
23
+ @node_configs = node_configs.map(&:dup)
24
24
  @default_options = options.dup
25
+ # Schemas registered via himport_prepare, replayed to nodes that join the ring later (see
26
+ # #add_node) so ring membership changes don't leave nodes without the fieldsets that
27
+ # key-routed himport_set calls expect. Initialized before the add_node loop below.
28
+ @himport_fieldsets = {}
25
29
  node_configs.each { |node_config| add_node(node_config) }
26
30
  @subscribed_node = nil
27
31
  @watch_key = nil
@@ -41,7 +45,14 @@ class Redis
41
45
  def add_node(options)
42
46
  options = { url: options } if options.is_a?(String)
43
47
  options = @default_options.merge(options)
44
- @ring.add_node Redis.new(options)
48
+ options.delete(:tag)
49
+ options.delete(:ring)
50
+ node = Redis.new(options)
51
+ # Replay registered fieldsets before the node can receive key-routed himport_set calls:
52
+ # a node joining after a himport_prepare fan-out has neither the server-side fieldset nor
53
+ # a populated registry to self-recover from "no such fieldset".
54
+ @himport_fieldsets.each { |name, fields| node.himport_prepare(name, fields) }
55
+ @ring.add_node node
45
56
  end
46
57
 
47
58
  # Change the selected database for the current connection.
@@ -64,6 +75,10 @@ class Redis
64
75
  on_each_node :quit
65
76
  end
66
77
 
78
+ def close
79
+ on_each_node :close
80
+ end
81
+
67
82
  # Asynchronously save the dataset to disk.
68
83
  def bgsave
69
84
  on_each_node :bgsave
@@ -124,6 +139,11 @@ class Redis
124
139
  node_for(key).expireat(key, unix_time, **kwargs)
125
140
  end
126
141
 
142
+ # Get the expiration for a key as a UNIX timestamp.
143
+ def expiretime(key)
144
+ node_for(key).expiretime(key)
145
+ end
146
+
127
147
  # Get the time to live (in seconds) for a key.
128
148
  def ttl(key)
129
149
  node_for(key).ttl(key)
@@ -139,6 +159,11 @@ class Redis
139
159
  node_for(key).pexpireat(key, ms_unix_time, **kwarg)
140
160
  end
141
161
 
162
+ # Get the expiration for a key as number of milliseconds from UNIX Epoch.
163
+ def pexpiretime(key)
164
+ node_for(key).pexpiretime(key)
165
+ end
166
+
142
167
  # Get the time to live (in milliseconds) for a key.
143
168
  def pttl(key)
144
169
  node_for(key).pttl(key)
@@ -161,6 +186,7 @@ class Redis
161
186
 
162
187
  # Delete a key.
163
188
  def del(*args)
189
+ args.flatten!(1)
164
190
  keys_per_node = args.group_by { |key| node_for(key) }
165
191
  keys_per_node.inject(0) do |sum, (node, keys)|
166
192
  sum + node.del(*keys)
@@ -169,6 +195,7 @@ class Redis
169
195
 
170
196
  # Unlink keys.
171
197
  def unlink(*args)
198
+ args.flatten!(1)
172
199
  keys_per_node = args.group_by { |key| node_for(key) }
173
200
  keys_per_node.inject(0) do |sum, (node, keys)|
174
201
  sum + node.unlink(*keys)
@@ -177,23 +204,16 @@ class Redis
177
204
 
178
205
  # Determine if a key exists.
179
206
  def exists(*args)
180
- if !Redis.exists_returns_integer && args.size == 1
181
- ::Redis.deprecate!(
182
- "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
183
- "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
184
- "(#{::Kernel.caller(1, 1).first})\n"
185
- )
186
- exists?(*args)
187
- else
188
- keys_per_node = args.group_by { |key| node_for(key) }
189
- keys_per_node.inject(0) do |sum, (node, keys)|
190
- sum + node._exists(*keys)
191
- end
207
+ args.flatten!(1)
208
+ keys_per_node = args.group_by { |key| node_for(key) }
209
+ keys_per_node.inject(0) do |sum, (node, keys)|
210
+ sum + node.exists(*keys)
192
211
  end
193
212
  end
194
213
 
195
214
  # Determine if any of the keys exists.
196
215
  def exists?(*args)
216
+ args.flatten!(1)
197
217
  keys_per_node = args.group_by { |key| node_for(key) }
198
218
  keys_per_node.each do |node, keys|
199
219
  return true if node.exists?(*keys)
@@ -297,7 +317,7 @@ class Redis
297
317
  end
298
318
 
299
319
  # Set multiple keys to multiple values.
300
- def mset(*_args)
320
+ def mset(*)
301
321
  raise CannotDistribute, :mset
302
322
  end
303
323
 
@@ -306,7 +326,7 @@ class Redis
306
326
  end
307
327
 
308
328
  # Set multiple keys to multiple values, only if none of the keys exist.
309
- def msetnx(*_args)
329
+ def msetnx(*)
310
330
  raise CannotDistribute, :msetnx
311
331
  end
312
332
 
@@ -329,13 +349,133 @@ class Redis
329
349
  node_for(key).getex(key, **options)
330
350
  end
331
351
 
352
+ # Set the JSON value at a path in the document stored under a key.
353
+ def json_set(key, path, value, **options)
354
+ node_for(key).json_set(key, path, value, **options)
355
+ end
356
+
357
+ # Get the JSON value(s) at one or more paths in the document stored under a key.
358
+ def json_get(key, *paths, **options)
359
+ node_for(key).json_get(key, *paths, **options)
360
+ end
361
+
362
+ # Set one or more JSON values. The keys may live on different nodes and the operation must be
363
+ # atomic, so it cannot be distributed.
364
+ def json_mset(*)
365
+ raise CannotDistribute, :json_mset
366
+ end
367
+
368
+ # Get the values at a path from several keys. Keys are grouped by node, queried per node, and
369
+ # reassembled in the original key order.
370
+ def json_mget(*keys, path, **options)
371
+ keys.flatten!(1)
372
+ values = keys.group_by { |key| node_for(key) }.each_with_object({}) do |(node, subkeys), acc|
373
+ node.json_mget(*subkeys, path, **options).each_with_index do |value, i|
374
+ acc[subkeys[i]] = value
375
+ end
376
+ end
377
+ keys.map { |key| values[key] }
378
+ end
379
+
380
+ # Delete the JSON value(s) at a path in the document stored under a key.
381
+ def json_del(key, path = nil)
382
+ node_for(key).json_del(key, path)
383
+ end
384
+
385
+ # Delete the JSON value(s) at a path in the document stored under a key (alias of json_del).
386
+ def json_forget(key, path = nil)
387
+ node_for(key).json_forget(key, path)
388
+ end
389
+
390
+ # Clear the container and numeric JSON value(s) at a path in the document stored under a key.
391
+ def json_clear(key, path = nil)
392
+ node_for(key).json_clear(key, path)
393
+ end
394
+
395
+ # Merge a JSON value into the document stored under a key at a path.
396
+ def json_merge(key, path, value, **options)
397
+ node_for(key).json_merge(key, path, value, **options)
398
+ end
399
+
400
+ # Append one or more values to the JSON array at a path in the document stored under a key.
401
+ def json_arrappend(key, path, *values, **options)
402
+ node_for(key).json_arrappend(key, path, *values, **options)
403
+ end
404
+
405
+ # Return the index of the first occurrence of a scalar in the JSON array at a path.
406
+ def json_arrindex(key, path, value, **options)
407
+ node_for(key).json_arrindex(key, path, value, **options)
408
+ end
409
+
410
+ # Insert one or more values into the JSON array at a path in the document stored under a key.
411
+ def json_arrinsert(key, path, index, *values, **options)
412
+ node_for(key).json_arrinsert(key, path, index, *values, **options)
413
+ end
414
+
415
+ # Return the length of the JSON array at a path in the document stored under a key.
416
+ def json_arrlen(key, path = nil)
417
+ node_for(key).json_arrlen(key, path)
418
+ end
419
+
420
+ # Remove and return an element from the JSON array at a path in the document stored under a key.
421
+ def json_arrpop(key, path = nil, index = nil, **options)
422
+ node_for(key).json_arrpop(key, path, index, **options)
423
+ end
424
+
425
+ # Trim the JSON array at a path in the document stored under a key to an inclusive range.
426
+ def json_arrtrim(key, path, start, stop)
427
+ node_for(key).json_arrtrim(key, path, start, stop)
428
+ end
429
+
430
+ # Increment the numeric JSON value(s) at a path in the document stored under a key.
431
+ def json_numincrby(key, path, number)
432
+ node_for(key).json_numincrby(key, path, number)
433
+ end
434
+
435
+ # Return the type name(s) of the JSON value(s) at a path in the document stored under a key.
436
+ def json_type(key, path = nil)
437
+ node_for(key).json_type(key, path)
438
+ end
439
+
440
+ # Return the key names of the JSON object(s) at a path in the document stored under a key.
441
+ def json_objkeys(key, path = nil)
442
+ node_for(key).json_objkeys(key, path)
443
+ end
444
+
445
+ # Return the number of keys in the JSON object(s) at a path in the document stored under a key.
446
+ def json_objlen(key, path = nil)
447
+ node_for(key).json_objlen(key, path)
448
+ end
449
+
450
+ # Return the length of the JSON string(s) at a path in the document stored under a key.
451
+ def json_strlen(key, path = nil)
452
+ node_for(key).json_strlen(key, path)
453
+ end
454
+
455
+ # Append a string to the JSON string(s) at a path in the document stored under a key.
456
+ def json_strappend(key, path, value, **options)
457
+ node_for(key).json_strappend(key, path, value, **options)
458
+ end
459
+
460
+ # Toggle the boolean JSON value(s) at a path in the document stored under a key.
461
+ def json_toggle(key, path)
462
+ node_for(key).json_toggle(key, path)
463
+ end
464
+
465
+ # Report the size in bytes of the JSON value(s) at a path in the document stored under a key.
466
+ def json_debug_memory(key, path = nil)
467
+ node_for(key).json_debug_memory(key, path)
468
+ end
469
+
332
470
  # Get the values of all the given keys as an Array.
333
471
  def mget(*keys)
472
+ keys.flatten!(1)
334
473
  mapped_mget(*keys).values_at(*keys)
335
474
  end
336
475
 
337
476
  # Get the values of all the given keys as a Hash.
338
477
  def mapped_mget(*keys)
478
+ keys.flatten!(1)
339
479
  keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
340
480
  results.merge! node.mapped_mget(*subkeys)
341
481
  end
@@ -367,20 +507,21 @@ class Redis
367
507
  end
368
508
 
369
509
  # Count the number of set bits in a range of the string value stored at key.
370
- def bitcount(key, start = 0, stop = -1)
371
- node_for(key).bitcount(key, start, stop)
510
+ def bitcount(key, start = 0, stop = -1, scale: nil)
511
+ node_for(key).bitcount(key, start, stop, scale: scale)
372
512
  end
373
513
 
374
514
  # Perform a bitwise operation between strings and store the resulting string in a key.
375
515
  def bitop(operation, destkey, *keys)
516
+ keys.flatten!(1)
376
517
  ensure_same_node(:bitop, [destkey] + keys) do |node|
377
- node.bitop(operation, destkey, *keys)
518
+ node.bitop(operation, destkey, keys)
378
519
  end
379
520
  end
380
521
 
381
522
  # Return the position of the first bit set to 1 or 0 in a string.
382
- def bitpos(key, bit, start = nil, stop = nil)
383
- node_for(key).bitpos(key, bit, start, stop)
523
+ def bitpos(key, bit, start = nil, stop = nil, scale: nil)
524
+ node_for(key).bitpos(key, bit, start, stop, scale: scale)
384
525
  end
385
526
 
386
527
  # Set the string value of a key and return its old value.
@@ -413,6 +554,15 @@ class Redis
413
554
  end
414
555
  end
415
556
 
557
+ # Remove multiple elements from the head/tail of a list, append/prepend
558
+ # them to another list and return them.
559
+ def lmovem(source, destination, where_source, where_destination, count: nil, exactly: nil, order: nil)
560
+ ensure_same_node(:lmovem, [source, destination]) do |node|
561
+ node.lmovem(source, destination, where_source, where_destination,
562
+ count: count, exactly: exactly, order: order)
563
+ end
564
+ end
565
+
416
566
  # Remove the first/last element in a list and append/prepend it
417
567
  # to another list and return it, or block until one is available.
418
568
  def blmove(source, destination, where_source, where_destination, timeout: 0)
@@ -421,6 +571,17 @@ class Redis
421
571
  end
422
572
  end
423
573
 
574
+ # Remove multiple elements from the head/tail of a list, append/prepend
575
+ # them to another list and return them; or block until the request can
576
+ # be satisfied or the timeout expires.
577
+ def blmovem(source, destination, where_source, where_destination, timeout: 0, count: nil, exactly: nil,
578
+ order: nil)
579
+ ensure_same_node(:blmovem, [source, destination]) do |node|
580
+ node.blmovem(source, destination, where_source, where_destination,
581
+ timeout: timeout, count: count, exactly: exactly, order: order)
582
+ end
583
+ end
584
+
424
585
  # Prepend one or more values to a list.
425
586
  def lpush(key, value)
426
587
  node_for(key).lpush(key, value)
@@ -463,23 +624,15 @@ class Redis
463
624
  timeout = if args.last.is_a?(Hash)
464
625
  options = args.pop
465
626
  options[:timeout]
466
- elsif args.last.respond_to?(:to_int)
467
- last_arg = args.pop
468
- ::Redis.deprecate!(
469
- "Passing the timeout as a positional argument is deprecated, it should be passed as a keyword argument:\n" \
470
- " redis.#{cmd}(#{args.map(&:inspect).join(', ')}, timeout: #{last_arg.to_int})" \
471
- "(called from: #{caller(2, 1).first})"
472
- )
473
- last_arg.to_int
474
627
  end
475
628
 
476
- keys = args.flatten
629
+ args.flatten!(1)
477
630
 
478
- ensure_same_node(cmd, keys) do |node|
631
+ ensure_same_node(cmd, args) do |node|
479
632
  if timeout
480
- node.__send__(cmd, keys, timeout: timeout)
633
+ node.__send__(cmd, args, timeout: timeout)
481
634
  else
482
- node.__send__(cmd, keys)
635
+ node.__send__(cmd, args)
483
636
  end
484
637
  end
485
638
  end
@@ -490,6 +643,18 @@ class Redis
490
643
  _bpop(:blpop, args)
491
644
  end
492
645
 
646
+ def bzpopmax(*args)
647
+ _bpop(:bzpopmax, args) do |reply|
648
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
649
+ end
650
+ end
651
+
652
+ def bzpopmin(*args)
653
+ _bpop(:bzpopmin, args) do |reply|
654
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
655
+ end
656
+ end
657
+
493
658
  # Remove and get the last element in a list, or block until one is
494
659
  # available.
495
660
  def brpop(*args)
@@ -498,9 +663,9 @@ class Redis
498
663
 
499
664
  # Pop a value from a list, push it to another list and return it; or block
500
665
  # until one is available.
501
- def brpoplpush(source, destination, deprecated_timeout = 0, **options)
666
+ def brpoplpush(source, destination, **options)
502
667
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
503
- node.brpoplpush(source, destination, deprecated_timeout, **options)
668
+ node.brpoplpush(source, destination, **options)
504
669
  end
505
670
  end
506
671
 
@@ -534,29 +699,43 @@ class Redis
534
699
  node_for(key).ltrim(key, start, stop)
535
700
  end
536
701
 
702
+ # Iterate over keys, blocking and removing elements from the first non empty liist found.
703
+ def blmpop(timeout, *keys, modifier: "LEFT", count: nil)
704
+ ensure_same_node(:blmpop, keys) do |node|
705
+ node.blmpop(timeout, *keys, modifier: modifier, count: count)
706
+ end
707
+ end
708
+
709
+ # Iterate over keys, removing elements from the first non list found.
710
+ def lmpop(*keys, modifier: "LEFT", count: nil)
711
+ ensure_same_node(:lmpop, keys) do |node|
712
+ node.lmpop(*keys, modifier: modifier, count: count)
713
+ end
714
+ end
715
+
537
716
  # Get the number of members in a set.
538
717
  def scard(key)
539
718
  node_for(key).scard(key)
540
719
  end
541
720
 
542
721
  # Add one or more members to a set.
543
- def sadd(key, member)
544
- node_for(key).sadd(key, member)
722
+ def sadd(key, *members)
723
+ node_for(key).sadd(key, *members)
545
724
  end
546
725
 
547
726
  # Add one or more members to a set.
548
- def sadd?(key, member)
549
- node_for(key).sadd?(key, member)
727
+ def sadd?(key, *members)
728
+ node_for(key).sadd?(key, *members)
550
729
  end
551
730
 
552
731
  # Remove one or more members from a set.
553
- def srem(key, member)
554
- node_for(key).srem(key, member)
732
+ def srem(key, *members)
733
+ node_for(key).srem(key, *members)
555
734
  end
556
735
 
557
736
  # Remove one or more members from a set.
558
- def srem?(key, member)
559
- node_for(key).srem?(key, member)
737
+ def srem?(key, *members)
738
+ node_for(key).srem?(key, *members)
560
739
  end
561
740
 
562
741
  # Remove and return a random member from a set.
@@ -603,43 +782,66 @@ class Redis
603
782
 
604
783
  # Subtract multiple sets.
605
784
  def sdiff(*keys)
785
+ keys.flatten!(1)
606
786
  ensure_same_node(:sdiff, keys) do |node|
607
- node.sdiff(*keys)
787
+ node.sdiff(keys)
608
788
  end
609
789
  end
610
790
 
611
791
  # Subtract multiple sets and store the resulting set in a key.
612
792
  def sdiffstore(destination, *keys)
613
- ensure_same_node(:sdiffstore, [destination] + keys) do |node|
614
- node.sdiffstore(destination, *keys)
793
+ keys.flatten!(1)
794
+ ensure_same_node(:sdiffstore, [destination].concat(keys)) do |node|
795
+ node.sdiffstore(destination, keys)
796
+ end
797
+ end
798
+
799
+ # Get the number of members in the difference between the first set and
800
+ # all successive sets.
801
+ def sdiffcard(*keys, limit: nil)
802
+ keys.flatten!(1)
803
+ ensure_same_node(:sdiffcard, keys) do |node|
804
+ node.sdiffcard(keys, limit: limit)
615
805
  end
616
806
  end
617
807
 
618
808
  # Intersect multiple sets.
619
809
  def sinter(*keys)
810
+ keys.flatten!(1)
620
811
  ensure_same_node(:sinter, keys) do |node|
621
- node.sinter(*keys)
812
+ node.sinter(keys)
622
813
  end
623
814
  end
624
815
 
625
816
  # Intersect multiple sets and store the resulting set in a key.
626
817
  def sinterstore(destination, *keys)
627
- ensure_same_node(:sinterstore, [destination] + keys) do |node|
628
- node.sinterstore(destination, *keys)
818
+ keys.flatten!(1)
819
+ ensure_same_node(:sinterstore, [destination].concat(keys)) do |node|
820
+ node.sinterstore(destination, keys)
629
821
  end
630
822
  end
631
823
 
632
824
  # Add multiple sets.
633
825
  def sunion(*keys)
826
+ keys.flatten!(1)
634
827
  ensure_same_node(:sunion, keys) do |node|
635
- node.sunion(*keys)
828
+ node.sunion(keys)
636
829
  end
637
830
  end
638
831
 
639
832
  # Add multiple sets and store the resulting set in a key.
640
833
  def sunionstore(destination, *keys)
641
- ensure_same_node(:sunionstore, [destination] + keys) do |node|
642
- node.sunionstore(destination, *keys)
834
+ keys.flatten!(1)
835
+ ensure_same_node(:sunionstore, [destination].concat(keys)) do |node|
836
+ node.sunionstore(destination, keys)
837
+ end
838
+ end
839
+
840
+ # Get the number of distinct members in the union of multiple sets.
841
+ def sunioncard(*keys, approx: false, limit: nil)
842
+ keys.flatten!(1)
843
+ ensure_same_node(:sunioncard, keys) do |node|
844
+ node.sunioncard(keys, approx: approx, limit: limit)
643
845
  end
644
846
  end
645
847
 
@@ -680,6 +882,20 @@ class Redis
680
882
  node_for(key).zmscore(key, *members)
681
883
  end
682
884
 
885
+ # Iterate over keys, blocking and removing members from the first non empty sorted set found.
886
+ def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
887
+ ensure_same_node(:bzmpop, keys) do |node|
888
+ node.bzmpop(timeout, *keys, modifier: modifier, count: count)
889
+ end
890
+ end
891
+
892
+ # Iterate over keys, removing members from the first non empty sorted set found.
893
+ def zmpop(*keys, modifier: "MIN", count: nil)
894
+ ensure_same_node(:zmpop, keys) do |node|
895
+ node.zmpop(*keys, modifier: modifier, count: count)
896
+ end
897
+ end
898
+
683
899
  # Return a range of members in a sorted set, by index, score or lexicographical ordering.
684
900
  def zrange(key, start, stop, **options)
685
901
  node_for(key).zrange(key, start, stop, **options)
@@ -700,14 +916,14 @@ class Redis
700
916
  end
701
917
 
702
918
  # Determine the index of a member in a sorted set.
703
- def zrank(key, member)
704
- node_for(key).zrank(key, member)
919
+ def zrank(key, member, **options)
920
+ node_for(key).zrank(key, member, **options)
705
921
  end
706
922
 
707
923
  # Determine the index of a member in a sorted set, with scores ordered from
708
924
  # high to low.
709
- def zrevrank(key, member)
710
- node_for(key).zrevrank(key, member)
925
+ def zrevrank(key, member, **options)
926
+ node_for(key).zrevrank(key, member, **options)
711
927
  end
712
928
 
713
929
  # Remove all members in a sorted set within the given indexes.
@@ -738,43 +954,49 @@ class Redis
738
954
 
739
955
  # Get the intersection of multiple sorted sets
740
956
  def zinter(*keys, **options)
957
+ keys.flatten!(1)
741
958
  ensure_same_node(:zinter, keys) do |node|
742
- node.zinter(*keys, **options)
959
+ node.zinter(keys, **options)
743
960
  end
744
961
  end
745
962
 
746
963
  # Intersect multiple sorted sets and store the resulting sorted set in a new
747
964
  # key.
748
- def zinterstore(destination, keys, **options)
749
- ensure_same_node(:zinterstore, [destination] + keys) do |node|
965
+ def zinterstore(destination, *keys, **options)
966
+ keys.flatten!(1)
967
+ ensure_same_node(:zinterstore, [destination].concat(keys)) do |node|
750
968
  node.zinterstore(destination, keys, **options)
751
969
  end
752
970
  end
753
971
 
754
972
  # Return the union of multiple sorted sets.
755
973
  def zunion(*keys, **options)
974
+ keys.flatten!(1)
756
975
  ensure_same_node(:zunion, keys) do |node|
757
- node.zunion(*keys, **options)
976
+ node.zunion(keys, **options)
758
977
  end
759
978
  end
760
979
 
761
980
  # Add multiple sorted sets and store the resulting sorted set in a new key.
762
- def zunionstore(destination, keys, **options)
763
- ensure_same_node(:zunionstore, [destination] + keys) do |node|
981
+ def zunionstore(destination, *keys, **options)
982
+ keys.flatten!(1)
983
+ ensure_same_node(:zunionstore, [destination].concat(keys)) do |node|
764
984
  node.zunionstore(destination, keys, **options)
765
985
  end
766
986
  end
767
987
 
768
988
  # Return the difference between the first and all successive input sorted sets.
769
989
  def zdiff(*keys, **options)
990
+ keys.flatten!(1)
770
991
  ensure_same_node(:zdiff, keys) do |node|
771
- node.zdiff(*keys, **options)
992
+ node.zdiff(keys, **options)
772
993
  end
773
994
  end
774
995
 
775
996
  # Compute the difference between the first and all successive input sorted sets
776
997
  # and store the resulting sorted set in a new key.
777
- def zdiffstore(destination, keys, **options)
998
+ def zdiffstore(destination, *keys, **options)
999
+ keys.flatten!(1)
778
1000
  ensure_same_node(:zdiffstore, [destination] + keys) do |node|
779
1001
  node.zdiffstore(destination, keys, **options)
780
1002
  end
@@ -801,7 +1023,7 @@ class Redis
801
1023
  end
802
1024
 
803
1025
  def mapped_hmset(key, hash)
804
- node_for(key).hmset(key, *hash.to_a.flatten)
1026
+ node_for(key).hmset(key, hash)
805
1027
  end
806
1028
 
807
1029
  # Get the value of a hash field.
@@ -811,11 +1033,13 @@ class Redis
811
1033
 
812
1034
  # Get the values of all the given hash fields.
813
1035
  def hmget(key, *fields)
814
- node_for(key).hmget(key, *fields)
1036
+ fields.flatten!(1)
1037
+ node_for(key).hmget(key, fields)
815
1038
  end
816
1039
 
817
1040
  def mapped_hmget(key, *fields)
818
- Hash[*fields.zip(hmget(key, *fields)).flatten]
1041
+ fields.flatten!(1)
1042
+ node_for(key).mapped_hmget(key, fields)
819
1043
  end
820
1044
 
821
1045
  def hrandfield(key, count = nil, **options)
@@ -824,7 +1048,8 @@ class Redis
824
1048
 
825
1049
  # Delete one or more hash fields.
826
1050
  def hdel(key, *fields)
827
- node_for(key).hdel(key, *fields)
1051
+ fields.flatten!(1)
1052
+ node_for(key).hdel(key, fields)
828
1053
  end
829
1054
 
830
1055
  # Determine if a hash field exists.
@@ -857,6 +1082,112 @@ class Redis
857
1082
  node_for(key).hgetall(key)
858
1083
  end
859
1084
 
1085
+ def hexpire(key, ttl, *fields)
1086
+ node_for(key).hexpire(key, ttl, *fields)
1087
+ end
1088
+
1089
+ def httl(key, *fields)
1090
+ node_for(key).httl(key, *fields)
1091
+ end
1092
+
1093
+ # Scan a hash
1094
+ def hscan(key, cursor, **options)
1095
+ node_for(key).hscan(key, cursor, **options)
1096
+ end
1097
+
1098
+ # Scan a hash and return an enumerator
1099
+ def hscan_each(key, **options, &block)
1100
+ node_for(key).hscan_each(key, **options, &block)
1101
+ end
1102
+
1103
+ # Get the length of the value stored in a hash field
1104
+ def hstrlen(key, field)
1105
+ node_for(key).hstrlen(key, field)
1106
+ end
1107
+
1108
+ def hpexpire(key, ttl, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
1109
+ node_for(key).hpexpire(key, ttl, *fields, nx: nx, xx: xx, gt: gt, lt: lt)
1110
+ end
1111
+
1112
+ def hpttl(key, *fields)
1113
+ node_for(key).hpttl(key, *fields)
1114
+ end
1115
+
1116
+ # Register a fieldset on every ring node. Fieldsets are scoped to a physical
1117
+ # connection, so every node that may receive an `himport_set` for a key it
1118
+ # owns needs the fieldset on its own connection.
1119
+ def himport_prepare(fieldset_name, *fields)
1120
+ fields.flatten!(1)
1121
+ results = on_each_node(:himport_prepare, fieldset_name, fields)
1122
+ @himport_fieldsets[fieldset_name.to_s] = fields.dup.freeze
1123
+ results
1124
+ end
1125
+
1126
+ # Create or fully replace a hash from a fieldset prepared on the key's node.
1127
+ def himport_set(key, fieldset_name, *values)
1128
+ values.flatten!(1)
1129
+ node_for(key).himport_set(key, fieldset_name, values)
1130
+ end
1131
+
1132
+ # Remove a fieldset from every ring node.
1133
+ def himport_discard(fieldset_name)
1134
+ results = on_each_node(:himport_discard, fieldset_name)
1135
+ @himport_fieldsets.delete(fieldset_name.to_s)
1136
+ results
1137
+ end
1138
+
1139
+ # Remove all fieldsets from every ring node.
1140
+ def himport_discard_all
1141
+ results = on_each_node(:himport_discard_all)
1142
+ @himport_fieldsets.clear
1143
+ results
1144
+ end
1145
+
1146
+ # Add one or more geospatial items to a sorted set.
1147
+ def geoadd(key, *member, **options)
1148
+ node_for(key).geoadd(key, *member, **options)
1149
+ end
1150
+
1151
+ # Get geohash strings representing the position of one or more members.
1152
+ def geohash(key, member)
1153
+ node_for(key).geohash(key, member)
1154
+ end
1155
+
1156
+ # Get longitude and latitude of one or more members of a geospatial index.
1157
+ def geopos(key, member)
1158
+ node_for(key).geopos(key, member)
1159
+ end
1160
+
1161
+ # Get the distance between two members of a geospatial index.
1162
+ def geodist(key, member1, member2, unit = 'm')
1163
+ node_for(key).geodist(key, member1, member2, unit)
1164
+ end
1165
+
1166
+ # Query a geospatial index for members within a given radius from a point.
1167
+ def georadius(*args, **geoptions)
1168
+ key = args.first
1169
+ node_for(key).georadius(*args, **geoptions)
1170
+ end
1171
+
1172
+ # Query a geospatial index for members within a given radius from an existing member.
1173
+ def georadiusbymember(*args, **geoptions)
1174
+ key = args.first
1175
+ node_for(key).georadiusbymember(*args, **geoptions)
1176
+ end
1177
+
1178
+ # Search a geospatial index for members within a given shape from a center point.
1179
+ def geosearch(key, **options)
1180
+ node_for(key).geosearch(key, **options)
1181
+ end
1182
+
1183
+ # Like #geosearch, but stores the result in a destination key.
1184
+ # Destination and source must hash to the same node; use a key tag to ensure that.
1185
+ def geosearchstore(destination, source, **options)
1186
+ ensure_same_node(:geosearchstore, [destination, source]) do |node|
1187
+ node.geosearchstore(destination, source, **options)
1188
+ end
1189
+ end
1190
+
860
1191
  # Post a message to a channel.
861
1192
  def publish(channel, message)
862
1193
  node_for(channel).publish(channel, message)
@@ -881,18 +1212,22 @@ class Redis
881
1212
 
882
1213
  # Stop listening for messages posted to the given channels.
883
1214
  def unsubscribe(*channels)
884
- raise "Can't unsubscribe if not subscribed." unless subscribed?
1215
+ raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
885
1216
 
886
1217
  @subscribed_node.unsubscribe(*channels)
887
1218
  end
888
1219
 
889
1220
  # Listen for messages published to channels matching the given patterns.
1221
+ # See the [Redis Server PSUBSCRIBE documentation](https://redis.io/docs/latest/commands/psubscribe/)
1222
+ # for further details
890
1223
  def psubscribe(*channels, &block)
891
1224
  raise NotImplementedError
892
1225
  end
893
1226
 
894
1227
  # Stop listening for messages posted to channels matching the given
895
1228
  # patterns.
1229
+ # See the [Redis Server PUNSUBSCRIBE documentation](https://redis.io/docs/latest/commands/punsubscribe/)
1230
+ # for further details
896
1231
  def punsubscribe(*channels)
897
1232
  raise NotImplementedError
898
1233
  end
@@ -928,9 +1263,7 @@ class Redis
928
1263
  def multi(&block)
929
1264
  raise CannotDistribute, :multi unless @watch_key
930
1265
 
931
- result = node_for(@watch_key).multi(&block)
932
- @watch_key = nil if block_given?
933
- result
1266
+ node_for(@watch_key).multi(&block)
934
1267
  end
935
1268
 
936
1269
  # Execute all commands issued after MULTI.
@@ -1020,7 +1353,8 @@ class Redis
1020
1353
  end
1021
1354
 
1022
1355
  def key_tag(key)
1023
- key.to_s[@tag, 1] if @tag
1356
+ key = key.to_s
1357
+ key[@tag, 1] if key.match?(@tag)
1024
1358
  end
1025
1359
 
1026
1360
  def ensure_same_node(command, keys)