MongoDB / Mongoose如何通过_id返回嵌套的子文档

时间:2019-11-18 19:49:32

标签: mongodb mongoose nested-documents

MongoDB新手在这里。

我有一个看起来像这样的“客户”文件:

{
  name: "myClient",
  products: [{
    name: "myProduct1",
    environments: [{
        name: "myEnvironment1",
        changeLogs: [
          { "some": "fields21" },
          { "some": "fields22" }
        ]
      },
      {
        name: "myEnvironment2",
        changeLogs: [
          { "some": "fields11" },
          { "some": "fields12" }
        ]
      }
    ]
  },
  {
    name: "myProduct2",
    environments: [{
        name: "myEnv1",
        changeLogs: [
          { "some": "fields1" },
          { "some": "fields2" }
        ]
      },
      {
        name: "myEnv1",
        changeLogs: [
          { "some": "fields3" },
          { "some": "fields4" }
        ]
      }
    ]
  }]
}

因此,一个客户有很多products,其中有很多environments,还有很多changeLogs。我希望返回给定环境的changeLogs列表,仅继续执行environment._id

仅使用此client,我就能找到正确的_id文档:

db.clients.find({'products.environments._id': ObjectId("5a1bf4b410842200043d56ff")})

但这将返回整个client文档。我想要的是使用changeLogs

从环境中仅返回_id: ObjectId("5a1bf4b410842200043d56ff")数组

假设我拥有第一个_id的第一个environment的{​​{1}},我想要的输出如下:

product

您会建议我使用哪种查询来实现这一目标?

在此先感谢您的帮助。到目前为止,这些文档只是令人困惑,但我相信我最终会到达那里!

3 个答案:

答案 0 :(得分:1)

这里的想法是$unwind个产品数组,以便可以将其环境作为对_id上的$filter之后的$match的输入。

(假设环境_id为1)

db.collection.aggregate([
  {
    $unwind: "$products"
  },
  {
    $match: {
      "products.environments._id": 1
    }
  },
  {
    $project: {
      "logsArray": {
        $filter: {
          input: "$products.environments",
          as: "env",
          cond: {
            $eq: [
              "$$env._id",
              1
            ]
          }
        }
      }
    }
  },
  {
    $unwind: "$logsArray"
  }
])

O / P应该是:

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "logsArray": {
      "changeLogs": [
        {
          "some": "fields21"
        },
        {
          "some": "fields22"
        }
      ],
      "id": 1,
      "name": "myEnvironment1"
    }
  }
]

注意:请注意$unwind的最后一个阶段logsArray,我认为这是相当不错的结果。否则,如果没有它,结果也是可以接受的(如果您同意,可以将其删除)。

答案 1 :(得分:1)

这只是进行聚合查询的另一种方法。这样可以达到预期的效果。

注意,我使用的是您提供的示例文档中“环境”的“名称”字段。可以根据需要用“ id”替换“名称”。

var ENV = "myEnvironment1";

db.env.aggregate( [
  { $match: { 
  { $unwind: "$products" },
  { $unwind: "$products.environments" },
  { $match: { "products.environments.name": ENV} },
  { $project: { _id: 0, changeLogs: "$products.environments.changeLogs" } },
] )

结果:

{ "changeLogs" : [ { "some" : "fields21" }, { "some" : "fields22" } ] }

如果变量ENV的值被更改,那么结果将是相应的;例如:ENV = "myEnv1";

{ "changeLogs" : [ { "some" : "fields1" }, { "some" : "fields2" } ] }
{ "changeLogs" : [ { "some" : "fields3" }, { "some" : "fields4" } ] }

答案 2 :(得分:0)

db.clients.aggregate([
  {
    $unwind: "$products"
  },
  {
    $unwind: "$products.environments" 
  },
  {
    $match: { "products.environments._id": ObjectId("5a1bf4b410842200043fffff") }
  },
  {
    $project: { _id: 0, changeLogs: "$products.environments.changeLogs" }
  }
]).pretty()

结果:

{
  "changeLogs": [
    { "some": "fields21" },
    { "some": "fields22" }
  ]
}

对于那些发现代码令人困惑的人,我发现一次只添加一个聚合方法,查看结果,然后在管道中添加下一个方法非常有用。管道完成后,我还尝试删除一些中间步骤,以查看是否可以通过减少管道次数获得相同的结果。