MongoDB 实现多表联查并更新数据

最近有个需求,用 SQL 描述起来大约是这样的:

# pages 表按照 projectId 统计每个 project 的页面数
# 然后更新到 projects 表的 `pageCount` 字段里
UPDATE `projects` a LEFT JOIN
  (
    SELECT COUNT('x') as `num`, `projectId`
    FROM `pages`
    WHERE `isDeleted`=false
    GROUP BY `projectId`
  ) b
  ON a.`_id`=b.`projectId`
SET `pageCount`=`num`
WHERE a.`isDeleted`=false

# 上面 SQL 凑合看,我好久不写了,有点忘记怎么写……

MongoDB Shell 其实就是封装好的 JavaScript + Node.js 16 REPL 环境,我们熟悉的箭头函数、异步函数等都可以放心使用,所以写起来大约是这样:

// 进入聚合状态
db.projects.aggregate([
  // 聚合状态会从上至下执行,所以我们先筛选出来合适的 projects
  {
    $match: {
      creatorId: '我的用户id',
      isDeleted: false,
      kind: 'Normal',
    },
  },
  // 因为我厂 pages 表里 projectId 是字符类型,所以这里要先转换一次
  {
    $addFields: {
      projectId: { '$toString': '$_id'},
    }
  },
  // 接下来就可以聚合了,用本地的 projectId 对上连表的 projectId,连起来之后的字段名为 pages,它应该是个数组
  {
    $lookup: {
      from: 'pages',
      localField: 'projectId',
      foreignField: 'projectId',
      as: 'pages',
    }
  },
  // 再添加一个字段用来存储 pages 的长度,即有多少个页面,这里要加 `$` 前缀
  {
    $addFields: {
      pageCount: {
        $size: '$pages'
      }
    }
  }
])
// 聚合完毕之后,再把内容写入数据库
.forEach(doc => {
  db.projects.updateOne({ _id: doc._id}, {
    $set: { pageCount: doc.pageCount }
  })
});

对于我来说,操作 mongosh 的难点主要在于:

  1. 很多时候要加 $ 前缀,一会儿一个一会儿两个,不熟悉经常搞错
  2. 不知道是否该用异步函数或者 .then()
  3. 不知道 $addFields 函数
  4. 没法打断点调试,需要一长串执行完才知道结果
  5. MongoDB 作为也有不短的历史,搜索得到的内容覆盖很多版本,很难直接应用

希望将来能慢慢克服这些难点。欢迎诸位读者指教,如有问题,也欢迎提出讨论。

如果您觉得文章内容对您有用,不妨支持我创作更多有价值的分享:


已发布

分类

来自

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据