valkey-rb 0.3.5

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 +43 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/README.md +34 -0
  5. data/Rakefile +23 -0
  6. data/lib/valkey/bindings.rb +173 -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 +454 -0
  12. data/lib/valkey/commands/geo_commands.rb +87 -0
  13. data/lib/valkey/commands/hash_commands.rb +586 -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 +217 -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 +756 -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 +69 -0
  28. data/lib/valkey/errors.rb +41 -0
  29. data/lib/valkey/libglide_ffi.dylib +0 -0
  30. data/lib/valkey/libglide_ffi.so +0 -0
  31. data/lib/valkey/pipeline.rb +20 -0
  32. data/lib/valkey/protobuf/command_request_pb.rb +30 -0
  33. data/lib/valkey/protobuf/connection_request_pb.rb +28 -0
  34. data/lib/valkey/protobuf/response_pb.rb +18 -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 +477 -0
  42. metadata +119 -0
@@ -0,0 +1,586 @@
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
+ Hash[fields.zip(reply)]
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 the string value of a hash field.
276
+ #
277
+ # @example
278
+ # valkey.hset("hash", "field", "value") # => true
279
+ #
280
+ # @param [String] key
281
+ # @param [String] field
282
+ # @param [String] value
283
+ # @return [Boolean] true if field is a new field in the hash and value was set, false if field already exists and the value was updated
284
+ def hset(key, field, value)
285
+ result = send_command(RequestType::HSET, [key, field, value])
286
+ result == 1
287
+ end
288
+
289
+ # Set the string value of a hash field, only if the field does not exist.
290
+ #
291
+ # @example
292
+ # valkey.hsetnx("hash", "field", "value")
293
+ # # => true
294
+ #
295
+ # @param [String] key
296
+ # @param [String] field
297
+ # @param [String] value
298
+ # @return [Boolean] whether the field was set or not
299
+ def hsetnx(key, field, value)
300
+ send_command(RequestType::HSET_NX, [key, field, value], &Utils::Boolify)
301
+ end
302
+
303
+ # Get the string length of the value associated with field in the hash stored at key.
304
+ #
305
+ # @example
306
+ # valkey.hstrlen("hash", "field")
307
+ # # => 5
308
+ #
309
+ # @param [String] key
310
+ # @param [String] field
311
+ # @return [Integer] the string length of the value associated with field, or 0 when field is not
312
+ # present in the hash or key does not exist
313
+ def hstrlen(key, field)
314
+ send_command(RequestType::HSTRLEN, [key, field])
315
+ end
316
+
317
+ # Get all the values in a hash.
318
+ #
319
+ # @example
320
+ # valkey.hvals("hash")
321
+ # # => ["value1", "value2"]
322
+ #
323
+ # @param [String] key
324
+ # @return [Array<String>] an array of values
325
+ def hvals(key)
326
+ send_command(RequestType::HVALS, [key])
327
+ end
328
+
329
+ # Set the string value of a hash field and set its expiration time in seconds.
330
+ #
331
+ # @example
332
+ # valkey.hsetex("hash", "field", "value", 60)
333
+ # # => 1
334
+ #
335
+ # @param [String] key
336
+ # @param [String] field
337
+ # @param [String] value
338
+ # @param [Integer] seconds expiration time in seconds
339
+ # @return [Integer] the number of fields that were added
340
+ def hsetex(key, field, value, seconds)
341
+ send_command(RequestType::HSETEX, [key, field, value, Integer(seconds)])
342
+ end
343
+
344
+ # Get the value of one or more hash fields and optionally set their expiration time.
345
+ #
346
+ # @example
347
+ # valkey.hgetex("hash", "field1", "field2", ex: 60)
348
+ # # => ["value1", "value2"]
349
+ #
350
+ # @param [String] key
351
+ # @param [String, Array<String>] fields one field, or array of fields
352
+ # @param [Hash] options
353
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
354
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
355
+ # - `:exat => Integer`: Set the specified Unix time at which the field will expire, in seconds.
356
+ # - `:pxat => Integer`: Set the specified Unix time at which the field will expire, in milliseconds.
357
+ # - `:persist => true`: Remove the time to live associated with the field.
358
+ # @return [String, Array<String, nil>] The value of the field for single field, or array of values
359
+ # for multiple fields. For every field that does not exist in the hash, a nil value is returned.
360
+ def hgetex(key, *fields, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
361
+ fields.flatten!(1)
362
+ args = [key, "FIELDS", fields.length, *fields]
363
+ args << "EX" << ex if ex
364
+ args << "PX" << px if px
365
+ args << "EXAT" << exat if exat
366
+ args << "PXAT" << pxat if pxat
367
+ args << "PERSIST" if persist
368
+
369
+ send_command(RequestType::HGETEX, args) do |reply|
370
+ fields.length == 1 ? reply[0] : reply
371
+ end
372
+ end
373
+
374
+ # Set a timeout on one or more hash fields.
375
+ #
376
+ # @example
377
+ # valkey.hexpire("hash", 60, "field1", "field2")
378
+ # # => [1, 1]
379
+ #
380
+ # @param [String] key
381
+ # @param [Integer] seconds time to live in seconds
382
+ # @param [String, Array<String>] fields one field, or array of fields
383
+ # @param [Hash] options
384
+ # - `:nx => true`: Set expiry only when the field has no expiry.
385
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
386
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
387
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
388
+ # @return [Array<Integer>] Array of results for each field.
389
+ # - `1` if the expiration time was successfully set for the field.
390
+ # - `0` if the specified condition was not met.
391
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
392
+ def hexpire(key, seconds, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
393
+ fields.flatten!(1)
394
+ args = [key, Integer(seconds), "FIELDS", fields.length, *fields]
395
+ args << "NX" if nx
396
+ args << "XX" if xx
397
+ args << "GT" if gt
398
+ args << "LT" if lt
399
+
400
+ send_command(RequestType::HEXPIRE, args)
401
+ end
402
+
403
+ # Set the expiration for one or more hash fields as a UNIX timestamp.
404
+ #
405
+ # @example
406
+ # valkey.hexpireat("hash", Time.now.to_i + 60, "field1", "field2")
407
+ # # => [1, 1]
408
+ #
409
+ # @param [String] key
410
+ # @param [Integer] unix_time expiry time specified as a UNIX timestamp in seconds
411
+ # @param [String, Array<String>] fields one field, or array of fields
412
+ # @param [Hash] options
413
+ # - `:nx => true`: Set expiry only when the field has no expiry.
414
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
415
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
416
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
417
+ # @return [Array<Integer>] Array of results for each field.
418
+ # - `1` if the expiration time was successfully set for the field.
419
+ # - `0` if the specified condition was not met.
420
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
421
+ def hexpireat(key, unix_time, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
422
+ fields.flatten!(1)
423
+ args = [key, Integer(unix_time), "FIELDS", fields.length, *fields]
424
+ args << "NX" if nx
425
+ args << "XX" if xx
426
+ args << "GT" if gt
427
+ args << "LT" if lt
428
+
429
+ send_command(RequestType::HEXPIREAT, args)
430
+ end
431
+
432
+ # Set a timeout on one or more hash fields in milliseconds.
433
+ #
434
+ # @example
435
+ # valkey.hpexpire("hash", 60000, "field1", "field2")
436
+ # # => [1, 1]
437
+ #
438
+ # @param [String] key
439
+ # @param [Integer] milliseconds time to live in milliseconds
440
+ # @param [String, Array<String>] fields one field, or array of fields
441
+ # @param [Hash] options
442
+ # - `:nx => true`: Set expiry only when the field has no expiry.
443
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
444
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
445
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
446
+ # @return [Array<Integer>] Array of results for each field.
447
+ # - `1` if the expiration time was successfully set for the field.
448
+ # - `0` if the specified condition was not met.
449
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
450
+ def hpexpire(key, milliseconds, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
451
+ fields.flatten!(1)
452
+ args = [key, Integer(milliseconds), "FIELDS", fields.length, *fields]
453
+ args << "NX" if nx
454
+ args << "XX" if xx
455
+ args << "GT" if gt
456
+ args << "LT" if lt
457
+
458
+ send_command(RequestType::HPEXPIRE, args)
459
+ end
460
+
461
+ # Set the expiration for one or more hash fields as a UNIX timestamp in milliseconds.
462
+ #
463
+ # @example
464
+ # valkey.hpexpireat("hash", (Time.now.to_i * 1000) + 60000, "field1", "field2")
465
+ # # => [1, 1]
466
+ #
467
+ # @param [String] key
468
+ # @param [Integer] unix_time_ms expiry time specified as a UNIX timestamp in milliseconds
469
+ # @param [String, Array<String>] fields one field, or array of fields
470
+ # @param [Hash] options
471
+ # - `:nx => true`: Set expiry only when the field has no expiry.
472
+ # - `:xx => true`: Set expiry only when the field has an existing expiry.
473
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
474
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
475
+ # @return [Array<Integer>] Array of results for each field.
476
+ # - `1` if the expiration time was successfully set for the field.
477
+ # - `0` if the specified condition was not met.
478
+ # - `-2` if the field does not exist in the HASH, or key does not exist.
479
+ def hpexpireat(key, unix_time_ms, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
480
+ fields.flatten!(1)
481
+ args = [key, Integer(unix_time_ms), "FIELDS", fields.length, *fields]
482
+ args << "NX" if nx
483
+ args << "XX" if xx
484
+ args << "GT" if gt
485
+ args << "LT" if lt
486
+
487
+ send_command(RequestType::HPEXPIREAT, args)
488
+ end
489
+
490
+ # Remove the expiration from one or more hash fields.
491
+ #
492
+ # @example
493
+ # valkey.hpersist("hash", "field1", "field2")
494
+ # # => [1, 1]
495
+ #
496
+ # @param [String] key
497
+ # @param [String, Array<String>] fields one field, or array of fields
498
+ # @return [Array<Integer>] Array of results for each field.
499
+ # - `1` if the expiration time was successfully removed from the field.
500
+ # - `-1` if the field exists but has no expiration time.
501
+ # - `-2` if the field does not exist in the provided hash key, or the hash key does not exist.
502
+ def hpersist(key, *fields)
503
+ fields.flatten!(1)
504
+ args = [key, "FIELDS", fields.length, *fields]
505
+
506
+ send_command(RequestType::HPERSIST, args)
507
+ end
508
+
509
+ # Get the time to live in seconds of one or more hash fields.
510
+ #
511
+ # @example
512
+ # valkey.httl("hash", "field1", "field2")
513
+ # # => [60, -1]
514
+ #
515
+ # @param [String] key
516
+ # @param [String, Array<String>] fields one field, or array of fields
517
+ # @return [Array<Integer>] Array of TTLs in seconds for each field.
518
+ # - TTL in seconds if the field exists and has a timeout.
519
+ # - `-1` if the field exists but has no associated expire.
520
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
521
+ def httl(key, *fields)
522
+ fields.flatten!(1)
523
+ args = [key, "FIELDS", fields.length, *fields]
524
+
525
+ send_command(RequestType::HTTL, args)
526
+ end
527
+
528
+ # Get the time to live in milliseconds of one or more hash fields.
529
+ #
530
+ # @example
531
+ # valkey.hpttl("hash", "field1", "field2")
532
+ # # => [60000, -1]
533
+ #
534
+ # @param [String] key
535
+ # @param [String, Array<String>] fields one field, or array of fields
536
+ # @return [Array<Integer>] Array of TTLs in milliseconds for each field.
537
+ # - TTL in milliseconds if the field exists and has a timeout.
538
+ # - `-1` if the field exists but has no associated expire.
539
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
540
+ def hpttl(key, *fields)
541
+ fields.flatten!(1)
542
+ args = [key, "FIELDS", fields.length, *fields]
543
+
544
+ send_command(RequestType::HPTTL, args)
545
+ end
546
+
547
+ # Get the expiration Unix timestamp in seconds for one or more hash fields.
548
+ #
549
+ # @example
550
+ # valkey.hexpiretime("hash", "field1", "field2")
551
+ # # => [1234567890, -1]
552
+ #
553
+ # @param [String] key
554
+ # @param [String, Array<String>] fields one field, or array of fields
555
+ # @return [Array<Integer>] Array of expiration timestamps in seconds for each field.
556
+ # - Expiration Unix timestamp in seconds if the field exists and has a timeout.
557
+ # - `-1` if the field exists but has no associated expire.
558
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
559
+ def hexpiretime(key, *fields)
560
+ fields.flatten!(1)
561
+ args = [key, "FIELDS", fields.length, *fields]
562
+
563
+ send_command(RequestType::HEXPIRETIME, args)
564
+ end
565
+
566
+ # Get the expiration Unix timestamp in milliseconds for one or more hash fields.
567
+ #
568
+ # @example
569
+ # valkey.hpexpiretime("hash", "field1", "field2")
570
+ # # => [1234567890000, -1]
571
+ #
572
+ # @param [String] key
573
+ # @param [String, Array<String>] fields one field, or array of fields
574
+ # @return [Array<Integer>] Array of expiration timestamps in milliseconds for each field.
575
+ # - Expiration Unix timestamp in milliseconds if the field exists and has a timeout.
576
+ # - `-1` if the field exists but has no associated expire.
577
+ # - `-2` if the field does not exist in the provided hash key, or the hash key is empty.
578
+ def hpexpiretime(key, *fields)
579
+ fields.flatten!(1)
580
+ args = [key, "FIELDS", fields.length, *fields]
581
+
582
+ send_command(RequestType::HPEXPIRETIME, args)
583
+ end
584
+ end
585
+ end
586
+ 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