I am trying to insert data using inertMany
.but I am not able to insert the data why ?I am using mongoose session
if any error occurred then I roll back changes
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);
}
});
Since you don't appear to have understood the marked duplicate or the comment on your last question, here's a direct demonstration:
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();
}
})();
And the output:
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
}
]
}
Note that when the transaction is in use there are no items inserted into the collection. Using the insertMany()
with the default behavior of ordered: true
will insert all batched items up until the point any error is encountered.
Note also as stated since you are indeed expecting an error you must include such a statement in it very own try..catch
or similar error handler. Otherwise any error ( which is expected in the example case ) would simply fall to the outer catch
, which of course in the demonstration simply exits the program.
Not actually in the question itself but something not actually mentioned in the demonstrations of How to use MongoDB transaction using Mongoose? is indeed that you should be aware that whlist a transaction is active you must also include the session
attribute on any subsequent reads in order to see the changes made within that transaction.
For instance, the following would show no content in a collection:
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();
}
However including the session
within a find()
will actually show what is inserted:
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();
}
And of course that read would in this case be dependent on the insertMany()
not failing for any reason, since any error would result in exiting to the catch
before the next request was made.
Once a transaction is committed, it is of course available to the global state of the connection. But whilst in progress only operations which include the same session
information on which the transaction was started will have visibility of any changes implemented within that transaction.