redis 5.4.1 → 6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -1
- data/README.md +165 -12
- data/lib/redis/client.rb +42 -10
- data/lib/redis/commands/geo.rb +108 -6
- data/lib/redis/commands/hashes.rb +166 -2
- data/lib/redis/commands/keys.rb +5 -1
- data/lib/redis/commands/lists.rb +93 -0
- data/lib/redis/commands/modules/json.rb +530 -0
- data/lib/redis/commands/modules/search/aggregation.rb +418 -0
- data/lib/redis/commands/modules/search/dialect.rb +14 -0
- data/lib/redis/commands/modules/search/field.rb +306 -0
- data/lib/redis/commands/modules/search/hybrid.rb +359 -0
- data/lib/redis/commands/modules/search/index.rb +351 -0
- data/lib/redis/commands/modules/search/index_definition.rb +114 -0
- data/lib/redis/commands/modules/search/miscellaneous.rb +607 -0
- data/lib/redis/commands/modules/search/query.rb +738 -0
- data/lib/redis/commands/modules/search/result.rb +488 -0
- data/lib/redis/commands/modules/search/schema.rb +211 -0
- data/lib/redis/commands/modules/search.rb +19 -0
- data/lib/redis/commands/sets.rb +41 -0
- data/lib/redis/commands/sorted_sets.rb +0 -1
- data/lib/redis/commands/streams.rb +28 -8
- data/lib/redis/commands.rb +30 -10
- data/lib/redis/distributed.rb +271 -1
- data/lib/redis/lib_identity.rb +105 -0
- data/lib/redis/subscribe.rb +1 -1
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +141 -11
- metadata +22 -9
|
@@ -220,13 +220,20 @@ class Redis
|
|
|
220
220
|
# @param [Hash] options
|
|
221
221
|
# - `:match => String`: only return keys matching the pattern
|
|
222
222
|
# - `:count => Integer`: return count keys at most per iteration
|
|
223
|
+
# - `:novalues => Boolean`: whether or not to include values in the output (default: false)
|
|
223
224
|
#
|
|
224
|
-
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
|
225
|
+
# @return [String, Array<[String, String]>, Array<String>] the next cursor and all found keys
|
|
226
|
+
# - when `:novalues` is false: [cursor, [[field1, value1], [field2, value2], ...]]
|
|
227
|
+
# - when `:novalues` is true: [cursor, [field1, field2, ...]]
|
|
225
228
|
#
|
|
226
229
|
# See the [Redis Server HSCAN documentation](https://redis.io/docs/latest/commands/hscan/) for further details
|
|
227
230
|
def hscan(key, cursor, **options)
|
|
228
231
|
_scan(:hscan, cursor, [key], **options) do |reply|
|
|
229
|
-
|
|
232
|
+
if options[:novalues]
|
|
233
|
+
reply
|
|
234
|
+
else
|
|
235
|
+
[reply[0], reply[1].each_slice(2).to_a]
|
|
236
|
+
end
|
|
230
237
|
end
|
|
231
238
|
end
|
|
232
239
|
|
|
@@ -239,6 +246,7 @@ class Redis
|
|
|
239
246
|
# @param [Hash] options
|
|
240
247
|
# - `:match => String`: only return keys matching the pattern
|
|
241
248
|
# - `:count => Integer`: return count keys at most per iteration
|
|
249
|
+
# - `:novalues => Boolean`: whether or not to include values in the output (default: false)
|
|
242
250
|
#
|
|
243
251
|
# @return [Enumerator] an enumerator for all found keys
|
|
244
252
|
#
|
|
@@ -253,6 +261,162 @@ class Redis
|
|
|
253
261
|
break if cursor == "0"
|
|
254
262
|
end
|
|
255
263
|
end
|
|
264
|
+
|
|
265
|
+
# Sets the time to live in seconds for one or more fields.
|
|
266
|
+
#
|
|
267
|
+
# @example
|
|
268
|
+
# redis.hset("hash", "f1", "v1")
|
|
269
|
+
# redis.hexpire("hash", 10, "f1", "f2") # => [1, -2]
|
|
270
|
+
#
|
|
271
|
+
# @param [String] key
|
|
272
|
+
# @param [Integer] ttl
|
|
273
|
+
# @param [Array<String>] fields
|
|
274
|
+
# @return [Array<Integer>] Feedback on if the fields have been updated.
|
|
275
|
+
#
|
|
276
|
+
# See https://redis.io/docs/latest/commands/hexpire/#return-information for array reply.
|
|
277
|
+
def hexpire(key, ttl, *fields)
|
|
278
|
+
send_command([:hexpire, key, ttl, 'FIELDS', fields.length, *fields])
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Returns the time to live in seconds for one or more fields.
|
|
282
|
+
#
|
|
283
|
+
# @example
|
|
284
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2")
|
|
285
|
+
# redis.hexpire("hash", 10, "f1") # => [1]
|
|
286
|
+
# redis.httl("hash", "f1", "f2", "f3") # => [10, -1, -2]
|
|
287
|
+
#
|
|
288
|
+
# @param [String] key
|
|
289
|
+
# @param [Array<String>] fields
|
|
290
|
+
# @return [Array<Integer>] Feedback on the TTL of the fields.
|
|
291
|
+
#
|
|
292
|
+
# See https://redis.io/docs/latest/commands/httl/#return-information for array reply.
|
|
293
|
+
def httl(key, *fields)
|
|
294
|
+
send_command([:httl, key, 'FIELDS', fields.length, *fields])
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Sets the time to live in milliseconds for one or more fields.
|
|
298
|
+
#
|
|
299
|
+
# @example
|
|
300
|
+
# redis.hset("hash", "f1", "v1")
|
|
301
|
+
# redis.hpexpire("hash", 500, "f1", "f2") # => [1, -2]
|
|
302
|
+
# redis.hpexpire("hash", 500, "f1", "f2", nx: true) # => [0, -2]
|
|
303
|
+
#
|
|
304
|
+
# @param [String] key
|
|
305
|
+
# @param [Integer] ttl
|
|
306
|
+
# @param [Hash] options
|
|
307
|
+
# - `:nx => true`: Set expiry only when the key has no expiry.
|
|
308
|
+
# - `:xx => true`: Set expiry only when the key has an existing expiry.
|
|
309
|
+
# - `:gt => true`: Set expiry only when the new expiry is greater than current one.
|
|
310
|
+
# - `:lt => true`: Set expiry only when the new expiry is less than current one.
|
|
311
|
+
# @param [Array<String>] fields
|
|
312
|
+
# @return [Array<Integer>] Feedback on if the fields have been updated.
|
|
313
|
+
#
|
|
314
|
+
# See https://redis.io/docs/latest/commands/hpexpire/#return-information for array reply.
|
|
315
|
+
def hpexpire(key, ttl, *fields, nx: nil, xx: nil, gt: nil, lt: nil)
|
|
316
|
+
args = [:hpexpire, key, ttl]
|
|
317
|
+
args << "NX" if nx
|
|
318
|
+
args << "XX" if xx
|
|
319
|
+
args << "GT" if gt
|
|
320
|
+
args << "LT" if lt
|
|
321
|
+
args.concat(['FIELDS', fields.length, *fields])
|
|
322
|
+
|
|
323
|
+
send_command(args)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Returns the time to live in milliseconds for one or more fields.
|
|
327
|
+
#
|
|
328
|
+
# @example
|
|
329
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2")
|
|
330
|
+
# redis.hpexpire("hash", 500, "f1") # => [1]
|
|
331
|
+
# redis.hpttl("hash", "f1", "f2", "f3") # => [500, -1, -2]
|
|
332
|
+
#
|
|
333
|
+
# @param [String] key
|
|
334
|
+
# @param [Array<String>] fields
|
|
335
|
+
# @return [Array<Integer>] Feedback on the TTL of the fields.
|
|
336
|
+
#
|
|
337
|
+
# See https://redis.io/docs/latest/commands/hpttl/#return-information for array reply.
|
|
338
|
+
def hpttl(key, *fields)
|
|
339
|
+
send_command([:hpttl, key, 'FIELDS', fields.length, *fields])
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Register an ordered list of hash field names under +fieldset_name+ for
|
|
343
|
+
# use by subsequent #himport_set calls on the same connection.
|
|
344
|
+
#
|
|
345
|
+
# Fieldsets are server-side session state scoped to the current physical
|
|
346
|
+
# connection: they vanish on disconnect or RESET and are invisible to
|
|
347
|
+
# other connections. See the README "Bulk hash ingestion (HIMPORT)"
|
|
348
|
+
# section for connection-scoping guidance. Re-preparing an existing
|
|
349
|
+
# +fieldset_name+ silently replaces it. Field order is preserved as
|
|
350
|
+
# given; it defines the positional pairing used by #himport_set.
|
|
351
|
+
#
|
|
352
|
+
# @note HIMPORT support is experimental: the client API may change in a
|
|
353
|
+
# future minor release without a major version bump.
|
|
354
|
+
#
|
|
355
|
+
# @example
|
|
356
|
+
# redis.himport_prepare("shared", ["name", "email", "age"])
|
|
357
|
+
# # => "OK"
|
|
358
|
+
#
|
|
359
|
+
# @param [String] fieldset_name name used by later SET and DISCARD calls
|
|
360
|
+
# @param [String, Array<String>] fields one or more field names
|
|
361
|
+
# @return [String] `"OK"`
|
|
362
|
+
# @api experimental
|
|
363
|
+
def himport_prepare(fieldset_name, *fields)
|
|
364
|
+
fields.flatten!(1)
|
|
365
|
+
raise ArgumentError, "fields must not be empty" if fields.empty?
|
|
366
|
+
|
|
367
|
+
send_command([:himport, "PREPARE", fieldset_name].concat(fields))
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Create or fully replace the hash at +key+ using the field list
|
|
371
|
+
# registered under +fieldset_name+ on this connection. Values pair
|
|
372
|
+
# positionally with the fields given to #himport_prepare; the value
|
|
373
|
+
# count must equal the field count.
|
|
374
|
+
#
|
|
375
|
+
# The fieldset must exist on the executing connection, otherwise the
|
|
376
|
+
# server replies with a "no such fieldset" error.
|
|
377
|
+
#
|
|
378
|
+
# @note HIMPORT support is experimental: the client API may change in a
|
|
379
|
+
# future minor release without a major version bump.
|
|
380
|
+
#
|
|
381
|
+
# @example
|
|
382
|
+
# redis.himport_set("shared:1", "shared", ["alice", "alice@example.com", "25"])
|
|
383
|
+
# # => "OK"
|
|
384
|
+
#
|
|
385
|
+
# @param [String] key hash key to create or overwrite
|
|
386
|
+
# @param [String] fieldset_name fieldset previously prepared on this connection
|
|
387
|
+
# @param [String, Array<String>] values one or more values, order preserved
|
|
388
|
+
# @return [String] `"OK"`
|
|
389
|
+
# @api experimental
|
|
390
|
+
def himport_set(key, fieldset_name, *values)
|
|
391
|
+
values.flatten!(1)
|
|
392
|
+
raise ArgumentError, "values must not be empty" if values.empty?
|
|
393
|
+
|
|
394
|
+
send_command([:himport, "SET", key, fieldset_name].concat(values))
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Remove +fieldset_name+ from this connection's session. Keys already
|
|
398
|
+
# written through the fieldset are not affected.
|
|
399
|
+
#
|
|
400
|
+
# @note HIMPORT support is experimental: the client API may change in a
|
|
401
|
+
# future minor release without a major version bump.
|
|
402
|
+
#
|
|
403
|
+
# @param [String] fieldset_name
|
|
404
|
+
# @return [Integer] `1` if the fieldset was removed, `0` if it did not exist
|
|
405
|
+
# @api experimental
|
|
406
|
+
def himport_discard(fieldset_name)
|
|
407
|
+
send_command([:himport, "DISCARD", fieldset_name])
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Remove all fieldsets from this connection's session.
|
|
411
|
+
#
|
|
412
|
+
# @note HIMPORT support is experimental: the client API may change in a
|
|
413
|
+
# future minor release without a major version bump.
|
|
414
|
+
#
|
|
415
|
+
# @return [Integer] number of fieldsets removed
|
|
416
|
+
# @api experimental
|
|
417
|
+
def himport_discard_all
|
|
418
|
+
send_command([:himport, "DISCARDALL"])
|
|
419
|
+
end
|
|
256
420
|
end
|
|
257
421
|
end
|
|
258
422
|
end
|
data/lib/redis/commands/keys.rb
CHANGED
|
@@ -261,6 +261,9 @@ class Redis
|
|
|
261
261
|
# @param [String, Array<String>] keys
|
|
262
262
|
# @return [Integer] number of keys that were unlinked
|
|
263
263
|
def unlink(*keys)
|
|
264
|
+
keys.flatten!(1)
|
|
265
|
+
return 0 if keys.empty?
|
|
266
|
+
|
|
264
267
|
send_command([:unlink] + keys)
|
|
265
268
|
end
|
|
266
269
|
|
|
@@ -444,13 +447,14 @@ class Redis
|
|
|
444
447
|
|
|
445
448
|
private
|
|
446
449
|
|
|
447
|
-
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
|
450
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, novalues: false, &block)
|
|
448
451
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
|
449
452
|
|
|
450
453
|
args << cursor
|
|
451
454
|
args << "MATCH" << match if match
|
|
452
455
|
args << "COUNT" << Integer(count) if count
|
|
453
456
|
args << "TYPE" << type if type
|
|
457
|
+
args << "NOVALUES" if novalues
|
|
454
458
|
|
|
455
459
|
send_command([command] + args, &block)
|
|
456
460
|
end
|
data/lib/redis/commands/lists.rb
CHANGED
|
@@ -30,6 +30,84 @@ class Redis
|
|
|
30
30
|
send_command([:lmove, source, destination, where_source, where_destination])
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# Remove multiple elements from the head/tail of a list, append/prepend
|
|
34
|
+
# them to another list and return them.
|
|
35
|
+
#
|
|
36
|
+
# @example Move a single element (still an array reply)
|
|
37
|
+
# redis.lmovem("foo", "bar", "LEFT", "LEFT")
|
|
38
|
+
# # => ["s1"]
|
|
39
|
+
# @example Move up to 3 elements, pushed one-by-one (block order reversed)
|
|
40
|
+
# redis.lmovem("foo", "bar", "LEFT", "LEFT", count: 3, order: "OBO")
|
|
41
|
+
# # => ["s3", "s2", "s1"]
|
|
42
|
+
# @example Move exactly 2 elements, preserving their relative order
|
|
43
|
+
# redis.lmovem("foo", "bar", "LEFT", "RIGHT", exactly: 2, order: "BULK")
|
|
44
|
+
# # => ["s1", "s2"]
|
|
45
|
+
#
|
|
46
|
+
# @param [String] source source key
|
|
47
|
+
# @param [String] destination destination key
|
|
48
|
+
# @param [String, Symbol] where_source from where to remove elements from the source list
|
|
49
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
|
50
|
+
# @param [String, Symbol] where_destination where to push elements to the destination list
|
|
51
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
|
52
|
+
# @param [Integer] count move up to `count` elements, fewer when the source holds fewer
|
|
53
|
+
# @param [Integer] exactly move exactly `exactly` elements; when the source holds fewer,
|
|
54
|
+
# nothing is moved
|
|
55
|
+
# @param [String, Symbol] order ordering at the destination, required together with
|
|
56
|
+
# `count` or `exactly`:
|
|
57
|
+
# - when `'OBO'` - push each element as popped, so the moved block order is reversed
|
|
58
|
+
# - when `'BULK'` - preserve the original relative order of the moved elements
|
|
59
|
+
#
|
|
60
|
+
# @return [nil, Array<String>] the moved elements in destination order, or nil when
|
|
61
|
+
# nothing was moved
|
|
62
|
+
def lmovem(source, destination, where_source, where_destination, count: nil, exactly: nil, order: nil)
|
|
63
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
|
64
|
+
|
|
65
|
+
args = [:lmovem, source, destination, where_source, where_destination]
|
|
66
|
+
args.concat(_movem_amount_args(count, exactly, order))
|
|
67
|
+
|
|
68
|
+
send_command(args)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Remove multiple elements from the head/tail of a list, append/prepend
|
|
72
|
+
# them to another list and return them; or block until the request can
|
|
73
|
+
# be satisfied or the timeout expires.
|
|
74
|
+
#
|
|
75
|
+
# @example Move up to 3 elements as soon as any are available
|
|
76
|
+
# redis.blmovem("foo", "bar", "LEFT", "LEFT", timeout: 1, count: 3, order: "BULK")
|
|
77
|
+
# # => ["s1", "s2", "s3"]
|
|
78
|
+
# @example Block until the source holds at least 2 elements
|
|
79
|
+
# redis.blmovem("foo", "bar", "LEFT", "RIGHT", timeout: 1, exactly: 2, order: "OBO")
|
|
80
|
+
# # => nil on timeout
|
|
81
|
+
#
|
|
82
|
+
# @param [String] source source key
|
|
83
|
+
# @param [String] destination destination key
|
|
84
|
+
# @param [String, Symbol] where_source from where to remove elements from the source list
|
|
85
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
|
86
|
+
# @param [String, Symbol] where_destination where to push elements to the destination list
|
|
87
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
|
88
|
+
# @param [Float, Integer] timeout seconds to block, 0 blocks indefinitely
|
|
89
|
+
# @param [Integer] count move up to `count` elements as soon as at least one is available
|
|
90
|
+
# @param [Integer] exactly move exactly `exactly` elements, blocking until the source
|
|
91
|
+
# holds that many
|
|
92
|
+
# @param [String, Symbol] order ordering at the destination, required together with
|
|
93
|
+
# `count` or `exactly`:
|
|
94
|
+
# - when `'OBO'` - push each element as popped, so the moved block order is reversed
|
|
95
|
+
# - when `'BULK'` - preserve the original relative order of the moved elements
|
|
96
|
+
#
|
|
97
|
+
# @return [nil, Array<String>] the moved elements in destination order, or nil when the
|
|
98
|
+
# timeout expired and nothing was moved
|
|
99
|
+
#
|
|
100
|
+
# @see #lmovem
|
|
101
|
+
def blmovem(source, destination, where_source, where_destination, timeout: 0, count: nil, exactly: nil,
|
|
102
|
+
order: nil)
|
|
103
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
|
104
|
+
|
|
105
|
+
command = [:blmovem, source, destination, where_source, where_destination, timeout]
|
|
106
|
+
command.concat(_movem_amount_args(count, exactly, order))
|
|
107
|
+
|
|
108
|
+
send_blocking_command(command, timeout)
|
|
109
|
+
end
|
|
110
|
+
|
|
33
111
|
# Remove the first/last element in a list and append/prepend it
|
|
34
112
|
# to another list and return it, or block until one is available.
|
|
35
113
|
#
|
|
@@ -320,6 +398,21 @@ class Redis
|
|
|
320
398
|
send_blocking_command(command, timeout, &blk)
|
|
321
399
|
end
|
|
322
400
|
|
|
401
|
+
def _movem_amount_args(count, exactly, order)
|
|
402
|
+
raise ArgumentError, "Pick either count or exactly, not both" if count && exactly
|
|
403
|
+
|
|
404
|
+
if count || exactly
|
|
405
|
+
order = order.to_s.upcase
|
|
406
|
+
raise ArgumentError, "order must be 'OBO' or 'BULK'" if order != "OBO" && order != "BULK"
|
|
407
|
+
|
|
408
|
+
[count ? "COUNT" : "EXACTLY", Integer(count || exactly), order]
|
|
409
|
+
elsif order
|
|
410
|
+
raise ArgumentError, "order requires count or exactly"
|
|
411
|
+
else
|
|
412
|
+
[]
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
323
416
|
def _normalize_move_wheres(where_source, where_destination)
|
|
324
417
|
where_source = where_source.to_s.upcase
|
|
325
418
|
where_destination = where_destination.to_s.upcase
|