Module: Redis::Commands::Json
| Relationships & Source Files | |
| Extension / Inclusion / Inheritance Descendants | |
|
Included In:
| |
| Defined in: | lib/redis/commands/modules/json.rb |
Overview
::Redis::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
-
NumincrbyNormalize =
# File 'lib/redis/commands/modules/json.rb', line 26
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 =
# File 'lib/redis/commands/modules/json.rb', line 42
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
-
#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+/+valuetriplet. -
#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"). -
#json_path?(path) ⇒ Boolean
private
RedisJSON distinguishes JSONPath expressions (which start with "$") from legacy paths.
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.
# 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.
# 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.
# 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.
# 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.
# 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).
# 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.
# 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.
# 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.
# 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.
# 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).
# 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.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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_path?(path) ⇒ Boolean (private)
RedisJSON distinguishes JSONPath expressions (which start with "$") from legacy paths. JSONPath queries return an array of matches; legacy paths return a single value.
# File 'lib/redis/commands/modules/json.rb', line 525
def json_path?(path) path.to_s.start_with?("$") 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.
# 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.
# 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.
# 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.
# 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.
# 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