在mongodb中,我发现$or
有一种奇怪的行为,想想下面的集合:
{ "_id" : 1, "to" : [ { "_id" : 2 }, { "_id" : 4, "valid" : true } ] }
与$match
汇总时:
db.ooo.aggregate([{$match:{ $or: ['$to', '$valid'] }}])
将aggregate failed
抛出错误:(当然,我知道如何修复它...)
"ok" : 0,
"errmsg" : "$or/$and/$nor entries need to be full objects",
"code" : 2,
"codeName" : "BadValue"
但如果$or
声明中使用的$cond
:
db.ooo.aggregate([{ "$redact": {
"$cond": {
"if": { $or: ["$to", "$valid"] },
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}])
结果将显示,不会抛出任何错误,请参阅mongodb aggregate $redact to filter array elements
问题是$or
语法是怎么回事?为什么同样的条件在$match
中不起作用,但在$cond
中起作用?
我也查阅了文档:
$ COND
如果计算结果为true,则$ cond计算并返回表达式的值。否则,$ cond计算并返回表达式的值。
参数可以是任何有效的表达式。有关表达式的更多信息,请参阅表达式。
$或
计算一个或多个表达式,如果任何表达式为真,则返回true。否则,$或返回false。
有关表达式的更多信息,请参阅表达式。
PS:我使用mongodb 3.4.5但未在其他版本上测试过。
我没有线索......
更新
根据@Neil的回答,我还尝试$or
使用db.ooo.aggregate([{ "$project": {
result:{
"$filter": {
"input": "$to",
"as": "el",
"cond": {$or: ["$$el.valid"]}
}
}
}}])
:
1
db.ooo.aggregate([{ "$project": {
result:{
"$filter": {
"input": "$to",
"as": "el",
"cond": {$or: "$$el.valid"}
}
}
}}])
2
db.ooo.aggregate([{ "$project": {
result:{
"$filter": {
"input": "$to",
"as": "el",
"cond": "$$el.valid"
}
}
}}])
3
$filter
以上3 $or
,语法正常,结果显示且没有错误抛出。
似乎$cond
只会在cond
或$or
中直接使用字段名称吗?
或者这是ereturn list
答案 0 :(得分:0)
$redact
管道阶段对于这种类型的操作是错误的。而是使用$filter
实际上“过滤”数组中的东西:
db.ooo.aggregate([
{ "$addFields": {
"to": {
"$filter": {
"input": "$to",
"as": "t",
"cond": { "$ifNull": [ "$$t.valid", false ] }
}
}
}}
])
产地:
{
"_id" : ObjectId("594c5b0a212a102096cebf7e"),
"id" : 1,
"to" : [
{
"id" : 4,
"valid" : true
}
]
}
在这种情况下,$redact
的问题是在$$DESCEND
条件下使用false
从数组中实际“编辑”的唯一方法。这是递归的,并从文档的顶层向下评估表达式。在不满足条件的任何级别,$redact
将丢弃它。 “顶级”中没有"valid"
字段表示它会丢弃整个文档,除非我们提供了替代条件。
由于并非所有数组元素都在文档顶层的“添加”中都有"valid"
字段,因此我们甚至无法“破解”以假装某些内容存在。
例如,您似乎尝试这样做:
db.ooo.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$ifNull": [ "$$ROOT.to", false ] },
"$valid"
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
但是当你仔细观察时,无论你多么努力地破解这种情况,这种比较基本上都会“永远”评估为true
。
你可以这样做:
db.ooo.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$ifNull": [ "$to", false ] },
"$valid"
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
哪个从数组中正确删除了元素:
{
"_id" : ObjectId("594c5b0a212a102096cebf7e"),
"id" : 1,
"to" : [
{
"id" : 4,
"valid" : true
}
]
}
但是在这种情况下,简单的$filter
将会对逻辑处理产生过度杀伤并增加不必要的开销。当实际存在需要递归处理的“嵌套”数组时,您应该只需要此表单,并且实际上可以在所有级别满足所有条件。
这里的教训是使用正确的运算符来实现其设计目的。