为什么 insertmany 不能使用 mongoos 进行交易?

use*_*513 0 javascript mongoose mongodb node.js mongoose-schema

我正在尝试使用 .inertMany但我无法插入数据为什么?我正在使用mongoose session如果发生任何错误然后我回滚更改

https://codesandbox.io/s/dreamy-bell-9u0bz

app.get("/saveData", async (req, res, next) => {
  const session = await mongoose.startSession();
  session.startTransaction();
  try {
    const data = [
      {
        empid: "Ad",
        id: 4,
        date: "19-Jul-2019"
      },
      {
        empid: "Bc",
        id: 56,
        date: "18-Jul-2019"
      },

      {
        empid: "C",
        id: 6,
        date: "11-Jul-2019"
      }
    ];
    console.log("before save");
    let saveBlog = await BlogPostModel.insertMany(data, { session }); //when fail its goes to catch
    await session.commitTransaction();
    return res.send(saveBlog);
  } catch (error) {
    console.log(error);
    await session.abortTransaction();
    return res.status(400).send(error);
  }
});
Run Code Online (Sandbox Code Playgroud)

Nei*_*unn 5

由于您似乎没有理解标记的重复项对您最后一个问题评论,这里是一个直接演示:

const { Schema } = mongoose = require('mongoose');

const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true, useUnifiedTopology: true };

mongoose.Promise = global.Promise;

mongoose.set('debug', true);
mongoose.set('useCreateIndex', true);
mongoose.set('useFindAndModify', false);

const blogPostSchema = new Schema({
  id: { type: Number, unique: true },
  empid: String,
  date: Date
});

const BlogPost = mongoose.model('BlogPost', blogPostSchema);

const sampleData = [
  { empid: "test13", id: 6, date: '11-Jul-2019' },
  { empid: "test123", id: 4, date: '19-Jul-2019' },
  { empid: "test13", id: 4, date: '18-Jul-2019' }
];

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  try {
    const conn = await mongoose.connect(uri, opts);

    // Clean data
    await Promise.all(
      Object.values(conn.models).map(m => m.deleteMany())
    );

    // Collections must existi in transactions
    await Promise.all(
      Object.values(conn.models).map(m => m.createCollection())
    );


    // With Transaction
    log("With Transaction");
    let session = await conn.startSession();
    session.startTransaction();

    try {
      await BlogPost.insertMany(sampleData, { session });
      await session.commitTransaction();
    } catch(e) {
      // Show the error and abort
      log({ err: e.errmsg, result: e.result.result.writeErrors });
      await session.abortTransaction();
    }

    log({ results: (await BlogPost.find()) });

    // No transaction
    log("Without Transaction");
    try {
      await BlogPost.insertMany(sampleData);
    } catch(e) {
      // Show the error
      log({ err: e.errmsg, result: e.result.result.writeErrors });
    }

    log({ results: (await BlogPost.find()) });


  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }


})();
Run Code Online (Sandbox Code Playgroud)

和输出:

Mongoose: blogposts.createIndex({ id: 1 }, { unique: true, background: true })
Mongoose: blogposts.deleteMany({}, {})
"With Transaction"
Mongoose: blogposts.insertMany([ { _id: 5d8f28ac462a1e1a8c6838a2, empid: 'test13', id: 6, date: 2019-07-10T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a3, empid: 'test123', id: 4, date: 2019-07-18T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a4, empid: 'test13', id: 4, date: 2019-07-17T14:00:00.000Z, __v: 0 } ], { session: ClientSession("650da06d23544ef8bc1d345d93331d1e") })
{
  "err": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
  "result": [
    {
      "code": 11000,
      "index": 2,
      "errmsg": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
      "op": {
        "_id": "5d8f28ac462a1e1a8c6838a4",
        "empid": "test13",
        "id": 4,
        "date": "2019-07-17T14:00:00.000Z",
        "__v": 0
      }
    }
  ]
}
Mongoose: blogposts.find({}, { projection: {} })
{
  "results": []
}
"Without Transaction"
Mongoose: blogposts.insertMany([ { _id: 5d8f28ac462a1e1a8c6838a5, empid: 'test13', id: 6, date: 2019-07-10T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a6, empid: 'test123', id: 4, date: 2019-07-18T14:00:00.000Z, __v: 0 }, { _id: 5d8f28ac462a1e1a8c6838a7, empid: 'test13', id: 4, date: 2019-07-17T14:00:00.000Z, __v: 0 } ], {})
{
  "err": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
  "result": [
    {
      "code": 11000,
      "index": 2,
      "errmsg": "E11000 duplicate key error collection: test.blogposts index: id_1 dup key: { id: 4 }",
      "op": {
        "_id": "5d8f28ac462a1e1a8c6838a7",
        "empid": "test13",
        "id": 4,
        "date": "2019-07-17T14:00:00.000Z",
        "__v": 0
      }
    }
  ]
}
Mongoose: blogposts.find({}, { projection: {} })
{
  "results": [
    {
      "_id": "5d8f28ac462a1e1a8c6838a5",
      "empid": "test13",
      "id": 6,
      "date": "2019-07-10T14:00:00.000Z",
      "__v": 0
    },
    {
      "_id": "5d8f28ac462a1e1a8c6838a6",
      "empid": "test123",
      "id": 4,
      "date": "2019-07-18T14:00:00.000Z",
      "__v": 0
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

请注意,在使用事务时,没有项目插入到集合中。使用insertMany()具有默认行为的ordered: true将插入所有批处理项目,直到遇到任何错误为止。

还请注意,因为您确实期望出现错误,因此必须在它自己的try..catch或类似的错误处理程序中包含这样的语句。否则任何错误(在示例案例中是预期的)将简单地落在外部 catch,当然在演示中它只是简单地退出程序。


实际上不是在问题本身,而是在如何使用 Mongoose 使用 MongoDB 事务的演示中实际上没有提到的东西确实,您应该知道当事务处于活动状态时,您还必须session任何后续读取中包含该属性,以便查看在该事务中所做的更改。

例如,以下将不显示集合中的内容:

let session = await conn.startSession();
session.startTransaction();

try {
  await BlogPost.insertMany(sampleData, { session });
  let documents = await BlogPost.find(); // This would return nothing
  await session.commitTransaction();
} catch(e) {
  // Show the error and abort
  log({ err: e.errmsg, result: e.result.result.writeErrors });
  await session.abortTransaction();
}
Run Code Online (Sandbox Code Playgroud)

但是,包括session在 a 中find()实际上会显示插入的内容:

try {
  await BlogPost.insertMany(sampleData, { session });
  // Actually includes the session and therefore the state
  let documents = await BlogPost.find({},{ session });

  await session.commitTransaction();
} catch(e) {
  // Show the error and abort
  log({ err: e.errmsg, result: e.result.result.writeErrors });
  await session.abortTransaction();
}
Run Code Online (Sandbox Code Playgroud)

当然,在这种情况下,读取将取决于insertMany() 不会因任何原因失败,因为任何错误都会导致在catch发出下一个请求之前退出。

一旦事务被提交,它当然对连接的全局状态可用。但是,在进行中,只有包含与session交易开始时相同的信息的操作才能看到在该交易中实施的任何更改。