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,587 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ # This module contains commands on the Hash data type.
6
+ #
7
+ # @see https://valkey.io/commands/#hash
8
+ #
9
+ module HashCommands
10
+ # Delete one or more hash fields.
11
+ #
12
+ # @example
13
+ # valkey.hdel("hash", "field1", "field2")
14
+ # # => 2
15
+ #
16
+ # @param [String] key
17
+ # @param [String, Array<String>] field one field, or array of fields
18
+ # @return [Integer] the number of fields that were removed
19
+ def hdel(key, *fields)
20
+ fields.flatten!(1)
21
+ send_command(RequestType::HDEL, [key].concat(fields))
22
+ end
23
+
24
+ # Determine if a hash field exists.
25
+ #
26
+ # @example
27
+ # valkey.hexists("hash", "field")
28
+ # # => true
29
+ #
30
+ # @param [String] key
31
+ # @param [String] field
32
+ # @return [Boolean] whether the field exists
33
+ def hexists(key, field)
34
+ send_command(RequestType::HEXISTS, [key, field], &Utils::Boolify)
35
+ end
36
+
37
+ # Get the value of a hash field.
38
+ #
39
+ # @example
40
+ # valkey.hget("hash", "field")
41
+ # # => "value"
42
+ #
43
+ # @param [String] key
44
+ # @param [String] field
45
+ # @return [String, nil] the value of the field, or nil if the field does not exist
46
+ def hget(key, field)
47
+ send_command(RequestType::HGET, [key, field])
48
+ end
49
+
50
+ # Get all the fields and values in a hash.
51
+ #
52
+ # @example
53
+ # valkey.hgetall("hash")
54
+ # # => {"field1" => "value1", "field2" => "value2"}
55
+ #
56
+ # @param [String] key
57
+ # @return [Hash] a hash mapping fields to their values
58
+ def hgetall(key)
59
+ send_command(RequestType::HGET_ALL, [key], &Utils::Hashify)
60
+ end
61
+
62
+ # Increment the integer value of a hash field by the given number.
63
+ #
64
+ # @example
65
+ # valkey.hincrby("hash", "field", 5)
66
+ # # => 10
67
+ #
68
+ # @param [String] key
69
+ # @param [String] field
70
+ # @param [Integer] increment
71
+ # @return [Integer] value after incrementing it
72
+ def hincrby(key, field, increment)
73
+ send_command(RequestType::HINCR_BY, [key, field, Integer(increment)])
74
+ end
75
+
76
+ # Increment the numeric value of a hash field by the given float number.
77
+ #
78
+ # @example
79
+ # valkey.hincrbyfloat("hash", "field", 1.23)
80
+ # # => 1.23
81
+ #
82
+ # @param [String] key
83
+ # @param [String] field
84
+ # @param [Float] increment
85
+ # @return [Float] value after incrementing it
86
+ def hincrbyfloat(key, field, increment)
87
+ send_command(RequestType::HINCR_BY_FLOAT, [key, field, Float(increment)], &Utils::Floatify)
88
+ end
89
+
90
+ # Get all the fields in a hash.
91
+ #
92
+ # @example
93
+ # valkey.hkeys("hash")
94
+ # # => ["field1", "field2"]
95
+ #
96
+ # @param [String] key
97
+ # @return [Array<String>] an array of field names
98
+ def hkeys(key)
99
+ send_command(RequestType::HKEYS, [key])
100
+ end
101
+
102
+ # Get the number of fields in a hash.
103
+ #
104
+ # @example
105
+ # valkey.hlen("hash")
106
+ # # => 2
107
+ #
108
+ # @param [String] key
109
+ # @return [Integer] the number of fields in the hash
110
+ def hlen(key)
111
+ send_command(RequestType::HLEN, [key])
112
+ end
113
+
114
+ # Get the values of all the given hash fields.
115
+ #
116
+ # @example
117
+ # valkey.hmget("hash", "field1", "field2")
118
+ # # => ["value1", "value2"]
119
+ #
120
+ # @param [String] key
121
+ # @param [String, Array<String>] field one field, or array of fields
122
+ # @return [Array<String, nil>] an array of values for the specified fields
123
+ #
124
+ # @see #mapped_hmget
125
+ def hmget(key, *fields, &blk)
126
+ fields.flatten!(1)
127
+ send_command(RequestType::HMGET, [key].concat(fields), &blk)
128
+ end
129
+
130
+ # Get the values of all the given hash fields.
131
+ #
132
+ # @example
133
+ # valkey.mapped_hmget("hash", "field1", "field2")
134
+ # # => {"field1" => "value1", "field2" => "value2"}
135
+ #
136
+ # @param [String] key
137
+ # @param [String, Array<String>] field one field, or array of fields
138
+ # @return [Hash] a hash mapping the specified fields to their values
139
+ #
140
+ # @see #hmget
141
+ def mapped_hmget(key, *fields)
142
+ fields.flatten!(1)
143
+ hmget(key, fields) do |reply|
144
+ if reply.is_a?(Array)
145
+ fields.zip(reply).to_h
146
+ else
147
+ reply
148
+ end
149
+ end
150
+ end
151
+
152
+ # Set multiple hash fields to multiple values.
153
+ #
154
+ # @example
155
+ # valkey.hmset("hash", "field1", "value1", "field2", "value2")
156
+ # # => "OK"
157
+ #
158
+ # @param [String] key
159
+ # @param [Array<String>] args array of field-value pairs
160
+ # @return [String] `"OK"`
161
+ #
162
+ # @see #mapped_hmset
163
+ def hmset(key, *args)
164
+ send_command(RequestType::HMSET, [key].concat(args))
165
+ end
166
+
167
+ # Set multiple hash fields to multiple values.
168
+ #
169
+ # @example
170
+ # valkey.mapped_hmset("hash", { "field1" => "value1", "field2" => "value2" })
171
+ # # => "OK"
172
+ #
173
+ # @param [String] key
174
+ # @param [Hash] hash fields mapping to values
175
+ # @return [String] `"OK"`
176
+ #
177
+ # @see #hmset
178
+ def mapped_hmset(key, hash)
179
+ hmset(key, *hash.flatten)
180
+ end
181
+
182
+ # Get one or multiple random fields from a hash.
183
+ #
184
+ # @example Get one random field
185
+ # valkey.hrandfield("hash")
186
+ # # => "field1"
187
+ # @example Get multiple random fields
188
+ # valkey.hrandfield("hash", 2)
189
+ # # => ["field1", "field2"]
190
+ # @example Get multiple random fields with values
191
+ # valkey.hrandfield("hash", 2, with_values: true)
192
+ # # => [["field1", "value1"], ["field2", "value2"]]
193
+ #
194
+ # @param [String] key
195
+ # @param [Integer] count number of fields to return (optional)
196
+ # @param [Hash] options
197
+ # - `:with_values => true`: include values in output
198
+ #
199
+ # @return [nil, String, Array<String>, Array<[String, String]>]
200
+ # - when `key` does not exist, `nil`
201
+ # - when `count` is not specified, a field name
202
+ # - when `count` is specified and `:with_values` is not specified, an array of field names
203
+ # - when `:with_values` is specified, an array with `[field, value]` pairs
204
+ def hrandfield(key, count = nil, withvalues: false, with_values: withvalues)
205
+ raise ArgumentError, "count argument must be specified" if with_values && count.nil?
206
+
207
+ args = [key]
208
+ args << Integer(count) if count
209
+ args << "WITHVALUES" if with_values
210
+
211
+ if with_values
212
+ send_command(RequestType::HRAND_FIELD, args) do |reply|
213
+ # Handle both ARRAY (flat) and MAP (already pairs) response types
214
+ if reply.is_a?(Array) && !reply.empty? && reply.first.is_a?(Array) && reply.first.size == 2
215
+ # Already in pairs format (from MAP response): [[field, value], ...]
216
+ reply
217
+ elsif reply.is_a?(Array) && reply.respond_to?(:each_slice)
218
+ # ARRAY response: flat array of field-value pairs, convert to pairs
219
+ reply.each_slice(2).to_a
220
+ else
221
+ # Fallback: try Pairify
222
+ Utils::Pairify.call(reply)
223
+ end
224
+ end
225
+ else
226
+ send_command(RequestType::HRAND_FIELD, args)
227
+ end
228
+ end
229
+
230
+ # Scan a hash
231
+ #
232
+ # @example Retrieve the first batch of key/value pairs in a hash
233
+ # valkey.hscan("hash", 0)
234
+ #
235
+ # @param [String] key
236
+ # @param [String, Integer] cursor the cursor of the iteration
237
+ # @param [Hash] options
238
+ # - `:match => String`: only return fields matching the pattern
239
+ # - `:count => Integer`: return count fields at most per iteration
240
+ #
241
+ # @return [String, Array<[String, String]>] the next cursor and all found key/value pairs
242
+ #
243
+ # See the [Valkey Server HSCAN documentation](https://valkey.io/commands/hscan/) for further details
244
+ def hscan(key, cursor, **options)
245
+ _scan(RequestType::HSCAN, cursor, [key], **options) do |reply|
246
+ [reply[0], reply[1].each_slice(2).to_a]
247
+ end
248
+ end
249
+
250
+ # Scan a hash
251
+ #
252
+ # @example Retrieve all of the key/value pairs in a hash
253
+ # valkey.hscan_each("hash").to_a
254
+ # # => [["field1", "value1"], ["field2", "value2"]]
255
+ #
256
+ # @param [String] key
257
+ # @param [Hash] options
258
+ # - `:match => String`: only return fields matching the pattern
259
+ # - `:count => Integer`: return count fields at most per iteration
260
+ #
261
+ # @return [Enumerator] an enumerator for all key/value pairs in the hash
262
+ #
263
+ # See the [Valkey Server HSCAN documentation](https://valkey.io/commands/hscan/) for further details
264
+ def hscan_each(key, **options, &block)
265
+ return to_enum(:hscan_each, key, **options) unless block_given?
266
+
267
+ cursor = 0
268
+ loop do
269
+ cursor, values = hscan(key, cursor, **options)
270
+ values.each(&block)
271
+ break if cursor == "0"
272
+ end
273
+ end
274
+
275
+ # Set one or more hash values.
276
+ #
277
+ # @example
278
+ # valkey.hset("hash", "f1", "v1", "f2", "v2") # => 2
279
+ # valkey.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
280
+ #
281
+ # @param [String] key
282
+ # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
283
+ # @return [Integer] The number of fields that were added to the hash
284
+ def hset(key, *attrs)
285
+ attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
286
+
287
+ send_command(RequestType::HSET, [key, *attrs])
288
+ end
289
+
290
+ # Set the string value of a hash field, only if the field does not exist.
291
+ #
292
+ # @example
293
+ # valkey.hsetnx("hash", "field", "value")
294
+ # # => true
295
+ #
296
+ # @param [String] key
297
+ # @param [String] field
298
+ # @param [String] value
299
+ # @return [Boolean] whether the field was set or not
300
+ def hsetnx(key, field, value)
301
+ send_command(RequestType::HSET_NX, [key, field, value], &Utils::Boolify)
302
+ end
303
+
304
+ # Get the string length of the value associated with field in the hash stored at key.
305
+ #
306
+ # @example
307
+ # valkey.hstrlen("hash", "field")
308
+ # # => 5
309
+ #
310
+ # @param [String] key
311
+ # @param [String] field
312
+ # @return [Integer] the string length of the value associated with field, or 0 when field is not
313
+ # present in the hash or key does not exist
314
+ def hstrlen(key, field)
315
+ send_command(RequestType::HSTRLEN, [key, field])
316
+ end
317
+
318
+ # Get all the values in a hash.
319
+ #
320
+ # @example
321
+ # valkey.hvals("hash")
322
+ # # => ["value1", "value2"]
323
+ #
324
+ # @param [String] key
325
+ # @return [Array<String>] an array of values
326
+ def hvals(key)
327
+ send_command(RequestType::HVALS, [key])
328
+ end
329
+
330
+ # Set the string value of a hash field and set its expiration time in seconds.
331
+ #
332
+ # @example
333
+ # valkey.hsetex("hash", "field", "value", 60)
334
+ # # => 1
335
+ #
336
+ # @param [String] key
337
+ # @param [String] field
338
+ # @param [String] value
339
+ # @param [Integer] seconds expiration time in seconds
340
+ # @return [Integer] the number of fields that were added
341
+ def hsetex(key, field, value, seconds)
342
+ send_command(RequestType::HSETEX, [key, field, value, Integer(seconds)])
343
+ end
344
+
345
+ # Get the value of one or more hash fields and optionally set their expiration time.
346
+ #
347
+ # @example
348
+ # valkey.hgetex("hash", "field1", "field2", ex: 60)
349
+ # # => ["value1", "value2"]
350
+ #
351
+ # @param [String] key
352
+ # @param [String, Array<String>] fields one field, or array of fields
353
+ # @param [Hash] options
354
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
355
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
356
+ # - `:exat => Integer`: Set the specified Unix time at which the field will expire, in seconds.
357
+ # - `:pxat => Integer`: Set the specified Unix time at which the field will expire, in milliseconds.
358
+ # - `:persist => true`: Remove the time to live associated with the field.
359
+ # @return [String, Array<String, nil>] The value of the field for single field, or array of values
360
+ # for multiple fields. For every field that does not exist in the hash, a nil value is returned.
361
+ def hgetex(key, *fields, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
362
+ fields.flatten!(1)
363
+ args = [key, "FIELDS", fields.length, *fields]
364
+ args << "EX" << ex if ex
365
+ args << "PX" << px if px
366
+ args << "EXAT" << exat if exat
367
+ args << "PXAT" << pxat if pxat
368
+ args << "PERSIST" if persist
369
+
370
+ send_command(RequestType::HGETEX, args) do |reply|
371
+ fields.length == 1 ? reply[0] : reply
372
+ end
373
+ end
374
+
375
+ # Set a timeout on one or more hash fields.
376
+ #
377
+ # @example
378
+ # valkey.hexpire("hash", 60, "field1", "field2")
379
+ # # => [1, 1]
380
+ #
381
+ # @param [String] key
382
+ # @param [Integer] seconds time to live in seconds
383
+ # @param [String, Array<String>] fields one field, or array of fields
384
+ # @param [Hash] options
385
+ # - `:nx => true`: Set expiry only when the field has no expiry.
386
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
387
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
388
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
389
+ # @return [Array<Integer>] Array of results for each field.
390
+ # - `1` if the expiration time was successfully set for the field.
391
+ # - `0` if the specified condition was not met.
392
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
393
+ def hexpire(key, seconds, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
394
+ fields.flatten!(1)
395
+ args = [key, Integer(seconds), "FIELDS", fields.length, *fields]
396
+ args << "NX" if nx
397
+ args << "XX" if xx
398
+ args << "GT" if gt
399
+ args << "LT" if lt
400
+
401
+ send_command(RequestType::HEXPIRE, args)
402
+ end
403
+
404
+ # Set the expiration for one or more hash fields as a UNIX timestamp.
405
+ #
406
+ # @example
407
+ # valkey.hexpireat("hash", Time.now.to_i + 60, "field1", "field2")
408
+ # # => [1, 1]
409
+ #
410
+ # @param [String] key
411
+ # @param [Integer] unix_time expiry time specified as a UNIX timestamp in seconds
412
+ # @param [String, Array<String>] fields one field, or array of fields
413
+ # @param [Hash] options
414
+ # - `:nx => true`: Set expiry only when the field has no expiry.
415
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
416
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
417
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
418
+ # @return [Array<Integer>] Array of results for each field.
419
+ # - `1` if the expiration time was successfully set for the field.
420
+ # - `0` if the specified condition was not met.
421
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
422
+ def hexpireat(key, unix_time, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
423
+ fields.flatten!(1)
424
+ args = [key, Integer(unix_time), "FIELDS", fields.length, *fields]
425
+ args << "NX" if nx
426
+ args << "XX" if xx
427
+ args << "GT" if gt
428
+ args << "LT" if lt
429
+
430
+ send_command(RequestType::HEXPIREAT, args)
431
+ end
432
+
433
+ # Set a timeout on one or more hash fields in milliseconds.
434
+ #
435
+ # @example
436
+ # valkey.hpexpire("hash", 60000, "field1", "field2")
437
+ # # => [1, 1]
438
+ #
439
+ # @param [String] key
440
+ # @param [Integer] milliseconds time to live in milliseconds
441
+ # @param [String, Array<String>] fields one field, or array of fields
442
+ # @param [Hash] options
443
+ # - `:nx => true`: Set expiry only when the field has no expiry.
444
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
445
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
446
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
447
+ # @return [Array<Integer>] Array of results for each field.
448
+ # - `1` if the expiration time was successfully set for the field.
449
+ # - `0` if the specified condition was not met.
450
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
451
+ def hpexpire(key, milliseconds, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
452
+ fields.flatten!(1)
453
+ args = [key, Integer(milliseconds), "FIELDS", fields.length, *fields]
454
+ args << "NX" if nx
455
+ args << "XX" if xx
456
+ args << "GT" if gt
457
+ args << "LT" if lt
458
+
459
+ send_command(RequestType::HPEXPIRE, args)
460
+ end
461
+
462
+ # Set the expiration for one or more hash fields as a UNIX timestamp in milliseconds.
463
+ #
464
+ # @example
465
+ # valkey.hpexpireat("hash", (Time.now.to_i * 1000) + 60000, "field1", "field2")
466
+ # # => [1, 1]
467
+ #
468
+ # @param [String] key
469
+ # @param [Integer] unix_time_ms expiry time specified as a UNIX timestamp in milliseconds
470
+ # @param [String, Array<String>] fields one field, or array of fields
471
+ # @param [Hash] options
472
+ # - `:nx => true`: Set expiry only when the field has no expiry.
473
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
474
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
475
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
476
+ # @return [Array<Integer>] Array of results for each field.
477
+ # - `1` if the expiration time was successfully set for the field.
478
+ # - `0` if the specified condition was not met.
479
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
480
+ def hpexpireat(key, unix_time_ms, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
481
+ fields.flatten!(1)
482
+ args = [key, Integer(unix_time_ms), "FIELDS", fields.length, *fields]
483
+ args << "NX" if nx
484
+ args << "XX" if xx
485
+ args << "GT" if gt
486
+ args << "LT" if lt
487
+
488
+ send_command(RequestType::HPEXPIREAT, args)
489
+ end
490
+
491
+ # Remove the expiration from one or more hash fields.
492
+ #
493
+ # @example
494
+ # valkey.hpersist("hash", "field1", "field2")
495
+ # # => [1, 1]
496
+ #
497
+ # @param [String] key
498
+ # @param [String, Array<String>] fields one field, or array of fields
499
+ # @return [Array<Integer>] Array of results for each field.
500
+ # - `1` if the expiration time was successfully removed from the field.
501
+ # - `-1` if the field exists but has no expiration time.
502
+ # - `-2` if the field does not exist in the provided hash key, or the hash key does not exist.
503
+ def hpersist(key, *fields)
504
+ fields.flatten!(1)
505
+ args = [key, "FIELDS", fields.length, *fields]
506
+
507
+ send_command(RequestType::HPERSIST, args)
508
+ end
509
+
510
+ # Get the time to live in seconds of one or more hash fields.
511
+ #
512
+ # @example
513
+ # valkey.httl("hash", "field1", "field2")
514
+ # # => [60, -1]
515
+ #
516
+ # @param [String] key
517
+ # @param [String, Array<String>] fields one field, or array of fields
518
+ # @return [Array<Integer>] Array of TTLs in seconds for each field.
519
+ # - TTL in seconds if the field exists and has a timeout.
520
+ # - `-1` if the field exists but has no associated expire.
521
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
522
+ def httl(key, *fields)
523
+ fields.flatten!(1)
524
+ args = [key, "FIELDS", fields.length, *fields]
525
+
526
+ send_command(RequestType::HTTL, args)
527
+ end
528
+
529
+ # Get the time to live in milliseconds of one or more hash fields.
530
+ #
531
+ # @example
532
+ # valkey.hpttl("hash", "field1", "field2")
533
+ # # => [60000, -1]
534
+ #
535
+ # @param [String] key
536
+ # @param [String, Array<String>] fields one field, or array of fields
537
+ # @return [Array<Integer>] Array of TTLs in milliseconds for each field.
538
+ # - TTL in milliseconds if the field exists and has a timeout.
539
+ # - `-1` if the field exists but has no associated expire.
540
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
541
+ def hpttl(key, *fields)
542
+ fields.flatten!(1)
543
+ args = [key, "FIELDS", fields.length, *fields]
544
+
545
+ send_command(RequestType::HPTTL, args)
546
+ end
547
+
548
+ # Get the expiration Unix timestamp in seconds for one or more hash fields.
549
+ #
550
+ # @example
551
+ # valkey.hexpiretime("hash", "field1", "field2")
552
+ # # => [1234567890, -1]
553
+ #
554
+ # @param [String] key
555
+ # @param [String, Array<String>] fields one field, or array of fields
556
+ # @return [Array<Integer>] Array of expiration timestamps in seconds for each field.
557
+ # - Expiration Unix timestamp in seconds if the field exists and has a timeout.
558
+ # - `-1` if the field exists but has no associated expire.
559
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
560
+ def hexpiretime(key, *fields)
561
+ fields.flatten!(1)
562
+ args = [key, "FIELDS", fields.length, *fields]
563
+
564
+ send_command(RequestType::HEXPIRETIME, args)
565
+ end
566
+
567
+ # Get the expiration Unix timestamp in milliseconds for one or more hash fields.
568
+ #
569
+ # @example
570
+ # valkey.hpexpiretime("hash", "field1", "field2")
571
+ # # => [1234567890000, -1]
572
+ #
573
+ # @param [String] key
574
+ # @param [String, Array<String>] fields one field, or array of fields
575
+ # @return [Array<Integer>] Array of expiration timestamps in milliseconds for each field.
576
+ # - Expiration Unix timestamp in milliseconds if the field exists and has a timeout.
577
+ # - `-1` if the field exists but has no associated expire.
578
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
579
+ def hpexpiretime(key, *fields)
580
+ fields.flatten!(1)
581
+ args = [key, "FIELDS", fields.length, *fields]
582
+
583
+ send_command(RequestType::HPEXPIRETIME, args)
584
+ end
585
+ end
586
+ end
587
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ module HyperLogLogCommands
6
+ # Add one or more members to a HyperLogLog structure.
7
+ #
8
+ # @param [String] key
9
+ # @param [String, Array<String>] member one member, or array of members
10
+ # @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
11
+ #
12
+ # @see https://valkey.io/commands/pfadd/
13
+ def pfadd(key, member)
14
+ args = [key]
15
+
16
+ if member.is_a?(Array)
17
+ args += member
18
+ else
19
+ args << member
20
+ end
21
+
22
+ send_command(RequestType::PFADD, args)
23
+ end
24
+
25
+ # Get the approximate cardinality of members added to HyperLogLog structure.
26
+ #
27
+ # If called with multiple keys, returns the approximate cardinality of the
28
+ # union of the HyperLogLogs contained in the keys.
29
+ #
30
+ # @param [String, Array<String>] keys
31
+ # @return [Integer]
32
+ #
33
+ # @see https://valkey.io/commands/pfcount
34
+ def pfcount(*keys)
35
+ send_command(RequestType::PFCOUNT, keys.flatten(1))
36
+ end
37
+
38
+ # Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
39
+ # the observed Sets of the source HyperLogLog structures.
40
+ #
41
+ # @param [String] dest_key destination key
42
+ # @param [String, Array<String>] source_key source key, or array of keys
43
+ # @return [Boolean]
44
+ #
45
+ # @see https://valkey.io/commands/pfmerge
46
+ def pfmerge(dest_key, *source_key)
47
+ send_command(RequestType::PFMERGE, [dest_key, *source_key], &Utils::BoolifySet)
48
+ end
49
+ end
50
+ end
51
+ end