mongodb查询中的多个查询

时间:2014-04-18 19:48:17

标签: node.js mongodb synchronous nosql

我在尝试根据mongodb上的其他查询结果进行查询时遇到问题。

我尝试进行初始查询,然后针对第一个查询的每个结果执行另一个查询。我之所以这样做是因为我有两个不同的集合,我需要将一个集合中的一些数据与另一个集合的数据相连接。在SQL世界中,我可以通过JOIN轻松完成此操作,但由于我在这个中使用mongodb,我无法真正使用JOIN,因此我猜测在第一个查询中执行for循环#s回调函数将是最佳选择。

这是我使用的代码......

var resultSet = [];
db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){
    if(e || docs.length === 0) {
        console.log("Sorry, wrong id.");
        return e;
    }
    for(var m=0; m<docs.length; m++){

        var auxRes = {};
        auxRes.id_bus = docs[m].id_bus;
        auxRes.id_bus_stop = docs[m].id_bus_stop;
        auxRes.coord_x = docs[m].coord_x;
        auxRes.coord_y = docs[m].coord_y;
        auxRes.id_bus_variation = docs[m].id_bus_variation;
        db.get('buscollection').find({id_bus: parseInt(docs[m].id_bus)}, function(e, busDocs){
            auxRes.s_origin_description = busDocs[0].s_origin_description;
            auxRes.s_destination_description = busDocs[0].id_destination_description;
            resultSet.push(auxRes);
        });
        res.send(JSON.stringify(resultSet));
    }
});

添加完所有值后,我需要res.send resultSet数组。

我已尝试过其他一些方法,但事实是,当达到res.send行时,第二个查询根本没有完成。我也尝试在内部查询的回调中执行此操作,但是我需要检查它是否是for循环中的最后一个,并且检查值是否胜利,因为它始终是等效的到docs.length

据我所知,在mongodb中没有同步查询,但也许我错了。

这样做的正确方法是什么?

修改

我找到了解决方法,但我确信必须有更好的方法。以下是我的表现方式......

db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){
    if(e || docs.length === 0) {
        console.log("Ha ocurrido un error, no existe esa parada");
        return e;
    }
    var busIDs = [];
    for(var m=0; m<docs.length; m++){
        busIDs.push(parseInt(docs[m].id_bus));
        var auxRes = {};
        auxRes.id_bus = docs[m].id_bus;
        auxRes.id_bus_stop = docs[m].id_bus_stop;
        auxRes.coord_x = docs[m].coord_x;
        auxRes.coord_y = docs[m].coord_y;
        auxRes.id_bus_variation = docs[m].id_bus_variation;
        resultSet.push(auxRes);
    }
    db.get('buscollection').find({id_bus: {$in: busIDs}}, function(e, busDocs){
        for(var n = 0; n<busDocs.length; n++){
            for(var k=0; k<resultSet.length; k++){
                if(resultSet[k].id_bus == busDocs[n].id_bus){
                    resultSet[k].s_origin_description = busDocs[n].s_origin_description;
                    resultSet[k].s_destination_description = busDocs[n].id_destination_description;
                }
            }
        }
        res.send(JSON.stringify(resultSet));
    });
});

2 个答案:

答案 0 :(得分:2)

Node.js行为是异步的,程序员必须考虑此行为进行编码。使用回调或承诺或流控制库。在你的程序中,你 将mongo查询放在循环中,这是一种不好的查询方法。相反,如果多次查询,请使用$ in运算符。它将优化您的代码性能和 解决了你的回复问题。

var resultSet = [];
        db.get('busstopcollection').find({id_bus_stop: parseInt(req.body.busstopid)}, function(e, docs){
            if(e || docs.length === 0) {
                console.log("Sorry, wrong id.");
                return e;
            }
            var bus_ids = [];
            for(var m=0; m<docs.length; m++){
                var auxRes = {};
                auxRes.id_bus = docs[m].id_bus; 
                bus_ids.push(parseInt(docs[m].id_bus)); // collect all ids 
                auxRes.id_bus_stop = docs[m].id_bus_stop;
                auxRes.coord_x = docs[m].coord_x;
                auxRes.coord_y = docs[m].coord_y;               
                auxRes.id_bus_variation = docs[m].id_bus_variation;
                resultSet.push(auxRes);         

            }
            // Query at one time for all document 
            db.get('buscollection').find({id_bus: {$in : bus_ids}}).toArray( function(e, busDocs){
                    // Now find and merge in one go
                    busDocs.forEach(function(eachBusDoc){
                        for(var i=0,len = resultSet.length;i< len;i++){
                            if(resultSet[i].id_bus == busDocs.id_bus ){
                                resultSet[i].s_origin_description = eachBusDoc.s_origin_description;
                                resultSet[i].s_destination_description = eachBusDoc.id_destination_description;
                            }
                        }
                    });             
                       res.send(JSON.stringify(resultSet));
                });
        });

答案 1 :(得分:1)

您问题中的更新解决方案通常很好,因为使用$in是获取一组结果的绝佳方式(您需要确保已将id_bus属性编入索引)。

以下是一些调整(稍加清理和优化):

db.get('busstopcollection')
  .find({id_bus_stop: parseInt(req.body.busstopid)}).toArray(function(e, docs){
    var auxById = {};   // store a dictionary of all the results for later
    if(e || docs === null || docs.length === 0) {
        console.log("Ha ocurrido un error, no existe esa parada");
        return e;
    }
    var busIDs = [];
    docs.forEach(function(doc) {
        busIDs.push(parseInt(doc.id_bus));
        // consider just using the doc directly rather than copying each property 
        // especially if you're not manipulating any of the data as it passes
        var auxRes = {        
            id_bus : doc.id_bus,
            id_bus_stop : doc.id_bus_stop,
            coord_x : doc.coord_x,
            coord_y : doc.coord_y,
            id_bus_variation : doc.id_bus_variation
        };
        // you could just use each doc directly ...
        // var auxRes = doc; ??
        // ** importantly, store off the id_bus for each one so you can
        // ** avoid a costly loop trying to match an id below.
        auxById[doc.id_bus] = auxRes;
        resultSet.push(auxRes);
    });
    // might want to consider using a cursor ... here's an example
    db.get('buscollection')
        .find({id_bus: {$in: busIDs}}).each(function(e, busDoc){
        // the last item in the cursor will be null
        if (busDoc === null) {
            res.send(JSON.stringify(resultSet)); 
            return;
        }
        var res = auxById[busDoc.id_bus];
        if (res) {   // did we find it in our dictionary of results?
           // yes, we did == copy the necessary field data
           res.s_origin_description = busDoc.s_origin_description;
           res.s_destination_description = busDoc.id_destination_description;
        }            
    });
});