在 mongodb 聚合中使用 $match 后如何包含一个空数组

How can I include an empty array after using $match in mongodb aggregation

我正在尝试获取具有空 cards 数组的 boards.lists 或一些未存档的卡片,但使用 $match$or 运算符不起作用。我该如何解决这个问题??

示例文档如下:

{
_id: ObjectId("616bcd746bfed71fdcf892c3"),
userId: ObjectId("611cb3a14f142d5d94daa395"),
name: "myworkspace",
description: "",
boards: [
 {
    _id: ObjectId("6175f8c69c03810ea8e7b31e"),
    name: 'myboard',
    color: '',
    labels: [
      { color: 'blue', title: 'project1' }
    ],
    lists: [
     {
       archived: true,
       _id: ObjectId("6175f8c69c03810ea8e7b321"),
       cards: []
     },
     {
       archived: false,
       _id: ObjectId("6175f8c69c03810ea8e7b322"),
       cards: []
     },
     {
       archived: false,
       _id: ObjectId("6175f8c69c03810ea8e7b323"),
       cards: [{
         _id: ObjectId("61721cbc3092d32970cf2fed")
         archived: true,
         labelSelected: [],
         title: "mycard",
         description: "",
         attachments: [],
         comments: []
       }]
     },
     {
       archived: false,
       _id: ObjectId("6175f8c69c03810ea8e7b324"),
       cards: [{
         _id: ObjectId("6175f8d09c03810ea8e7b32d")
         archived: false,
         labelSelected: [],
         title: "mycard",
         description: "",
         attachments: [],
         comments: []
       }]
     },
   ]
  }
 ]
}

我做了什么:

 docs = await WorkspaceModel.aggregate([
      { $match: { _id: mongoose.Types.ObjectId(workspaceId) } },
      { $unwind: "$boards" },
      { $match: { "boards._id": mongoose.Types.ObjectId(boardId) } },
      { $unwind: "$boards.lists" },
      { $match: { "boards.lists.archived": false } },
      {
        $unwind: {
          path: "$boards.lists.cards",
          preserveNullAndEmptyArrays: true,
        },
      },
      {  
        $match: {
          $or: [
             { "boards.lists.cards": { $exists: true, $size: 0 } },
             { "boards.lists.cards.archived": false }
           ]
         },
       },
      {
        $group: {
          _id: "$boards._id",
          name: { $first: "$boards.name" },
          color: { $first: "$boards.color" },
          labels: { $first: "$boards.labels" },
          lists: { $addToSet: "$boards.lists" },
        },
      },
    ]).exec();

我得到的:[],在$match语句之前,它只显示所有未归档的列表。

我的期望:

[
  {
    _id: ObjectId("6175f8c69c03810ea8e7b31e"),
    name: 'myboard',
    color: '',
    labels: [
      { color: 'blue', title: 'project1' }
    ],
    lists: [
     {
       archived: false,
       _id: ObjectId("6175f8c69c03810ea8e7b322"),
       cards: []
     },
     {
       archived: false,
       _id: ObjectId("6175f8c69c03810ea8e7b324"),
       cards: [{
         _id: ObjectId("6175f8d09c03810ea8e7b32d")
         archived: false,
         labelSelected: [],
         title: "mycard",
         description: "",
         attachments: [],
         comments: []
       }]
     },
   ]
  }
]

问题出在你的匹配上 根据您的示例数据,如果您需要,我可以使用匹配和组创建这样的聚合,您可以更改或添加一些管道

https://mongoplayground.net/p/SWr_q-bI9A5

db.collection.aggregate([
  {
    "$unwind": "$lists"
  },
  {
    $match: {
      $or: [
        {
          "lists.cards": []
        },
        {
          "lists.cards": {
            $elemMatch: {
              "archived": false
            }
          }
        }
      ]
    }
  },
  {
    "$group": {
      "_id": "_id",
      "lists": {
        "$addToSet": "$lists"
      },
      "name": {
        "$first": "$name"
      },
      "lables": {
        "$first": "$labels"
      },
      "color": {
        "$first": "$color"
      },
      
    }
  }
])

查询

  • 展开板
  • 从列表中只保留具有
    (and (= archived false) (or empty_cards cards_contains_not_archived_card)
  • 如果您想过滤掉空列表,您也可以添加一个匹配项
    {"$match" : {"lists": {"$ne" : [] }}}
  • 在您的预期输出中,您没有 _id 来知道每个列表属于哪个文档,在下面的查询中会发生同样的情况

PlayMongo

aggregate(
[{"$unwind": {"path": "$boards"}},
 {"$replaceRoot": {"newRoot": "$boards"}},
 {"$set": 
   {"lists": 
     {"$filter": 
       {"input": "$lists",
        "cond": 
        {"$and": 
          [{"$eq": ["$$this.archived", false]},
           {"$or": 
             [{"$eq": ["$$this.cards", []]},
             {"$in": [false, "$$this.cards.archived"]}]}]}}}}}])

这是我的解决方案:

  1. $filter 非存档列表首先用作 $map
  2. 的输入
  3. $filter 非存档或空卡片并将结果映射到每个列表
  4. 它将显示没有卡片或有非存档卡片的非存档列表
 docs = await WorkspaceModel.aggregate([
      { $match: { _id: mongoose.Types.ObjectId(workspaceId) } },
      { $unwind: "$boards" },
      { $match: { "boards._id": mongoose.Types.ObjectId(boardId) } },
      { $replaceRoot: { newRoot: "$boards" } },
      {
        $set: {
          lists: {
            $map: {
              input: {
                $filter: {
                  input: "$lists",
                  as: "list",
                  cond: {
                    $eq: ["$$list.archived", false],
                  },
                },
              },
              as: "filteredList",
              in: {
                title: "$$filteredList.title",
                cards: {
                  $filter: {
                    input: "$$filteredList.cards",
                    as: "card",
                    cond: {
                      $or: [
                        { $eq: ["$$card", []] },
                        { $eq: ["$$card.archived", false] },
                      ],
                    },
                  },
                },
              },
            },
          },
        },
      },
    ]).exec();