valkey-rb 1.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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +58 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/README.md +95 -0
  5. data/Rakefile +23 -0
  6. data/lib/valkey/bindings.rb +224 -0
  7. data/lib/valkey/commands/bitmap_commands.rb +86 -0
  8. data/lib/valkey/commands/cluster_commands.rb +259 -0
  9. data/lib/valkey/commands/connection_commands.rb +318 -0
  10. data/lib/valkey/commands/function_commands.rb +255 -0
  11. data/lib/valkey/commands/generic_commands.rb +525 -0
  12. data/lib/valkey/commands/geo_commands.rb +87 -0
  13. data/lib/valkey/commands/hash_commands.rb +587 -0
  14. data/lib/valkey/commands/hyper_log_log_commands.rb +51 -0
  15. data/lib/valkey/commands/json_commands.rb +389 -0
  16. data/lib/valkey/commands/list_commands.rb +348 -0
  17. data/lib/valkey/commands/module_commands.rb +125 -0
  18. data/lib/valkey/commands/pubsub_commands.rb +237 -0
  19. data/lib/valkey/commands/scripting_commands.rb +286 -0
  20. data/lib/valkey/commands/server_commands.rb +961 -0
  21. data/lib/valkey/commands/set_commands.rb +220 -0
  22. data/lib/valkey/commands/sorted_set_commands.rb +971 -0
  23. data/lib/valkey/commands/stream_commands.rb +636 -0
  24. data/lib/valkey/commands/string_commands.rb +359 -0
  25. data/lib/valkey/commands/transaction_commands.rb +175 -0
  26. data/lib/valkey/commands/vector_search_commands.rb +271 -0
  27. data/lib/valkey/commands.rb +68 -0
  28. data/lib/valkey/errors.rb +41 -0
  29. data/lib/valkey/libglide_ffi.so +0 -0
  30. data/lib/valkey/opentelemetry.rb +207 -0
  31. data/lib/valkey/pipeline.rb +20 -0
  32. data/lib/valkey/protobuf/command_request_pb.rb +51 -0
  33. data/lib/valkey/protobuf/connection_request_pb.rb +51 -0
  34. data/lib/valkey/protobuf/response_pb.rb +39 -0
  35. data/lib/valkey/pubsub_callback.rb +10 -0
  36. data/lib/valkey/request_error_type.rb +10 -0
  37. data/lib/valkey/request_type.rb +436 -0
  38. data/lib/valkey/response_type.rb +20 -0
  39. data/lib/valkey/utils.rb +253 -0
  40. data/lib/valkey/version.rb +5 -0
  41. data/lib/valkey.rb +551 -0
  42. metadata +119 -0
@@ -0,0 +1,971 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ module SortedSetCommands
6
+ # Get the number of members in a sorted set.
7
+ #
8
+ # @example
9
+ # valkey.zcard("zset")
10
+ # # => 4
11
+ #
12
+ # @param [String] key
13
+ # @return [Integer]
14
+ def zcard(key)
15
+ send_command(RequestType::Z_CARD, [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
+ # valkey.zadd("zset", 32.0, "member")
23
+ # @example Add an array of `[score, member]` pairs to a sorted set
24
+ # valkey.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_args = [key]
55
+ command_args << "NX" if nx
56
+ command_args << "XX" if xx
57
+ command_args << "LT" if lt
58
+ command_args << "GT" if gt
59
+ command_args << "CH" if ch
60
+ command_args << "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(RequestType::Z_ADD, command_args + members_to_add.flatten, &(incr ? Utils::Floatify : nil))
68
+ elsif args.size == 2
69
+ # Single pair: return float if INCR, boolean if !INCR
70
+ send_command(RequestType::Z_ADD, command_args + args.flatten.flatten, &(incr ? Utils::Floatify : Utils::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
+ # valkey.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(RequestType::Z_INCR_BY, [key, increment, member], &Utils::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
+ # valkey.zrem("zset", "a")
94
+ # @example Remove an array of members from a sorted set
95
+ # valkey.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
+ send_command(RequestType::Z_REM, [key, member].flatten) do |reply|
113
+ if member.is_a? Array
114
+ # Variadic: return integer
115
+ reply
116
+ else
117
+ # Single argument: return boolean
118
+ Utils::Boolify.call(reply)
119
+ end
120
+ end
121
+ end
122
+
123
+ # Removes and returns up to count members with the highest scores in the sorted set stored at key.
124
+ #
125
+ # @example Popping a member
126
+ # valkey.zpopmax('zset')
127
+ # #=> ['b', 2.0]
128
+ # @example With count option
129
+ # valkey.zpopmax('zset', 2)
130
+ # #=> [['b', 2.0], ['a', 1.0]]
131
+ #
132
+ # @params key [String] a key of the sorted set
133
+ # @params count [Integer] a number of members
134
+ #
135
+ # @return [Array<String, Float>] element and score pair if count is not specified
136
+ # @return [Array<Array<String, Float>>] list of popped elements and scores
137
+ def zpopmax(key, count = nil)
138
+ command_args = [key]
139
+ command_args << Integer(count) if count
140
+ send_command(RequestType::Z_POP_MAX, command_args) do |members|
141
+ members = Utils::FloatifyPairs.call(members)
142
+ count.to_i > 1 ? members : members.first
143
+ end
144
+ end
145
+
146
+ # Removes and returns up to count members with the lowest scores in the sorted set stored at key.
147
+ #
148
+ # @example Popping a member
149
+ # valkey.zpopmin('zset')
150
+ # #=> ['a', 1.0]
151
+ # @example With count option
152
+ # valkey.zpopmin('zset', 2)
153
+ # #=> [['a', 1.0], ['b', 2.0]]
154
+ #
155
+ # @params key [String] a key of the sorted set
156
+ # @params count [Integer] a number of members
157
+ #
158
+ # @return [Array<String, Float>] element and score pair if count is not specified
159
+ # @return [Array<Array<String, Float>>] list of popped elements and scores
160
+ def zpopmin(key, count = nil)
161
+ command_args = [key]
162
+ command_args << Integer(count) if count
163
+ send_command(RequestType::Z_POP_MIN, command_args) do |members|
164
+ members = Utils::FloatifyPairs.call(members)
165
+ count.to_i > 1 ? members : members.first
166
+ end
167
+ end
168
+
169
+ # Get the score associated with the given member in a sorted set.
170
+ #
171
+ # @example Get the score for member "a"
172
+ # valkey.zscore("zset", "a")
173
+ # # => 32.0
174
+ #
175
+ # @param [String] key
176
+ # @param [String] member
177
+ # @return [Float] score of the member
178
+ def zscore(key, member)
179
+ send_command(RequestType::Z_SCORE, [key, member], &Utils::Floatify)
180
+ end
181
+
182
+ # Get the scores associated with the given members in a sorted set.
183
+ #
184
+ # @example Get the scores for members "a" and "b"
185
+ # valkey.zmscore("zset", "a", "b")
186
+ # # => [32.0, 48.0]
187
+ #
188
+ # @param [String] key
189
+ # @param [String, Array<String>] members
190
+ # @return [Array<Float>] scores of the members
191
+ def zmscore(key, *members)
192
+ send_command(RequestType::Z_MSCORE, [key, *members]) do |reply|
193
+ reply.map(&Utils::Floatify)
194
+ end
195
+ end
196
+
197
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
198
+ #
199
+ # @example Retrieve all members from a sorted set, by index
200
+ # valkey.zrange("zset", 0, -1)
201
+ # # => ["a", "b"]
202
+ # @example Retrieve all members and their scores from a sorted set
203
+ # valkey.zrange("zset", 0, -1, :with_scores => true)
204
+ # # => [["a", 32.0], ["b", 64.0]]
205
+ #
206
+ # @param [String] key
207
+ # @param [Integer] start start index
208
+ # @param [Integer] stop stop index
209
+ # @param [Hash] options
210
+ # - `:by_score => false`: return members by score
211
+ # - `:by_lex => false`: return members by lexicographical ordering
212
+ # - `:rev => false`: reverse the ordering, from highest to lowest
213
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
214
+ # `count` members
215
+ # - `:with_scores => true`: include scores in output
216
+ #
217
+ # @return [Array<String>, Array<[String, Float]>]
218
+ # - when `:with_scores` is not specified, an array of members
219
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
220
+ def zrange(key, start, stop, byscore: false, by_score: byscore, bylex: false, by_lex: bylex,
221
+ rev: false, limit: nil, withscores: false, with_scores: withscores)
222
+ raise ArgumentError, "only one of :by_score or :by_lex can be specified" if by_score && by_lex
223
+
224
+ args = [key, start, stop]
225
+
226
+ if by_score
227
+ args << "BYSCORE"
228
+ elsif by_lex
229
+ args << "BYLEX"
230
+ end
231
+
232
+ args << "REV" if rev
233
+
234
+ if limit
235
+ args << "LIMIT"
236
+ args.concat(limit.map { |l| Integer(l) })
237
+ end
238
+
239
+ if with_scores
240
+ args << "WITHSCORES"
241
+ block = Utils::FloatifyPairs
242
+ end
243
+
244
+ send_command(RequestType::Z_RANGE, args, &block)
245
+ end
246
+
247
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
248
+ # and store the resulting sorted set in a new key.
249
+ #
250
+ # @example
251
+ # valkey.zadd("foo", [[1.0, "s1"], [2.0, "s2"], [3.0, "s3"]])
252
+ # valkey.zrangestore("bar", "foo", 0, 1)
253
+ # # => 2
254
+ # valkey.zrange("bar", 0, -1)
255
+ # # => ["s1", "s2"]
256
+ #
257
+ # @return [Integer] the number of elements in the resulting sorted set
258
+ # @see #zrange
259
+ def zrangestore(dest_key, src_key, start, stop, byscore: false, by_score: byscore,
260
+ bylex: false, by_lex: bylex, rev: false, limit: nil)
261
+ raise ArgumentError, "only one of :by_score or :by_lex can be specified" if by_score && by_lex
262
+
263
+ args = [dest_key, src_key, start, stop]
264
+
265
+ if by_score
266
+ args << "BYSCORE"
267
+ elsif by_lex
268
+ args << "BYLEX"
269
+ end
270
+
271
+ args << "REV" if rev
272
+
273
+ if limit
274
+ args << "LIMIT"
275
+ args.concat(limit.map { |l| Integer(l) })
276
+ end
277
+
278
+ send_command(RequestType::Z_RANGE_STORE, args)
279
+ end
280
+
281
+ # Determine the index of a member in a sorted set.
282
+ #
283
+ # @example Retrieve member rank
284
+ # valkey.zrank("zset", "a")
285
+ # # => 3
286
+ # @example Retrieve member rank with their score
287
+ # valkey.zrank("zset", "a", :with_score => true)
288
+ # # => [3, 32.0]
289
+ #
290
+ # @param [String] key
291
+ # @param [String] member
292
+ #
293
+ # @return [Integer, [Integer, Float]]
294
+ # - when `:with_score` is not specified, an Integer
295
+ # - when `:with_score` is specified, a `[rank, score]` pair
296
+ def zrank(key, member, withscore: false, with_score: withscore)
297
+ args = [key, member]
298
+
299
+ if with_score
300
+ args << "WITHSCORE"
301
+ block = Utils::FloatifyPair
302
+ end
303
+
304
+ send_command(RequestType::Z_RANK, args, &block)
305
+ end
306
+
307
+ # Determine the index of a member in a sorted set, with scores ordered from
308
+ # high to low.
309
+ #
310
+ # @example Retrieve member rank
311
+ # valkey.zrevrank("zset", "a")
312
+ # # => 3
313
+ # @example Retrieve member rank with their score
314
+ # valkey.zrevrank("zset", "a", :with_score => true)
315
+ # # => [3, 32.0]
316
+ #
317
+ # @param [String] key
318
+ # @param [String] member
319
+ #
320
+ # @return [Integer, [Integer, Float]]
321
+ # - when `:with_score` is not specified, an Integer
322
+ # - when `:with_score` is specified, a `[rank, score]` pair
323
+ def zrevrank(key, member, withscore: false, with_score: withscore)
324
+ args = [key, member]
325
+
326
+ if with_score
327
+ args << "WITHSCORE"
328
+ block = Utils::FloatifyPair
329
+ end
330
+
331
+ send_command(RequestType::Z_REV_RANK, args, &block)
332
+ end
333
+
334
+ # Remove all members in a sorted set within the given indexes.
335
+ #
336
+ # @example Remove first 5 members
337
+ # valkey.zremrangebyrank("zset", 0, 4)
338
+ # # => 5
339
+ # @example Remove last 5 members
340
+ # valkey.zremrangebyrank("zset", -5, -1)
341
+ # # => 5
342
+ #
343
+ # @param [String] key
344
+ # @param [Integer] start start index
345
+ # @param [Integer] stop stop index
346
+ # @return [Integer] number of members that were removed
347
+ def zremrangebyrank(key, start, stop)
348
+ send_command(RequestType::Z_REM_RANGE_BY_RANK, [key, start, stop])
349
+ end
350
+
351
+ # Count the members, with the same score in a sorted set, within the given lexicographical range.
352
+ #
353
+ # @example Count members matching a
354
+ # valkey.zlexcount("zset", "[a", "[a\xff")
355
+ # # => 1
356
+ # @example Count members matching a-z
357
+ # valkey.zlexcount("zset", "[a", "[z\xff")
358
+ # # => 26
359
+ #
360
+ # @param [String] key
361
+ # @param [String] min
362
+ # - inclusive minimum is specified by prefixing `(`
363
+ # - exclusive minimum is specified by prefixing `[`
364
+ # @param [String] max
365
+ # - inclusive maximum is specified by prefixing `(`
366
+ # - exclusive maximum is specified by prefixing `[`
367
+ #
368
+ # @return [Integer] number of members within the specified lexicographical range
369
+ def zlexcount(key, min, max)
370
+ send_command(RequestType::Z_LEX_COUNT, [key, min, max])
371
+ end
372
+
373
+ # Remove all members in a sorted set within the given scores.
374
+ #
375
+ # @example Remove members with score `>= 5` and `< 100`
376
+ # valkey.zremrangebyscore("zset", "5", "(100")
377
+ # # => 2
378
+ # @example Remove members with scores `> 5`
379
+ # valkey.zremrangebyscore("zset", "(5", "+inf")
380
+ # # => 2
381
+ #
382
+ # @param [String] key
383
+ # @param [String] min
384
+ # - inclusive minimum score is specified verbatim
385
+ # - exclusive minimum score is specified by prefixing `(`
386
+ # @param [String] max
387
+ # - inclusive maximum score is specified verbatim
388
+ # - exclusive maximum score is specified by prefixing `(`
389
+ # @return [Integer] number of members that were removed
390
+ def zremrangebyscore(key, min, max)
391
+ send_command(RequestType::Z_REM_RANGE_BY_SCORE, [key, min, max])
392
+ end
393
+
394
+ # Count the members in a sorted set with scores within the given values.
395
+ #
396
+ # @example Count members with score `>= 5` and `< 100`
397
+ # valkey.zcount("zset", "5", "(100")
398
+ # # => 2
399
+ # @example Count members with scores `> 5`
400
+ # valkey.zcount("zset", "(5", "+inf")
401
+ # # => 2
402
+ #
403
+ # @param [String] key
404
+ # @param [String] min
405
+ # - inclusive minimum score is specified verbatim
406
+ # - exclusive minimum score is specified by prefixing `(`
407
+ # @param [String] max
408
+ # - inclusive maximum score is specified verbatim
409
+ # - exclusive maximum score is specified by prefixing `(`
410
+ # @return [Integer] number of members in within the specified range
411
+ def zcount(key, min, max)
412
+ send_command(RequestType::Z_COUNT, [key, min, max])
413
+ end
414
+
415
+ # Return the intersection of multiple sorted sets
416
+ #
417
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
418
+ # valkey.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
419
+ # # => ["v1", "v2"]
420
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
421
+ # valkey.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
422
+ # # => [["v1", 3.0], ["v2", 6.0]]
423
+ #
424
+ # @param [String, Array<String>] keys one or more keys to intersect
425
+ # @param [Hash] options
426
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
427
+ # sorted sets
428
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
429
+ # - `:with_scores => true`: include scores in output
430
+ #
431
+ # @return [Array<String>, Array<[String, Float]>]
432
+ # - when `:with_scores` is not specified, an array of members
433
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
434
+ def zinter(*args)
435
+ _zsets_operation(RequestType::Z_INTER, *args)
436
+ end
437
+ ruby2_keywords(:zinter) if respond_to?(:ruby2_keywords, true)
438
+
439
+ # Intersect multiple sorted sets and store the resulting sorted set in a new
440
+ # key.
441
+ #
442
+ # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
443
+ # valkey.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
444
+ # # => 4
445
+ #
446
+ # @param [String] destination destination key
447
+ # @param [Array<String>] keys source keys
448
+ # @param [Hash] options
449
+ # - `:weights => [Array<Float>]`: weights to associate with source
450
+ # sorted sets
451
+ # - `:aggregate => String`: aggregate function to use (sum, min, max)
452
+ # @return [Integer] number of elements in the resulting sorted set
453
+ def zinterstore(*args)
454
+ _zsets_operation_store(RequestType::Z_INTER_STORE, *args)
455
+ end
456
+ ruby2_keywords(:zinterstore) if respond_to?(:ruby2_keywords, true)
457
+
458
+ # Return the union of multiple sorted sets
459
+ #
460
+ # @example Retrieve the union of `2*zsetA` and `1*zsetB`
461
+ # valkey.zunion("zsetA", "zsetB", :weights => [2.0, 1.0])
462
+ # # => ["v1", "v2"]
463
+ # @example Retrieve the union of `2*zsetA` and `1*zsetB`, and their scores
464
+ # valkey.zunion("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
465
+ # # => [["v1", 3.0], ["v2", 6.0]]
466
+ #
467
+ # @param [String, Array<String>] keys one or more keys to union
468
+ # @param [Hash] options
469
+ # - `:weights => [Array<Float>]`: weights to associate with source
470
+ # sorted sets
471
+ # - `:aggregate => String`: aggregate function to use (sum, min, max)
472
+ # - `:with_scores => true`: include scores in output
473
+ #
474
+ # @return [Array<String>, Array<[String, Float]>]
475
+ # - when `:with_scores` is not specified, an array of members
476
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
477
+ def zunion(*args)
478
+ _zsets_operation(RequestType::Z_UNION, *args)
479
+ end
480
+ ruby2_keywords(:zunion) if respond_to?(:ruby2_keywords, true)
481
+
482
+ # Add multiple sorted sets and store the resulting sorted set in a new key.
483
+ #
484
+ # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
485
+ # valkey.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
486
+ # # => 8
487
+ #
488
+ # @param [String] destination destination key
489
+ # @param [Array<String>] keys source keys
490
+ # @param [Hash] options
491
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
492
+ # sorted sets
493
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
494
+ # @return [Integer] number of elements in the resulting sorted set
495
+ def zunionstore(*args)
496
+ _zsets_operation_store(RequestType::Z_UNION_STORE, *args)
497
+ end
498
+ ruby2_keywords(:zunionstore) if respond_to?(:ruby2_keywords, true)
499
+
500
+ # Return the difference between the first and all successive input sorted sets
501
+ #
502
+ # @example
503
+ # valkey.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
504
+ # valkey.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
505
+ # valkey.zdiff("zsetA", "zsetB")
506
+ # => ["v1"]
507
+ # @example With scores
508
+ # valkey.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
509
+ # valkey.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
510
+ # valkey.zdiff("zsetA", "zsetB", :with_scores => true)
511
+ # => [["v1", 1.0]]
512
+ #
513
+ # @param [String, Array<String>] keys one or more keys to compute the difference
514
+ # @param [Hash] options
515
+ # - `:with_scores => true`: include scores in output
516
+ #
517
+ # @return [Array<String>, Array<[String, Float]>]
518
+ # - when `:with_scores` is not specified, an array of members
519
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
520
+ def zdiff(*keys, with_scores: false)
521
+ _zsets_operation(RequestType::Z_DIFF, *keys, with_scores: with_scores)
522
+ end
523
+
524
+ # Compute the difference between the first and all successive input sorted sets
525
+ # and store the resulting sorted set in a new key
526
+ #
527
+ # @example
528
+ # valkey.zadd("zsetA", [[1.0, "v1"], [2.0, "v2"]])
529
+ # valkey.zadd("zsetB", [[3.0, "v2"], [2.0, "v3"]])
530
+ # valkey.zdiffstore("zsetA", "zsetB")
531
+ # # => 1
532
+ #
533
+ # @param [String] destination destination key
534
+ # @param [Array<String>] keys source keys
535
+ # @return [Integer] number of elements in the resulting sorted set
536
+ def zdiffstore(*args)
537
+ _zsets_operation_store(RequestType::Z_DIFF_STORE, *args)
538
+ end
539
+ ruby2_keywords(:zdiffstore) if respond_to?(:ruby2_keywords, true)
540
+
541
+ # Scan a sorted set
542
+ #
543
+ # @example Retrieve the first batch of key/value pairs in a hash
544
+ # valkey.zscan("zset", 0)
545
+ #
546
+ # @param [String, Integer] cursor the cursor of the iteration
547
+ # @param [Hash] options
548
+ # - `:match => String`: only return keys matching the pattern
549
+ # - `:count => Integer`: return count keys at most per iteration
550
+ #
551
+ # @return [String, Array<[String, Float]>] the next cursor and all found
552
+ # members and scores
553
+ #
554
+ # See the [Valkey Server ZSCAN documentation](https://valkey.io/docs/latest/commands/zscan/) for further details
555
+ def zscan(key, cursor, **options)
556
+ _scan(RequestType::Z_SCAN, cursor, [key], **options) do |reply|
557
+ [reply[0], Utils::FloatifyPairs.call(reply[1])]
558
+ end
559
+ end
560
+
561
+ # Remove and return members with scores from one or more sorted sets, blocking until available.
562
+ #
563
+ # @example Pop from multiple keys
564
+ # valkey.bzmpop(1.0, "key1", "key2", modifier: "MAX", count: 2)
565
+ # # => ["key1", [["member1", 3.0], ["member2", 2.0]]]
566
+ #
567
+ # @param [Float] timeout timeout in seconds
568
+ # @param [Array<String>] keys sorted set keys
569
+ # @param [String] modifier "MIN" or "MAX"
570
+ # @param [Integer] count number of elements to pop
571
+ # @return [Array, nil] key and array of [member, score] pairs, or nil if timeout
572
+ #
573
+ # @see https://valkey.io/commands/bzmpop/
574
+ def bzmpop(timeout, *keys, modifier: "MIN", count: 1)
575
+ args = [timeout, keys.size] + keys + [modifier, "COUNT", count]
576
+ send_command(RequestType::BZ_MPOP, args) do |reply|
577
+ reply && [reply[0], Utils::FloatifyPairs.call(reply[1])]
578
+ end
579
+ end
580
+
581
+ # Remove and return the member with the highest score from a sorted set, blocking until available.
582
+ #
583
+ # @example Pop from single key
584
+ # valkey.bzpopmax("key1", timeout: 1.0)
585
+ # # => ["key1", "member", 3.0]
586
+ # @example Pop from multiple keys
587
+ # valkey.bzpopmax("key1", "key2", timeout: 1.0)
588
+ # # => ["key2", "member", 2.0]
589
+ #
590
+ # @param [Array<String>] keys sorted set keys
591
+ # @param [Float] timeout timeout in seconds
592
+ # @return [Array, nil] key, member, and score, or nil if timeout
593
+ #
594
+ # @see https://valkey.io/commands/bzpopmax/
595
+ def bzpopmax(*keys, timeout:)
596
+ args = keys + [timeout]
597
+ send_command(RequestType::BZ_POP_MAX, args) do |reply|
598
+ reply && [reply[0], reply[1], Utils::Floatify.call(reply[2])]
599
+ end
600
+ end
601
+
602
+ # Remove and return the member with the lowest score from a sorted set, blocking until available.
603
+ #
604
+ # @example Pop from single key
605
+ # valkey.bzpopmin("key1", timeout: 1.0)
606
+ # # => ["key1", "member", 1.0]
607
+ # @example Pop from multiple keys
608
+ # valkey.bzpopmin("key1", "key2", timeout: 1.0)
609
+ # # => ["key2", "member", 0.5]
610
+ #
611
+ # @param [Array<String>] keys sorted set keys
612
+ # @param [Float] timeout timeout in seconds
613
+ # @return [Array, nil] key, member, and score, or nil if timeout
614
+ #
615
+ # @see https://valkey.io/commands/bzpopmin/
616
+ def bzpopmin(*keys, timeout:)
617
+ args = keys + [timeout]
618
+ send_command(RequestType::BZ_POP_MIN, args) do |reply|
619
+ reply && [reply[0], reply[1], Utils::Floatify.call(reply[2])]
620
+ end
621
+ end
622
+
623
+ # Return the number of elements in the intersection of sorted sets.
624
+ #
625
+ # @example Get intersection cardinality
626
+ # valkey.zintercard("key1", "key2")
627
+ # # => 3
628
+ # @example With limit
629
+ # valkey.zintercard("key1", "key2", limit: 10)
630
+ # # => 3
631
+ #
632
+ # @param [Array<String>] keys sorted set keys
633
+ # @param [Integer] limit maximum number to count (optional)
634
+ # @return [Integer] number of elements in intersection
635
+ #
636
+ # @see https://valkey.io/commands/zintercard/
637
+ def zintercard(*keys, limit: nil)
638
+ args = [keys.size] + keys
639
+ args += ["LIMIT", limit] if limit
640
+ send_command(RequestType::Z_INTER_CARD, args)
641
+ end
642
+
643
+ # Remove and return members with scores from one or more sorted sets.
644
+ #
645
+ # @example Pop from multiple keys
646
+ # valkey.zmpop("key1", "key2", modifier: "MAX", count: 2)
647
+ # # => ["key1", [["member1", 3.0], ["member2", 2.0]]]
648
+ #
649
+ # @param [Array<String>] keys sorted set keys
650
+ # @param [String] modifier "MIN" or "MAX"
651
+ # @param [Integer] count number of elements to pop
652
+ # @return [Array, nil] key and array of [member, score] pairs, or nil if no elements
653
+ #
654
+ # @see https://valkey.io/commands/zmpop/
655
+ def zmpop(*keys, modifier: "MIN", count: 1)
656
+ args = [keys.size] + keys + [modifier, "COUNT", count]
657
+ send_command(RequestType::Z_MPOP, args) do |reply|
658
+ reply && [reply[0], Utils::FloatifyPairs.call(reply[1])]
659
+ end
660
+ end
661
+
662
+ # Return random members from a sorted set.
663
+ #
664
+ # @example Get single random member
665
+ # valkey.zrandmember("key")
666
+ # # => "member"
667
+ # @example Get multiple random members
668
+ # valkey.zrandmember("key", 3)
669
+ # # => ["member1", "member2", "member3"]
670
+ # @example Get random members with scores
671
+ # valkey.zrandmember("key", 2, with_scores: true)
672
+ # # => [["member1", 1.0], ["member2", 2.0]]
673
+ #
674
+ # @param [String] key sorted set key
675
+ # @param [Integer] count number of members to return (optional)
676
+ # @param [Boolean] with_scores return scores with members
677
+ # @return [String, Array] random member(s), optionally with scores
678
+ #
679
+ # @see https://valkey.io/commands/zrandmember/
680
+ def zrandmember(key, count = nil, with_scores: false)
681
+ args = [key]
682
+ if count
683
+ args << count
684
+ args << "WITHSCORES" if with_scores
685
+ end
686
+
687
+ send_command(RequestType::Z_RAND_MEMBER, args) do |reply|
688
+ if with_scores
689
+ # The reply is already in pairs format from the server
690
+ # Just need to convert scores to floats
691
+ reply.map { |pair| [pair[0], Utils::Floatify.call(pair[1])] }
692
+ else
693
+ reply
694
+ end
695
+ end
696
+ end
697
+
698
+ # Remove members from a sorted set within a lexicographical range.
699
+ #
700
+ # @example Remove by lexicographical range
701
+ # valkey.zremrangebylex("key", "[a", "[c")
702
+ # # => 2
703
+ #
704
+ # @param [String] key sorted set key
705
+ # @param [String] min minimum lexicographical value
706
+ # - inclusive minimum is specified by prefixing `[`
707
+ # - exclusive minimum is specified by prefixing `(`
708
+ # - `-` represents negative infinity
709
+ # @param [String] max maximum lexicographical value
710
+ # - inclusive maximum is specified by prefixing `[`
711
+ # - exclusive maximum is specified by prefixing `(`
712
+ # - `+` represents positive infinity
713
+ # @return [Integer] number of members that were removed
714
+ #
715
+ # @see https://valkey.io/commands/zremrangebylex/
716
+ def zremrangebylex(key, min, max)
717
+ send_command(RequestType::Z_REM_RANGE_BY_LEX, [key, min, max])
718
+ end
719
+
720
+ # Return a range of members in a sorted set, by index, with scores ordered from high to low.
721
+ #
722
+ # @note This command is deprecated since Redis 6.2.0. Use {#zrange} with the `:rev` option instead.
723
+ #
724
+ # @example Retrieve members from a sorted set in reverse order
725
+ # valkey.zrevrange("zset", 0, -1)
726
+ # # => ["b", "a"]
727
+ # @example Retrieve members and their scores from a sorted set in reverse order
728
+ # valkey.zrevrange("zset", 0, -1, :with_scores => true)
729
+ # # => [["b", 64.0], ["a", 32.0]]
730
+ #
731
+ # @param [String] key
732
+ # @param [Integer] start start index
733
+ # @param [Integer] stop stop index
734
+ # @param [Hash] options
735
+ # - `:with_scores => true`: include scores in output
736
+ #
737
+ # @return [Array<String>, Array<[String, Float]>]
738
+ # - when `:with_scores` is not specified, an array of members
739
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
740
+ #
741
+ # @see https://valkey.io/commands/zrevrange/
742
+ def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
743
+ args = [key, start, stop]
744
+
745
+ if with_scores
746
+ args << "WITHSCORES"
747
+ block = Utils::FloatifyPairs
748
+ end
749
+
750
+ send_command(RequestType::Z_REV_RANGE, args, &block)
751
+ end
752
+
753
+ # Return a range of members in a sorted set, by score, with scores ordered from low to high.
754
+ #
755
+ # @note This command is deprecated since Redis 6.2.0. Use {#zrange} with the `:by_score` option instead.
756
+ #
757
+ # @example Retrieve members with scores between 1 and 3
758
+ # valkey.zrangebyscore("zset", 1, 3)
759
+ # # => ["s1", "s2", "s3"]
760
+ # @example Retrieve members with scores between 1 and 3, with scores
761
+ # valkey.zrangebyscore("zset", 1, 3, :with_scores => true)
762
+ # # => [["s1", 1.0], ["s2", 2.0], ["s3", 3.0]]
763
+ # @example With exclusive ranges
764
+ # valkey.zrangebyscore("zset", "(1", "(3")
765
+ # # => ["s2"]
766
+ # @example With limit
767
+ # valkey.zrangebyscore("zset", 1, 3, :limit => [0, 2])
768
+ # # => ["s1", "s2"]
769
+ #
770
+ # @param [String] key
771
+ # @param [String, Numeric] min minimum score
772
+ # - inclusive minimum is specified verbatim
773
+ # - exclusive minimum is specified by prefixing `(`
774
+ # - `-inf` represents negative infinity
775
+ # @param [String, Numeric] max maximum score
776
+ # - inclusive maximum is specified verbatim
777
+ # - exclusive maximum is specified by prefixing `(`
778
+ # - `+inf` represents positive infinity
779
+ # @param [Hash] options
780
+ # - `:with_scores => true`: include scores in output
781
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of `count` members
782
+ #
783
+ # @return [Array<String>, Array<[String, Float]>]
784
+ # - when `:with_scores` is not specified, an array of members
785
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
786
+ #
787
+ # @see https://valkey.io/commands/zrangebyscore/
788
+ def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
789
+ args = [key, min, max]
790
+
791
+ if with_scores
792
+ args << "WITHSCORES"
793
+ block = Utils::FloatifyPairs
794
+ end
795
+
796
+ if limit
797
+ args << "LIMIT"
798
+ args.concat(limit.map { |l| Integer(l) })
799
+ end
800
+
801
+ send_command(RequestType::Z_RANGE_BY_SCORE, args, &block)
802
+ end
803
+
804
+ # Return a range of members in a sorted set, by lexicographical ordering.
805
+ #
806
+ # @note This command is deprecated since Redis 6.2.0. Use {#zrange} with the `:by_lex` option instead.
807
+ #
808
+ # @example Retrieve members within a lexicographical range
809
+ # valkey.zrangebylex("zset", "[aaa", "[g")
810
+ # # => ["aaa", "b", "c", "d", "e", "foo", "g"]
811
+ # @example With exclusive ranges
812
+ # valkey.zrangebylex("zset", "(aaa", "[g")
813
+ # # => ["b", "c", "d", "e", "foo", "g"]
814
+ # @example With limit
815
+ # valkey.zrangebylex("zset", "[aaa", "[g", :limit => [0, 3])
816
+ # # => ["aaa", "b", "c"]
817
+ #
818
+ # @param [String] key
819
+ # @param [String] min minimum lexicographical value
820
+ # - inclusive minimum is specified by prefixing `[`
821
+ # - exclusive minimum is specified by prefixing `(`
822
+ # - `-` represents negative infinity
823
+ # @param [String] max maximum lexicographical value
824
+ # - inclusive maximum is specified by prefixing `[`
825
+ # - exclusive maximum is specified by prefixing `(`
826
+ # - `+` represents positive infinity
827
+ # @param [Hash] options
828
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of `count` members
829
+ #
830
+ # @return [Array<String>] array of members
831
+ #
832
+ # @see https://valkey.io/commands/zrangebylex/
833
+ def zrangebylex(key, min, max, limit: nil)
834
+ args = [key, min, max]
835
+
836
+ if limit
837
+ args << "LIMIT"
838
+ args.concat(limit.map { |l| Integer(l) })
839
+ end
840
+
841
+ send_command(RequestType::Z_RANGE_BY_LEX, args)
842
+ end
843
+
844
+ # Return a range of members in a sorted set, by score, with scores ordered from high to low.
845
+ #
846
+ # @note This command is deprecated since Redis 6.2.0. Use {#zrange} with `:by_score` and `:rev` instead.
847
+ #
848
+ # @example Retrieve members with scores between 3 and 1 (reversed)
849
+ # valkey.zrevrangebyscore("zset", 3, 1)
850
+ # # => ["s3", "s2", "s1"]
851
+ # @example Retrieve members with scores between 3 and 1, with scores
852
+ # valkey.zrevrangebyscore("zset", 3, 1, :with_scores => true)
853
+ # # => [["s3", 3.0], ["s2", 2.0], ["s1", 1.0]]
854
+ # @example With exclusive ranges
855
+ # valkey.zrevrangebyscore("zset", "(3", "(1")
856
+ # # => ["s2"]
857
+ # @example With limit
858
+ # valkey.zrevrangebyscore("zset", 3, 1, :limit => [0, 2])
859
+ # # => ["s3", "s2"]
860
+ #
861
+ # @param [String] key
862
+ # @param [String, Numeric] max maximum score
863
+ # - inclusive maximum is specified verbatim
864
+ # - exclusive maximum is specified by prefixing `(`
865
+ # - `+inf` represents positive infinity
866
+ # @param [String, Numeric] min minimum score
867
+ # - inclusive minimum is specified verbatim
868
+ # - exclusive minimum is specified by prefixing `(`
869
+ # - `-inf` represents negative infinity
870
+ # @param [Hash] options
871
+ # - `:with_scores => true`: include scores in output
872
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of `count` members
873
+ #
874
+ # @return [Array<String>, Array<[String, Float]>]
875
+ # - when `:with_scores` is not specified, an array of members
876
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
877
+ #
878
+ # @see https://valkey.io/commands/zrevrangebyscore/
879
+ def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
880
+ args = [key, max, min]
881
+
882
+ if with_scores
883
+ args << "WITHSCORES"
884
+ block = Utils::FloatifyPairs
885
+ end
886
+
887
+ if limit
888
+ args << "LIMIT"
889
+ args.concat(limit.map { |l| Integer(l) })
890
+ end
891
+
892
+ send_command(RequestType::Z_REV_RANGE_BY_SCORE, args, &block)
893
+ end
894
+
895
+ # Return a range of members in a sorted set, by lexicographical ordering, ordered from high to low.
896
+ #
897
+ # @note This command is deprecated since Redis 6.2.0. Use {#zrange} with the `:by_lex` and `:rev` options instead.
898
+ #
899
+ # @example Retrieve members within a lexicographical range (reversed)
900
+ # valkey.zrevrangebylex("zset", "[g", "[aaa")
901
+ # # => ["g", "foo", "e", "d", "c", "b", "aaa"]
902
+ # @example With exclusive ranges
903
+ # valkey.zrevrangebylex("zset", "[g", "(aaa")
904
+ # # => ["g", "foo", "e", "d", "c", "b"]
905
+ # @example With limit
906
+ # valkey.zrevrangebylex("zset", "[g", "[aaa", :limit => [0, 3])
907
+ # # => ["g", "foo", "e"]
908
+ #
909
+ # @param [String] key
910
+ # @param [String] max maximum lexicographical value
911
+ # - inclusive maximum is specified by prefixing `[`
912
+ # - exclusive maximum is specified by prefixing `(`
913
+ # - `+` represents positive infinity
914
+ # @param [String] min minimum lexicographical value
915
+ # - inclusive minimum is specified by prefixing `[`
916
+ # - exclusive minimum is specified by prefixing `(`
917
+ # - `-` represents negative infinity
918
+ # @param [Hash] options
919
+ # - `:limit => [offset, count]`: skip `offset` members, return a maximum of `count` members
920
+ #
921
+ # @return [Array<String>] array of members
922
+ #
923
+ # @see https://valkey.io/commands/zrevrangebylex/
924
+ def zrevrangebylex(key, max, min, limit: nil)
925
+ args = [key, max, min]
926
+
927
+ if limit
928
+ args << "LIMIT"
929
+ args.concat(limit.map { |l| Integer(l) })
930
+ end
931
+
932
+ send_command(RequestType::Z_REV_RANGE_BY_LEX, args)
933
+ end
934
+
935
+ private
936
+
937
+ def _zsets_operation(cmd, *keys, weights: nil, aggregate: nil, with_scores: false)
938
+ keys.flatten!(1)
939
+ command_args = [keys.size].concat(keys)
940
+
941
+ if weights
942
+ command_args << "WEIGHTS"
943
+ command_args.concat(weights)
944
+ end
945
+
946
+ command_args << "AGGREGATE" << aggregate if aggregate
947
+
948
+ if with_scores
949
+ command_args << "WITHSCORES"
950
+ block = Utils::FloatifyPairs
951
+ end
952
+
953
+ send_command(cmd, command_args, &block)
954
+ end
955
+
956
+ def _zsets_operation_store(cmd, destination, keys, weights: nil, aggregate: nil)
957
+ keys.flatten!(1)
958
+ command_args = [destination, keys.size].concat(keys)
959
+
960
+ if weights
961
+ command_args << "WEIGHTS"
962
+ command_args.concat(weights)
963
+ end
964
+
965
+ command_args << "AGGREGATE" << aggregate if aggregate
966
+
967
+ send_command(cmd, command_args)
968
+ end
969
+ end
970
+ end
971
+ end