redis 4.4.0 → 5.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +150 -0
  3. data/README.md +95 -160
  4. data/lib/redis/client.rb +84 -608
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +339 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +884 -0
  18. data/lib/redis/commands/streams.rb +402 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +237 -0
  22. data/lib/redis/distributed.rb +208 -70
  23. data/lib/redis/errors.rb +15 -41
  24. data/lib/redis/hash_ring.rb +26 -26
  25. data/lib/redis/pipeline.rb +66 -120
  26. data/lib/redis/subscribe.rb +23 -15
  27. data/lib/redis/version.rb +1 -1
  28. data/lib/redis.rb +109 -3546
  29. metadata +27 -54
  30. data/lib/redis/cluster/command.rb +0 -81
  31. data/lib/redis/cluster/command_loader.rb +0 -34
  32. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  33. data/lib/redis/cluster/node.rb +0 -108
  34. data/lib/redis/cluster/node_key.rb +0 -31
  35. data/lib/redis/cluster/node_loader.rb +0 -37
  36. data/lib/redis/cluster/option.rb +0 -93
  37. data/lib/redis/cluster/slot.rb +0 -86
  38. data/lib/redis/cluster/slot_loader.rb +0 -49
  39. data/lib/redis/cluster.rb +0 -291
  40. data/lib/redis/connection/command_helper.rb +0 -39
  41. data/lib/redis/connection/hiredis.rb +0 -67
  42. data/lib/redis/connection/registry.rb +0 -13
  43. data/lib/redis/connection/ruby.rb +0 -427
  44. data/lib/redis/connection/synchrony.rb +0 -146
  45. data/lib/redis/connection.rb +0 -11
@@ -0,0 +1,437 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Keys
6
+ # Scan the keyspace
7
+ #
8
+ # @example Retrieve the first batch of keys
9
+ # redis.scan(0)
10
+ # # => ["4", ["key:21", "key:47", "key:42"]]
11
+ # @example Retrieve a batch of keys matching a pattern
12
+ # redis.scan(4, :match => "key:1?")
13
+ # # => ["92", ["key:13", "key:18"]]
14
+ # @example Retrieve a batch of keys of a certain type
15
+ # redis.scan(92, :type => "zset")
16
+ # # => ["173", ["sortedset:14", "sortedset:78"]]
17
+ #
18
+ # @param [String, Integer] cursor the cursor of the iteration
19
+ # @param [Hash] options
20
+ # - `:match => String`: only return keys matching the pattern
21
+ # - `:count => Integer`: return count keys at most per iteration
22
+ # - `:type => String`: return keys only of the given type
23
+ #
24
+ # @return [String, Array<String>] the next cursor and all found keys
25
+ def scan(cursor, **options)
26
+ _scan(:scan, cursor, [], **options)
27
+ end
28
+
29
+ # Scan the keyspace
30
+ #
31
+ # @example Retrieve all of the keys (with possible duplicates)
32
+ # redis.scan_each.to_a
33
+ # # => ["key:21", "key:47", "key:42"]
34
+ # @example Execute block for each key matching a pattern
35
+ # redis.scan_each(:match => "key:1?") {|key| puts key}
36
+ # # => key:13
37
+ # # => key:18
38
+ # @example Execute block for each key of a type
39
+ # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
40
+ # # => "hash"
41
+ # # => "hash"
42
+ #
43
+ # @param [Hash] options
44
+ # - `:match => String`: only return keys matching the pattern
45
+ # - `:count => Integer`: return count keys at most per iteration
46
+ # - `:type => String`: return keys only of the given type
47
+ #
48
+ # @return [Enumerator] an enumerator for all found keys
49
+ def scan_each(**options, &block)
50
+ return to_enum(:scan_each, **options) unless block_given?
51
+
52
+ cursor = 0
53
+ loop do
54
+ cursor, keys = scan(cursor, **options)
55
+ keys.each(&block)
56
+ break if cursor == "0"
57
+ end
58
+ end
59
+
60
+ # Remove the expiration from a key.
61
+ #
62
+ # @param [String] key
63
+ # @return [Boolean] whether the timeout was removed or not
64
+ def persist(key)
65
+ send_command([:persist, key], &Boolify)
66
+ end
67
+
68
+ # Set a key's time to live in seconds.
69
+ #
70
+ # @param [String] key
71
+ # @param [Integer] seconds time to live
72
+ # @param [Hash] options
73
+ # - `:nx => true`: Set expiry only when the key has no expiry.
74
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
75
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
76
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
77
+ # @return [Boolean] whether the timeout was set or not
78
+ def expire(key, seconds, nx: nil, xx: nil, gt: nil, lt: nil)
79
+ args = [:expire, key, Integer(seconds)]
80
+ args << "NX" if nx
81
+ args << "XX" if xx
82
+ args << "GT" if gt
83
+ args << "LT" if lt
84
+
85
+ send_command(args, &Boolify)
86
+ end
87
+
88
+ # Set the expiration for a key as a UNIX timestamp.
89
+ #
90
+ # @param [String] key
91
+ # @param [Integer] unix_time expiry time specified as a UNIX timestamp
92
+ # @param [Hash] options
93
+ # - `:nx => true`: Set expiry only when the key has no expiry.
94
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
95
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
96
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
97
+ # @return [Boolean] whether the timeout was set or not
98
+ def expireat(key, unix_time, nx: nil, xx: nil, gt: nil, lt: nil)
99
+ args = [:expireat, key, Integer(unix_time)]
100
+ args << "NX" if nx
101
+ args << "XX" if xx
102
+ args << "GT" if gt
103
+ args << "LT" if lt
104
+
105
+ send_command(args, &Boolify)
106
+ end
107
+
108
+ # Get the time to live (in seconds) for a key.
109
+ #
110
+ # @param [String] key
111
+ # @return [Integer] remaining time to live in seconds.
112
+ #
113
+ # In Redis 2.6 or older the command returns -1 if the key does not exist or if
114
+ # the key exist but has no associated expire.
115
+ #
116
+ # Starting with Redis 2.8 the return value in case of error changed:
117
+ #
118
+ # - The command returns -2 if the key does not exist.
119
+ # - The command returns -1 if the key exists but has no associated expire.
120
+ def ttl(key)
121
+ send_command([:ttl, key])
122
+ end
123
+
124
+ # Set a key's time to live in milliseconds.
125
+ #
126
+ # @param [String] key
127
+ # @param [Integer] milliseconds time to live
128
+ # @param [Hash] options
129
+ # - `:nx => true`: Set expiry only when the key has no expiry.
130
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
131
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
132
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
133
+ # @return [Boolean] whether the timeout was set or not
134
+ def pexpire(key, milliseconds, nx: nil, xx: nil, gt: nil, lt: nil)
135
+ args = [:pexpire, key, Integer(milliseconds)]
136
+ args << "NX" if nx
137
+ args << "XX" if xx
138
+ args << "GT" if gt
139
+ args << "LT" if lt
140
+
141
+ send_command(args, &Boolify)
142
+ end
143
+
144
+ # Set the expiration for a key as number of milliseconds from UNIX Epoch.
145
+ #
146
+ # @param [String] key
147
+ # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
148
+ # @param [Hash] options
149
+ # - `:nx => true`: Set expiry only when the key has no expiry.
150
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
151
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
152
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
153
+ # @return [Boolean] whether the timeout was set or not
154
+ def pexpireat(key, ms_unix_time, nx: nil, xx: nil, gt: nil, lt: nil)
155
+ args = [:pexpireat, key, Integer(ms_unix_time)]
156
+ args << "NX" if nx
157
+ args << "XX" if xx
158
+ args << "GT" if gt
159
+ args << "LT" if lt
160
+
161
+ send_command(args, &Boolify)
162
+ end
163
+
164
+ # Get the time to live (in milliseconds) for a key.
165
+ #
166
+ # @param [String] key
167
+ # @return [Integer] remaining time to live in milliseconds
168
+ # In Redis 2.6 or older the command returns -1 if the key does not exist or if
169
+ # the key exist but has no associated expire.
170
+ #
171
+ # Starting with Redis 2.8 the return value in case of error changed:
172
+ #
173
+ # - The command returns -2 if the key does not exist.
174
+ # - The command returns -1 if the key exists but has no associated expire.
175
+ def pttl(key)
176
+ send_command([:pttl, key])
177
+ end
178
+
179
+ # Return a serialized version of the value stored at a key.
180
+ #
181
+ # @param [String] key
182
+ # @return [String] serialized_value
183
+ def dump(key)
184
+ send_command([:dump, key])
185
+ end
186
+
187
+ # Create a key using the serialized value, previously obtained using DUMP.
188
+ #
189
+ # @param [String] key
190
+ # @param [String] ttl
191
+ # @param [String] serialized_value
192
+ # @param [Hash] options
193
+ # - `:replace => Boolean`: if false, raises an error if key already exists
194
+ # @raise [Redis::CommandError]
195
+ # @return [String] `"OK"`
196
+ def restore(key, ttl, serialized_value, replace: nil)
197
+ args = [:restore, key, ttl, serialized_value]
198
+ args << 'REPLACE' if replace
199
+
200
+ send_command(args)
201
+ end
202
+
203
+ # Transfer a key from the connected instance to another instance.
204
+ #
205
+ # @param [String, Array<String>] key
206
+ # @param [Hash] options
207
+ # - `:host => String`: host of instance to migrate to
208
+ # - `:port => Integer`: port of instance to migrate to
209
+ # - `:db => Integer`: database to migrate to (default: same as source)
210
+ # - `:timeout => Integer`: timeout (default: same as connection timeout)
211
+ # - `:copy => Boolean`: Do not remove the key from the local instance.
212
+ # - `:replace => Boolean`: Replace existing key on the remote instance.
213
+ # @return [String] `"OK"`
214
+ def migrate(key, options)
215
+ args = [:migrate]
216
+ args << (options[:host] || raise(':host not specified'))
217
+ args << (options[:port] || raise(':port not specified'))
218
+ args << (key.is_a?(String) ? key : '')
219
+ args << (options[:db] || @client.db).to_i
220
+ args << (options[:timeout] || @client.timeout).to_i
221
+ args << 'COPY' if options[:copy]
222
+ args << 'REPLACE' if options[:replace]
223
+ args += ['KEYS', *key] if key.is_a?(Array)
224
+
225
+ send_command(args)
226
+ end
227
+
228
+ # Delete one or more keys.
229
+ #
230
+ # @param [String, Array<String>] keys
231
+ # @return [Integer] number of keys that were deleted
232
+ def del(*keys)
233
+ keys.flatten!(1)
234
+ return 0 if keys.empty?
235
+
236
+ send_command([:del] + keys)
237
+ end
238
+
239
+ # Unlink one or more keys.
240
+ #
241
+ # @param [String, Array<String>] keys
242
+ # @return [Integer] number of keys that were unlinked
243
+ def unlink(*keys)
244
+ send_command([:unlink] + keys)
245
+ end
246
+
247
+ # Determine how many of the keys exists.
248
+ #
249
+ # @param [String, Array<String>] keys
250
+ # @return [Integer]
251
+ def exists(*keys)
252
+ send_command([:exists, *keys])
253
+ end
254
+
255
+ # Determine if any of the keys exists.
256
+ #
257
+ # @param [String, Array<String>] keys
258
+ # @return [Boolean]
259
+ def exists?(*keys)
260
+ send_command([:exists, *keys]) do |value|
261
+ value > 0
262
+ end
263
+ end
264
+
265
+ # Find all keys matching the given pattern.
266
+ #
267
+ # @param [String] pattern
268
+ # @return [Array<String>]
269
+ def keys(pattern = "*")
270
+ send_command([:keys, pattern]) do |reply|
271
+ if reply.is_a?(String)
272
+ reply.split(" ")
273
+ else
274
+ reply
275
+ end
276
+ end
277
+ end
278
+
279
+ # Move a key to another database.
280
+ #
281
+ # @example Move a key to another database
282
+ # redis.set "foo", "bar"
283
+ # # => "OK"
284
+ # redis.move "foo", 2
285
+ # # => true
286
+ # redis.exists "foo"
287
+ # # => false
288
+ # redis.select 2
289
+ # # => "OK"
290
+ # redis.exists "foo"
291
+ # # => true
292
+ # redis.get "foo"
293
+ # # => "bar"
294
+ #
295
+ # @param [String] key
296
+ # @param [Integer] db
297
+ # @return [Boolean] whether the key was moved or not
298
+ def move(key, db)
299
+ send_command([:move, key, db], &Boolify)
300
+ end
301
+
302
+ # Copy a value from one key to another.
303
+ #
304
+ # @example Copy a value to another key
305
+ # redis.set "foo", "value"
306
+ # # => "OK"
307
+ # redis.copy "foo", "bar"
308
+ # # => true
309
+ # redis.get "bar"
310
+ # # => "value"
311
+ #
312
+ # @example Copy a value to a key in another database
313
+ # redis.set "foo", "value"
314
+ # # => "OK"
315
+ # redis.copy "foo", "bar", db: 2
316
+ # # => true
317
+ # redis.select 2
318
+ # # => "OK"
319
+ # redis.get "bar"
320
+ # # => "value"
321
+ #
322
+ # @param [String] source
323
+ # @param [String] destination
324
+ # @param [Integer] db
325
+ # @param [Boolean] replace removes the `destination` key before copying value to it
326
+ # @return [Boolean] whether the key was copied or not
327
+ def copy(source, destination, db: nil, replace: false)
328
+ command = [:copy, source, destination]
329
+ command << "DB" << db if db
330
+ command << "REPLACE" if replace
331
+
332
+ send_command(command, &Boolify)
333
+ end
334
+
335
+ def object(*args)
336
+ send_command([:object] + args)
337
+ end
338
+
339
+ # Return a random key from the keyspace.
340
+ #
341
+ # @return [String]
342
+ def randomkey
343
+ send_command([:randomkey])
344
+ end
345
+
346
+ # Rename a key. If the new key already exists it is overwritten.
347
+ #
348
+ # @param [String] old_name
349
+ # @param [String] new_name
350
+ # @return [String] `OK`
351
+ def rename(old_name, new_name)
352
+ send_command([:rename, old_name, new_name])
353
+ end
354
+
355
+ # Rename a key, only if the new key does not exist.
356
+ #
357
+ # @param [String] old_name
358
+ # @param [String] new_name
359
+ # @return [Boolean] whether the key was renamed or not
360
+ def renamenx(old_name, new_name)
361
+ send_command([:renamenx, old_name, new_name], &Boolify)
362
+ end
363
+
364
+ # Sort the elements in a list, set or sorted set.
365
+ #
366
+ # @example Retrieve the first 2 elements from an alphabetically sorted "list"
367
+ # redis.sort("list", :order => "alpha", :limit => [0, 2])
368
+ # # => ["a", "b"]
369
+ # @example Store an alphabetically descending list in "target"
370
+ # redis.sort("list", :order => "desc alpha", :store => "target")
371
+ # # => 26
372
+ #
373
+ # @param [String] key
374
+ # @param [Hash] options
375
+ # - `:by => String`: use external key to sort elements by
376
+ # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
377
+ # of `count` elements
378
+ # - `:get => [String, Array<String>]`: single key or array of keys to
379
+ # retrieve per element in the result
380
+ # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
381
+ # - `:store => String`: key to store the result at
382
+ #
383
+ # @return [Array<String>, Array<Array<String>>, Integer]
384
+ # - when `:get` is not specified, or holds a single element, an array of elements
385
+ # - when `:get` is specified, and holds more than one element, an array of
386
+ # elements where every element is an array with the result for every
387
+ # element specified in `:get`
388
+ # - when `:store` is specified, the number of elements in the stored result
389
+ def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
390
+ args = [:sort, key]
391
+ args << "BY" << by if by
392
+
393
+ if limit
394
+ args << "LIMIT"
395
+ args.concat(limit)
396
+ end
397
+
398
+ get = Array(get)
399
+ get.each do |item|
400
+ args << "GET" << item
401
+ end
402
+
403
+ args.concat(order.split(" ")) if order
404
+ args << "STORE" << store if store
405
+
406
+ send_command(args) do |reply|
407
+ if get.size > 1 && !store
408
+ reply.each_slice(get.size).to_a if reply
409
+ else
410
+ reply
411
+ end
412
+ end
413
+ end
414
+
415
+ # Determine the type stored at key.
416
+ #
417
+ # @param [String] key
418
+ # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
419
+ def type(key)
420
+ send_command([:type, key])
421
+ end
422
+
423
+ private
424
+
425
+ def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
426
+ # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
427
+
428
+ args << cursor
429
+ args << "MATCH" << match if match
430
+ args << "COUNT" << Integer(count) if count
431
+ args << "TYPE" << type if type
432
+
433
+ send_command([command] + args, &block)
434
+ end
435
+ end
436
+ end
437
+ end