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

MongoDB find if all array elements are in the other bigger array

我在乐高积木建筑中有一个乐高零件 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
 }

我怎样才能找到建造建筑物的可能性? IE。只有当建筑文档中的 "elements" 数组由我在仓库(元素集合)中拥有的那些元素组成时,数据库才会 return 建筑需要更少(或相等)数量的特定元素。

在SQL(这是我最近来的)我会写一些像SELECT * FROM building WHERE id NOT IN(SELECT fk_building FROM building_elemnt_amt WHERE fk_element NOT IN (1, 3)) 提前致谢!

我不会假装我在 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 之前是 pre-processed 才能计算 non-unique 个 ID 的总数 requiredElementAmt

编辑:工作原理:

Select all/some 来自 buildings 集合 find 的文档:

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

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

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

函数本身为每个 buildings 文档遍历 elements 数组 b

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

find elements 集合中每个元素 e

的文档数

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

符合条件:

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

直到第一次请求 return 0。

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

函数 return 是当前 buildings 文档,或者是错误的:

return ok ? b : false

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

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

filter(function(b){return b})

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