redis 4.5.1 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,804 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module SortedSets
6
+ # Get the number of members in a sorted set.
7
+ #
8
+ # @example
9
+ # redis.zcard("zset")
10
+ # # => 4
11
+ #
12
+ # @param [String] key
13
+ # @return [Integer]
14
+ def zcard(key)
15
+ send_command([:zcard, key])
16
+ end
17
+
18
+ # Add one or more members to a sorted set, or update the score for members
19
+ # that already exist.
20
+ #
21
+ # @example Add a single `[score, member]` pair to a sorted set
22
+ # redis.zadd("zset", 32.0, "member")
23
+ # @example Add an array of `[score, member]` pairs to a sorted set
24
+ # redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
25
+ #
26
+ # @param [String] key
27
+ # @param [[Float, String], Array<[Float, String]>] args
28
+ # - a single `[score, member]` pair
29
+ # - an array of `[score, member]` pairs
30
+ # @param [Hash] options
31
+ # - `:xx => true`: Only update elements that already exist (never
32
+ # add elements)
33
+ # - `:nx => true`: Don't update already existing elements (always
34
+ # add new elements)
35
+ # - `:lt => true`: Only update existing elements if the new score
36
+ # is less than the current score
37
+ # - `:gt => true`: Only update existing elements if the new score
38
+ # is greater than the current score
39
+ # - `:ch => true`: Modify the return value from the number of new
40
+ # elements added, to the total number of elements changed (CH is an
41
+ # abbreviation of changed); changed elements are new elements added
42
+ # and elements already existing for which the score was updated
43
+ # - `:incr => true`: When this option is specified ZADD acts like
44
+ # ZINCRBY; only one score-element pair can be specified in this mode
45
+ #
46
+ # @return [Boolean, Integer, Float]
47
+ # - `Boolean` when a single pair is specified, holding whether or not it was
48
+ # **added** to the sorted set.
49
+ # - `Integer` when an array of pairs is specified, holding the number of
50
+ # pairs that were **added** to the sorted set.
51
+ # - `Float` when option :incr is specified, holding the score of the member
52
+ # after incrementing it.
53
+ def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
54
+ command = [:zadd, key]
55
+ command << "NX" if nx
56
+ command << "XX" if xx
57
+ command << "LT" if lt
58
+ command << "GT" if gt
59
+ command << "CH" if ch
60
+ command << "INCR" if incr
61
+
62
+ if args.size == 1 && args[0].is_a?(Array)
63
+ # Variadic: return float if INCR, integer if !INCR
64
+ send_command(command + args[0], &(incr ? Floatify : nil))
65
+ elsif args.size == 2
66
+ # Single pair: return float if INCR, boolean if !INCR
67
+ send_command(command + args, &(incr ? Floatify : Boolify))
68
+ else
69
+ raise ArgumentError, "wrong number of arguments"
70
+ end
71
+ end
72
+
73
+ # Increment the score of a member in a sorted set.
74
+ #
75
+ # @example
76
+ # redis.zincrby("zset", 32.0, "a")
77
+ # # => 64.0
78
+ #
79
+ # @param [String] key
80
+ # @param [Float] increment
81
+ # @param [String] member
82
+ # @return [Float] score of the member after incrementing it
83
+ def zincrby(key, increment, member)
84
+ send_command([:zincrby, key, increment, member], &Floatify)
85
+ end
86
+
87
+ # Remove one or more members from a sorted set.
88
+ #
89
+ # @example Remove a single member from a sorted set
90
+ # redis.zrem("zset", "a")
91
+ # @example Remove an array of members from a sorted set
92
+ # redis.zrem("zset", ["a", "b"])
93
+ #
94
+ # @param [String] key
95
+ # @param [String, Array<String>] member
96
+ # - a single member
97
+ # - an array of members
98
+ #
99
+ # @return [Boolean, Integer]
100
+ # - `Boolean` when a single member is specified, holding whether or not it
101
+ # was removed from the sorted set
102
+ # - `Integer` when an array of pairs is specified, holding the number of
103
+ # members that were removed to the sorted set
104
+ def zrem(key, member)
105
+ send_command([:zrem, key, member]) do |reply|
106
+ if member.is_a? Array
107
+ # Variadic: return integer
108
+ reply
109
+ else
110
+ # Single argument: return boolean
111
+ Boolify.call(reply)
112
+ end
113
+ end
114
+ end
115
+
116
+ # Removes and returns up to count members with the highest scores in the sorted set stored at key.
117
+ #
118
+ # @example Popping a member
119
+ # redis.zpopmax('zset')
120
+ # #=> ['b', 2.0]
121
+ # @example With count option
122
+ # redis.zpopmax('zset', 2)
123
+ # #=> [['b', 2.0], ['a', 1.0]]
124
+ #
125
+ # @params key [String] a key of the sorted set
126
+ # @params count [Integer] a number of members
127
+ #
128
+ # @return [Array<String, Float>] element and score pair if count is not specified
129
+ # @return [Array<Array<String, Float>>] list of popped elements and scores
130
+ def zpopmax(key, count = nil)
131
+ send_command([:zpopmax, key, count].compact) do |members|
132
+ members = FloatifyPairs.call(members)
133
+ count.to_i > 1 ? members : members.first
134
+ end
135
+ end
136
+
137
+ # Removes and returns up to count members with the lowest scores in the sorted set stored at key.
138
+ #
139
+ # @example Popping a member
140
+ # redis.zpopmin('zset')
141
+ # #=> ['a', 1.0]
142
+ # @example With count option
143
+ # redis.zpopmin('zset', 2)
144
+ # #=> [['a', 1.0], ['b', 2.0]]
145
+ #
146
+ # @params key [String] a key of the sorted set
147
+ # @params count [Integer] a number of members
148
+ #
149
+ # @return [Array<String, Float>] element and score pair if count is not specified
150
+ # @return [Array<Array<String, Float>>] list of popped elements and scores
151
+ def zpopmin(key, count = nil)
152
+ send_command([:zpopmin, key, count].compact) do |members|
153
+ members = FloatifyPairs.call(members)
154
+ count.to_i > 1 ? members : members.first
155
+ end
156
+ end
157
+
158
+ # Removes and returns up to count members with the highest scores in the sorted set stored at keys,
159
+ # or block until one is available.
160
+ #
161
+ # @example Popping a member from a sorted set
162
+ # redis.bzpopmax('zset', 1)
163
+ # #=> ['zset', 'b', 2.0]
164
+ # @example Popping a member from multiple sorted sets
165
+ # redis.bzpopmax('zset1', 'zset2', 1)
166
+ # #=> ['zset1', 'b', 2.0]
167
+ #
168
+ # @params keys [Array<String>] one or multiple keys of the sorted sets
169
+ # @params timeout [Integer] the maximum number of seconds to block
170
+ #
171
+ # @return [Array<String, String, Float>] a touple of key, member and score
172
+ # @return [nil] when no element could be popped and the timeout expired
173
+ def bzpopmax(*args)
174
+ _bpop(:bzpopmax, args) do |reply|
175
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
176
+ end
177
+ end
178
+
179
+ # Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
180
+ # or block until one is available.
181
+ #
182
+ # @example Popping a member from a sorted set
183
+ # redis.bzpopmin('zset', 1)
184
+ # #=> ['zset', 'a', 1.0]
185
+ # @example Popping a member from multiple sorted sets
186
+ # redis.bzpopmin('zset1', 'zset2', 1)
187
+ # #=> ['zset1', 'a', 1.0]
188
+ #
189
+ # @params keys [Array<String>] one or multiple keys of the sorted sets
190
+ # @params timeout [Integer] the maximum number of seconds to block
191
+ #
192
+ # @return [Array<String, String, Float>] a touple of key, member and score
193
+ # @return [nil] when no element could be popped and the timeout expired
194
+ def bzpopmin(*args)
195
+ _bpop(:bzpopmin, args) do |reply|
196
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
197
+ end
198
+ end
199
+
200
+ # Get the score associated with the given member in a sorted set.
201
+ #
202
+ # @example Get the score for member "a"
203
+ # redis.zscore("zset", "a")
204
+ # # => 32.0
205
+ #
206
+ # @param [String] key
207
+ # @param [String] member
208
+ # @return [Float] score of the member
209
+ def zscore(key, member)
210
+ send_command([:zscore, key, member], &Floatify)
211
+ end
212
+
213
+ # Get the scores associated with the given members in a sorted set.
214
+ #
215
+ # @example Get the scores for members "a" and "b"
216
+ # redis.zmscore("zset", "a", "b")
217
+ # # => [32.0, 48.0]
218
+ #
219
+ # @param [String] key
220
+ # @param [String, Array<String>] members
221
+ # @return [Array<Float>] scores of the members
222
+ def zmscore(key, *members)
223
+ send_command([:zmscore, key, *members]) do |reply|
224
+ reply.map(&Floatify)
225
+ end
226
+ end
227
+
228
+ # Get one or more random members from a sorted set.
229
+ #
230
+ # @example Get one random member
231
+ # redis.zrandmember("zset")
232
+ # # => "a"
233
+ # @example Get multiple random members
234
+ # redis.zrandmember("zset", 2)
235
+ # # => ["a", "b"]
236
+ # @example Get multiple random members with scores
237
+ # redis.zrandmember("zset", 2, with_scores: true)
238
+ # # => [["a", 2.0], ["b", 3.0]]
239
+ #
240
+ # @param [String] key
241
+ # @param [Integer] count
242
+ # @param [Hash] options
243
+ # - `:with_scores => true`: include scores in output
244
+ #
245
+ # @return [nil, String, Array<String>, Array<[String, Float]>]
246
+ # - when `key` does not exist or set is empty, `nil`
247
+ # - when `count` is not specified, a member
248
+ # - when `count` is specified and `:with_scores` is not specified, an array of members
249
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
250
+ def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
251
+ if with_scores && count.nil?
252
+ raise ArgumentError, "count argument must be specified"
253
+ end
254
+
255
+ args = [:zrandmember, key]
256
+ args << count if count
257
+
258
+ if with_scores
259
+ args << "WITHSCORES"
260
+ block = FloatifyPairs
261
+ end
262
+
263
+ send_command(args, &block)
264
+ end
265
+
266
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
267
+ #
268
+ # @example Retrieve all members from a sorted set, by index
269
+ # redis.zrange("zset", 0, -1)
270
+ # # => ["a", "b"]
271
+ # @example Retrieve all members and their scores from a sorted set
272
+ # redis.zrange("zset", 0, -1, :with_scores => true)
273
+ # # => [["a", 32.0], ["b", 64.0]]
274
+ #
275
+ # @param [String] key
276
+ # @param [Integer] start start index
277
+ # @param [Integer] stop stop index
278
+ # @param [Hash] options
279
+ # - `:by_score => false`: return members by score
280
+ # - `:by_lex => false`: return members by lexicographical ordering
281
+ # - `:rev => false`: reverse the ordering, from highest to lowest
282
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
283
+ # `count` members
284
+ # - `:with_scores => true`: include scores in output
285
+ #
286
+ # @return [Array<String>, Array<[String, Float]>]
287
+ # - when `:with_scores` is not specified, an array of members
288
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
289
+ def zrange(key, start, stop, byscore: false, by_score: byscore, bylex: false, by_lex: bylex,
290
+ rev: false, limit: nil, withscores: false, with_scores: withscores)
291
+
292
+ if by_score && by_lex
293
+ raise ArgumentError, "only one of :by_score or :by_lex can be specified"
294
+ end
295
+
296
+ args = [:zrange, key, start, stop]
297
+
298
+ if by_score
299
+ args << "BYSCORE"
300
+ elsif by_lex
301
+ args << "BYLEX"
302
+ end
303
+
304
+ args << "REV" if rev
305
+
306
+ if limit
307
+ args << "LIMIT"
308
+ args.concat(limit)
309
+ end
310
+
311
+ if with_scores
312
+ args << "WITHSCORES"
313
+ block = FloatifyPairs
314
+ end
315
+
316
+ send_command(args, &block)
317
+ end
318
+
319
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
320
+ # and store the resulting sorted set in a new key.
321
+ #
322
+ # @example
323
+ # redis.zadd("foo", [[1.0, "s1"], [2.0, "s2"], [3.0, "s3"]])
324
+ # redis.zrangestore("bar", "foo", 0, 1)
325
+ # # => 2
326
+ # redis.zrange("bar", 0, -1)
327
+ # # => ["s1", "s2"]
328
+ #
329
+ # @return [Integer] the number of elements in the resulting sorted set
330
+ # @see #zrange
331
+ def zrangestore(dest_key, src_key, start, stop, byscore: false, by_score: byscore,
332
+ bylex: false, by_lex: bylex, rev: false, limit: nil)
333
+ if by_score && by_lex
334
+ raise ArgumentError, "only one of :by_score or :by_lex can be specified"
335
+ end
336
+
337
+ args = [:zrangestore, dest_key, src_key, start, stop]
338
+
339
+ if by_score
340
+ args << "BYSCORE"
341
+ elsif by_lex
342
+ args << "BYLEX"
343
+ end
344
+
345
+ args << "REV" if rev
346
+
347
+ if limit
348
+ args << "LIMIT"
349
+ args.concat(limit)
350
+ end
351
+
352
+ send_command(args)
353
+ end
354
+
355
+ # Return a range of members in a sorted set, by index, with scores ordered
356
+ # from high to low.
357
+ #
358
+ # @example Retrieve all members from a sorted set
359
+ # redis.zrevrange("zset", 0, -1)
360
+ # # => ["b", "a"]
361
+ # @example Retrieve all members and their scores from a sorted set
362
+ # redis.zrevrange("zset", 0, -1, :with_scores => true)
363
+ # # => [["b", 64.0], ["a", 32.0]]
364
+ #
365
+ # @see #zrange
366
+ def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
367
+ args = [:zrevrange, key, start, stop]
368
+
369
+ if with_scores
370
+ args << "WITHSCORES"
371
+ block = FloatifyPairs
372
+ end
373
+
374
+ send_command(args, &block)
375
+ end
376
+
377
+ # Determine the index of a member in a sorted set.
378
+ #
379
+ # @param [String] key
380
+ # @param [String] member
381
+ # @return [Integer]
382
+ def zrank(key, member)
383
+ send_command([:zrank, key, member])
384
+ end
385
+
386
+ # Determine the index of a member in a sorted set, with scores ordered from
387
+ # high to low.
388
+ #
389
+ # @param [String] key
390
+ # @param [String] member
391
+ # @return [Integer]
392
+ def zrevrank(key, member)
393
+ send_command([:zrevrank, key, member])
394
+ end
395
+
396
+ # Remove all members in a sorted set within the given indexes.
397
+ #
398
+ # @example Remove first 5 members
399
+ # redis.zremrangebyrank("zset", 0, 4)
400
+ # # => 5
401
+ # @example Remove last 5 members
402
+ # redis.zremrangebyrank("zset", -5, -1)
403
+ # # => 5
404
+ #
405
+ # @param [String] key
406
+ # @param [Integer] start start index
407
+ # @param [Integer] stop stop index
408
+ # @return [Integer] number of members that were removed
409
+ def zremrangebyrank(key, start, stop)
410
+ send_command([:zremrangebyrank, key, start, stop])
411
+ end
412
+
413
+ # Count the members, with the same score in a sorted set, within the given lexicographical range.
414
+ #
415
+ # @example Count members matching a
416
+ # redis.zlexcount("zset", "[a", "[a\xff")
417
+ # # => 1
418
+ # @example Count members matching a-z
419
+ # redis.zlexcount("zset", "[a", "[z\xff")
420
+ # # => 26
421
+ #
422
+ # @param [String] key
423
+ # @param [String] min
424
+ # - inclusive minimum is specified by prefixing `(`
425
+ # - exclusive minimum is specified by prefixing `[`
426
+ # @param [String] max
427
+ # - inclusive maximum is specified by prefixing `(`
428
+ # - exclusive maximum is specified by prefixing `[`
429
+ #
430
+ # @return [Integer] number of members within the specified lexicographical range
431
+ def zlexcount(key, min, max)
432
+ send_command([:zlexcount, key, min, max])
433
+ end
434
+
435
+ # Return a range of members with the same score in a sorted set, by lexicographical ordering
436
+ #
437
+ # @example Retrieve members matching a
438
+ # redis.zrangebylex("zset", "[a", "[a\xff")
439
+ # # => ["aaren", "aarika", "abagael", "abby"]
440
+ # @example Retrieve the first 2 members matching a
441
+ # redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
442
+ # # => ["aaren", "aarika"]
443
+ #
444
+ # @param [String] key
445
+ # @param [String] min
446
+ # - inclusive minimum is specified by prefixing `(`
447
+ # - exclusive minimum is specified by prefixing `[`
448
+ # @param [String] max
449
+ # - inclusive maximum is specified by prefixing `(`
450
+ # - exclusive maximum is specified by prefixing `[`
451
+ # @param [Hash] options
452
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
453
+ # `count` members
454
+ #
455
+ # @return [Array<String>, Array<[String, Float]>]
456
+ def zrangebylex(key, min, max, limit: nil)
457
+ args = [:zrangebylex, key, min, max]
458
+
459
+ if limit
460
+ args << "LIMIT"
461
+ args.concat(limit)
462
+ end
463
+
464
+ send_command(args)
465
+ end
466
+
467
+ # Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
468
+ # Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
469
+ #
470
+ # @example Retrieve members matching a
471
+ # redis.zrevrangebylex("zset", "[a", "[a\xff")
472
+ # # => ["abbygail", "abby", "abagael", "aaren"]
473
+ # @example Retrieve the last 2 members matching a
474
+ # redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
475
+ # # => ["abbygail", "abby"]
476
+ #
477
+ # @see #zrangebylex
478
+ def zrevrangebylex(key, max, min, limit: nil)
479
+ args = [:zrevrangebylex, key, max, min]
480
+
481
+ if limit
482
+ args << "LIMIT"
483
+ args.concat(limit)
484
+ end
485
+
486
+ send_command(args)
487
+ end
488
+
489
+ # Return a range of members in a sorted set, by score.
490
+ #
491
+ # @example Retrieve members with score `>= 5` and `< 100`
492
+ # redis.zrangebyscore("zset", "5", "(100")
493
+ # # => ["a", "b"]
494
+ # @example Retrieve the first 2 members with score `>= 0`
495
+ # redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
496
+ # # => ["a", "b"]
497
+ # @example Retrieve members and their scores with scores `> 5`
498
+ # redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
499
+ # # => [["a", 32.0], ["b", 64.0]]
500
+ #
501
+ # @param [String] key
502
+ # @param [String] min
503
+ # - inclusive minimum score is specified verbatim
504
+ # - exclusive minimum score is specified by prefixing `(`
505
+ # @param [String] max
506
+ # - inclusive maximum score is specified verbatim
507
+ # - exclusive maximum score is specified by prefixing `(`
508
+ # @param [Hash] options
509
+ # - `:with_scores => true`: include scores in output
510
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
511
+ # `count` members
512
+ #
513
+ # @return [Array<String>, Array<[String, Float]>]
514
+ # - when `:with_scores` is not specified, an array of members
515
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
516
+ def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
517
+ args = [:zrangebyscore, key, min, max]
518
+
519
+ if with_scores
520
+ args << "WITHSCORES"
521
+ block = FloatifyPairs
522
+ end
523
+
524
+ if limit
525
+ args << "LIMIT"
526
+ args.concat(limit)
527
+ end
528
+
529
+ send_command(args, &block)
530
+ end
531
+
532
+ # Return a range of members in a sorted set, by score, with scores ordered
533
+ # from high to low.
534
+ #
535
+ # @example Retrieve members with score `< 100` and `>= 5`
536
+ # redis.zrevrangebyscore("zset", "(100", "5")
537
+ # # => ["b", "a"]
538
+ # @example Retrieve the first 2 members with score `<= 0`
539
+ # redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
540
+ # # => ["b", "a"]
541
+ # @example Retrieve members and their scores with scores `> 5`
542
+ # redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
543
+ # # => [["b", 64.0], ["a", 32.0]]
544
+ #
545
+ # @see #zrangebyscore
546
+ def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
547
+ args = [:zrevrangebyscore, key, max, min]
548
+
549
+ if with_scores
550
+ args << "WITHSCORES"
551
+ block = FloatifyPairs
552
+ end
553
+
554
+ if limit
555
+ args << "LIMIT"
556
+ args.concat(limit)
557
+ end
558
+
559
+ send_command(args, &block)
560
+ end
561
+
562
+ # Remove all members in a sorted set within the given scores.
563
+ #
564
+ # @example Remove members with score `>= 5` and `< 100`
565
+ # redis.zremrangebyscore("zset", "5", "(100")
566
+ # # => 2
567
+ # @example Remove members with scores `> 5`
568
+ # redis.zremrangebyscore("zset", "(5", "+inf")
569
+ # # => 2
570
+ #
571
+ # @param [String] key
572
+ # @param [String] min
573
+ # - inclusive minimum score is specified verbatim
574
+ # - exclusive minimum score is specified by prefixing `(`
575
+ # @param [String] max
576
+ # - inclusive maximum score is specified verbatim
577
+ # - exclusive maximum score is specified by prefixing `(`
578
+ # @return [Integer] number of members that were removed
579
+ def zremrangebyscore(key, min, max)
580
+ send_command([:zremrangebyscore, key, min, max])
581
+ end
582
+
583
+ # Count the members in a sorted set with scores within the given values.
584
+ #
585
+ # @example Count members with score `>= 5` and `< 100`
586
+ # redis.zcount("zset", "5", "(100")
587
+ # # => 2
588
+ # @example Count members with scores `> 5`
589
+ # redis.zcount("zset", "(5", "+inf")
590
+ # # => 2
591
+ #
592
+ # @param [String] key
593
+ # @param [String] min
594
+ # - inclusive minimum score is specified verbatim
595
+ # - exclusive minimum score is specified by prefixing `(`
596
+ # @param [String] max
597
+ # - inclusive maximum score is specified verbatim
598
+ # - exclusive maximum score is specified by prefixing `(`
599
+ # @return [Integer] number of members in within the specified range
600
+ def zcount(key, min, max)
601
+ send_command([:zcount, key, min, max])
602
+ end
603
+
604
+ # Return the intersection of multiple sorted sets
605
+ #
606
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
607
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
608
+ # # => ["v1", "v2"]
609
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
610
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
611
+ # # => [["v1", 3.0], ["v2", 6.0]]
612
+ #
613
+ # @param [String, Array<String>] keys one or more keys to intersect
614
+ # @param [Hash] options
615
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
616
+ # sorted sets
617
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
618
+ # - `:with_scores => true`: include scores in output
619
+ #
620
+ # @return [Array<String>, Array<[String, Float]>]
621
+ # - when `:with_scores` is not specified, an array of members
622
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
623
+ def zinter(*args)
624
+ _zsets_operation(:zinter, *args)
625
+ end
626
+ ruby2_keywords(:zinter) if respond_to?(:ruby2_keywords, true)
627
+
628
+ # Intersect multiple sorted sets and store the resulting sorted set in a new
629
+ # key.
630
+ #
631
+ # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
632
+ # redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
633
+ # # => 4
634
+ #
635
+ # @param [String] destination destination key
636
+ # @param [Array<String>] keys source keys
637
+ # @param [Hash] options
638
+ # - `:weights => [Array<Float>]`: weights to associate with source
639
+ # sorted sets
640
+ # - `:aggregate => String`: aggregate function to use (sum, min, max)
641
+ # @return [Integer] number of elements in the resulting sorted set
642
+ def zinterstore(*args)
643
+ _zsets_operation_store(:zinterstore, *args)
644
+ end
645
+ ruby2_keywords(:zinterstore) if respond_to?(:ruby2_keywords, true)
646
+
647
+ # Return the union of multiple sorted sets
648
+ #
649
+ # @example Retrieve the union of `2*zsetA` and `1*zsetB`
650
+ # redis.zunion("zsetA", "zsetB", :weights => [2.0, 1.0])
651
+ # # => ["v1", "v2"]
652
+ # @example Retrieve the union of `2*zsetA` and `1*zsetB`, and their scores
653
+ # redis.zunion("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
654
+ # # => [["v1", 3.0], ["v2", 6.0]]
655
+ #
656
+ # @param [String, Array<String>] keys one or more keys to union
657
+ # @param [Hash] options
658
+ # - `:weights => [Array<Float>]`: weights to associate with source
659
+ # sorted sets
660
+ # - `:aggregate => String`: aggregate function to use (sum, min, max)
661
+ # - `:with_scores => true`: include scores in output
662
+ #
663
+ # @return [Array<String>, Array<[String, Float]>]
664
+ # - when `:with_scores` is not specified, an array of members
665
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
666
+ def zunion(*args)
667
+ _zsets_operation(:zunion, *args)
668
+ end
669
+ ruby2_keywords(:zunion) if respond_to?(:ruby2_keywords, true)
670
+
671
+ # Add multiple sorted sets and store the resulting sorted set in a new key.
672
+ #
673
+ # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
674
+ # redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
675
+ # # => 8
676
+ #
677
+ # @param [String] destination destination key
678
+ # @param [Array<String>] keys source keys
679
+ # @param [Hash] options
680
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
681
+ # sorted sets
682
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
683
+ # @return [Integer] number of elements in the resulting sorted set
684
+ def zunionstore(*args)
685
+ _zsets_operation_store(:zunionstore, *args)
686
+ end
687
+ ruby2_keywords(:zunionstore) if respond_to?(:ruby2_keywords, true)
688
+
689
+ # Return the difference between the first and all successive input sorted sets
690
+ #
691
+ # @example
692
+ # redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
693
+ # redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
694
+ # redis.zdiff("zsetA", "zsetB")
695
+ # => ["v1"]
696
+ # @example With scores
697
+ # redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
698
+ # redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
699
+ # redis.zdiff("zsetA", "zsetB", :with_scores => true)
700
+ # => [["v1", 1.0]]
701
+ #
702
+ # @param [String, Array<String>] keys one or more keys to compute the difference
703
+ # @param [Hash] options
704
+ # - `:with_scores => true`: include scores in output
705
+ #
706
+ # @return [Array<String>, Array<[String, Float]>]
707
+ # - when `:with_scores` is not specified, an array of members
708
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
709
+ def zdiff(*keys, with_scores: false)
710
+ _zsets_operation(:zdiff, *keys, with_scores: with_scores)
711
+ end
712
+
713
+ # Compute the difference between the first and all successive input sorted sets
714
+ # and store the resulting sorted set in a new key
715
+ #
716
+ # @example
717
+ # redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
718
+ # redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
719
+ # redis.zdiffstore("zsetA", "zsetB")
720
+ # # => 1
721
+ #
722
+ # @param [String] destination destination key
723
+ # @param [Array<String>] keys source keys
724
+ # @return [Integer] number of elements in the resulting sorted set
725
+ def zdiffstore(*args)
726
+ _zsets_operation_store(:zdiffstore, *args)
727
+ end
728
+ ruby2_keywords(:zdiffstore) if respond_to?(:ruby2_keywords, true)
729
+
730
+ # Scan a sorted set
731
+ #
732
+ # @example Retrieve the first batch of key/value pairs in a hash
733
+ # redis.zscan("zset", 0)
734
+ #
735
+ # @param [String, Integer] cursor the cursor of the iteration
736
+ # @param [Hash] options
737
+ # - `:match => String`: only return keys matching the pattern
738
+ # - `:count => Integer`: return count keys at most per iteration
739
+ #
740
+ # @return [String, Array<[String, Float]>] the next cursor and all found
741
+ # members and scores
742
+ def zscan(key, cursor, **options)
743
+ _scan(:zscan, cursor, [key], **options) do |reply|
744
+ [reply[0], FloatifyPairs.call(reply[1])]
745
+ end
746
+ end
747
+
748
+ # Scan a sorted set
749
+ #
750
+ # @example Retrieve all of the members/scores in a sorted set
751
+ # redis.zscan_each("zset").to_a
752
+ # # => [["key70", "70"], ["key80", "80"]]
753
+ #
754
+ # @param [Hash] options
755
+ # - `:match => String`: only return keys matching the pattern
756
+ # - `:count => Integer`: return count keys at most per iteration
757
+ #
758
+ # @return [Enumerator] an enumerator for all found scores and members
759
+ def zscan_each(key, **options, &block)
760
+ return to_enum(:zscan_each, key, **options) unless block_given?
761
+
762
+ cursor = 0
763
+ loop do
764
+ cursor, values = zscan(key, cursor, **options)
765
+ values.each(&block)
766
+ break if cursor == "0"
767
+ end
768
+ end
769
+
770
+ private
771
+
772
+ def _zsets_operation(cmd, *keys, weights: nil, aggregate: nil, with_scores: false)
773
+ command = [cmd, keys.size, *keys]
774
+
775
+ if weights
776
+ command << "WEIGHTS"
777
+ command.concat(weights)
778
+ end
779
+
780
+ command << "AGGREGATE" << aggregate if aggregate
781
+
782
+ if with_scores
783
+ command << "WITHSCORES"
784
+ block = FloatifyPairs
785
+ end
786
+
787
+ send_command(command, &block)
788
+ end
789
+
790
+ def _zsets_operation_store(cmd, destination, keys, weights: nil, aggregate: nil)
791
+ command = [cmd, destination, keys.size, *keys]
792
+
793
+ if weights
794
+ command << "WEIGHTS"
795
+ command.concat(weights)
796
+ end
797
+
798
+ command << "AGGREGATE" << aggregate if aggregate
799
+
800
+ send_command(command)
801
+ end
802
+ end
803
+ end
804
+ end