【问题标题】:Understanding overlapping mongodb projections了解重叠的 mongodb 投影
【发布时间】:2014-05-23 23:17:38
【问题描述】:

我很难理解重叠投影在 mongodb 中的工作原理。

这里有一个简单的例子来说明我的难题(in-browser mongodb console 的结果)。

首先,我创建并插入了一个简单的文档:

var doc = {
  id:"universe", 
  systems: {
    1: {
      name:"milky_way",
      coords:{x:1, y:1}
    }
  }
}

db.test.insert(doc);
// doc succesfully inserted

接下来,我尝试了一个有点奇怪的投影:

db.test.find({}, {"systems.1.coords":1, "systems.1":1});

//result
"_id" : ObjectId("537fd3541cdcaf1ba735becb"),
    "systems" : {
        "1" : {
            "coords" : {
                "y" : 1,
                "x" : 1
            }
        }
    }
}

我希望看到整个“系统 1”,包括名称字段。但似乎“systems.1.coords”的更深层路径覆盖了“system.1”的更浅路径?

我决定测试一下这个“深路径压倒浅路径”的理论:

db.test.find({}, {"systems.1.coords.x":1, "systems.1.coords":1});

//result
"_id" : ObjectId("537fd3541cdcaf1ba735becb"),
    "systems" : {
        "1" : {
            "coords" : {
                "y" : 1, // how'd this get here, but for the shallower projection?
                "x" : 1
            }
        }
    }
}

在这里,我的深层投影没有覆盖较浅的投影。

什么给了? mongodb如何处理重叠投影?我找不到它的逻辑。

编辑:

我的困惑源于所谓的“顶级”路径。

这正如我预期的那样工作:.findOne({}, {"systems.1":1, "systems":1})(即返回一整套系统,尽管我从看起来“更窄”的投影开始)。

但是,这并没有像我预期的那样工作:.findOne({}, {"systems.1.name":1, "systems.1":1})(即只返回 system.1 的名称字段)。

简而言之,超过“一个点”的深度会导致已接受答案中讨论的覆盖。

【问题讨论】:

    标签: mongodb projection aggregation-framework


    【解决方案1】:

    您不能使用 .find() 进行此类投影,因为允许的一般投影是基本字段选择。您所说的是文档重塑,为此您可以使用 $project 运算符和 .aggregate() 方法。

    所以以你最初的例子:

    db.test.aggregate([
        { "$project": {
            "coords": "$systems.1.coords",
            "systems": 1
        }}
    ])
    

    这会给你这样的输出:

    {
        "_id" : ObjectId("537fe2127cb762d14e2a1007"),
        "systems" : {
                "1" : {
                        "name" : "milky_way",
                        "coords" : {
                                "x" : 1,
                                "y" : 1
                        }
                }
        },
        "coords" : {
                "x" : 1,
                "y" : 1
        }
    }
    

    还要注意那里的不同字段命名,好像没有其他原因,来自.find() 的版本会导致您尝试选择的字段级别的路径重叠(“系统”是相同的)和因此不能像您在此处所做的那样将其投影为 两个 字段。

    以同样的方式,考虑如下语句:

    db.test.aggregate([
        { "$project": {
            "systems": {
                "1": {
                    "coords": "$systems.1.coords"
                }
            },
            "systems": 1
        }}
    ])
    

    所以这并不是告诉你它是无效的,只是投影中的一个结果覆盖了另一个,因为基本上在顶层它们都被称为“系统”。

    这基本上是您在尝试使用 .find() 可用的投影方法执行类似操作时最终得到的结果。所以最重要的部分是你需要一个不同的字段名,这就是聚合框架(虽然这里没有聚合)允许你做的事情。

    【讨论】:

    • 感谢您的回复!您能否详细说明什么违反了“基本字段选择”?我的用例是游戏。当玩家在系统之间移动他的飞船时,我的服务器将找到包含该游戏数据的一个文档(使用 findOne)以及一个子集的投影。不获取整个游戏文档的唯一原因是性能。 感觉我想要的是基本的字段选择,但也许我需要更改生成投影的方式以免达到不同的深度?
    • @akbr 问题是您试图从顶层的基本相同字段中选择两个东西。我已经使用 $project 扩展了原理以清楚地说明这一点。同名时不能进行字段选择,需要更改。使用带有$match$project 的聚合管道与.findOne() 完全相同,但它更灵活。这并不是说您在这里失去了性能,尤其是对于一个文档。
    • 感谢您引导我完成它。一旦我看到覆盖行为出现在“两个点深”,它就全部点击了。
    猜你喜欢
    • 2017-05-24
    • 2011-08-31
    • 1970-01-01
    • 2016-02-15
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多