【问题标题】:MongoDB - previous and next inside subdocumentMongoDB - 子文档中的上一个和下一个
【发布时间】:2020-11-02 21:56:11
【问题描述】:

我正在尝试使用聚合框架从我的集合中获取 3 个子文档(我只想使用聚合,因为这是长管道的一部分)。 我只知道一个子文档的一个属性。我还需要检索 subdoc 以及上一个和下一个。 示例:

[
  {
    "title": "Title 34",
    "subDoc": {
      "subtitle": "subtitle 8",
      "order": 8
    }
  },
  {
    "title": "Title 34",
    "subDoc": {
      "subtitle": "subtitle 6",
      "order": 6
    }
  },
  {
    "title": "Title 34",
    "subDoc": {
      "subtitle": "subtitle 11",
      "order": 11
    }
  },
  {
    "title": "Title 34",
    "subDoc": {
      "subtitle": "subtitle 13",
      "order": 13
    }
  },
  {
    "title": "Title 34",
    "subDoc": {
      "subtitle": "subtitle 7",
      "order": 7
    }
  }
]

我只知道一个字幕顺序(例如“11”),我需要得到这样的东西:

[
  {
    "title": "Title 34",
    "subDoc": [
      {
        "subtitle": "subtitle 8",
        "order": 8
      },
      {
        "subtitle": "subtitle 11",
        "order": 11
      },
      {
        "subtitle": "subtitle 13",
        "order": 13
      }
    ]
  }
]

我尝试过使用 unwind、order、limit,但我只能获得上一个或下一个文档,不能同时获得两者。

这里的游乐场: https://mongoplayground.net/p/0228GD-b6oZ

【问题讨论】:

  • 您可以将所有文档分组到一个数组中,然后找到您知道的数组元素的索引。这将为您提供上一个和下一个索引值 - 以及元素。

标签: mongodb aggregation-framework


【解决方案1】:

你可以试试,

  • $sort order 按升序排列
  • $group by title 并制作 subDoc 对象数组和 subDocOrder 订单字段数组
  • $addFields 添加三个字段 prevOrder 上一个订单号($indeOfArray 获取上一个索引 11 并减 1)和 nextOrder 下一个订单号($indeOfArray 获取下一个索引 11 并加 2),@ 987654333@subDoc数组的大小
let order = 11;
db.collection.aggregate([
  { $sort: { "subDoc.order": 1 } },
  {
    $group: {
      _id: "$title",
      subDoc: { $push: "$subDoc" },
      subDocOrder: { $push: "$subDoc.order" }
    }
  },
  {
    $addFields: {
      prevOrder: {
        $subtract: [{ $indexOfArray: ["$subDocOrder", order] }, 1]
      },
      nextOrder: {
        $add: [{ $indexOfArray: ["$subDocOrder", order] }, 2]
      },
      sizeSubDoc: { $size: "$subDoc" }
    }
  },
  • $project显示必填字段,并获取subDoc数组
  • $range 从 2 个条件中获取范围,首先检查 prevOrder 小于 0 然后返回 0 否则返回 preOrder,第二次检查 nextOrder 大于 subDoc 数组的大小然后返回大小否则返回 nextOrder ,最后我们得到了范围的开始号和结束号
  • $map 迭代上面创建的范围数组的循环,我们将在 map 中输入,签入将返回 $arrayElemAt 形成范围数组
  {
    $project: {
      _id: 0,
      title: "$_id",
      subDoc: {
        $cond: [
          { $in: [order, "$subDocOrder"] },
          {
            $map: {
              input: {
                $range: [
                  { $cond: [{ $lt: ["$prevOrder", 0] }, 0, "$prevOrder"] },
                  { $cond: [{ $gt: ["$nextOrder", "$sizeSubDoc"] }, "$sizeSubDoc", "$nextOrder"] }
                ]
              },
              in: { $arrayElemAt: ["$subDoc", "$$this"] }
            }
          },
          []
        ]
      }
    }
  }
])

Playground


涵盖的测试用例:

  1. 如果当前元素是第一个元素意味着没有前一个元素怎么办? (这将只返回当前和下一个元素)
  2. 如果当前元素是最后一个元素意味着没有下一个元素怎么办? (这将只返回当前和以前的元素)
  3. 如果subDoc 只有一个元素是当前的怎么办? (这将只返回单个当前元素)
  4. 找不到您的输入 11 是什么? (这将返回空白数组)

【讨论】:

    【解决方案2】:

    您可以使用以下阶段实现您的用例:

    1. $排序subDoc.order
    2. $将所有文档分组到一个数组中
    3. 查找前一个和当前数组元素的索引
    4. $group 基于title 并使用 $addToSet 附加所需的文档
    5. $project 阶段将数据转换为预期输出

    根据示例文档考虑的边缘情况:

    1. 没有以前的文档 例如:subDoc.order=6,返回文档 [6,7]
    2. 没有当前文档 例如:subDoc.order= 14,返回空
    3. 没有下一个文档 例如:subDoc.order= 13,返回文档 [11,13]
    4. 当前文档存在,但没有上一个或下一个文档,当document count=1

    您可以使用以下聚合查询:

    db.collection.aggregate([
      {
        "$sort": {
          "subDoc.order": 1
        }
      },
      {
        $project: {
          "title": 1,
          "order": "$subDoc.order",
          "subtitle": "$subDoc.subtitle"
        }
      },
      {
        $group: {
          _id: null,
          data: {
            $push: "$$ROOT"
          },
          count: {
            "$sum": 1
          }
        }
      },
      {
        $addFields: {
          "previousDoc": {
            $subtract: [
              {
                $indexOfArray: [
                  "$data.order",
                  11
                ]
              },
              1
            ]
          },
          "nextDoc": {
            $add: [
              {
                $indexOfArray: [
                  "$data.order",
                  11
                ]
              },
              1
            ]
          },
          currentDoc: {
            $indexOfArray: [
              "$data.order",
              11
            ]
          }
        }
      },
      {
        "$addFields": {
          "next": {
            $arrayElemAt: [
              "$data",
              "$nextDoc"
            ]
          },
          previous: {
            $arrayElemAt: [
              "$data",
              "$previousDoc"
            ]
          },
          current: {
            $arrayElemAt: [
              "$data",
              "$currentDoc"
            ]
          }
        }
      },
      {
        $unwind: "$data"
      },
      {
        $group: {
          _id: "$data.title",
          p: {
            "$addToSet": {
              $cond: [
                {
                  $and: [
                    {
                      $lt: [
                        "$currentDoc",
                        1
                      ]
                    },
                    
                  ]
                },
                "$$REMOVE",
                "$previous"
              ]
            }
          },
          c: {
            "$addToSet": {
              $cond: [
                {
                  $and: [
                    {
                      $lt: [
                        "$currentDoc",
                        0
                      ]
                    },
                    {
                      $ne: [
                        "$count",
                        1
                      ]
                    }
                  ]
                },
                "$$REMOVE",
                "$current"
              ]
            }
          },
          n: {
            "$addToSet": {
              $cond: [
                {
                  $lt: [
                    "$nextDoc",
                    1
                  ]
                },
                "$$REMOVE",
                "$next"
              ]
            }
          },
          
        }
      },
      {
        $project: {
          subDoc: {
            "$concatArrays": [
              "$p",
              "$c",
              "$n"
            ]
          }
        }
      },
      {
        $project: {
          "subDoc.subtitle": 1,
          "subDoc.order": 1
        }
      }
    ])
    

    MongoDB Playground

    【讨论】:

      【解决方案3】:

      如果你真的想用聚合来做,但结构被修改了,我认为这是解决方案,你可以在方便时映射它

      db.collection.aggregate([
        {
          "$sort": {
            "subDoc.order": 1
          }
        },
        {
          "$group": {
            "_id": "$title",
            "next": {
              "$push": {
                "$cond": [
                  {
                    "$gt": [
                      "$subDoc.order",
                      11
                    ]
                  },
                  {
                    "id": "$_id",
                    "item": "$$CURRENT.subDoc"
                  },
                  "$$REMOVE"
                ]
              }
            },
            "prev": {
              "$push": {
                "$cond": [
                  {
                    "$lt": [
                      "$subDoc.order",
                      11
                    ]
                  },
                  {
                    "id": "$_id",
                    "item": "$$CURRENT.subDoc"
                  },
                  "$$REMOVE"
                ]
              }
            },
            "current": {
              "$push": {
                "$cond": [
                  {
                    "$eq": [
                      "$subDoc.order",
                      11
                    ]
                  },
                  {
                    "id": "$_id",
                    "item": "$$CURRENT.subDoc"
                  },
                  "$$REMOVE"
                ]
              }
            }
          }
        },
        {
          $project: {
            _id: 1,
            next: {
              $slice: [
                "$next",
                1
              ]
            },
            prev: {
              $slice: [
                "$prev",
                -1
              ]
            },
            current: 1
          }
        }
      ])
      

      输出如下所示

      [
        {
          "_id": "Title 34",
          "current": [
            {
              "id": "33",
              "item": {
                "order": 11,
                "subtitle": "subtitle 11"
              }
            }
          ],
          "next": [
            {
              "id": "15",
              "item": {
                "order": 12,
                "subtitle": "subtitle 12"
              }
            }
          ],
          "prev": [
            {
              "id": "34",
              "item": {
                "order": 8,
                "subtitle": "subtitle 8"
              }
            }
          ]
        }
      ]
      

      【讨论】:

        猜你喜欢
        • 2014-11-15
        • 1970-01-01
        • 2021-12-15
        • 2021-02-01
        • 2020-07-23
        • 1970-01-01
        • 1970-01-01
        • 2017-01-18
        相关资源
        最近更新 更多