node.jsredisredisjson

Storing and querying JSON arrays in Redisjson with nodejs


What I was hoping to do was store an array of objects using RedisJSON very simply and then query that array.

I have something similar to this:

const data = [
  {
    _id: '63e7d1d85ad7e2f69df8ed6e',
    artist: {
      genre: 'rock',
    },
  },
  {
    _id: '63e7d1d85ad7e2f69df8ed6f',
    artist: {
      genre: 'metal',
    },
  },
  {
    _id: '63e7d1d85ad7e2f69df8ed6g',
    artist: {
      genre: 'rock',
    },
  },
]

then I can easily store and retrieve this:

await redisClient.json.set(cacheKey, '$', data)
await redisClient.json.get(cacheKey)

works great. but now I want to also query this data, I've tried creating an index as below:

await redisClient.ft.create(
  `idx:gigs`,
  {
    '$.[0].artist.genre': {
      type: SchemaFieldTypes.TEXT,
      AS: 'genre',
    },
  },
  {
    ON: 'JSON',
    PREFIX: 'GIGS',
  }
)

and when I try and search this index what I expect is it to return the 2 documents with the correct search filter, but instead it always returns the entire array:

const searchResult = await redisClient.ft.search(`idx:gigs`, '@genre:(rock)')

produces:

{
    total: 1,
    documents: [
        { id: 'cacheKey', value: [Array] }
    ]
}

I can't quite work out at which level I'm getting this wrong, but any help would be greatly appreciated.

Is it possible to store an array of objects and then search the nested objects for nested values with RedisJSON?


Solution

  • The Search capability in Redis stack treats each key containing a JSON document as a separate search index entry. I think what you are doing is perhaps storing your whole array of documents in a single Redis key, which means any matches will return the document at that key which contains all of your data.

    I would suggest that you store each object in your data array as its own key in Redis. Make sure that these will be indexed by using the GIGS prefix in the key name, for example GIGS:63e7d1d85ad7e2f69df8ed6e and GIGS:63e7d1d85ad7e2f69df8ed6f.

    You'd want to change your index definition to account for each document being an object too so it would look something like this:

    await redisClient.ft.create(
      `idx:gigs`,
      {
        '$.artist.genre': {
          type: SchemaFieldTypes.TEXT,
          AS: 'genre',
        },
      },
      {
        ON: 'JSON',
        PREFIX: 'GIGS:',
      }
    )
    

    Note I also updated your PREFIX to be GIGS: not GIGS - this isn't strictly necessary, but does stop your index from accidentally looking at other keys in Redis whose name begins GIGS<whatever other characters>.