javascriptnode.jscloudant

How to pass two requests in single API call with Node js - Cloudant


I'm trying to pass two search requests in single API call But here some times I am getting both requests results and some times getting only one request results. Basically this is my JSON structure in cloudant db:

{
    "_id": "123",
    "name": "Naveen",
    "hoby": "Cricket"
},
{
    "_id": "234",
    "name": "Varun",
    "hoby": "chess"
},
{
    "_id": "345",
    "name": "Tarun",
    "hoby": "Cricket"
},
{
    "_id": "456",
    "name": "pavan",
    "hoby": "chess"
}

Here my requirement would be to get 'hoby' of Cricket 50 members and 'hoby' of Chess 50 members. For that this is how I am trying currently:

doGet: function(request, response) {
    var usersState = [];
    var names = { "usersState": usersState, "message": "ok" };
    if (!myDb) {
        response.json({ "message": "Dataabase connection failed" });
        return;
    }
    var queryForCricket = {
        "selector": {
            "hoby": "Cricket"
        },
        "fields": [
            "_id",
            "name",
            "hoby"
        ],
        "sort": [],
        "limit": 50,
        "skip": 0
    };
    var queryForChess = {
        "selector": {
            "hoby": "chess",
        },
        "fields": [
            "_id",
            "name",
            "hoby"
        ],
        "sort": [],
        "limit": 50,
        "skip": 0
    };
    async.parallel(
        [
            myDb.find(queryForCricket, function (err, body) {
                if (!err) {
                    body.docs.forEach(function (doc) {
                        if (doc)
                            usersState.push(doc);
                    });
                    response.json(names);
                }
            }),
            myDb.find(queryForChess, function (err, body) {
                if (!err) {
                    body.docs.forEach(function (doc) {
                        if (doc)
                            usersState.push(doc);
                    });
                }
            })
        ], function (err, results) {
            if (err) {
                response.send({ "message": "Read operration failed" });
                return;
            }
        });
}

I have written two queries and passing two through Async call but not getting results properly all the time. So is there any optimised way to handle query part and getting results.


Solution

  • The issue here is your final result depends on execution order of both callbacks of your async.parallel find functions. When the second find finishes first you'll get both results but when the first find finishes first you'll get only results of queryForCricked query.

    To get both results you should collect them in the final callback function and use response.json there:

    ...
     async.parallel(
        [
            myDb.find(queryForCricket, function (err, body) {
                if (!err) {
                    body.docs.forEach(function (doc) {
                        if (doc)
                            usersState.push(doc);
                    });
                }
            }),
            myDb.find(queryForChess, function (err, body) {
                if (!err) {
                    body.docs.forEach(function (doc) {
                        if (doc)
                            usersState.push(doc);
                    });
                }
            })
        ], function (err, results) {
            if (err) {
                response.send({ "message": "Read operration failed" });
                return;
            }
    
            response.json(names);
        }
    );
    ...
    

    While collecting data in the shared usersState array is completely working approach it has flaws. For example, you cannot control the order in which your find data will be inserted. async.parallel gives a better way to collect the data from the functions it's running. You can use callback async.parallel inserts into each function to collect the data in the final callback:

    async.parallel(
        [
            function (cb) { myDb.find(queryForCricket, cb); },
            function (cb) { myDb.find(queryForChess, cb); },
        ], function (err, results) {
            if (err) {
                response.send({ "message": "Read operration failed" });
                return;
            }
     
            // result is an array with data from queries: [crickets.body, chess.body]
            results.forEach(function (body) {
                body.docs.forEach(function (doc) {
                    if (doc) usersState.push(doc);
                });
            });
    
            response.json(names);
        }
    );
    ...