标签: join tables

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

    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 作为也有不短的历史,搜索得到的内容覆盖很多版本,很难直接应用

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