render view in SailsJS after mongodb forEach loop query

1.2k views Asked by At

I have 2 collections name "Keywords" and "Company", I use MongoDB aggregate framework to retrieve related object._id from "Keywords" collection base on keywords user key in.

After i got the object._id from keywords collection, i want to query and compile a final complete docs from Company collection using the object._id.

I stuck on the part where res.view() run first before the result[] collect all the docs from Company collection.

I need to help to turn my code to Synchronous approach. Please help me. Below are what i did.

Sample of doc from "Keywords" Collection

{
"_id": ObjectID("52ac7130bd40d00a7beb7a29"),
"keyword": "Sunshine",
"object": [
    {
        "_id": ObjectID("528443ce751fc9b805d640ad"),
        "type": "companyName"
  }
]
}

Sample of doc from "Company" Collection

{
"_id": ObjectID("528443ce751fc9b805d640ad"),
"name": "Sunshine Plaza",
...
...
...
}

SearchController in SailsJS

var keyWords = req.query.q,
    searchKeywords = keyWords.toLowerCase().replace(/[^\w\s]/gi, ' ').split(' '), //For example user key in "Sunshine Plaza"
    results = [];

    Keyword.native(function(err,collection){
        collection.aggregate([
            {
                $project : {
                    '_id' : 0,
                    'keyword' : 1,
                    'object' : 1
                }
            }, {
                $match : {
                    'keyword' : {
                        '$in' : searchKeywords
                    }
                }
            } , {
                $unwind : '$object'
            } , {
                $group : {
                    _id : '$object._id',
                    count : {
                        $sum : 1
                    }
                }
            } , {
                $sort : {
                    count: -1
                }
            } , {
                 $skip : 0
            } , {
                $limit : 10
            }
        ], function (err, docs){
            docs.forEach(function (doc, i){
                Company.findOne({
                    '_id' : doc._id
                },function(err,docs){
                    results.push(docs);
               });
            });
        });
    });

    res.view({
        key : keyWords,
        results : results,
        layout: "layouts/search"
    });
1

There are 1 answers

8
BRogers On

What you're missing is that this isn't blocking code.

So the following

setTimeout(function() {
    console.log('hi');
}, 2000);

console.log('bob');

Bob would happen first. Then hi. This is because it doesn't stop.

So you should rewrite this part of your code:

function (err, docs){
            docs.forEach(function (doc, i){
                Company.findOne({
                    '_id' : doc._id
                },function(err,docs){
                    results.push(docs);
               });
            });
        });
    });

    res.view({
        key : keyWords,
        results : results,
        layout: "layouts/search"
    });

To something like the following:

function (err, docs){
        docs.forEach(function (doc, i){
            Company.findOne({
                '_id' : doc._id
            },function(err,docs){
                results.push(docs);   
           });
        }, myFunction());
    });
});
function myFunction() {
    res.view({key : keywords, results : results, layout: "layouts/search" });
}