skip to Main Content

For the API I’m developing, I’m in the process of saying "goodbye" to Mongo and I’d like to use Redis as my primary database.

The authentication of my API is based on HTTP basic auth where user name is essentially an API key. Currently, I have the following user data structure in Mongo (redacted for brevity):

{
    "_id" : ObjectId("6001a80743ceef211d9dee56"),
    "user_id" : 123,
    "email" : "[email protected]",
    "keys" : [
        {
            "key" : "mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2",
            "usage" : 0
        },
        {
            "key" : "wcGXmeOCKSQ2UVocg1PibQQowwJJfmIqZb7TbwKn",
            "usage" : 40
        }
    ]
}

When the request comes in I query Mongo like the following:

db.users.find({
    "keys.key": "mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2"
});

How do I model the data in Redis to achieve pretty much the same result? My main concern is to have a single query which will give me the entire User account like in the example above. If that was not the case, I could have the following structures:

api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2 : 123
api:wcGXmeOCKSQ2UVocg1PibQQowwJJfmIqZb7TbwKn : 123

So the api key maps to the user ID. And than another set of data with the actual user data like so:

user:123 : name Bob email [email protected]

That poses a problem because I need to query Redis twice. First I need to GET the api key:

GET api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2

which gives me user ID 123 and only than I can get user by ID:

HGETALL user:123

Is there any way I could model the data in Redis and query by api key and get the entire User data?

2

Answers


  1. MongoDB works with BOSN/JSON documents, while Redis is a key-value store. As they do not store the same data structure, you are not going to be able to deal with it the same way. As you have mentioned in your question, now you need two queries in Redis, rather than an only one in Mongo.

    According to Data Types documentation Redis does not provide a JSON data type out of the box. However, RedisLabs provides a Redis JSON module. I have not used it, but maybe it can suit your needs.

    Login or Signup to reply.
  2. I understand your concern is:

    Do not like two queries may be due to network latency.

    Query 1 (to get user id):

    GET api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2
    

    Query 2:(use user id to fetch its value)

    HGETALL user:123
    

    Option 1:

    Is to have a hash like this:

    hmset api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2 user_id 123 email [email protected]
    

    and

    hmset api:wcGXmeOCKSQ2UVocg1PibQQowwJJfmIqZb7TbwKn user_id 123 email [email protected]
    

    And you can fetch all the fields like this in a single REDIS call like this:

    hgetall api:wcGXmeOCKSQ2UVocg1PibQQowwJJfmIqZb7TbwKn 
    1) "user_id"
    2) "123"
    3) "email"
    4) "[email protected]"
    

    Disadvantage:

    1. If you need an update in the user email you may have to scan each record and do that, which will be very slow.
    2. This will take more memory.

    Option 2:
    Use Lua script.
    Keep the same data structure as you are keeping it.

    api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2 : user:123
    api:wcGXmeOCKSQ2UVocg1PibQQowwJJfmIqZb7TbwKn : user:123
    

    Then user in hash like:

    HMSET user:123 name Bob email [email protected]
    

    Now make an LUA script for queries like:

    local key =  KEYS[1] 
    local info =  redis.call('GET', key)
    if info == false then
       return nil
    end
    return redis.call('HGETALL', info)
    

    Here in Lua KEYS[1] –api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2

    you can run this using eval like this:

    eval "local key =  KEYS[1] local info =  redis.call('GET', key) if info == false then return nil end return redis.call('HGETALL', info)" 1 api:mavgPVUG2ufy7grBTpfBG7aPb1vKlxO9rQUA8El2
    

    which will return

    1) "name"
    2) "Bob"
    3) "email"
    4) "[email protected]"
    

    I feel Option 2 is better since both Options 1 disadvantages are addressed.

    Refer: https://redis.io/commands/eval

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search