对于此问题Find documents with arrays that contain a document with a particular field的评论,$setDifference
和$map
应解决此问题。以下是这个问题的详细信息。
我想只查找数组
'docs.foo'
中存在所有docs
的文档。鉴于以下两个文件{ _id : 1, docs : [ { bar : 2}, { foo : 3, bar : 3} ] }, { _id : 2, docs : [ { foo : 2, bar : 2}, { foo : 3, bar : 3} ] }
这是我的代码
db.collection.aggregate([
{$project: {
docs: {$setDifference:
[{$map:
{input: '$docs',
as: 'item',
in: {$cond: [{$ne: [
{$ifNull: ['$$item.foo', null]},
null]},
'$$item',
null]}}
},
[null]
]
}
}}
])
然而,结果是
{
_id : 1,
docs : [
{ foo : 3, bar : 3}
]
},
{
_id : 2,
docs : [
{ foo : 2, bar : 2},
{ foo : 3, bar : 3}
]
}
仅从{bar: 2}
文档中删除了_id: 1
。我希望输出是
{
_id : 1,
docs : [ ]
},
{
_id : 2,
docs : [
{ foo : 2, bar : 2},
{ foo : 3, bar : 3}
]
}
是否可以通过$setDifference
?
答案 0 :(得分:3)
这里你要求的是与引用的问题有点不同,因为如果其中包含的所有文档与一组条件不匹配,你故意想要使数组“为空”。
在这种情况下,此处更好的运算符为$allElementsTrue
,您可以将其应用于$map
,如下所示:
db.collection.aggregate([
{ "$project": {
"docs": {
"$cond": {
"if": {
"$allElementsTrue": {
"$map": {
"input": "$docs",
"as": "el",
"in": { "$ifNull": [ "$$el.foo", false ] }
}
}
},
"then": "$docs",
"else": { "$literal": [] }
}
}
}}
])
因此逻辑基本上通过$map
测试每个数组元素而不是“过滤”数组内容,而是返回true/false
值的数组。如果“所有元素确实都是true
,那么这将产生单个true/false
值作为响应。
然后由三元$cond
考虑这一点,它说true
(所有条件都匹配)然后只是呈现已经形成的数组。但是当这是false
(并非所有匹配的)而是返回一个空数组时。
非常确定$literal
参数并不真正需要"else"
,并且只能以[]
的形式写入。但我只是在确定何时只是在这里键入逻辑而不是测试。
但是这个逻辑会返回“空”结果,因为“all”元素不匹配数组中所有子文档都包含"foo"
键的必要条件。