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 年註定是不平凡的一年,你們且行且珍惜!

相關文章
相關標籤/搜索