MongoDB查找是否所有数组元素都在另一个更大的数组中

时间:2016-01-25 16:16:28

标签: mongodb nosql

我在LEGO建筑中有一系列乐高零件的id。

// building collection
{
   "name": "Gingerbird House",
   "buildingTime": 45,
   "rating": 4.5,
   "elements": [
     {
       "_id": 23,
       "requiredElementAmt": 14
     },
     {
       "_id": 13,
       "requiredElementAmt": 42
     }
   ]
}

然后

//elements collection
 {
 "_id": 23,
 "name": "blue 6 dots brick",
 "availableAmt":20
 }

 {
 "_id": 13,
 "name": "red 8 dots brick",
 "availableAmt":50
 }
 {"_id":254,
 "name": "green 4 dots brick",
 "availableAmt":12
 }

我如何才能找到建造建筑物的可能性?即仅当建筑文档中的“elements”数组包含我在仓库中的元素(元素集合)需要较少(或相等)的某些元素时,数据库才会返回建筑物。

在SQL中(我最近来的)我会写SELECT * FROM building WHERE id NOT IN(SELECT fk_building FROM building_elemnt_amt WHERE fk_element NOT IN (1, 3))之类的东西 提前谢谢!

1 个答案:

答案 0 :(得分:1)

我不会假装我在没有任何比较的情况下得到它在SQL中的工作方式,但是在mongodb中你可以做类似的事情:

db.buildings.find({/* building filter, if any */}).map(function(b){
    var ok = true;
    b.elements.forEach(function(e){
        ok = ok && 1 == db.elements.find({_id:e._id, availableAmt:{$gt:e.requiredElementAmt}}).count();
    })
    return ok ? b : false;
}).filter(function(b){return b});

db.buildings.find({/* building filter, if any */}).map( function(b){
    var condition = [];
    b.elements.forEach(function(e){
        condition.push({_id:e._id, availableAmt:{$gt:e.requiredElementAmt}});
    })
    return db.elements.find({$or:condition}).count() == b.elements.length ? b : false;
}).filter(function(b){return b});    

最后一个应该更快一点,但我没有测试。如果性能是关键,那么mapReduce它必须更好地并行运行子查询。

注意:上面的示例假设buildings.elements没有具有相同ID的元素。否则,elements数组需要在b.elements.forEach之前进行预处理,以计算非唯一ID的总requiredElementAmt

编辑:工作原理:

使用find选择buildings集合中的所有/部分文档:

db.buildings.find({/* building filter, if any */})

返回一个游标,我们使用map进行迭代,将函数应用于每个文档:

map(function(b){...})

对于每个elements文档buildings,函数本身会迭代b数组:

b.elements.forEach(function(e){...})

每个元素的elements集合中的

find number个文档e

db.elements.find({_id:e._id, availableAmt:{$gte:e.requiredElementAmt}}).count();

符合条件的

elements._id == e._id
and
elements.availableAmt >= e.requiredElementAmt

直到第一次请求返回0.

由于elements._id是唯一的,因此该子查询返回0或1。 表达式ok = ok && 1 == 0中的前0个将ok变为false,因此elements数组的其余部分将在不触及数据库的情况下进行迭代。

该函数返回当前buildings文档或false:

return ok ? b : false

因此map函数的结果是一个数组,包含可以构建的完整buildings文档,或者对于缺少至少1个资源的文档,则为false。

然后我们filter这个数组去掉了false元素,因为它们没有有用的信息:

filter(function(b){return b})

它会返回一个新数组,其中包含function(b){return b}未返回false的所有元素,即只有完整的buildings个文档。