将本地字段设置为外表

时间:2018-06-06 03:20:19

标签: mongodb aggregation-framework

假设有一个表包含这样的文档:

{ _id: "aaaa", name: "One" }
{ _id: "bbbb", name: "Two" }

包含这样的文档的外表:

{ _id: "cccc", state: "pending",  source_id: "aaaa" }
{ _id: "dddd", state: "finished", source_id: "aaaa" }
{ _id: "eeee", state: "finished", source_id: "aaaa" }
{ _id: "ffff", state: "pending",  source_id: "aaaa" }

现在,我希望得到一个这样的数组:

[
    { _id: "aaaa", name: "One", pending: 2 },
    { _id: "bbbb", name: "Two", pending: 0 }
]

结果文件来自第一张表格。聚合使用状态为“待处理”的匹配(source_id = _id)外国文档的数量填充“pending”字段

怎么会这样做?什么是正确的查询语法?

我尝试过的一个解决方案:使用$ lookup,$ match和$ sort来检索这样的结果:

{
  _id: "aaaa",
  name: "One",
  foreign: [
    { _id: "cccc", state: "pending",  source_id: "aaaa" },
    { _id: "ffff", state: "pending",  source_id: "aaaa" }
  ]
},
{ 
  _id: "bbbb",
  name: "Two"
  foreign: []
}

我能够在我的代码中获取子数组的长度,但是当外表有太多文档时,我最终得到“聚合超过最大文档大小”。因为结果超过16MB。

取得进展

之前的帮助。但是我正在试图弄清楚如何过滤待处理的状态,正如原始问题所述:

db.apps.aggregate([
  {
    "$lookup": {
      "from": "requests",
      "localField": "_id",
      "foreignField": "app_id",
      "as": "foreign"
    }
  },
  {
    "$unwind": "$foreign"
  },
  {
    $match: {
      "foreign.state": 0
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "name": {
        "$first": "$name"
      },
      "pending": {
        "$sum": 1
      }
    }
  }
])

我得到了一些结果。但是,本地/第一个表中没有关联外国文档的文档(没有状态为0的外国文档)根本不会被返回。我希望仍然包含有0个待处理外国文档的行在结果中。

解决方案

db.apps.aggregate([{
    "$lookup": {
      "from": "requests",
      "as": "pending",
      "let": {
        "id": "$_id"
      },
      "pipeline": [{
          "$match": {
            "$expr": {
              "$eq": ["$$id", "$app_id"]
            }
          }
        },
        {
          "$match": {
            "$expr": {
              "$eq": [0, "$state"]
            }
          }
        },
        {
          "$count": "count"
        }
      ]
    }
  },
  {
    "$addFields": {
      "pending": {
        "$arrayElemAt": ["$pending.count", 0]
      }
    }
  }
])

1 个答案:

答案 0 :(得分:1)

您需要$unwind并且基本上减少

db.localcollection.aggregate([
  { "$lookup": {
    "from": "foreigncollection",
    "localField": "_id",
    "foreignField": "source_id",
    "as": "foreign"
  }},
  { "$unwind": "$foreign" },
  { "$group": {
    "_id": "$_id",
    "name":  { "$first": "$name" },
    "pending": { "$sum": 1 }
  }}
])

或者使用MongoDB 3.6,使用富有表现力的$lookup

来减少内部管道
db.localcollection.aggregate([
  { "$lookup": {
    "from": "foreigncollection",
    "as": "pending",
    "let": { "id": "$_id" },
    "pipeline": [
      { "$match": { "$expr": { "$eq": [ "$$id", "$source_id" ] },
      { "$count": "count" }
    ]
  }},
  { "$addFields": { "pending": { "$arrayElemAt": [ "$pending.count", 0 ] } } }
])

如果还有其他任何需要按标准“减少匹配”的话,则会在$match之后直接$unwind或在$match内使用“管道“你有可能做到这一点。

任何一种形式都经过优化,可以在“返回”数组之前执行操作,甚至“不”返回数组以便不破坏BSON限制。关于管道优化和技术在Aggregate $lookup Total size of documents in matching pipeline exceeds maximum document size处发生的情况有一个更详细的解释,它通过示例显示甚至故意破坏该限制然后避免它。