Mongodb 如何对子文档数组使用$lookup

Mongodb 如何对子文档数组使用$lookup,mongodb,mongoose,aggregation-framework,Mongodb,Mongoose,Aggregation Framework,我有以下模式: const chatbots = new Schema({ name: String, campaigns: [{ name: String, channels: [{ _id: String, name: String, budget: Number }] }] }); const chatbotusers = new Schema({ name: String, campaign_channel

我有以下模式:

const chatbots = new Schema({
  name: String,
  campaigns: [{
    name: String,
    channels: [{
      _id: String,
      name: String,
      budget: Number
    }]
  }]
});

const chatbotusers = new Schema({
  name: String,
  campaign_channel: String
})
我需要得到一个活动列表,每个频道的用户总数。大概是这样的:

[
  {
    "name": "Campaign #1",
    "channels": {
      "_id": "eyRyZ1gD0",
      "name": "Channel #1",
      "users": 10
    }
  },
  {
    "name": "Campaign #1",
    "channels": {
      "_id": "tsKH7WxE",
      "name": "Channel #2",
      "users": 4
    }
  }
]
{
  $lookup: {
    from: "chatbotusers",
    localField: "channels._id",
    foreignField: "campaign_channel",
    as: "users",
  }
},
{
  $project: {
    name: "$name",
    channels: {
      $map: {
        input: "$channels",
        as: "channel",
        in: {
          _id: "$$channel._id",
          name: "$$channel.name",
          users: { $size: "$users" },
        }
      }
    }
  }
}
有什么想法吗

我得到的最远的结果是这样的:

[
  {
    "name": "Campaign #1",
    "channels": {
      "_id": "eyRyZ1gD0",
      "name": "Channel #1",
      "users": 10
    }
  },
  {
    "name": "Campaign #1",
    "channels": {
      "_id": "tsKH7WxE",
      "name": "Channel #2",
      "users": 4
    }
  }
]
{
  $lookup: {
    from: "chatbotusers",
    localField: "channels._id",
    foreignField: "campaign_channel",
    as: "users",
  }
},
{
  $project: {
    name: "$name",
    channels: {
      $map: {
        input: "$channels",
        as: "channel",
        in: {
          _id: "$$channel._id",
          name: "$$channel.name",
          users: { $size: "$users" },
        }
      }
    }
  }
}
但它计算的是该活动的用户数,而不是该频道的用户数


(很抱歉,如果问题标题不合适,我甚至不知道如何正确提问)

您可以尝试以下查询:

db.chatbots.aggregate([
    {
        $lookup: {
            from: "chatbotusers",
            localField: "campaigns.channels._id",
            foreignField: "campaign_channel",
            as: "users"
        }
    },
    {
        $addFields: {
            campaigns: {
                $map: {
                    input: "$campaigns",
                    as: "eachCampaign",
                    in: {
                        $mergeObjects: ['$$eachCampaign', {
                            channels:
                            {
                                $reduce: {
                                    input: "$$eachCampaign.channels",
                                    initialValue: [],
                                    in: {
                                        $concatArrays: [
                                            "$$value",
                                            [
                                                {
                                                    $mergeObjects: [
                                                        "$$this",
                                                        {
                                                            user: {
                                                                $size: {
                                                                    $filter: {
                                                                        input: "$users",
                                                                        as: "e",
                                                                        cond: {
                                                                            $eq: [
                                                                                "$$e.campaign_channel",
                                                                                "$$this._id"
                                                                            ]
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    ]
                                                }
                                            ]
                                        ]
                                    }
                                }
                            }
                        }]
                    }
                }
            }
        }
    },
    {
        $project: {
            users: 0
        }
    }
])
注意:有多种方法可以做到这一点,但通过这种方法,我们正在处理来自聊天机器人集合的相同数量的文档,而不是通过执行
$unwind
来分解文档,这在您拥有庞大数据集时可能会有所帮助

测试:

上面的查询应该可以满足您的需要,但在任何情况下,如果查询速度较慢或您认为需要增强,请点击此处:

{
    user: {
        $size: {
            $filter: {
                input: "$users", as: "e",
                    cond: {
                    $eq: [
                        "$$e.campaign_channel",
                        "$$this._id"
                    ]
                }
            }
        }
    }
}
我们在每个活动中通过用户数组对每个频道进行迭代,因此在查找之后,您可以使用
reduce
用户进行一次迭代,以获得每个唯一的活动_频道的计数将此数据替换为用户数组,这样,您可以直接获得用户数。一般来说,上述查询的主要目的是保留原始文档结构,使用较少的阶段

或者,您可以使用此查询,该查询不保留原始文档结构(输出中的文档数量也可以超过集合中的文档数量),但可以执行您需要的操作:

db.chatbots.aggregate([
  {
    $unwind: "$campaigns"
  },
  {
    $unwind: "$campaigns.channels"
  },
  {
    $lookup: {
      from: "chatbotusers",
      localField: "campaigns.channels._id",
      foreignField: "campaign_channel",
      as: "users"
    }
  },
  {
    $addFields: {
      "channels": "$campaigns.channels",
      campaigns: "$campaigns.name"
    }
  },
  {
    $addFields: {
      "channels.users": {
        $size: "$users"
      }
    }
  },
  {
    $project: {
      users: 0
    }
  }
])

测试:

如果您可以显示示例数据(简化,在查找后)和预期输出,例如使用mongoplayground.net、thanksThanks和@mickl,则会更容易。我不知道mongoplayground.net。非常感谢你给我这个超级完整的答案,@whoami。我最后选择了最后一个选项,因为我还需要其他一些东西,但我没有把它们包括在问题中。无论如何,非常感谢!