March 21, 2017·4 min read·engineeringvalkeyredisjsonmodules

ReJSON gives Redis a structured JSON value

ReJSON stores a parsed JSON tree and exposes commands for its subvalues. Its initial path rules and memory limitations matter when choosing it over serialized strings or hashes.

A JSON document stored in a Redis string is still a string to Redis. Changing one nested value requires some other code to understand the document. ReJSON adds that understanding as a module data type, with commands that navigate and modify the stored JSON structure. The distinction is between retaining serialized bytes and giving the server operations on the values those bytes describe.

Itamar Haber's 21 March announcement explains the motivation through the limitations of strings and hashes. The repository at this date calls the project ReJSON and labels it alpha. That contemporary name and maturity level are relevant when assessing the module being introduced.

I like Redis's model of data types with explicit operations. JSON fits that model when the server can operate on nested values instead of treating every document as an opaque payload. The benefit depends on the shape of the application data and on the module's initial limits.

The server retains a parsed tree

The announcement describes a streaming lexer parsing input supplied to JSON.SET and building a tree. Values live in binary form in that tree's nodes. ReJSON can therefore locate a subvalue without requiring the client to fetch and parse the whole serialized document first.

This differs from flattening a document into a Redis hash. A hash can represent a flat object's fields, but nested arrays and objects need an additional representation convention. ReJSON defines JSON containers and scalars directly. Its command reference includes objects and arrays alongside strings, numbers, booleans, and null.

Returning data still involves serialization. JSON.GET returns a JSON serialization of the value at the requested path as a bulk string. The internal binary representation does not require a Redis client to understand a new binary document protocol. It does require the client to issue the module's commands and interpret their documented replies.

The announcement says clients capable of sending raw commands can use the module. That makes the command interface available without waiting for every client library to add dedicated wrappers. It does not automatically give a client typed helper methods or path validation.

Paths address values inside a Redis key

The contemporary path documentation denotes the root with a period. Child paths may omit the leading period. Object navigation accepts dotted notation and quoted bracket notation, while arrays use zero-based indexes in brackets.

For example, these commands create a document and read a nested scalar using the documented initial syntax:

JSON.SET account . '{"profile":{"name":"Ada"},"scores":[4,7]}'
JSON.GET account .profile.name
JSON.GET account .scores[1]

The root path is required when creating a new Redis key. Once the document exists, commands can address subvalues. The command reference also permits JSON.GET to take several paths, returning an object whose keys are those paths. A single path instead returns the selected value itself in serialized form.

Negative array indexes count backward from the end. The path documentation gives -1 as the final element and -2 as the preceding element. This makes some operations independent of the array's current length, provided the requested index identifies an existing value under the command's rules.

The initial implementation does not make every JSON object key addressable by a path. Its documentation restricts path names to letters, digits, dollar signs, and underscores, with additional restrictions on the first character. Path names are case-sensitive as well. JSON can store strings outside those naming rules as object keys, so valid stored JSON and addressable path components are not identical sets.

Atomic commands change the update boundary

The announcement identifies commands such as JSON.STRAPPEND, JSON.NUMMULTBY, and JSON.ARRTRIM as atomic operations on their corresponding value types. A client can request the operation it intends instead of implementing a whole-document read, modification, and write sequence.

That distinction matters when several clients modify a shared document. A plain client-side read followed by replacement has a period between the operations in which another client can change the stored value. A single module command moves the specified mutation into one server operation. It does not make an arbitrary sequence of separate commands into one transaction.

JSON.SET also has NX and XX conditions. The command reference applies them to creating or replacing values at the relevant key or object position. It returns OK when successful and a null bulk reply when the supplied condition is not met. Applications therefore need to interpret the reply, not simply assume that sending a valid JSON payload changed the document.

I find these operations the main reason to consider the module. Keeping JSON in Redis already works at the byte level. The new capability is expressing changes in terms of the document's structure while leaving the implementation of those changes on the server.

The alpha representation has visible costs

The README lists object-key searching as linear in the number of keys. The path documentation explains that each level adds a search, with object lookup depending on parent-object size and array lookup constant-time. A path into a wide object therefore has different costs from an array access at the same nesting depth.

The README also says containers do not shrink after item deletion, so removing items does not reclaim that unused container memory. Numbers use 64-bit integers or doubles, and out-of-range values are rejected. These are concrete constraints on the initial representation, beyond a general warning that the project is young.

Persistence has a stated limit too. The README warns that AOF rewriting will probably fail for documents whose serialization exceeds 0.5 GB. That is a documented alpha limitation, not a supported maximum-size promise. The module's own license is AGPLv3, according to the same repository revision.

ReJSON adds a structured value with server-side navigation and mutation. The initial implementation has explicit naming rules for paths and specific storage limitations. Those details determine whether its command model matches a document workload, including workloads where repeated deletion changes container occupancy or where wide objects make path lookup more expensive.