Mongo可以使用引用并在聚合期间比较值吗?

时间:2014-08-26 15:28:30

标签: mongodb mongodb-query aggregation-framework

鉴于此文件:

{
        "_id" : ObjectId("53f7287881a97c71e58e3514"),
        "title" : "My Page",
        "currentVersion" : 1,
        "versions" : [ 
            {
                "version" : 0,
                "content" : [ 
                    {
                        "data" : "foo",
                    }
                ]
            }, 
            {
                "version" : 1,
                "content" : [ 
                    {
                        "data" : "bar",
                    }
                ]
            }
        ]
}

是否有(通过聚合框架或其他方式)执行文档投影,其中versions中的唯一内容是每个文档满足versions.version==currentVersion的内容?我知道第一步可能是放松,但我很难过滤出currentVersion!=versions.version的文件。

理想情况下,我想省略versions.version并使用currentVersion作为versions的位置指针。

谢谢!

更新

我已经提出了以下内容,它可以解决问题,但感觉有些不雅

db.test.aggregate(
    [        
        { $unwind: "$versions" },
        { $project: 
            {
                title:1,
                versionMatch: { $cmp: [ "$currentVersion","$versions.version"] },
                content: "$versions.content"
            }
        },
        { $match: 
            {
                versionMatch: {$eq:0}
            }
        },
        { $project: 
            {
                title:1,
                content: 1              
            }
        },
    ]
)

更好的解决方案是能够使用versions作为指针直接从currentVersion中提取条目,但我不确定这是完全可能的。

1 个答案:

答案 0 :(得分:1)

当然,你可以改善这一点。如果您拥有MongoDB 2.6或更高版本,那么您实际上可以过滤已经唯一的版本而无需处理$unwind。这使用了该版本中引入的$map$setDifference运算符:

db.test.aggregate([
    { "$project": {
       "title": 1,
       "content": {
           "$setDifference": [
               { "$map": {
                   "input": "$versions",
                   "as": "el",
                   "in": {
                       "$cond": [
                           { "$eq": [ "$currentVersion", "$$el.version" ] },
                           "$$el.content",
                           false
                       ]
                   }
               }},
               [false]
           ]
       }
    }}
])

甚至可能还引入了$redact。请注意这是递归的,所以这涉及一个任意的项目"匹配值的值,其中要比较的字段不存在:

db.test.aggregate([
   { "$redact": {
       "$cond": [
           { "$eq": [ 
               { "$ifNull": [ "$version", "$$ROOT.currentVersion" ] },
               "$ROOT.currentVersion"
           ]},
           "$$DESCEND",
           "$$PRUNE"
       ]
   }}
])

两者都将匹配"版本"内容到" currentVersion"并删除其他元素,虽然两者仍然保持嵌套数组,甚至后者没有更漂亮的投影,但你总是可以在这些元素上处理$unwind而几乎没有性能损失,因为数据已经减少到一个匹配。

早期版本的MongoDB当然没有执行此操作所需的操作符,因此您很难使用$unwind$project来获得两者之间的逻辑匹配:

db.test.aggregate([
  { "$unwind": "$versions" },
  { "$project": {
      "title": 1,
      "content": "$versions.content",
      "matched": {
          "$eq": [ "$currentVersion", "$versions.version" ]
      }  
  }},
  { "$match": { "matched": true } }
])

请注意,在所有情况下,最佳逻辑测试均为$eq,因为$cmp只应在BSON类型变化时才需要,例如NumberLongNumberInt打算按comparison order比较类型。作为基础测试也应该更有效率。

对于较大的结果集,删除最终的$project也有帮助,因为处理所有结果只是为了去除不需要的字段的成本超过这样做的任何好处,并且通常更好地忽略或以其他方式删除后处理。

但在可能的情况下,前两种形式将提供最佳表现,这主要是由于不需要放松"这会产生更多的文档,以便在管道阶段进行处理。