MongoDB多维数组投影

9

我刚开始学习MongoDB,但是找不到解决我的问题的方法。

有一个文档:

> db.test.insert({"name" : "Anika", "arr" : [ [11, 22],[33,44] ] })

请注意"arr"字段,它是一个多维数组。
现在我正在寻找一个查询,只返回arr [0] [1]的值,即22。我尝试使用$slice来实现这一点,但我不知道如何处理第二个维度。
> db.test.find({},{_id:0,"arr":{$slice: [0,1]}})
{ "name" : "ha", "arr" : [ [ 11, 22 ] ] }

我也尝试了。
> db.test.find({},{_id:0,"arr":{$slice: [0,1][1,1]}})
{ "name" : "ha", "arr" : [ [ 11, 22 ] ] }

期望的输出结果应该是:
22

或者

{"arr":[[22]]}

谢谢


编辑:

在阅读评论后,我认为我简化了示例数据并且需要提供更多信息:

  1. 集合中有许多与我提供的那个文档结构相同的文档。
  2. 除了两个之外,还有更多的数组元素。
  3. 在真实世界中,数组包含非常长的文本(500kb-1mb),因此将整个数据传输到客户端非常昂贵。
  4. 在聚合之前,我将通过“name”字段进行查询。 仅出于简单起见,在示例中跳过了该部分。
  5. 目标索引是可变的,因此有时我需要知道arr [0] [1]的值,下一次是arr [1] [4]

示例数据:

> db.test.insert({"name" : "Olivia", "arr" : [ [11, 22, 33, 44],[55,66,77,88],[99] ] })
> db.test.insert({"name" : "Walter", "arr" : [ [11], [22, 33, 44],[55,66,77,88],[99] ] })
> db.test.insert({"name" : "Astrid", "arr" : [ [11, 22, 33, 44],[55,66],[77,88],[99] ] })
> db.test.insert({"name" : "Peter",  "arr" : [ [11, 22, 33, 44],[55,66,77,88],[99] ] })

示例查询:

> db.test.find({name:"Olivia"},{"arr:"...})
2个回答

4

您可以使用聚合框架:

db.test.aggregate([
    { $unwind: '$arr' },
    { $limit: 1 },
    { $project: { _id: 0, arr: 1 } },
    { $unwind: '$arr' },
    { $skip: 1 },
    { $limit: 1 }
])

返回:

{ "arr": 22 }

编辑:原帖作者修改了我的解决方案以满足他的需求,得出了以下内容:

db.test.aggregate([
    { $match: { name:"Olivia" } },
    { $project: { _id: 0,arr: 1 } },
    { $unwind: '$arr' },
    { $skip: 1 },
    { $limit:1 },
    { $unwind: "$arr" },
    { $skip: 2 },
    { $limit: 1 }
])

在提供的扩展数据的基础上,此查询将导致{ arr: 77 }。请注意,$skip和$limit需要选择数组层次结构中正确的元素。


如果期望的输出是单个文档,那么这将会奏效。使用 $match 可以始终获得一个期望的文档。 - Dmytro Shevchenko
1
问题说:“期望的输出应该是22 ...”。我很确定他们只需要一个文档的值作为结果。 - Dmytro Shevchenko
这几乎就是我要找的东西。根据您的代码,我能够找到完全解决我的问题的解决方案:> db.test.aggregate([{$match: {name:"Olivia"}}, {$project: {_id:0,arr:1}},{ $unwind: '$arr' },{$skip: 1},{$limit:1},{$unwind:"$arr"},{$skip:2},{$limit:1}])。您能否更新您的回答,这样我就可以将其标记为正确的答案? - Humppakäräjät
无论如何,我很惊讶这样一个简单的任务需要这么多的代码。或者是我错过了什么? - Humppakäräjät
@Humppakäräjät 完成了。我很高兴能够帮助! - Dmytro Shevchenko
显示剩余4条评论

1
你所要求的$slice形式不适用于多维数组。每个数组都被单独考虑,因此当前的$slice不支持这种方式。
因此,与使用.aggregate()建议的方式相比,它实际上可以更短地使用索引的“第一个”和“最后一个”值来完成。
db.test.aggregate([
    { "$unwind": "$arr" },
    { "$group": {
        "_id": "$_id",
        "arr": { "$first": "$arr" }
    }},
    { "$unwind": "$arr" },
    { "$group": {
        "_id": "$_id",
        "arr": { "$last": "$arr" }
    }}
])

但在未来的 MongoDB 版本中(当前开发分支3.18),您可以使用$arrayElemAt作为聚合框架的操作符,其工作方式如下:

db.test.aggregate([
    { "$project": {
        "arr": {
            "$arrayElemAt": [
                { "$arrayElemAt": [ "$arr", 0 ] },
                1
            ]
        }
    }}
])

两种方法基本上都会得到相同的结果{ "arr": 22 },不过未来可用的形式在数组索引值上非常灵活,而不是第一个和最后一个。


此外,如果您不知道数组中元素的数量,那么您可能无法使用 $first$last。因此,在我看来,使用 $limit 更加灵活多用途。 - Dmytro Shevchenko
1
@DmytroShevchenko 不是的。更多的聚合管道阶段==更昂贵。完结撒花。越少越好。当然,如果有新的操作符(当它们可用时),意味着单个阶段,因此是最佳结果。老实说,如果这就是你所问的全部复杂性,那么聚合框架“目前”不是答案。在客户端代码中完成,效率更高。 - Blakes Seven
@DmytroShevchenko 哦,是的。如果没有 $group 操作,那么其他呈现的“答案”(带讽刺的兔耳朵)就不可能在未展开的数组上运行。 - Blakes Seven
2
顺便说一下,我已经为你的答案点了赞。虽然我不认为它一定适用于原始帖子的情况。例如,给定以下数组:"arr" : [ [11, 22, 33], [44, 55, 66], [77, 88, 99] ],如何选择[1][1]位置上的元素? - Dmytro Shevchenko
1
@BlakesSeven,恐怕我的问题不够详细。正如@DmytroShevchenko所正确假设的那样,数组可能不仅有两个字段。我肯定需要从"arr":[[11,22,33],[44,55,66],[77,88,99]]中选择arr [1] [1]。因此,$last$first对我帮助不大。 - Humppakäräjät
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接