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){...})
的文档数
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
文档。
我在乐高积木建筑中有一个乐高零件 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){...})
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
文档。