123456789_123456789_123456789_123456789_123456789_

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 =

    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.

    # File 'lib/redis/commands/modules/json.rb', line 26
    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.

    # File 'lib/redis/commands/modules/json.rb', line 42
    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

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.

Examples:

redis.json_arrappend("doc", "$.colors", "blue")
  # => [3]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the target array

  • values (Array<Object>)

    one or more JSON values to append

  • raw (Boolean)

    treat each value as an already-encoded JSON string

Returns:

  • (Array<Integer>, Integer)

    the new array length(s); an Array for a JSONPath, a single Integer for a legacy path (nil for a match that is not an array)

[ GitHub ]

  
# 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.

Examples:

redis.json_arrindex("doc", "$.colors", "silver")
  # => [1]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the target array

  • value (Object)

    the scalar JSON value to search for

  • start (Integer)

    optional inclusive start index

  • stop (Integer)

    optional exclusive stop index

  • raw (Boolean)

    treat value as an already-encoded JSON string

Returns:

  • (Array<Integer>, Integer)

    the index/indices of the first match (-1 if not found)

[ GitHub ]

  
# 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.

Examples:

redis.json_arrinsert("doc", "$.colors", 1, "gold")
  # => [4]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the target array

  • index (Integer)

    the position to insert before

  • values (Array<Object>)

    one or more JSON values to insert

  • raw (Boolean)

    treat each value as an already-encoded JSON string

Returns:

  • (Array<Integer>, Integer)

    the new array length(s)

[ GitHub ]

  
# 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.

Examples:

redis.json_arrlen("doc", "$.colors")
  # => [2]

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root "$")

Returns:

  • (Array<Integer>, Integer, nil)

    the array length(s); nil for a match that is not an array, or when the key/path does not exist

[ GitHub ]

  
# 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.

Examples:

redis.json_arrpop("doc", "$.colors", 0)
  # => ["black"]

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath to the target array (defaults to the root "$")

  • index (Integer) (defaults to: nil)

    an optional position to pop from (defaults to -1, the last element)

  • raw (Boolean)

    return the unparsed JSON string(s) instead of parsed Ruby objects

Returns:

  • (Array, Object, nil)

    the popped value(s); an Array for a JSONPath, a single value for a legacy path, nil for an empty array or a non-array match

Raises:

  • (ArgumentError)

    if index is given without a path

[ GitHub ]

  
# 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).

Examples:

redis.json_arrtrim("doc", "$.colors", 0, 1)
  # => [2]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the target array

  • start (Integer)

    inclusive index of the first element to keep

  • stop (Integer)

    inclusive index of the last element to keep

Returns:

  • (Array<Integer>, Integer)

    the new array length(s)

[ GitHub ]

  
# 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.

Examples:

redis.json_clear("doc", "$.arr")
  # => 1

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root "$")

Returns:

  • (Integer)

    the number of values cleared

[ GitHub ]

  
# 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.

Examples:

redis.json_debug_memory("doc")
  # => 264

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root)

Returns:

  • (Array<Integer>, Integer)

    the size(s) in bytes: an Array for a JSONPath, a single integer for a legacy path or when no path is given (0 for a missing key)

[ GitHub ]

  
# 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.

Examples:

redis.json_del("doc", "$.a")
  # => 1

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root "$")

Returns:

  • (Integer)

    the number of values deleted

[ GitHub ]

  
# 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.

Examples:

redis.json_forget("doc", "$.a")
  # => 1

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root "$")

Returns:

  • (Integer)

    the number of values deleted

[ GitHub ]

  
# 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).

Examples:

redis.json_get("doc")
  # => { "a" => 1, "nested" => { "b" => 2 } }

raw JSON string

redis.json_get("doc", raw: true)
  # => '{"a":1,"nested":{"b":2}}'

Parameters:

  • key (String)
  • paths (Array<String>)

    zero or more JSONPath expressions; with none the whole document is returned

  • raw (Boolean)

    return the unparsed JSON string instead of a parsed Ruby object

Returns:

  • (Object, String, nil)

    the parsed JSON value (or the raw JSON string when raw is true), or nil if the key does not exist

[ GitHub ]

  
# 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.

Examples:

redis.json_merge("doc", "$.b", 8)
  # => "OK"

Parameters:

  • key (String)
  • path (String)

    a JSONPath (must be "$" when creating a new key)

  • value (Object)

    a JSON-serializable Ruby object, or a pre-encoded JSON string when raw is true

  • raw (Boolean)

    treat value as an already-encoded JSON string and send it as-is

Returns:

  • (String)

    the raw "OK" reply

[ GitHub ]

  
# 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.

Examples:

redis.json_mget("doc1", "doc2", "$.a")
  # => [[1], [2]]

Parameters:

  • keys (Array<String>)

    one or more keys to read

  • path (String)

    a single JSONPath applied to every key

  • raw (Boolean)

    return the unparsed JSON strings instead of parsed Ruby objects

Returns:

  • (Array)

    one value per key (nil for a missing key/path)

[ GitHub ]

  
# 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.

Examples:

redis.json_mset("doc1", "$", { "a" => 1 }, "doc2", "$", { "b" => 2 })
  # => "OK"

Parameters:

  • args (Array)

    a flat list of key, path, value triplets

  • raw (Boolean)

    treat each value as an already-encoded JSON string

Returns:

  • (String)

    the raw "OK" reply

Raises:

  • (ArgumentError)

    unless args is a non-empty list of complete triplets

[ GitHub ]

  
# 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.

Examples:

redis.json_numincrby("doc", "$.a", 2)
  # => [3]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the numeric value(s)

  • number (Numeric)

    the amount to add

Returns:

  • (Array<Numeric>, Numeric, nil)

    the new value(s): an Array for a JSONPath, a single number for a legacy path; nil (or nil element) for a non-numeric match

[ GitHub ]

  
# 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.

Examples:

redis.json_objkeys("doc", "$.nested")
  # => [["b", "c"]]

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root)

Returns:

  • (Array)

    an array of key-name arrays (JSONPath) or a single array of key names (legacy path); nil for a non-object match

[ GitHub ]

  
# 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.

Examples:

redis.json_objlen("doc", "$.nested")
  # => [2]

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root)

Returns:

  • (Array<Integer>, Integer, nil)

    the key count(s): an Array for a JSONPath, a single integer for a legacy path; nil for a non-object match

[ GitHub ]

  
# 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.

[ GitHub ]

  
# 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.

Examples:

redis.json_set("doc", "$", { "a" => 1, "nested" => { "b" => 2 } })
  # => "OK"

pre-encoded JSON

redis.json_set("doc", "$", '{"a":1}', raw: true)
  # => "OK"

conditional set with NX/XX returns a boolean

redis.json_set("doc", "$.a", 2, nx: true)
  # => false

Parameters:

  • key (String)
  • path (String)

    a JSONPath, e.g. "$" for the document root

  • value (Object)

    a JSON-serializable Ruby object, or a pre-encoded JSON string when raw is true

  • nx (Boolean)

    only set when the path does not already exist

  • xx (Boolean)

    only set when the path already exists

  • raw (Boolean)

    treat value as an already-encoded JSON string and send it as-is

Returns:

  • (Boolean, String)

    when nx or xx is given, true on success and false when the condition was not met; otherwise the raw "OK" reply

Raises:

  • (ArgumentError)

    if both nx and xx are given (they are mutually exclusive)

[ GitHub ]

  
# 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.

Examples:

redis.json_strappend("doc", "$.a", "bar")
  # => [6]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the target string(s)

  • value (String)

    the string to append

  • raw (Boolean)

    treat value as an already-encoded JSON string and send it as-is

Returns:

  • (Array<Integer>, Integer, nil)

    the new string length(s): an Array for a JSONPath, a single integer for a legacy path; nil for a non-string match

[ GitHub ]

  
# 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.

Examples:

redis.json_strlen("doc", "$.a")
  # => [3]

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root)

Returns:

  • (Array<Integer>, Integer, nil)

    the string length(s): an Array for a JSONPath, a single integer for a legacy path; nil for a non-string match

[ GitHub ]

  
# 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.

Examples:

redis.json_toggle("doc", "$.flag")
  # => [0]

Parameters:

  • key (String)
  • path (String)

    a JSONPath to the target boolean(s)

Returns:

  • (Array, Object, nil)

    1+/+0 for the new true/false value(s): an Array for a JSONPath, a single value for a legacy path; nil for a non-boolean match

[ GitHub ]

  
# 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.

Examples:

redis.json_type("doc", "$.a")
  # => ["integer"]

Parameters:

  • key (String)
  • path (String) (defaults to: nil)

    an optional JSONPath (defaults to the root)

Returns:

  • (Array<String>, String, nil)

    the type name(s): an Array for a JSONPath, a single string for a legacy path

[ GitHub ]

  
# 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