Mongo可以在汇总过程中使用参考并比较值吗? [英] Can Mongo use a reference and compare values during aggregation?

查看:88
本文介绍了Mongo可以在汇总过程中使用参考并比较值吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出此文档:

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

无论如何(通过聚合框架或其他方式)是否可以执行文档投影,其中versions中的唯一内容是每个文档都满足versions.version==currentVersion的内容?我知道第一步可能是$ unwind,但是我对如何在currentVersion!=versions.version处过滤掉文档感到困惑.

Is there anyway, (via the aggregation framework or otherwise) to perform a document projection where the only content in versions is the one that satisfies versions.version==currentVersion for each document? I understand that the first step might be to $unwind but I'm stumped as to how to filter out documents where currentVersion!=versions.version.

理想情况下,我想省去versions.version并将currentVersion用作versions的位置指针.

Ideally, I would like to dispense with versions.version and use currentVersion as a positional pointer for versions.

谢谢!

我想出了以下方法,可以解决问题,但是感觉不太优雅

I've come up with the following, which does the trick, but feels slightly inelegant

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              
            }
        },
    ]
)

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

The better solution would be to be able to pluck the entry directly from versionsusing currentVersion as a pointer but I am not sure that's entirely possible.

推荐答案

当然,您可以对此进行改进.如果您拥有MongoDB 2.6或更高版本,则实际上可以过滤您已经独特的版本,而无需处理 $map $setDifference 运算符引入从该版本开始:

Sure you can improve upon this. Provided you have MongoDB 2.6 or greater then you can actually filter your already unique versions without needing to process an $unwind. This makes use of the $map and $setDifference operators introduced as of that release:

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

或者甚至可以使用 $redact 也介绍了.请注意,这是递归的,因此涉及一个不存在要比较字段的匹配值的任意项目":

Or even possibly with $redact that is also introduced. Mind that this is recursive so this involves an arbitrary "project" of a matching value where the field to compare does not exist:

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

两者都将"versions"内容与"currentVersion"进行匹配并删除其他元素,尽管两者都仍保留嵌套数组,甚至保留没有漂亮投影的后者,但是您始终可以处理

Both will match the "versions" content to the "currentVersion" and remove other elements, though both do still keep a nested array and even the latter without the prettier projection, but you can always process $unwind on these with little performance penalty as the data is already reduced to one match.

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

Earlier versions of MongoDB of course do not have the operators required to do this, so you are pretty much stuck with using $unwind and $project to get a logical match between the two:

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

请注意,在所有情况下,最好的逻辑测试是使用 $eq 作为 $cmp 仅在BSON类型有所不同(例如NumberLongNumberInt)或您实际上打算通过

Noting that in all cases the best logical test is with $eq as $cmp should only be needed where the BSON Type varies such as NumberLong and NumberInt or you actually intend to compare types in by comparison order. It should also be more efficient being a basic test.

对于更大的结果集,删除最后的 $project 也有帮助,因为处理所有结果以消除不想要的字段所花费的成本胜过这样做的任何好处,并且通常更好地忽略或以其他方式删除后处理.

For larger result sets, dropping that final $project helps as well since the cost of processing through all results just to strip an unwanted field outweighs any benefit of doing so, and your generally better of ignoring or otherwise removing in post processing.

但是在可能的情况下,前两种形式将提供最佳性能,这在很大程度上是由于消除了放卷"的需要,这将产生更多的文档以在管道阶段进行处理.

But where possible the first two forms will give the best performance which is largely due to removing the need to "unwind" which produces significantly more documents to process in the pipeline stages.

这篇关于Mongo可以在汇总过程中使用参考并比较值吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆