ultra-smart-kit 0.0.1

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,922 @@
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
+ members_to_add = args[0]
64
+ return 0 if members_to_add.empty?
65
+
66
+ # Variadic: return float if INCR, integer if !INCR
67
+ send_command(command + members_to_add, &(incr ? Floatify : nil))
68
+ elsif args.size == 2
69
+ # Single pair: return float if INCR, boolean if !INCR
70
+ send_command(command + args, &(incr ? Floatify : Boolify))
71
+ else
72
+ raise ArgumentError, "wrong number of arguments"
73
+ end
74
+ end
75
+
76
+ # Increment the score of a member in a sorted set.
77
+ #
78
+ # @example
79
+ # redis.zincrby("zset", 32.0, "a")
80
+ # # => 64.0
81
+ #
82
+ # @param [String] key
83
+ # @param [Float] increment
84
+ # @param [String] member
85
+ # @return [Float] score of the member after incrementing it
86
+ def zincrby(key, increment, member)
87
+ send_command([:zincrby, key, increment, member], &Floatify)
88
+ end
89
+
90
+ # Remove one or more members from a sorted set.
91
+ #
92
+ # @example Remove a single member from a sorted set
93
+ # redis.zrem("zset", "a")
94
+ # @example Remove an array of members from a sorted set
95
+ # redis.zrem("zset", ["a", "b"])
96
+ #
97
+ # @param [String] key
98
+ # @param [String, Array<String>] member
99
+ # - a single member
100
+ # - an array of members
101
+ #
102
+ # @return [Boolean, Integer]
103
+ # - `Boolean` when a single member is specified, holding whether or not it
104
+ # was removed from the sorted set
105
+ # - `Integer` when an array of pairs is specified, holding the number of
106
+ # members that were removed to the sorted set
107
+ def zrem(key, member)
108
+ if member.is_a?(Array)
109
+ members_to_remove = member
110
+ return 0 if members_to_remove.empty?
111
+ end
112
+
113
+ send_command([:zrem, key, member]) do |reply|
114
+ if member.is_a? Array
115
+ # Variadic: return integer
116
+ reply
117
+ else
118
+ # Single argument: return boolean
119
+ Boolify.call(reply)
120
+ end
121
+ end
122
+ end
123
+
124
+ # Removes and returns up to count members with the highest scores in the sorted set stored at key.
125
+ #
126
+ # @example Popping a member
127
+ # redis.zpopmax('zset')
128
+ # #=> ['b', 2.0]
129
+ # @example With count option
130
+ # redis.zpopmax('zset', 2)
131
+ # #=> [['b', 2.0], ['a', 1.0]]
132
+ #
133
+ # @params key [String] a key of the sorted set
134
+ # @params count [Integer] a number of members
135
+ #
136
+ # @return [Array<String, Float>] element and score pair if count is not specified
137
+ # @return [Array<Array<String, Float>>] list of popped elements and scores
138
+ def zpopmax(key, count = nil)
139
+ command = [:zpopmax, key]
140
+ command << Integer(count) if count
141
+ send_command(command) do |members|
142
+ members = FloatifyPairs.call(members)
143
+ count.to_i > 1 ? members : members.first
144
+ end
145
+ end
146
+
147
+ # Removes and returns up to count members with the lowest scores in the sorted set stored at key.
148
+ #
149
+ # @example Popping a member
150
+ # redis.zpopmin('zset')
151
+ # #=> ['a', 1.0]
152
+ # @example With count option
153
+ # redis.zpopmin('zset', 2)
154
+ # #=> [['a', 1.0], ['b', 2.0]]
155
+ #
156
+ # @params key [String] a key of the sorted set
157
+ # @params count [Integer] a number of members
158
+ #
159
+ # @return [Array<String, Float>] element and score pair if count is not specified
160
+ # @return [Array<Array<String, Float>>] list of popped elements and scores
161
+ def zpopmin(key, count = nil)
162
+ command = [:zpopmin, key]
163
+ command << Integer(count) if count
164
+ send_command(command) do |members|
165
+ members = FloatifyPairs.call(members)
166
+ count.to_i > 1 ? members : members.first
167
+ end
168
+ end
169
+
170
+ # Removes and returns up to count members with scores in the sorted set stored at key.
171
+ #
172
+ # @example Popping a member
173
+ # redis.bzmpop('zset')
174
+ # #=> ['zset', ['a', 1.0]]
175
+ # @example With count option
176
+ # redis.bzmpop('zset', count: 2)
177
+ # #=> ['zset', [['a', 1.0], ['b', 2.0]]
178
+ #
179
+ # @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
180
+ # A timeout of zero can be used to block indefinitely.
181
+ # @params key [String, Array<String>] one or more keys with sorted sets
182
+ # @params modifier [String]
183
+ # - when `"MIN"` - the elements popped are those with lowest scores
184
+ # - when `"MAX"` - the elements popped are those with the highest scores
185
+ # @params count [Integer] a number of members to pop
186
+ #
187
+ # @return [Array<String, Array<String, Float>>] list of popped elements and scores
188
+ def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
189
+ raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
190
+
191
+ args = [:bzmpop, timeout, keys.size, *keys, modifier]
192
+ args << "COUNT" << Integer(count) if count
193
+
194
+ send_blocking_command(args, timeout) do |response|
195
+ response&.map do |entry|
196
+ case entry
197
+ when String then entry
198
+ when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # Removes and returns up to count members with scores in the sorted set stored at key.
205
+ #
206
+ # @example Popping a member
207
+ # redis.zmpop('zset')
208
+ # #=> ['zset', ['a', 1.0]]
209
+ # @example With count option
210
+ # redis.zmpop('zset', count: 2)
211
+ # #=> ['zset', [['a', 1.0], ['b', 2.0]]
212
+ #
213
+ # @params key [String, Array<String>] one or more keys with sorted sets
214
+ # @params modifier [String]
215
+ # - when `"MIN"` - the elements popped are those with lowest scores
216
+ # - when `"MAX"` - the elements popped are those with the highest scores
217
+ # @params count [Integer] a number of members to pop
218
+ #
219
+ # @return [Array<String, Array<String, Float>>] list of popped elements and scores
220
+ def zmpop(*keys, modifier: "MIN", count: nil)
221
+ raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
222
+
223
+ args = [:zmpop, keys.size, *keys, modifier]
224
+ args << "COUNT" << Integer(count) if count
225
+
226
+ send_command(args) do |response|
227
+ response&.map do |entry|
228
+ case entry
229
+ when String then entry
230
+ when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ # Removes and returns up to count members with the highest scores in the sorted set stored at keys,
237
+ # or block until one is available.
238
+ #
239
+ # @example Popping a member from a sorted set
240
+ # redis.bzpopmax('zset', 1)
241
+ # #=> ['zset', 'b', 2.0]
242
+ # @example Popping a member from multiple sorted sets
243
+ # redis.bzpopmax('zset1', 'zset2', 1)
244
+ # #=> ['zset1', 'b', 2.0]
245
+ #
246
+ # @params keys [Array<String>] one or multiple keys of the sorted sets
247
+ # @params timeout [Integer] the maximum number of seconds to block
248
+ #
249
+ # @return [Array<String, String, Float>] a touple of key, member and score
250
+ # @return [nil] when no element could be popped and the timeout expired
251
+ def bzpopmax(*args)
252
+ _bpop(:bzpopmax, args) do |reply|
253
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
254
+ end
255
+ end
256
+
257
+ # Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
258
+ # or block until one is available.
259
+ #
260
+ # @example Popping a member from a sorted set
261
+ # redis.bzpopmin('zset', 1)
262
+ # #=> ['zset', 'a', 1.0]
263
+ # @example Popping a member from multiple sorted sets
264
+ # redis.bzpopmin('zset1', 'zset2', 1)
265
+ # #=> ['zset1', 'a', 1.0]
266
+ #
267
+ # @params keys [Array<String>] one or multiple keys of the sorted sets
268
+ # @params timeout [Integer] the maximum number of seconds to block
269
+ #
270
+ # @return [Array<String, String, Float>] a touple of key, member and score
271
+ # @return [nil] when no element could be popped and the timeout expired
272
+ def bzpopmin(*args)
273
+ _bpop(:bzpopmin, args) do |reply|
274
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
275
+ end
276
+ end
277
+
278
+ # Get the score associated with the given member in a sorted set.
279
+ #
280
+ # @example Get the score for member "a"
281
+ # redis.zscore("zset", "a")
282
+ # # => 32.0
283
+ #
284
+ # @param [String] key
285
+ # @param [String] member
286
+ # @return [Float] score of the member
287
+ def zscore(key, member)
288
+ send_command([:zscore, key, member], &Floatify)
289
+ end
290
+
291
+ # Get the scores associated with the given members in a sorted set.
292
+ #
293
+ # @example Get the scores for members "a" and "b"
294
+ # redis.zmscore("zset", "a", "b")
295
+ # # => [32.0, 48.0]
296
+ #
297
+ # @param [String] key
298
+ # @param [String, Array<String>] members
299
+ # @return [Array<Float>] scores of the members
300
+ def zmscore(key, *members)
301
+ send_command([:zmscore, key, *members]) do |reply|
302
+ reply.map(&Floatify)
303
+ end
304
+ end
305
+
306
+ # Get one or more random members from a sorted set.
307
+ #
308
+ # @example Get one random member
309
+ # redis.zrandmember("zset")
310
+ # # => "a"
311
+ # @example Get multiple random members
312
+ # redis.zrandmember("zset", 2)
313
+ # # => ["a", "b"]
314
+ # @example Get multiple random members with scores
315
+ # redis.zrandmember("zset", 2, with_scores: true)
316
+ # # => [["a", 2.0], ["b", 3.0]]
317
+ #
318
+ # @param [String] key
319
+ # @param [Integer] count
320
+ # @param [Hash] options
321
+ # - `:with_scores => true`: include scores in output
322
+ #
323
+ # @return [nil, String, Array<String>, Array<[String, Float]>]
324
+ # - when `key` does not exist or set is empty, `nil`
325
+ # - when `count` is not specified, a member
326
+ # - when `count` is specified and `:with_scores` is not specified, an array of members
327
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
328
+ def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
329
+ if with_scores && count.nil?
330
+ raise ArgumentError, "count argument must be specified"
331
+ end
332
+
333
+ args = [:zrandmember, key]
334
+ args << Integer(count) if count
335
+
336
+ if with_scores
337
+ args << "WITHSCORES"
338
+ block = FloatifyPairs
339
+ end
340
+
341
+ send_command(args, &block)
342
+ end
343
+
344
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
345
+ #
346
+ # @example Retrieve all members from a sorted set, by index
347
+ # redis.zrange("zset", 0, -1)
348
+ # # => ["a", "b"]
349
+ # @example Retrieve all members and their scores from a sorted set
350
+ # redis.zrange("zset", 0, -1, :with_scores => true)
351
+ # # => [["a", 32.0], ["b", 64.0]]
352
+ #
353
+ # @param [String] key
354
+ # @param [Integer] start start index
355
+ # @param [Integer] stop stop index
356
+ # @param [Hash] options
357
+ # - `:by_score => false`: return members by score
358
+ # - `:by_lex => false`: return members by lexicographical ordering
359
+ # - `:rev => false`: reverse the ordering, from highest to lowest
360
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
361
+ # `count` members
362
+ # - `:with_scores => true`: include scores in output
363
+ #
364
+ # @return [Array<String>, Array<[String, Float]>]
365
+ # - when `:with_scores` is not specified, an array of members
366
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
367
+ def zrange(key, start, stop, byscore: false, by_score: byscore, bylex: false, by_lex: bylex,
368
+ rev: false, limit: nil, withscores: false, with_scores: withscores)
369
+
370
+ if by_score && by_lex
371
+ raise ArgumentError, "only one of :by_score or :by_lex can be specified"
372
+ end
373
+
374
+ args = [:zrange, key, start, stop]
375
+
376
+ if by_score
377
+ args << "BYSCORE"
378
+ elsif by_lex
379
+ args << "BYLEX"
380
+ end
381
+
382
+ args << "REV" if rev
383
+
384
+ if limit
385
+ args << "LIMIT"
386
+ args.concat(limit.map { |l| Integer(l) })
387
+ end
388
+
389
+ if with_scores
390
+ args << "WITHSCORES"
391
+ block = FloatifyPairs
392
+ end
393
+
394
+ send_command(args, &block)
395
+ end
396
+
397
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
398
+ # and store the resulting sorted set in a new key.
399
+ #
400
+ # @example
401
+ # redis.zadd("foo", [[1.0, "s1"], [2.0, "s2"], [3.0, "s3"]])
402
+ # redis.zrangestore("bar", "foo", 0, 1)
403
+ # # => 2
404
+ # redis.zrange("bar", 0, -1)
405
+ # # => ["s1", "s2"]
406
+ #
407
+ # @return [Integer] the number of elements in the resulting sorted set
408
+ # @see #zrange
409
+ def zrangestore(dest_key, src_key, start, stop, byscore: false, by_score: byscore,
410
+ bylex: false, by_lex: bylex, rev: false, limit: nil)
411
+ if by_score && by_lex
412
+ raise ArgumentError, "only one of :by_score or :by_lex can be specified"
413
+ end
414
+
415
+ args = [:zrangestore, dest_key, src_key, start, stop]
416
+
417
+ if by_score
418
+ args << "BYSCORE"
419
+ elsif by_lex
420
+ args << "BYLEX"
421
+ end
422
+
423
+ args << "REV" if rev
424
+
425
+ if limit
426
+ args << "LIMIT"
427
+ args.concat(limit.map { |l| Integer(l) })
428
+ end
429
+
430
+ send_command(args)
431
+ end
432
+
433
+ # Return a range of members in a sorted set, by index, with scores ordered
434
+ # from high to low.
435
+ #
436
+ # @example Retrieve all members from a sorted set
437
+ # redis.zrevrange("zset", 0, -1)
438
+ # # => ["b", "a"]
439
+ # @example Retrieve all members and their scores from a sorted set
440
+ # redis.zrevrange("zset", 0, -1, :with_scores => true)
441
+ # # => [["b", 64.0], ["a", 32.0]]
442
+ #
443
+ # @see #zrange
444
+ def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
445
+ args = [:zrevrange, key, Integer(start), Integer(stop)]
446
+
447
+ if with_scores
448
+ args << "WITHSCORES"
449
+ block = FloatifyPairs
450
+ end
451
+
452
+ send_command(args, &block)
453
+ end
454
+
455
+ # Determine the index of a member in a sorted set.
456
+ #
457
+ # @example Retrieve member rank
458
+ # redis.zrank("zset", "a")
459
+ # # => 3
460
+ # @example Retrieve member rank with their score
461
+ # redis.zrank("zset", "a", :with_score => true)
462
+ # # => [3, 32.0]
463
+ #
464
+ # @param [String] key
465
+ # @param [String] member
466
+ #
467
+ # @return [Integer, [Integer, Float]]
468
+ # - when `:with_score` is not specified, an Integer
469
+ # - when `:with_score` is specified, a `[rank, score]` pair
470
+ def zrank(key, member, withscore: false, with_score: withscore)
471
+ args = [:zrank, key, member]
472
+
473
+ if with_score
474
+ args << "WITHSCORE"
475
+ block = FloatifyPair
476
+ end
477
+
478
+ send_command(args, &block)
479
+ end
480
+
481
+ # Determine the index of a member in a sorted set, with scores ordered from
482
+ # high to low.
483
+ #
484
+ # @example Retrieve member rank
485
+ # redis.zrevrank("zset", "a")
486
+ # # => 3
487
+ # @example Retrieve member rank with their score
488
+ # redis.zrevrank("zset", "a", :with_score => true)
489
+ # # => [3, 32.0]
490
+ #
491
+ # @param [String] key
492
+ # @param [String] member
493
+ #
494
+ # @return [Integer, [Integer, Float]]
495
+ # - when `:with_score` is not specified, an Integer
496
+ # - when `:with_score` is specified, a `[rank, score]` pair
497
+ def zrevrank(key, member, withscore: false, with_score: withscore)
498
+ args = [:zrevrank, key, member]
499
+
500
+ if with_score
501
+ args << "WITHSCORE"
502
+ block = FloatifyPair
503
+ end
504
+
505
+ send_command(args, &block)
506
+ end
507
+
508
+ # Remove all members in a sorted set within the given indexes.
509
+ #
510
+ # @example Remove first 5 members
511
+ # redis.zremrangebyrank("zset", 0, 4)
512
+ # # => 5
513
+ # @example Remove last 5 members
514
+ # redis.zremrangebyrank("zset", -5, -1)
515
+ # # => 5
516
+ #
517
+ # @param [String] key
518
+ # @param [Integer] start start index
519
+ # @param [Integer] stop stop index
520
+ # @return [Integer] number of members that were removed
521
+ def zremrangebyrank(key, start, stop)
522
+ send_command([:zremrangebyrank, key, start, stop])
523
+ end
524
+
525
+ # Count the members, with the same score in a sorted set, within the given lexicographical range.
526
+ #
527
+ # @example Count members matching a
528
+ # redis.zlexcount("zset", "[a", "[a\xff")
529
+ # # => 1
530
+ # @example Count members matching a-z
531
+ # redis.zlexcount("zset", "[a", "[z\xff")
532
+ # # => 26
533
+ #
534
+ # @param [String] key
535
+ # @param [String] min
536
+ # - inclusive minimum is specified by prefixing `(`
537
+ # - exclusive minimum is specified by prefixing `[`
538
+ # @param [String] max
539
+ # - inclusive maximum is specified by prefixing `(`
540
+ # - exclusive maximum is specified by prefixing `[`
541
+ #
542
+ # @return [Integer] number of members within the specified lexicographical range
543
+ def zlexcount(key, min, max)
544
+ send_command([:zlexcount, key, min, max])
545
+ end
546
+
547
+ # Return a range of members with the same score in a sorted set, by lexicographical ordering
548
+ #
549
+ # @example Retrieve members matching a
550
+ # redis.zrangebylex("zset", "[a", "[a\xff")
551
+ # # => ["aaren", "aarika", "abagael", "abby"]
552
+ # @example Retrieve the first 2 members matching a
553
+ # redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
554
+ # # => ["aaren", "aarika"]
555
+ #
556
+ # @param [String] key
557
+ # @param [String] min
558
+ # - inclusive minimum is specified by prefixing `(`
559
+ # - exclusive minimum is specified by prefixing `[`
560
+ # @param [String] max
561
+ # - inclusive maximum is specified by prefixing `(`
562
+ # - exclusive maximum is specified by prefixing `[`
563
+ # @param [Hash] options
564
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
565
+ # `count` members
566
+ #
567
+ # @return [Array<String>, Array<[String, Float]>]
568
+ def zrangebylex(key, min, max, limit: nil)
569
+ args = [:zrangebylex, key, min, max]
570
+
571
+ if limit
572
+ args << "LIMIT"
573
+ args.concat(limit.map { |l| Integer(l) })
574
+ end
575
+
576
+ send_command(args)
577
+ end
578
+
579
+ # Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
580
+ # Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
581
+ #
582
+ # @example Retrieve members matching a
583
+ # redis.zrevrangebylex("zset", "[a", "[a\xff")
584
+ # # => ["abbygail", "abby", "abagael", "aaren"]
585
+ # @example Retrieve the last 2 members matching a
586
+ # redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
587
+ # # => ["abbygail", "abby"]
588
+ #
589
+ # @see #zrangebylex
590
+ def zrevrangebylex(key, max, min, limit: nil)
591
+ args = [:zrevrangebylex, key, max, min]
592
+
593
+ if limit
594
+ args << "LIMIT"
595
+ args.concat(limit.map { |l| Integer(l) })
596
+ end
597
+
598
+ send_command(args)
599
+ end
600
+
601
+ # Return a range of members in a sorted set, by score.
602
+ #
603
+ # @example Retrieve members with score `>= 5` and `< 100`
604
+ # redis.zrangebyscore("zset", "5", "(100")
605
+ # # => ["a", "b"]
606
+ # @example Retrieve the first 2 members with score `>= 0`
607
+ # redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
608
+ # # => ["a", "b"]
609
+ # @example Retrieve members and their scores with scores `> 5`
610
+ # redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
611
+ # # => [["a", 32.0], ["b", 64.0]]
612
+ #
613
+ # @param [String] key
614
+ # @param [String] min
615
+ # - inclusive minimum score is specified verbatim
616
+ # - exclusive minimum score is specified by prefixing `(`
617
+ # @param [String] max
618
+ # - inclusive maximum score is specified verbatim
619
+ # - exclusive maximum score is specified by prefixing `(`
620
+ # @param [Hash] options
621
+ # - `:with_scores => true`: include scores in output
622
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
623
+ # `count` members
624
+ #
625
+ # @return [Array<String>, Array<[String, Float]>]
626
+ # - when `:with_scores` is not specified, an array of members
627
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
628
+ def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
629
+ args = [:zrangebyscore, key, min, max]
630
+
631
+ if with_scores
632
+ args << "WITHSCORES"
633
+ block = FloatifyPairs
634
+ end
635
+
636
+ if limit
637
+ args << "LIMIT"
638
+ args.concat(limit.map { |l| Integer(l) })
639
+ end
640
+
641
+ send_command(args, &block)
642
+ end
643
+
644
+ # Return a range of members in a sorted set, by score, with scores ordered
645
+ # from high to low.
646
+ #
647
+ # @example Retrieve members with score `< 100` and `>= 5`
648
+ # redis.zrevrangebyscore("zset", "(100", "5")
649
+ # # => ["b", "a"]
650
+ # @example Retrieve the first 2 members with score `<= 0`
651
+ # redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
652
+ # # => ["b", "a"]
653
+ # @example Retrieve members and their scores with scores `> 5`
654
+ # redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
655
+ # # => [["b", 64.0], ["a", 32.0]]
656
+ #
657
+ # @see #zrangebyscore
658
+ def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
659
+ args = [:zrevrangebyscore, key, max, min]
660
+
661
+ if with_scores
662
+ args << "WITHSCORES"
663
+ block = FloatifyPairs
664
+ end
665
+
666
+ if limit
667
+ args << "LIMIT"
668
+ args.concat(limit.map { |l| Integer(l) })
669
+ end
670
+
671
+ send_command(args, &block)
672
+ end
673
+
674
+ # Remove all members in a sorted set within the given scores.
675
+ #
676
+ # @example Remove members with score `>= 5` and `< 100`
677
+ # redis.zremrangebyscore("zset", "5", "(100")
678
+ # # => 2
679
+ # @example Remove members with scores `> 5`
680
+ # redis.zremrangebyscore("zset", "(5", "+inf")
681
+ # # => 2
682
+ #
683
+ # @param [String] key
684
+ # @param [String] min
685
+ # - inclusive minimum score is specified verbatim
686
+ # - exclusive minimum score is specified by prefixing `(`
687
+ # @param [String] max
688
+ # - inclusive maximum score is specified verbatim
689
+ # - exclusive maximum score is specified by prefixing `(`
690
+ # @return [Integer] number of members that were removed
691
+ def zremrangebyscore(key, min, max)
692
+ send_command([:zremrangebyscore, key, min, max])
693
+ end
694
+
695
+ # Count the members in a sorted set with scores within the given values.
696
+ #
697
+ # @example Count members with score `>= 5` and `< 100`
698
+ # redis.zcount("zset", "5", "(100")
699
+ # # => 2
700
+ # @example Count members with scores `> 5`
701
+ # redis.zcount("zset", "(5", "+inf")
702
+ # # => 2
703
+ #
704
+ # @param [String] key
705
+ # @param [String] min
706
+ # - inclusive minimum score is specified verbatim
707
+ # - exclusive minimum score is specified by prefixing `(`
708
+ # @param [String] max
709
+ # - inclusive maximum score is specified verbatim
710
+ # - exclusive maximum score is specified by prefixing `(`
711
+ # @return [Integer] number of members in within the specified range
712
+ def zcount(key, min, max)
713
+ send_command([:zcount, key, min, max])
714
+ end
715
+
716
+ # Return the intersection of multiple sorted sets
717
+ #
718
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
719
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
720
+ # # => ["v1", "v2"]
721
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
722
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
723
+ # # => [["v1", 3.0], ["v2", 6.0]]
724
+ #
725
+ # @param [String, Array<String>] keys one or more keys to intersect
726
+ # @param [Hash] options
727
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
728
+ # sorted sets
729
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
730
+ # - `:with_scores => true`: include scores in output
731
+ #
732
+ # @return [Array<String>, Array<[String, Float]>]
733
+ # - when `:with_scores` is not specified, an array of members
734
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
735
+ def zinter(*args)
736
+ _zsets_operation(:zinter, *args)
737
+ end
738
+ ruby2_keywords(:zinter) if respond_to?(:ruby2_keywords, true)
739
+
740
+ # Intersect multiple sorted sets and store the resulting sorted set in a new
741
+ # key.
742
+ #
743
+ # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
744
+ # redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
745
+ # # => 4
746
+ #
747
+ # @param [String] destination destination key
748
+ # @param [Array<String>] keys source keys
749
+ # @param [Hash] options
750
+ # - `:weights => [Array<Float>]`: weights to associate with source
751
+ # sorted sets
752
+ # - `:aggregate => String`: aggregate function to use (sum, min, max)
753
+ # @return [Integer] number of elements in the resulting sorted set
754
+ def zinterstore(*args)
755
+ _zsets_operation_store(:zinterstore, *args)
756
+ end
757
+ ruby2_keywords(:zinterstore) if respond_to?(:ruby2_keywords, true)
758
+
759
+ # Return the union of multiple sorted sets
760
+ #
761
+ # @example Retrieve the union of `2*zsetA` and `1*zsetB`
762
+ # redis.zunion("zsetA", "zsetB", :weights => [2.0, 1.0])
763
+ # # => ["v1", "v2"]
764
+ # @example Retrieve the union of `2*zsetA` and `1*zsetB`, and their scores
765
+ # redis.zunion("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
766
+ # # => [["v1", 3.0], ["v2", 6.0]]
767
+ #
768
+ # @param [String, Array<String>] keys one or more keys to union
769
+ # @param [Hash] options
770
+ # - `:weights => [Array<Float>]`: weights to associate with source
771
+ # sorted sets
772
+ # - `:aggregate => String`: aggregate function to use (sum, min, max)
773
+ # - `:with_scores => true`: include scores in output
774
+ #
775
+ # @return [Array<String>, Array<[String, Float]>]
776
+ # - when `:with_scores` is not specified, an array of members
777
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
778
+ def zunion(*args)
779
+ _zsets_operation(:zunion, *args)
780
+ end
781
+ ruby2_keywords(:zunion) if respond_to?(:ruby2_keywords, true)
782
+
783
+ # Add multiple sorted sets and store the resulting sorted set in a new key.
784
+ #
785
+ # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
786
+ # redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
787
+ # # => 8
788
+ #
789
+ # @param [String] destination destination key
790
+ # @param [Array<String>] keys source keys
791
+ # @param [Hash] options
792
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
793
+ # sorted sets
794
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
795
+ # @return [Integer] number of elements in the resulting sorted set
796
+ def zunionstore(*args)
797
+ _zsets_operation_store(:zunionstore, *args)
798
+ end
799
+ ruby2_keywords(:zunionstore) if respond_to?(:ruby2_keywords, true)
800
+
801
+ # Return the difference between the first and all successive input sorted sets
802
+ #
803
+ # @example
804
+ # redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
805
+ # redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
806
+ # redis.zdiff("zsetA", "zsetB")
807
+ # => ["v1"]
808
+ # @example With scores
809
+ # redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
810
+ # redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
811
+ # redis.zdiff("zsetA", "zsetB", :with_scores => true)
812
+ # => [["v1", 1.0]]
813
+ #
814
+ # @param [String, Array<String>] keys one or more keys to compute the difference
815
+ # @param [Hash] options
816
+ # - `:with_scores => true`: include scores in output
817
+ #
818
+ # @return [Array<String>, Array<[String, Float]>]
819
+ # - when `:with_scores` is not specified, an array of members
820
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
821
+ def zdiff(*keys, with_scores: false)
822
+ _zsets_operation(:zdiff, *keys, with_scores: with_scores)
823
+ end
824
+
825
+ # Compute the difference between the first and all successive input sorted sets
826
+ # and store the resulting sorted set in a new key
827
+ #
828
+ # @example
829
+ # redis.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
830
+ # redis.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
831
+ # redis.zdiffstore("zsetA", "zsetB")
832
+ # # => 1
833
+ #
834
+ # @param [String] destination destination key
835
+ # @param [Array<String>] keys source keys
836
+ # @return [Integer] number of elements in the resulting sorted set
837
+ def zdiffstore(*args)
838
+ _zsets_operation_store(:zdiffstore, *args)
839
+ end
840
+ ruby2_keywords(:zdiffstore) if respond_to?(:ruby2_keywords, true)
841
+
842
+ # Scan a sorted set
843
+ #
844
+ # @example Retrieve the first batch of key/value pairs in a hash
845
+ # redis.zscan("zset", 0)
846
+ #
847
+ # @param [String, Integer] cursor the cursor of the iteration
848
+ # @param [Hash] options
849
+ # - `:match => String`: only return keys matching the pattern
850
+ # - `:count => Integer`: return count keys at most per iteration
851
+ #
852
+ # @return [String, Array<[String, Float]>] the next cursor and all found
853
+ # members and scores
854
+ #
855
+ # See the [Redis Server ZSCAN documentation](https://redis.io/docs/latest/commands/zscan/) for further details
856
+ def zscan(key, cursor, **options)
857
+ _scan(:zscan, cursor, [key], **options) do |reply|
858
+ [reply[0], FloatifyPairs.call(reply[1])]
859
+ end
860
+ end
861
+
862
+ # Scan a sorted set
863
+ #
864
+ # @example Retrieve all of the members/scores in a sorted set
865
+ # redis.zscan_each("zset").to_a
866
+ # # => [["key70", "70"], ["key80", "80"]]
867
+ #
868
+ # @param [Hash] options
869
+ # - `:match => String`: only return keys matching the pattern
870
+ # - `:count => Integer`: return count keys at most per iteration
871
+ #
872
+ # @return [Enumerator] an enumerator for all found scores and members
873
+ #
874
+ # See the [Redis Server ZSCAN documentation](https://redis.io/docs/latest/commands/zscan/) for further details
875
+ def zscan_each(key, **options, &block)
876
+ return to_enum(:zscan_each, key, **options) unless block_given?
877
+
878
+ cursor = 0
879
+ loop do
880
+ cursor, values = zscan(key, cursor, **options)
881
+ values.each(&block)
882
+ break if cursor == "0"
883
+ end
884
+ end
885
+
886
+ private
887
+
888
+ def _zsets_operation(cmd, *keys, weights: nil, aggregate: nil, with_scores: false)
889
+ keys.flatten!(1)
890
+ command = [cmd, keys.size].concat(keys)
891
+
892
+ if weights
893
+ command << "WEIGHTS"
894
+ command.concat(weights)
895
+ end
896
+
897
+ command << "AGGREGATE" << aggregate if aggregate
898
+
899
+ if with_scores
900
+ command << "WITHSCORES"
901
+ block = FloatifyPairs
902
+ end
903
+
904
+ send_command(command, &block)
905
+ end
906
+
907
+ def _zsets_operation_store(cmd, destination, keys, weights: nil, aggregate: nil)
908
+ keys.flatten!(1)
909
+ command = [cmd, destination, keys.size].concat(keys)
910
+
911
+ if weights
912
+ command << "WEIGHTS"
913
+ command.concat(weights)
914
+ end
915
+
916
+ command << "AGGREGATE" << aggregate if aggregate
917
+
918
+ send_command(command)
919
+ end
920
+ end
921
+ end
922
+ end