如何在 MongoDB 中排序后查找 prev/next 文档

How to find prev/next document after sort in MongoDB

我想查找 prev/next 个发布日期最接近输入文档的博客文档。

下面是文档结构。

集合示例(博客)

{
    blogCode: "B0001",
    publishDate: "2020-09-21"
},
{
    blogCode: "B0002",
    publishDate: "2020-09-22"
},
{
    blogCode: "B0003",
    publishDate: "2020-09-13"
},
{
    blogCode: "B0004",
    publishDate: "2020-09-24"
},
{
    blogCode: "B0005",
    publishDate: "2020-09-05"
}

如果输入是blogCode = B0003

预期输出

{
    blogCode: "B0005",
    publishDate: "2020-09-05"
},
{
    blogCode: "B0001",
    publishDate: "2020-09-21"
}

如何得到输出结果?在sql中,似乎使用ROW_NUMBER可以解决我的问题,但是我找不到解决方案来实现MongoDB中的功能。替代方案可以参考这个 answer (But, it seems inefficient). Maybe using mapReduce 还有更好的方案吗?我现在很迷茫,请大家多多指教

你可以像下面这样。

  1. 我们需要将现有日期与给定日期进行比较。所以我使用 $facet 对两个日期进行分类
  2. 原始数据应该是一个 Eg : B0003。这样我就可以得到 origin[] 数组的第一个元素来与 rest[] 数组
  3. 进行比较
  4. $unwind平了rest[]
  5. 减去两个日期之间的差值
  6. 再次使用 $facet 查找上一个和下一个日期。
  7. 然后将两者结合起来得到你预期的结果

注意 :最终数组可能有 0<elements<=2。如果有一个元素,你给出的预期结果将不会发现它是上一个还是下一个日期。所以我的建议是添加另一个字段来说明 mongo 游乐场显示的日期

[{
    $facet: {
        origin: [{
            $match: { blogCode: 'B0001' }
        }],
        rest: [{
            $match: {
                $expr: {
                    $ne: ['$blogCode','B0001']
                }
            }
        }]
    }
}, {
    $project: {
        origin: {
            $arrayElemAt: ['$origin',0]
        },
        rest: 1
    }
}, {
    $unwind: {path: '$rest'}
}, {
    $project: {
        diff: {
            $subtract: [{ $toDate: '$rest.publishDate' },{ $toDate: '$origin.publishDate'}]
        },
        rest: 1,
        origin: 1
    }
}, {
    $facet: {
        prev: [{
                $sort: {diff: -1}
            },
            {
                $match: {
                    diff: {$lt: 0 }
                }
            },
            {
                $limit: 1
            },
            {
                $addFields:{"rest.type":"PREV"}
            }
        ],
        next: [{
                $sort: { diff: 1 }
            },
            {
                $match: {
                    diff: { $gt: 0 }
                }
            },
            {
                $limit: 1
            },
            {
                $addFields:{"rest.type":"NEXT"}
            }
        ]
    }
}, {
    $project: {
        combined: {
            $concatArrays: ["$prev", "$next"]
        }
    }
}, {
    $unwind: {
        path: "$combined"
    }
}, {
    $replaceRoot: {
        newRoot: "$combined.rest"
    }
}]

工作Mongo playground

启发 varman 提出的解决方案。我还通过使用 includeArrayIndex.

找到了另一种解决问题的方法
[
  {
    $sort: {
      "publishDate": 1
    },
    
  },
  {
    $group: {
      _id: 1,
      root: {
        $push: "$$ROOT"
      }
    },
    
  },
  {
    $unwind: {
      path: "$root",
      includeArrayIndex: "rownum"
    }
  },
  {
    $replaceRoot: {
      newRoot: {
        $mergeObjects: [
          "$root",
          {
            rownum: "$rownum"
          }
        ]
      }
    }
  },
  {
    $facet: {
      currRow: [
        {
          $match: {
            blogCode: "B0004"
          },
          
        },
        {
          $project: {
            rownum: 1
          }
        }
      ],
      root: [
        {
          $match: {
            blogCode: {
              $exists: true
            }
          }
        },
        
      ]
    }
  },
  {
    $project: {
      currRow: {
        $arrayElemAt: [
          "$currRow",
          0
        ]
      },
      root: 1
    }
  },
  {
    $project: {
      rownum: {
        prev: {
          $add: [
            "$currRow.rownum",
            -1
          ]
        },
        next: {
          $add: [
            "$currRow.rownum",
            1
          ]
        }
      },
      root: 1
    }
  },
  {
    $unwind: "$root"
  },
  {
    $facet: {
      prev: [
        {
          $match: {
            $expr: {
              $eq: [
                "$root.rownum",
                "$rownum.prev"
              ]
            }
          }
        },
        {
          $replaceRoot: {
            newRoot: "$root"
          }
        }
      ],
      next: [
        {
          $match: {
            $expr: {
              $eq: [
                "$root.rownum",
                "$rownum.next"
              ]
            }
          }
        },
        {
          $replaceRoot: {
            newRoot: "$root"
          }
        }
      ],
      
    }
  },
  {
    $project: {
      prev: {
        $arrayElemAt: [
          "$prev",
          0
        ]
      },
      next: {
        $arrayElemAt: [
          "$next",
          0
        ]
      },
    }
  },
]

工作 Mongo playground