Learn to store, read and search sessions in JSON format

Last updated 23, Apr 2024

Goal

Understand how to model sessions stored in Redis using the JSON data type.

Solution

Redis offers many options to store and retrieve data efficiently; traditionally, ecosystems leverage the string and hash data types. However, sessions store different types of data: metadata, lists, geographical locations, and entire objects. Finding the right data structure, using low-complexity data access patterns, and managing session expiration in a highly concurrent environment may be challenging. This document will introduce ideas for a solid session data management strategy using the JSON data type. The hash and JSON data structures can store objects when properly transformed, but JSON is a natural choice to store multiple nested objects. JSON is also more flexible when it comes to indexing and searching.

Consider the following session data, which stores session metadata, a shopping cart, a list of visited websites, and a location.

{
    "lastAccessedTime":1673354843,
    "creationTime":1673354843,
    "cart":[
        {
            "itemId":"hp-2341",
            "itemCost":1990.99,
            "quantity":3
        },
        {
            "itemId":"MacBook",
            "itemCost":2990.99,
            "quantity":15
        }
    ],
    "location":"34.638,31.79",
    ["www.redis.com","www.google.com"]
}

Redis can manage JSON documents efficiently and enables searching. Let’s consider this example of two sessions with some user data.

JSON.SET session:a30d0c64-4cad-4088-a9ef-f1889d182df4 $ '{"lastAccessedTime":1672475765650,"creationTime":1672475765649,"user":{"name":"John","last":"Doe"},"visited":["www.redis.io","www.mortensi.com"], "location": "34.638,31.79", "cart":[{"itemId":"hp-2341","itemCost":1990.99,"quantity":2},{"itemId":"MacBook","itemCost":2990.99,"quantity":1}]}'

JSON.SET session:18920ac6-a2f0-4019-8250-e0036d17d015 $ '{"lastAccessedTime":1672475765645,"creationTime":1672475765549,"user":{"name":"Jane","last":"Appleseed"},"visited":["www.redis.io","www.microsoft.com"], "location": "35.178,31.768", "cart":[{"itemId": "invicta-jolly","itemCost":68.99,"quantity":1},{"itemId":"MacBook","itemCost":2990.99,"quantity":1}]}'

Redis supports secondary indexing for JSON documents. We can index the session as follows:

  • NUMERIC SORTABLE for lastAccessedTime and creationTime
  • TAG for the collection of visited URLs
  • TAG for the itemId
  • GEO for the location of the user

The syntax to create the index would be using the FT.CREATE:

FT.CREATE session_idx ON JSON PREFIX 1 session: SCHEMA $.lastAccessedTime AS lastAccessedTime NUMERIC SORTABLE $.creationTime AS creationTime NUMERIC SORTABLE $.visited[*] AS visited TAG $.cart[*].itemId AS itemid TAG $.location AS loc GEO

Verify that the index has been created and two documents have been indexed: check the output of

FT.INFO session_idx

and look for the value of num_docs.

Now, we can use the index and perform searches across our sessions. A search to return all the sessions containing a MacBook in the shopping cart:

FT.SEARCH session_idx '@itemid:{MacBook}' RETURN 0
1) (integer) 2
2) "session:a30d0c64-4cad-4088-a9ef-f1889d182df4"
3) "session:18920ac6-a2f0-4019-8250-e0036d17d015"

A search to check in a single shopping cart whether a product exists (uses the JSONPath syntax):

JSON.GET session:a30d0c64-4cad-4088-a9ef-f1889d182df4 '$.cart[?(@.itemId=="MacBook")]'
"[{\"itemId\":\"MacBook\",\"itemCost\":2990.99,\"quantity\":1}]"

A search to get the sessions close to a given location (within a radius of 40km), gets advantage of

FT.SEARCH session_idx '@loc:[34.5 31.5 40 km]' return 0
1) (integer) 1
2) "session:a30d0c64-4cad-4088-a9ef-f1889d182df4"

A search to return the last session created:

FT.SEARCH session_idx "@creationTime:[-inf, +inf]" RETURN 1 creationTime LIMIT 0 1 SORTBY creationTime DESC
1) (integer) 2
2) "session:a30d0c64-4cad-4088-a9ef-f1889d182df4"
3) 1) "creationTime"
   2) "1672475765649"

in those use cases requiring a compact session representation for collections, objects, and geographical locations together with the ability to perform efficient in-session and cross-session queries and searches, the JSON data type is proposed as a valid and performing solution.

References

Read more about the JSON support for Redis