Luc
Luc

Reputation: 17072

Cleaner way to perform several redis queries within node.js

I did some node.js code to retrieve data within a redis DB but I'm not really happy and I'd like to improve it...
Basically, I have a record "people" in my redis db. From "people" I get a list of "person:i" (i being an integer) and, for each "person:i", I make an additional query to retrieve the hash with key "person:i".

This is how I do it:

db.smembers("people", function(err1, people){
  var jsonObj;
  var jsonArr = [];
  if(!err1) {
    var i = 0;
    res.writeHead(200, {'content-type': 'application/json'});
    // people will provide a list like [person:1, person:2, ..., person:n]
    people.forEach(function(person){
      // In redis I get the hash with key "person:i" 
      db.hgetall(person, function(err2,obj){
        if(!err2){
          // Add person into array
          jsonArr.push({ "id" : person.substring(7), "lastname" : obj["lastname"], "firstname" : obj["firstname"]});

          // I'm not happy with this part where I check if I reached the last item of people array....
          i = i + 1;
          if(i == people.length){
            res.write(JSON.stringify(jsonArr));
            res.end();
          }
        } else {
          var jsonObj = { "error" : "database error", "message" : "Cannot get hash " + person}; 
          res.write(JSON.stringify(jsonObj));
          res.end();
        }
      });
    });
  } else {
    jsonObj = { "error" : "database error", "message" : err1.message };
    res.writeHead(200, {'content-type': 'application/json'});
    res.write(JSON.stringify(jsonObj));
    res.end();
  }
});

What would be the cleanest (at least a cleaner) way to do it ?

Upvotes: 1

Views: 1536

Answers (1)

Zikes
Zikes

Reputation: 5886

What you're looking for is an asynchronous control flow system. An example would be Step or streamline.js.

An alternative would be to abstract the process, creating a data model for Person that would fetch an individual person object, and a People model that's a collection of Persons that would include a method for fetching multiple people following the structure you're using.

Edit: I found a comprehensive listing of node compatible control flow/async libraries: https://github.com/joyent/node/wiki/modules#wiki-async-flow

Edit: After reviewing your code I've thought of another alternative method which is fairly specific to this case, but doesn't directly address the control flow nature of the question.

By altering your schema to store only the ID of the person in the people key you open yourself up to using redis's SORT command, which would enable the entire collection to be fetched in a single command. To do this in redis:

> SADD people 1 2 3 4
> HMSET person:1 firstname John lastname Smith
> HMSET person:2 firstname Jane lastname Smith
> HMSET person:3 firstname John lastname Doe
> HMSET person:4 firstname Jane lastname Doe
> SORT people GET # GET person:*->firstname GET person:*->lastname
 1) "1"
 2) "Jane"
 3) "Doe"
 4) "2"
 5) "Jane"
 6) "Smith"
 7) "3"
 8) "John"
 9) "Doe"
10) "4"
11) "Jane"
12) "Doe"

This has the added benefit of memory savings in the people key, and enables pagination/sorting via the SORT command's by and limit options.

Upvotes: 4

Related Questions