think-mongo 升级适配 MongoDB 4

今天是端午节,首先祝你们端午节快乐,而后今天要和你们说一下最近对 think-mongo 模块作了一些升级。ThinkJS 3 虽然已经支持使用 think-mongoose 来接入 mongoose 模块,不过由于文档这块默认仍是 think-mongo 因此用这个模块的同窗仍是比较多。而这个模块由于是两三年前开发的了,依赖的 mongodb 模块一直是 2.x 的版本,适配 MongoDB 数据库 2.0 版本。而 MongoDB 在 2015 年的时候就升级到了 3.0 版本,2018 年的时候升级到了 4.0 的版本。其中 4.0 的版本增长了事务的功能,这个让许多同窗很是心动。因此不少同窗提 issue 问可否将 think-mongo 升级一下支持最新版的 MongoDB。javascript

想要适配 MongoDB 其实就是要升级底层依赖的 mongodb 模块,而它在适配新版的同时修改了大量的 API 接口,致使咱们没办法直接修改版本号搞定这件事情。以前由于业务中没有 MongoDB 数据库的需求,因此咱们也就没有对这个事情作处理,本来是但愿有需求的同窗能够自行提 PullRequest 的。不过刚好最近新项目中有须要使用 MongoDB 的事务,因此我这边就对其进行了新版本适配的处理。html

API 适配

大部分的 API 变动能够在 CHANGES_3.0.0.md 这个文档中找到。首先是以前全部在 Db 类上的方法拆分红了 Client 类和 Db 类,这个影响了 MongoDB 建立链接时的逻辑。java

//以前的写法
MongoClient.connect('mongodb://localhost:27017/test', (err, db) => {
  // Database returned
});

//如今的写法
MongoClient.connect('mongodb://localhost:27017/test', (err, client) => {
  // Client returned
  var db = client.db('test');
});
复制代码

另外就是增删改查的 API 作了修改,包括但不局限于node

  • collection.insert() 被细化成了 collection.insertOne()collection.insertMany()
  • collection.remove() 被细化成了 collection.deleteOne()collection.deleteMany()
  • collection.update() 被细化成了 collection.updateOne()collection.updateMany()
  • collection.find(where, field) 被拆分红了 collection.find(where).project(field)

能够看到操做都分红了单个操做和多个操做。think-mongo 中由于有 add()addMany() 因此正好能够对应 insertOne()insertMany() ,而 update()delete() 是没有作单个和多个的区别的,因此只能映射到 updateMany()deleteMany() 方法。git

还有一个修改是 collection.aggregate() 方法,原先直接返回结果如今返回后还须要执行下 cursor.toArray() 才能拿到结果。github

事务

升级后就可使用 MongDB 原生的事务特性了。MongoDB 的事务操做和 SQL 的事务操做不太同样,SQL 类的是经过 START TRANSACTIONCOMMITROLLBACK 语句标记来记录一次事务的。MongoDB 的我以为更像是 JS 操做。mongodb

const client = new MongoClient(uri);
await client.connect();
const session = client.startSession();

try {
  session.startTransaction();
  await client.db('think_db').add({name: 'thinkjs'}, {session});
  await session.commitTransaction();
} catch(e) {
  await session.abortTransaction(); 
} finally {
  await session.endSession();
}

await client.close();
复制代码

能够看到 MongoDB 会给每次事务建立一个 session 会话记录,全部的 CURD 操做须要将会话标记经过参数的形式传入进去,这样全部的操做就能挂载到当前的事务会话中。最后经过 commitTransactionabortTransaction() 来对事务提交进行操做。基于这个原理咱们包装了 transaction() 方法,用来帮助你们方便的实现事务操做。数据库

// src/controller/user.js
module.exports = class extends think.Controller{
  async indexAction() {
    const UserModel = this.mongo('user');
    const PostModel = this.mongo('post');
    await UserModel.transaction(async session => {
      PostModel.options.session = session;
      const userId = await UserModel.add({name: 'lizheming'});
      await PoserModel.add({userId, content: 'Hello World'});
    });
  }
}
复制代码

能够看到咱们并无在 add() 操做中将 session 会话标记传入,这是由于咱们会将它放在示例的 options 属性中,以后的全部的 CURD 操做都会将它透传下去,这样就避免了咱们人工显式地去传递它了。同时因为 session 标记只在当前建立事务的表中存在,因此在进行多表操做的时候,为了让其它表也能透传会话标记,须要显式的进行标记赋值操做PostModel.options.session = session;,保证跨表操做都能记录在一次事务中。session

后记

因为事务须要 MongoDB 开启集群模式,而咱们在 Travis CI 上跑单元测试的时候依赖了它提供的 mongodb 服务,为了让这个服务能切换成集群模式顺利将单元测试跑成功我又作了好多无心义的提交。主要是网上查到的资料也都比较老了,不少 MongoDB 的配置都作了修改,好比找到的资料是 toml 格式的配置,而新的配置已是 yaml 的配置了。固然这些也都是我踩了好几下坑才发现的就是了。async

以上就是 think-mongo 升级适配最新的 MongoDB 4.x 的一些总结,内部的适配都已经在 think-mongo@2.1.0 版本中集成,有须要的同窗能够升级下对应的模块使用。若是项目中有直接使用 mongodb 模块提供的原生方法操做数据的话须要注意按照本文说的一些变动进行修改适配,切记!

最后的最后,再一次祝你们端午节快乐。2020 年注定是不平凡的一年,你们且行且珍惜!

相关文章
相关标签/搜索