Module: Redis::Commands::Json
- Included in:
- Redis::Commands
- Defined in:
- lib/redis/commands/modules/json.rb
Overview
Commands for the RedisJSON module (built into Redis core since 8.0, or available earlier through the RedisJSON module in Redis Stack).
Values are serialized to JSON text on the client before they are sent, and replies that carry JSON text are parsed back into Ruby objects. The serialized form of these replies is identical under RESP2 and RESP3 (RESP3 has no native JSON type), so the handling here is protocol-independent. Note that some other RedisJSON commands — e.g. JSON.NUMINCRBY — DO return differently shaped replies under RESP3 and will need protocol-aware reshaping once RESP3 is supported at this layer.
Constant Summary collapse
- NumincrbyNormalize =
Normalize a JSON.NUMINCRBY reply to a protocol-independent value: an array of numbers for a JSONPath, a single number for a legacy path. Under RESP2 the result arrives as a JSON-encoded string ("[3,4]" / "7"); under RESP3 it arrives as native numbers (an array, even for a legacy path). Both shapes map to the same value.
A legacy path can also match multiple values (e.g. "..a"). RESP2 collapses those to the last changed scalar ("7"), while RESP3 returns the full match array with nil for every non-numeric match ([nil, 4, 7, nil]). To stay RESP2-compatible we take the last non-nil element of that array, not its first.
lambda do |reply, jsonpath| return nil if reply.nil? reply = ::JSON.parse(reply) if reply.is_a?(String) # RESP2 string form if jsonpath Array(reply) elsif reply.is_a?(Array) reply.compact.last # RESP3 wraps even a legacy reply in an array; RESP2 keeps the last changed scalar else reply end end
- TypeNormalize =
Normalize a JSON.TYPE reply to a protocol-independent value: an array of type strings for a JSONPath, a single type string for a legacy path. RESP2 returns ["integer"] / "integer"; RESP3 nests one level further as [["integer"]] / ["integer"]. Both map to the same value.
lambda do |reply, jsonpath| return nil if reply.nil? if jsonpath reply.flatten # RESP2 ["integer"] stays flat; RESP3 [["integer"]] collapses elsif reply.is_a?(Array) reply.first # RESP3 legacy ["integer"] -> "integer" else reply # RESP2 legacy "integer" end end
Instance Method Summary collapse
-
#json_arrappend(key, path, *values, raw: false) ⇒ Array<Integer>, Integer
Append one or more values to the array at
pathin the document stored underkey. -
#json_arrindex(key, path, value, start: nil, stop: nil, raw: false) ⇒ Array<Integer>, Integer
Return the index of the first occurrence of a scalar
valuein the array atpath. -
#json_arrinsert(key, path, index, *values, raw: false) ⇒ Array<Integer>, Integer
Insert one or more values into the array at
pathbeforeindex(existing elements at and afterindexshift right; 0 prepends, negative counts from the end). -
#json_arrlen(key, path = nil) ⇒ Array<Integer>, ...
Return the length of the array at
path. -
#json_arrpop(key, path = nil, index = nil, raw: false) ⇒ Array, ...
Remove and return an element from the array at
path. -
#json_arrtrim(key, path, start, stop) ⇒ Array<Integer>, Integer
Trim the array at
pathso it keeps only the inclusive range of elements fromstarttostop(negative indices count from the end). -
#json_clear(key, path = nil) ⇒ Integer
Clear the container and numeric value(s) at
path: arrays and objects are emptied and numbers are set to 0. -
#json_debug_memory(key, path = nil) ⇒ Array<Integer>, Integer
Report the size in bytes of the JSON value(s) at
path(the JSON.DEBUG MEMORY subcommand). -
#json_del(key, path = nil) ⇒ Integer
Delete the value(s) at
pathin the document stored underkey. -
#json_forget(key, path = nil) ⇒ Integer
Delete the value(s) at
pathin the document stored underkey. -
#json_get(key, *paths, raw: false) ⇒ Object, ...
Get the JSON value(s) at one or more
pathsin the document stored underkey. -
#json_merge(key, path, value, raw: false) ⇒ String
Merge
valueinto the document stored underkeyatpath, following RFC 7396 (JSON Merge Patch): a null value deletes a key, a non-null value creates or updates it, and any value merged into an existing array replaces the whole array. -
#json_mget(*keys, path, raw: false) ⇒ Array
Get the values at a single
pathfrom one or morekeys. -
#json_mset(*args, raw: false) ⇒ String
Set one or more JSON values atomically, one per +key+/+path+/+value+ triplet.
-
#json_numincrby(key, path, number) ⇒ Array<Numeric>, ...
Increment the numeric value(s) at
pathin the document stored underkeybynumber. -
#json_objkeys(key, path = nil) ⇒ Array
Return the key names of the JSON object(s) at
path. -
#json_objlen(key, path = nil) ⇒ Array<Integer>, ...
Return the number of keys in the JSON object(s) at
path. -
#json_set(key, path, value, nx: false, xx: false, raw: false) ⇒ Boolean, String
Set the JSON value at
pathin the document stored underkey. -
#json_strappend(key, path, value, raw: false) ⇒ Array<Integer>, ...
Append
valueto the JSON string(s) atpathin the document stored underkey. -
#json_strlen(key, path = nil) ⇒ Array<Integer>, ...
Return the length of the JSON string(s) at
path. -
#json_toggle(key, path) ⇒ Array, ...
Toggle the boolean value(s) at
pathin the document stored underkey. -
#json_type(key, path = nil) ⇒ Array<String>, ...
Return the type name of the value(s) at
path(e.g. "integer", "string", "object").
Instance Method Details
#json_arrappend(key, path, *values, raw: false) ⇒ Array<Integer>, Integer
Append one or more values to the array at path in the document stored under key.
By default each value is a Ruby object serialized with JSON.generate; pass raw: true to
send already-encoded JSON strings through untouched.
262 263 264 265 |
# File 'lib/redis/commands/modules/json.rb', line 262 def json_arrappend(key, path, *values, raw: false) values = values.map { |value| ::JSON.generate(value) } unless raw send_command([:"JSON.ARRAPPEND", key, path, *values]) end |
#json_arrindex(key, path, value, start: nil, stop: nil, raw: false) ⇒ Array<Integer>, Integer
Return the index of the first occurrence of a scalar value in the array at path. The
optional start (inclusive) and stop (exclusive) bound the search.
281 282 283 284 285 286 287 288 289 |
# File 'lib/redis/commands/modules/json.rb', line 281 def json_arrindex(key, path, value, start: nil, stop: nil, raw: false) value = ::JSON.generate(value) unless raw args = [:"JSON.ARRINDEX", key, path, value] unless start.nil? && stop.nil? args << Integer(start || 0) args << Integer(stop) unless stop.nil? end send_command(args) end |
#json_arrinsert(key, path, index, *values, raw: false) ⇒ Array<Integer>, Integer
Insert one or more values into the array at path before index (existing elements at
and after index shift right; 0 prepends, negative counts from the end).
By default each value is a Ruby object serialized with JSON.generate; pass raw: true to
send already-encoded JSON strings through untouched.
307 308 309 310 |
# File 'lib/redis/commands/modules/json.rb', line 307 def json_arrinsert(key, path, index, *values, raw: false) values = values.map { |value| ::JSON.generate(value) } unless raw send_command([:"JSON.ARRINSERT", key, path, Integer(index), *values]) end |
#json_arrlen(key, path = nil) ⇒ Array<Integer>, ...
Return the length of the array at path. When path is omitted it defaults to the root.
322 323 324 325 326 |
# File 'lib/redis/commands/modules/json.rb', line 322 def json_arrlen(key, path = nil) args = [:"JSON.ARRLEN", key] args << path if path send_command(args) end |
#json_arrpop(key, path = nil, index = nil, raw: false) ⇒ Array, ...
Remove and return an element from the array at path. When path is omitted it defaults
to the root; when index is omitted it defaults to -1 (the last element).
The popped element is returned as parsed JSON (a Ruby object), or as the unparsed JSON
string when raw: true.
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/redis/commands/modules/json.rb', line 345 def json_arrpop(key, path = nil, index = nil, raw: false) raise ArgumentError, "index requires a path" if !index.nil? && path.nil? args = [:"JSON.ARRPOP", key] if path args << path args << Integer(index) unless index.nil? end send_command(args) do |reply| if reply.nil? || raw reply elsif reply.is_a?(Array) reply.map { |value| value.nil? ? nil : ::JSON.parse(value) } else ::JSON.parse(reply) end end end |
#json_arrtrim(key, path, start, stop) ⇒ Array<Integer>, Integer
Trim the array at path so it keeps only the inclusive range of elements from start to
stop (negative indices count from the end).
377 378 379 |
# File 'lib/redis/commands/modules/json.rb', line 377 def json_arrtrim(key, path, start, stop) send_command([:"JSON.ARRTRIM", key, path, Integer(start), Integer(stop)]) end |
#json_clear(key, path = nil) ⇒ Integer
Clear the container and numeric value(s) at path: arrays and objects are emptied and
numbers are set to 0. Strings, booleans and null are left unchanged. When path is
omitted it defaults to the root.
218 219 220 221 222 |
# File 'lib/redis/commands/modules/json.rb', line 218 def json_clear(key, path = nil) args = [:"JSON.CLEAR", key] args << path if path send_command(args) end |
#json_debug_memory(key, path = nil) ⇒ Array<Integer>, Integer
Report the size in bytes of the JSON value(s) at path (the JSON.DEBUG MEMORY
subcommand). When path is omitted it defaults to the root.
515 516 517 518 519 |
# File 'lib/redis/commands/modules/json.rb', line 515 def json_debug_memory(key, path = nil) args = [:"JSON.DEBUG", "MEMORY", key] args << path if path send_command(args) end |
#json_del(key, path = nil) ⇒ Integer
Delete the value(s) at path in the document stored under key. When path is omitted
it defaults to the root, so deleting the root removes the whole key.
186 187 188 189 190 |
# File 'lib/redis/commands/modules/json.rb', line 186 def json_del(key, path = nil) args = [:"JSON.DEL", key] args << path if path send_command(args) end |
#json_forget(key, path = nil) ⇒ Integer
Delete the value(s) at path in the document stored under key. Alias of #json_del.
201 202 203 204 205 |
# File 'lib/redis/commands/modules/json.rb', line 201 def json_forget(key, path = nil) args = [:"JSON.FORGET", key] args << path if path send_command(args) end |
#json_get(key, *paths, raw: false) ⇒ Object, ...
Get the JSON value(s) at one or more paths in the document stored under key.
By default the reply is parsed from JSON text into a Ruby object. Pass raw: true to
get the unparsed JSON string back instead (no JSON.parse).
116 117 118 119 120 121 122 123 124 |
# File 'lib/redis/commands/modules/json.rb', line 116 def json_get(key, *paths, raw: false) send_command([:"JSON.GET", key, *paths]) do |reply| if reply.nil? || raw reply else ::JSON.parse(reply) end end end |
#json_merge(key, path, value, raw: false) ⇒ String
Merge value into the document stored under key at path, following RFC 7396 (JSON
Merge Patch): a null value deletes a key, a non-null value creates or updates it, and any
value merged into an existing array replaces the whole array. For a key that does not yet
exist the path must be the root ("$").
By default value is a Ruby object serialized with JSON.generate; pass raw: true to
send an already-encoded JSON string through untouched.
242 243 244 245 |
# File 'lib/redis/commands/modules/json.rb', line 242 def json_merge(key, path, value, raw: false) value = ::JSON.generate(value) unless raw send_command([:"JSON.MERGE", key, path, value]) end |
#json_mget(*keys, path, raw: false) ⇒ Array
Get the values at a single path from one or more keys.
Returns one element per key, in order, parsed from JSON text into a Ruby object (or nil
when the key or path does not exist). Pass raw: true to get the unparsed JSON strings.
165 166 167 168 169 170 171 172 173 174 |
# File 'lib/redis/commands/modules/json.rb', line 165 def json_mget(*keys, path, raw: false) keys.flatten!(1) send_command([:"JSON.MGET", *keys, path]) do |reply| if reply.nil? reply else reply.map { |value| value.nil? || raw ? value : ::JSON.parse(value) } end end end |
#json_mset(*args, raw: false) ⇒ String
Set one or more JSON values atomically, one per +key+/+path+/+value+ triplet. Either all
of the writes are applied or none are. For a key that does not yet exist the path must
be the root ("$").
By default each value is a Ruby object serialized with JSON.generate; pass raw: true to
send already-encoded JSON strings through untouched.
141 142 143 144 145 146 147 148 149 150 |
# File 'lib/redis/commands/modules/json.rb', line 141 def json_mset(*args, raw: false) raise ArgumentError, "wrong number of arguments (expected key/path/value triplets)" \ if args.empty? || !(args.size % 3).zero? command = [:"JSON.MSET"] args.each_slice(3) do |key, path, value| command << key << path << (raw ? value : ::JSON.generate(value)) end send_command(command) end |
#json_numincrby(key, path, number) ⇒ Array<Numeric>, ...
Increment the numeric value(s) at path in the document stored under key by number.
392 393 394 395 396 397 |
# File 'lib/redis/commands/modules/json.rb', line 392 def json_numincrby(key, path, number) jsonpath = json_path?(path) send_command([:"JSON.NUMINCRBY", key, path, number]) do |reply| NumincrbyNormalize.call(reply, jsonpath) end end |
#json_objkeys(key, path = nil) ⇒ Array
Return the key names of the JSON object(s) at path. When path is omitted it defaults
to the root.
430 431 432 433 434 |
# File 'lib/redis/commands/modules/json.rb', line 430 def json_objkeys(key, path = nil) args = [:"JSON.OBJKEYS", key] args << path if path send_command(args) end |
#json_objlen(key, path = nil) ⇒ Array<Integer>, ...
Return the number of keys in the JSON object(s) at path. When path is omitted it
defaults to the root.
447 448 449 450 451 |
# File 'lib/redis/commands/modules/json.rb', line 447 def json_objlen(key, path = nil) args = [:"JSON.OBJLEN", key] args << path if path send_command(args) end |
#json_set(key, path, value, nx: false, xx: false, raw: false) ⇒ Boolean, String
Set the JSON value at path in the document stored under key.
By default value is a Ruby object that is serialized to JSON text with
JSON.generate. Pass raw: true to send an already-encoded JSON string through
untouched (no re-serialization), which avoids double-encoding pre-built JSON.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/redis/commands/modules/json.rb', line 82 def json_set(key, path, value, nx: false, xx: false, raw: false) raise ArgumentError, "nx and xx are mutually exclusive" if nx && xx value = ::JSON.generate(value) unless raw args = [:"JSON.SET", key, path, value] args << "NX" if nx args << "XX" if xx if nx || xx send_command(args, &BoolifySet) else send_command(args) end end |
#json_strappend(key, path, value, raw: false) ⇒ Array<Integer>, ...
Append value to the JSON string(s) at path in the document stored under key.
By default value is a Ruby string serialized with JSON.generate; pass raw: true to
send an already-encoded JSON string through untouched.
485 486 487 488 |
# File 'lib/redis/commands/modules/json.rb', line 485 def json_strappend(key, path, value, raw: false) value = ::JSON.generate(value) unless raw send_command([:"JSON.STRAPPEND", key, path, value]) end |
#json_strlen(key, path = nil) ⇒ Array<Integer>, ...
Return the length of the JSON string(s) at path. When path is omitted it defaults to
the root.
464 465 466 467 468 |
# File 'lib/redis/commands/modules/json.rb', line 464 def json_strlen(key, path = nil) args = [:"JSON.STRLEN", key] args << path if path send_command(args) end |
#json_toggle(key, path) ⇒ Array, ...
Toggle the boolean value(s) at path in the document stored under key.
500 501 502 |
# File 'lib/redis/commands/modules/json.rb', line 500 def json_toggle(key, path) send_command([:"JSON.TOGGLE", key, path]) end |
#json_type(key, path = nil) ⇒ Array<String>, ...
Return the type name of the value(s) at path (e.g. "integer", "string", "object"). When
path is omitted it defaults to the root.
410 411 412 413 414 415 416 417 |
# File 'lib/redis/commands/modules/json.rb', line 410 def json_type(key, path = nil) jsonpath = json_path?(path) args = [:"JSON.TYPE", key] args << path if path send_command(args) do |reply| TypeNormalize.call(reply, jsonpath) end end |