mongodb $或聚合管道中的语法

时间:2017-06-22 23:49:48

标签: mongodb

在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

的黑客用法

1 个答案:

答案 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将会对逻辑处理产生过度杀伤并增加不必要的开销。当实际存在需要递归处理的“嵌套”数组时,您应该只需要此表单,并且实际上可以在所有级别满足所有条件。

这里的教训是使用正确的运算符来实现其设计目的。