How to use MongoDB transaction using Mongoose?











up vote
0
down vote

favorite
1












I am using MongoDB Atlas cloud(https://cloud.mongodb.com/) and Mongoose library.



I tried to create multiple documents using transaction concept, but it is not working.
I am not getting any error. but, it seems rollback is not working properly.



app.js



//*** more code here

var app = express();

require('./models/db');

//*** more code here


models/db.js



var mongoose = require( 'mongoose' );

// Build the connection string
var dbURI = 'mongodb+srv://mydb:pass@cluster0-****.mongodb.net/mydb?retryWrites=true';

// Create the database connection
mongoose.connect(dbURI, {
useCreateIndex: true,
useNewUrlParser: true,
});

// Get Mongoose to use the global promise library
mongoose.Promise = global.Promise;


models/user.js



const mongoose = require("mongoose");

const UserSchema = new mongoose.Schema({
userName: {
type: String,
required: true
},
pass: {
type: String,
select: false
}
});

module.exports = mongoose.model("User", UserSchema, "user");


myroute.js



const db = require("mongoose");
const User = require("./models/user");

router.post("/addusers", async (req, res, next) => {

const SESSION = await db.startSession();

await SESSION.startTransaction();

try {

const newUser = new User({
//*** data for user ***
});
await newUser.save();

//*** for test purpose, trigger some error ***
throw new Error("some error");

await SESSION.commitTransaction();

//*** return data

} catch (error) {
await SESSION.abortTransaction();
} finally {
SESSION.endSession();
}

});


Above code works without error, but it still creates user in the DB. It suppose to rollback the created user and the collection should be empty.



I don't know what I have missed here. Can anyone please let me know whats wrong here?



app, models, schema and router are in different files.










share|improve this question




























    up vote
    0
    down vote

    favorite
    1












    I am using MongoDB Atlas cloud(https://cloud.mongodb.com/) and Mongoose library.



    I tried to create multiple documents using transaction concept, but it is not working.
    I am not getting any error. but, it seems rollback is not working properly.



    app.js



    //*** more code here

    var app = express();

    require('./models/db');

    //*** more code here


    models/db.js



    var mongoose = require( 'mongoose' );

    // Build the connection string
    var dbURI = 'mongodb+srv://mydb:pass@cluster0-****.mongodb.net/mydb?retryWrites=true';

    // Create the database connection
    mongoose.connect(dbURI, {
    useCreateIndex: true,
    useNewUrlParser: true,
    });

    // Get Mongoose to use the global promise library
    mongoose.Promise = global.Promise;


    models/user.js



    const mongoose = require("mongoose");

    const UserSchema = new mongoose.Schema({
    userName: {
    type: String,
    required: true
    },
    pass: {
    type: String,
    select: false
    }
    });

    module.exports = mongoose.model("User", UserSchema, "user");


    myroute.js



    const db = require("mongoose");
    const User = require("./models/user");

    router.post("/addusers", async (req, res, next) => {

    const SESSION = await db.startSession();

    await SESSION.startTransaction();

    try {

    const newUser = new User({
    //*** data for user ***
    });
    await newUser.save();

    //*** for test purpose, trigger some error ***
    throw new Error("some error");

    await SESSION.commitTransaction();

    //*** return data

    } catch (error) {
    await SESSION.abortTransaction();
    } finally {
    SESSION.endSession();
    }

    });


    Above code works without error, but it still creates user in the DB. It suppose to rollback the created user and the collection should be empty.



    I don't know what I have missed here. Can anyone please let me know whats wrong here?



    app, models, schema and router are in different files.










    share|improve this question


























      up vote
      0
      down vote

      favorite
      1









      up vote
      0
      down vote

      favorite
      1






      1





      I am using MongoDB Atlas cloud(https://cloud.mongodb.com/) and Mongoose library.



      I tried to create multiple documents using transaction concept, but it is not working.
      I am not getting any error. but, it seems rollback is not working properly.



      app.js



      //*** more code here

      var app = express();

      require('./models/db');

      //*** more code here


      models/db.js



      var mongoose = require( 'mongoose' );

      // Build the connection string
      var dbURI = 'mongodb+srv://mydb:pass@cluster0-****.mongodb.net/mydb?retryWrites=true';

      // Create the database connection
      mongoose.connect(dbURI, {
      useCreateIndex: true,
      useNewUrlParser: true,
      });

      // Get Mongoose to use the global promise library
      mongoose.Promise = global.Promise;


      models/user.js



      const mongoose = require("mongoose");

      const UserSchema = new mongoose.Schema({
      userName: {
      type: String,
      required: true
      },
      pass: {
      type: String,
      select: false
      }
      });

      module.exports = mongoose.model("User", UserSchema, "user");


      myroute.js



      const db = require("mongoose");
      const User = require("./models/user");

      router.post("/addusers", async (req, res, next) => {

      const SESSION = await db.startSession();

      await SESSION.startTransaction();

      try {

      const newUser = new User({
      //*** data for user ***
      });
      await newUser.save();

      //*** for test purpose, trigger some error ***
      throw new Error("some error");

      await SESSION.commitTransaction();

      //*** return data

      } catch (error) {
      await SESSION.abortTransaction();
      } finally {
      SESSION.endSession();
      }

      });


      Above code works without error, but it still creates user in the DB. It suppose to rollback the created user and the collection should be empty.



      I don't know what I have missed here. Can anyone please let me know whats wrong here?



      app, models, schema and router are in different files.










      share|improve this question















      I am using MongoDB Atlas cloud(https://cloud.mongodb.com/) and Mongoose library.



      I tried to create multiple documents using transaction concept, but it is not working.
      I am not getting any error. but, it seems rollback is not working properly.



      app.js



      //*** more code here

      var app = express();

      require('./models/db');

      //*** more code here


      models/db.js



      var mongoose = require( 'mongoose' );

      // Build the connection string
      var dbURI = 'mongodb+srv://mydb:pass@cluster0-****.mongodb.net/mydb?retryWrites=true';

      // Create the database connection
      mongoose.connect(dbURI, {
      useCreateIndex: true,
      useNewUrlParser: true,
      });

      // Get Mongoose to use the global promise library
      mongoose.Promise = global.Promise;


      models/user.js



      const mongoose = require("mongoose");

      const UserSchema = new mongoose.Schema({
      userName: {
      type: String,
      required: true
      },
      pass: {
      type: String,
      select: false
      }
      });

      module.exports = mongoose.model("User", UserSchema, "user");


      myroute.js



      const db = require("mongoose");
      const User = require("./models/user");

      router.post("/addusers", async (req, res, next) => {

      const SESSION = await db.startSession();

      await SESSION.startTransaction();

      try {

      const newUser = new User({
      //*** data for user ***
      });
      await newUser.save();

      //*** for test purpose, trigger some error ***
      throw new Error("some error");

      await SESSION.commitTransaction();

      //*** return data

      } catch (error) {
      await SESSION.abortTransaction();
      } finally {
      SESSION.endSession();
      }

      });


      Above code works without error, but it still creates user in the DB. It suppose to rollback the created user and the collection should be empty.



      I don't know what I have missed here. Can anyone please let me know whats wrong here?



      app, models, schema and router are in different files.







      javascript node.js mongodb mongoose mongodb-query






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 23 at 3:39









      Neil Lunn

      96.5k22169180




      96.5k22169180










      asked Nov 22 at 17:08









      M.Kumaran

      461321




      461321
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          You need to include the session within the options for all read/write operations which are active during a transaction. Only then are they actually applied to the transaction scope where you are able to roll them back.



          As a bit more complete listing, and just using the more classic Order/OrderItems modelling which should be pretty familiar to most people with some relational transactions experience:



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

          const uri = 'mongodb://localhost:27017/trandemo';
          const opts = { useNewUrlParser: true };

          // sensible defaults
          mongoose.Promise = global.Promise;
          mongoose.set('debug', true);
          mongoose.set('useFindAndModify', false);
          mongoose.set('useCreateIndex', true);

          // schema defs

          const orderSchema = new Schema({
          name: String
          });

          const orderItemsSchema = new Schema({
          order: { type: Schema.Types.ObjectId, ref: 'Order' },
          itemName: String,
          price: Number
          });

          const Order = mongoose.model('Order', orderSchema);
          const OrderItems = mongoose.model('OrderItems', orderItemsSchema);

          // log helper

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

          // main

          (async function() {

          try {

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

          // clean models
          await Promise.all(
          Object.entries(conn.models).map(([k,m]) => m.deleteMany())
          )

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

          // Collections must exist in transactions
          await Promise.all(
          Object.entries(conn.models).map(([k,m]) => m.createCollection())
          );

          let [order, other] = await Order.insertMany([
          { name: 'Bill' },
          { name: 'Ted' }
          ], { session });

          let fred = new Order({ name: 'Fred' });
          await fred.save({ session });

          let items = await OrderItems.insertMany(
          [
          { order: order._id, itemName: 'Cheese', price: 1 },
          { order: order._id, itemName: 'Bread', price: 2 },
          { order: order._id, itemName: 'Milk', price: 3 }
          ],
          { session }
          );

          // update an item
          let result1 = await OrderItems.updateOne(
          { order: order._id, itemName: 'Milk' },
          { $inc: { price: 1 } },
          { session }
          );
          log(result1);

          // commit
          await session.commitTransaction();

          // start another
          session.startTransaction();

          // Update and abort
          let result2 = await OrderItems.findOneAndUpdate(
          { order: order._id, itemName: 'Milk' },
          { $inc: { price: 1 } },
          { 'new': true, session }
          );
          log(result2);

          await session.abortTransaction();

          /*
          * $lookup join - expect Milk to be price: 4
          *
          */

          let joined = await Order.aggregate([
          { '$match': { _id: order._id } },
          { '$lookup': {
          'from': OrderItems.collection.name,
          'foreignField': 'order',
          'localField': '_id',
          'as': 'orderitems'
          }}
          ]);
          log(joined);


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

          })()


          So I would generally recommend calling the variable session in lowercase, since this is the name of the key for the "options" object where it is required on all operations. Keeping this in the lowercase convention allows for using things like the ES6 Object assignment as well:



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

          ...

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


          Also the mongoose documentation on transactions is a little misleading, or at least it could be more descriptive. What it refers to as db in the examples is actually the Mongoose Connection instance, and not the underlying Db or even the mongoose global import as some may misinterpret this. Note in the listing and above excerpt this is obtained from mongoose.connect() and should be kept within your code as something you can access from a shared import.



          Alternately you can even grab this in modular code via the mongoose.connection property, at any time after a connection has been established. This is usually safe inside things such as server route handlers and the like since there will be a database connection by the time that code is called.



          The code also demonstrates the session usage in the different model methods:



          let [order, other] = await Order.insertMany([
          { name: 'Bill' },
          { name: 'Ted' }
          ], { session });

          let fred = new Order({ name: 'Fred' });
          await fred.save({ session });


          All the find() based methods and the update() or insert() and delete() based methods all have a final "options block" where this session key and value are expected. The save() method's only argument is this options block. This is what tells MongoDB to apply these actions to the current transaction on that referenced session.



          In much the same way, before a transaction is committed any requests for a find() or similar which do not specify that session option do not see the state of the data whilst that transaction is in progress. The modified data state is only available to other operations once the transaction completes. Note this has effects on writes as covered in the documentation.



          When an "abort" is issued:



          // Update and abort
          let result2 = await OrderItems.findOneAndUpdate(
          { order: order._id, itemName: 'Milk' },
          { $inc: { price: 1 } },
          { 'new': true, session }
          );
          log(result2);

          await session.abortTransaction();


          Any operations on the active transaction are removed from state and are not applied. As such they are not visible to resulting operations afterwards. In the example here the value in the document is incremented and will show a retrieved value of 5 on the current session. However after session.abortTransaction() the previous state of the document is reverted. Note that any global context which was not reading data on the same session, does not see that state change unless committed.



          That should give the general overview. There is more complexity that can be added to handle varying levels of write failure and retries, but that is already extensively covered in documentation and many samples, or can be answered to a more specific question.





          Output



          For reference, the output of the included listing is shown here:



          Mongoose: orders.deleteMany({}, {})
          Mongoose: orderitems.deleteMany({}, {})
          Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
          Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
          Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
          Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
          {
          "n": 1,
          "nModified": 1,
          "opTime": {
          "ts": "6626894672394452998",
          "t": 139
          },
          "electionId": "7fffffff000000000000008b",
          "ok": 1,
          "operationTime": "6626894672394452998",
          "$clusterTime": {
          "clusterTime": "6626894672394452998",
          "signature": {
          "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
          "keyId": 0
          }
          }
          }
          Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false })
          {
          "_id": "5bf775986c7c1a61d12137e2",
          "order": "5bf775986c7c1a61d12137dd",
          "itemName": "Milk",
          "price": 5,
          "__v": 0
          }
          Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {})
          [
          {
          "_id": "5bf775986c7c1a61d12137dd",
          "name": "Bill",
          "__v": 0,
          "orderitems": [
          {
          "_id": "5bf775986c7c1a61d12137e0",
          "order": "5bf775986c7c1a61d12137dd",
          "itemName": "Cheese",
          "price": 1,
          "__v": 0
          },
          {
          "_id": "5bf775986c7c1a61d12137e1",
          "order": "5bf775986c7c1a61d12137dd",
          "itemName": "Bread",
          "price": 2,
          "__v": 0
          },
          {
          "_id": "5bf775986c7c1a61d12137e2",
          "order": "5bf775986c7c1a61d12137dd",
          "itemName": "Milk",
          "price": 4,
          "__v": 0
          }
          ]
          }
          ]





          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53435616%2fhow-to-use-mongodb-transaction-using-mongoose%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote













            You need to include the session within the options for all read/write operations which are active during a transaction. Only then are they actually applied to the transaction scope where you are able to roll them back.



            As a bit more complete listing, and just using the more classic Order/OrderItems modelling which should be pretty familiar to most people with some relational transactions experience:



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

            const uri = 'mongodb://localhost:27017/trandemo';
            const opts = { useNewUrlParser: true };

            // sensible defaults
            mongoose.Promise = global.Promise;
            mongoose.set('debug', true);
            mongoose.set('useFindAndModify', false);
            mongoose.set('useCreateIndex', true);

            // schema defs

            const orderSchema = new Schema({
            name: String
            });

            const orderItemsSchema = new Schema({
            order: { type: Schema.Types.ObjectId, ref: 'Order' },
            itemName: String,
            price: Number
            });

            const Order = mongoose.model('Order', orderSchema);
            const OrderItems = mongoose.model('OrderItems', orderItemsSchema);

            // log helper

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

            // main

            (async function() {

            try {

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

            // clean models
            await Promise.all(
            Object.entries(conn.models).map(([k,m]) => m.deleteMany())
            )

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

            // Collections must exist in transactions
            await Promise.all(
            Object.entries(conn.models).map(([k,m]) => m.createCollection())
            );

            let [order, other] = await Order.insertMany([
            { name: 'Bill' },
            { name: 'Ted' }
            ], { session });

            let fred = new Order({ name: 'Fred' });
            await fred.save({ session });

            let items = await OrderItems.insertMany(
            [
            { order: order._id, itemName: 'Cheese', price: 1 },
            { order: order._id, itemName: 'Bread', price: 2 },
            { order: order._id, itemName: 'Milk', price: 3 }
            ],
            { session }
            );

            // update an item
            let result1 = await OrderItems.updateOne(
            { order: order._id, itemName: 'Milk' },
            { $inc: { price: 1 } },
            { session }
            );
            log(result1);

            // commit
            await session.commitTransaction();

            // start another
            session.startTransaction();

            // Update and abort
            let result2 = await OrderItems.findOneAndUpdate(
            { order: order._id, itemName: 'Milk' },
            { $inc: { price: 1 } },
            { 'new': true, session }
            );
            log(result2);

            await session.abortTransaction();

            /*
            * $lookup join - expect Milk to be price: 4
            *
            */

            let joined = await Order.aggregate([
            { '$match': { _id: order._id } },
            { '$lookup': {
            'from': OrderItems.collection.name,
            'foreignField': 'order',
            'localField': '_id',
            'as': 'orderitems'
            }}
            ]);
            log(joined);


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

            })()


            So I would generally recommend calling the variable session in lowercase, since this is the name of the key for the "options" object where it is required on all operations. Keeping this in the lowercase convention allows for using things like the ES6 Object assignment as well:



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

            ...

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


            Also the mongoose documentation on transactions is a little misleading, or at least it could be more descriptive. What it refers to as db in the examples is actually the Mongoose Connection instance, and not the underlying Db or even the mongoose global import as some may misinterpret this. Note in the listing and above excerpt this is obtained from mongoose.connect() and should be kept within your code as something you can access from a shared import.



            Alternately you can even grab this in modular code via the mongoose.connection property, at any time after a connection has been established. This is usually safe inside things such as server route handlers and the like since there will be a database connection by the time that code is called.



            The code also demonstrates the session usage in the different model methods:



            let [order, other] = await Order.insertMany([
            { name: 'Bill' },
            { name: 'Ted' }
            ], { session });

            let fred = new Order({ name: 'Fred' });
            await fred.save({ session });


            All the find() based methods and the update() or insert() and delete() based methods all have a final "options block" where this session key and value are expected. The save() method's only argument is this options block. This is what tells MongoDB to apply these actions to the current transaction on that referenced session.



            In much the same way, before a transaction is committed any requests for a find() or similar which do not specify that session option do not see the state of the data whilst that transaction is in progress. The modified data state is only available to other operations once the transaction completes. Note this has effects on writes as covered in the documentation.



            When an "abort" is issued:



            // Update and abort
            let result2 = await OrderItems.findOneAndUpdate(
            { order: order._id, itemName: 'Milk' },
            { $inc: { price: 1 } },
            { 'new': true, session }
            );
            log(result2);

            await session.abortTransaction();


            Any operations on the active transaction are removed from state and are not applied. As such they are not visible to resulting operations afterwards. In the example here the value in the document is incremented and will show a retrieved value of 5 on the current session. However after session.abortTransaction() the previous state of the document is reverted. Note that any global context which was not reading data on the same session, does not see that state change unless committed.



            That should give the general overview. There is more complexity that can be added to handle varying levels of write failure and retries, but that is already extensively covered in documentation and many samples, or can be answered to a more specific question.





            Output



            For reference, the output of the included listing is shown here:



            Mongoose: orders.deleteMany({}, {})
            Mongoose: orderitems.deleteMany({}, {})
            Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
            Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
            Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
            Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
            {
            "n": 1,
            "nModified": 1,
            "opTime": {
            "ts": "6626894672394452998",
            "t": 139
            },
            "electionId": "7fffffff000000000000008b",
            "ok": 1,
            "operationTime": "6626894672394452998",
            "$clusterTime": {
            "clusterTime": "6626894672394452998",
            "signature": {
            "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
            "keyId": 0
            }
            }
            }
            Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false })
            {
            "_id": "5bf775986c7c1a61d12137e2",
            "order": "5bf775986c7c1a61d12137dd",
            "itemName": "Milk",
            "price": 5,
            "__v": 0
            }
            Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {})
            [
            {
            "_id": "5bf775986c7c1a61d12137dd",
            "name": "Bill",
            "__v": 0,
            "orderitems": [
            {
            "_id": "5bf775986c7c1a61d12137e0",
            "order": "5bf775986c7c1a61d12137dd",
            "itemName": "Cheese",
            "price": 1,
            "__v": 0
            },
            {
            "_id": "5bf775986c7c1a61d12137e1",
            "order": "5bf775986c7c1a61d12137dd",
            "itemName": "Bread",
            "price": 2,
            "__v": 0
            },
            {
            "_id": "5bf775986c7c1a61d12137e2",
            "order": "5bf775986c7c1a61d12137dd",
            "itemName": "Milk",
            "price": 4,
            "__v": 0
            }
            ]
            }
            ]





            share|improve this answer

























              up vote
              1
              down vote













              You need to include the session within the options for all read/write operations which are active during a transaction. Only then are they actually applied to the transaction scope where you are able to roll them back.



              As a bit more complete listing, and just using the more classic Order/OrderItems modelling which should be pretty familiar to most people with some relational transactions experience:



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

              const uri = 'mongodb://localhost:27017/trandemo';
              const opts = { useNewUrlParser: true };

              // sensible defaults
              mongoose.Promise = global.Promise;
              mongoose.set('debug', true);
              mongoose.set('useFindAndModify', false);
              mongoose.set('useCreateIndex', true);

              // schema defs

              const orderSchema = new Schema({
              name: String
              });

              const orderItemsSchema = new Schema({
              order: { type: Schema.Types.ObjectId, ref: 'Order' },
              itemName: String,
              price: Number
              });

              const Order = mongoose.model('Order', orderSchema);
              const OrderItems = mongoose.model('OrderItems', orderItemsSchema);

              // log helper

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

              // main

              (async function() {

              try {

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

              // clean models
              await Promise.all(
              Object.entries(conn.models).map(([k,m]) => m.deleteMany())
              )

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

              // Collections must exist in transactions
              await Promise.all(
              Object.entries(conn.models).map(([k,m]) => m.createCollection())
              );

              let [order, other] = await Order.insertMany([
              { name: 'Bill' },
              { name: 'Ted' }
              ], { session });

              let fred = new Order({ name: 'Fred' });
              await fred.save({ session });

              let items = await OrderItems.insertMany(
              [
              { order: order._id, itemName: 'Cheese', price: 1 },
              { order: order._id, itemName: 'Bread', price: 2 },
              { order: order._id, itemName: 'Milk', price: 3 }
              ],
              { session }
              );

              // update an item
              let result1 = await OrderItems.updateOne(
              { order: order._id, itemName: 'Milk' },
              { $inc: { price: 1 } },
              { session }
              );
              log(result1);

              // commit
              await session.commitTransaction();

              // start another
              session.startTransaction();

              // Update and abort
              let result2 = await OrderItems.findOneAndUpdate(
              { order: order._id, itemName: 'Milk' },
              { $inc: { price: 1 } },
              { 'new': true, session }
              );
              log(result2);

              await session.abortTransaction();

              /*
              * $lookup join - expect Milk to be price: 4
              *
              */

              let joined = await Order.aggregate([
              { '$match': { _id: order._id } },
              { '$lookup': {
              'from': OrderItems.collection.name,
              'foreignField': 'order',
              'localField': '_id',
              'as': 'orderitems'
              }}
              ]);
              log(joined);


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

              })()


              So I would generally recommend calling the variable session in lowercase, since this is the name of the key for the "options" object where it is required on all operations. Keeping this in the lowercase convention allows for using things like the ES6 Object assignment as well:



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

              ...

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


              Also the mongoose documentation on transactions is a little misleading, or at least it could be more descriptive. What it refers to as db in the examples is actually the Mongoose Connection instance, and not the underlying Db or even the mongoose global import as some may misinterpret this. Note in the listing and above excerpt this is obtained from mongoose.connect() and should be kept within your code as something you can access from a shared import.



              Alternately you can even grab this in modular code via the mongoose.connection property, at any time after a connection has been established. This is usually safe inside things such as server route handlers and the like since there will be a database connection by the time that code is called.



              The code also demonstrates the session usage in the different model methods:



              let [order, other] = await Order.insertMany([
              { name: 'Bill' },
              { name: 'Ted' }
              ], { session });

              let fred = new Order({ name: 'Fred' });
              await fred.save({ session });


              All the find() based methods and the update() or insert() and delete() based methods all have a final "options block" where this session key and value are expected. The save() method's only argument is this options block. This is what tells MongoDB to apply these actions to the current transaction on that referenced session.



              In much the same way, before a transaction is committed any requests for a find() or similar which do not specify that session option do not see the state of the data whilst that transaction is in progress. The modified data state is only available to other operations once the transaction completes. Note this has effects on writes as covered in the documentation.



              When an "abort" is issued:



              // Update and abort
              let result2 = await OrderItems.findOneAndUpdate(
              { order: order._id, itemName: 'Milk' },
              { $inc: { price: 1 } },
              { 'new': true, session }
              );
              log(result2);

              await session.abortTransaction();


              Any operations on the active transaction are removed from state and are not applied. As such they are not visible to resulting operations afterwards. In the example here the value in the document is incremented and will show a retrieved value of 5 on the current session. However after session.abortTransaction() the previous state of the document is reverted. Note that any global context which was not reading data on the same session, does not see that state change unless committed.



              That should give the general overview. There is more complexity that can be added to handle varying levels of write failure and retries, but that is already extensively covered in documentation and many samples, or can be answered to a more specific question.





              Output



              For reference, the output of the included listing is shown here:



              Mongoose: orders.deleteMany({}, {})
              Mongoose: orderitems.deleteMany({}, {})
              Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
              Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
              Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
              Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
              {
              "n": 1,
              "nModified": 1,
              "opTime": {
              "ts": "6626894672394452998",
              "t": 139
              },
              "electionId": "7fffffff000000000000008b",
              "ok": 1,
              "operationTime": "6626894672394452998",
              "$clusterTime": {
              "clusterTime": "6626894672394452998",
              "signature": {
              "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
              "keyId": 0
              }
              }
              }
              Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false })
              {
              "_id": "5bf775986c7c1a61d12137e2",
              "order": "5bf775986c7c1a61d12137dd",
              "itemName": "Milk",
              "price": 5,
              "__v": 0
              }
              Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {})
              [
              {
              "_id": "5bf775986c7c1a61d12137dd",
              "name": "Bill",
              "__v": 0,
              "orderitems": [
              {
              "_id": "5bf775986c7c1a61d12137e0",
              "order": "5bf775986c7c1a61d12137dd",
              "itemName": "Cheese",
              "price": 1,
              "__v": 0
              },
              {
              "_id": "5bf775986c7c1a61d12137e1",
              "order": "5bf775986c7c1a61d12137dd",
              "itemName": "Bread",
              "price": 2,
              "__v": 0
              },
              {
              "_id": "5bf775986c7c1a61d12137e2",
              "order": "5bf775986c7c1a61d12137dd",
              "itemName": "Milk",
              "price": 4,
              "__v": 0
              }
              ]
              }
              ]





              share|improve this answer























                up vote
                1
                down vote










                up vote
                1
                down vote









                You need to include the session within the options for all read/write operations which are active during a transaction. Only then are they actually applied to the transaction scope where you are able to roll them back.



                As a bit more complete listing, and just using the more classic Order/OrderItems modelling which should be pretty familiar to most people with some relational transactions experience:



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

                const uri = 'mongodb://localhost:27017/trandemo';
                const opts = { useNewUrlParser: true };

                // sensible defaults
                mongoose.Promise = global.Promise;
                mongoose.set('debug', true);
                mongoose.set('useFindAndModify', false);
                mongoose.set('useCreateIndex', true);

                // schema defs

                const orderSchema = new Schema({
                name: String
                });

                const orderItemsSchema = new Schema({
                order: { type: Schema.Types.ObjectId, ref: 'Order' },
                itemName: String,
                price: Number
                });

                const Order = mongoose.model('Order', orderSchema);
                const OrderItems = mongoose.model('OrderItems', orderItemsSchema);

                // log helper

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

                // main

                (async function() {

                try {

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

                // clean models
                await Promise.all(
                Object.entries(conn.models).map(([k,m]) => m.deleteMany())
                )

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

                // Collections must exist in transactions
                await Promise.all(
                Object.entries(conn.models).map(([k,m]) => m.createCollection())
                );

                let [order, other] = await Order.insertMany([
                { name: 'Bill' },
                { name: 'Ted' }
                ], { session });

                let fred = new Order({ name: 'Fred' });
                await fred.save({ session });

                let items = await OrderItems.insertMany(
                [
                { order: order._id, itemName: 'Cheese', price: 1 },
                { order: order._id, itemName: 'Bread', price: 2 },
                { order: order._id, itemName: 'Milk', price: 3 }
                ],
                { session }
                );

                // update an item
                let result1 = await OrderItems.updateOne(
                { order: order._id, itemName: 'Milk' },
                { $inc: { price: 1 } },
                { session }
                );
                log(result1);

                // commit
                await session.commitTransaction();

                // start another
                session.startTransaction();

                // Update and abort
                let result2 = await OrderItems.findOneAndUpdate(
                { order: order._id, itemName: 'Milk' },
                { $inc: { price: 1 } },
                { 'new': true, session }
                );
                log(result2);

                await session.abortTransaction();

                /*
                * $lookup join - expect Milk to be price: 4
                *
                */

                let joined = await Order.aggregate([
                { '$match': { _id: order._id } },
                { '$lookup': {
                'from': OrderItems.collection.name,
                'foreignField': 'order',
                'localField': '_id',
                'as': 'orderitems'
                }}
                ]);
                log(joined);


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

                })()


                So I would generally recommend calling the variable session in lowercase, since this is the name of the key for the "options" object where it is required on all operations. Keeping this in the lowercase convention allows for using things like the ES6 Object assignment as well:



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

                ...

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


                Also the mongoose documentation on transactions is a little misleading, or at least it could be more descriptive. What it refers to as db in the examples is actually the Mongoose Connection instance, and not the underlying Db or even the mongoose global import as some may misinterpret this. Note in the listing and above excerpt this is obtained from mongoose.connect() and should be kept within your code as something you can access from a shared import.



                Alternately you can even grab this in modular code via the mongoose.connection property, at any time after a connection has been established. This is usually safe inside things such as server route handlers and the like since there will be a database connection by the time that code is called.



                The code also demonstrates the session usage in the different model methods:



                let [order, other] = await Order.insertMany([
                { name: 'Bill' },
                { name: 'Ted' }
                ], { session });

                let fred = new Order({ name: 'Fred' });
                await fred.save({ session });


                All the find() based methods and the update() or insert() and delete() based methods all have a final "options block" where this session key and value are expected. The save() method's only argument is this options block. This is what tells MongoDB to apply these actions to the current transaction on that referenced session.



                In much the same way, before a transaction is committed any requests for a find() or similar which do not specify that session option do not see the state of the data whilst that transaction is in progress. The modified data state is only available to other operations once the transaction completes. Note this has effects on writes as covered in the documentation.



                When an "abort" is issued:



                // Update and abort
                let result2 = await OrderItems.findOneAndUpdate(
                { order: order._id, itemName: 'Milk' },
                { $inc: { price: 1 } },
                { 'new': true, session }
                );
                log(result2);

                await session.abortTransaction();


                Any operations on the active transaction are removed from state and are not applied. As such they are not visible to resulting operations afterwards. In the example here the value in the document is incremented and will show a retrieved value of 5 on the current session. However after session.abortTransaction() the previous state of the document is reverted. Note that any global context which was not reading data on the same session, does not see that state change unless committed.



                That should give the general overview. There is more complexity that can be added to handle varying levels of write failure and retries, but that is already extensively covered in documentation and many samples, or can be answered to a more specific question.





                Output



                For reference, the output of the included listing is shown here:



                Mongoose: orders.deleteMany({}, {})
                Mongoose: orderitems.deleteMany({}, {})
                Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                {
                "n": 1,
                "nModified": 1,
                "opTime": {
                "ts": "6626894672394452998",
                "t": 139
                },
                "electionId": "7fffffff000000000000008b",
                "ok": 1,
                "operationTime": "6626894672394452998",
                "$clusterTime": {
                "clusterTime": "6626894672394452998",
                "signature": {
                "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
                "keyId": 0
                }
                }
                }
                Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false })
                {
                "_id": "5bf775986c7c1a61d12137e2",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Milk",
                "price": 5,
                "__v": 0
                }
                Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {})
                [
                {
                "_id": "5bf775986c7c1a61d12137dd",
                "name": "Bill",
                "__v": 0,
                "orderitems": [
                {
                "_id": "5bf775986c7c1a61d12137e0",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Cheese",
                "price": 1,
                "__v": 0
                },
                {
                "_id": "5bf775986c7c1a61d12137e1",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Bread",
                "price": 2,
                "__v": 0
                },
                {
                "_id": "5bf775986c7c1a61d12137e2",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Milk",
                "price": 4,
                "__v": 0
                }
                ]
                }
                ]





                share|improve this answer












                You need to include the session within the options for all read/write operations which are active during a transaction. Only then are they actually applied to the transaction scope where you are able to roll them back.



                As a bit more complete listing, and just using the more classic Order/OrderItems modelling which should be pretty familiar to most people with some relational transactions experience:



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

                const uri = 'mongodb://localhost:27017/trandemo';
                const opts = { useNewUrlParser: true };

                // sensible defaults
                mongoose.Promise = global.Promise;
                mongoose.set('debug', true);
                mongoose.set('useFindAndModify', false);
                mongoose.set('useCreateIndex', true);

                // schema defs

                const orderSchema = new Schema({
                name: String
                });

                const orderItemsSchema = new Schema({
                order: { type: Schema.Types.ObjectId, ref: 'Order' },
                itemName: String,
                price: Number
                });

                const Order = mongoose.model('Order', orderSchema);
                const OrderItems = mongoose.model('OrderItems', orderItemsSchema);

                // log helper

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

                // main

                (async function() {

                try {

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

                // clean models
                await Promise.all(
                Object.entries(conn.models).map(([k,m]) => m.deleteMany())
                )

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

                // Collections must exist in transactions
                await Promise.all(
                Object.entries(conn.models).map(([k,m]) => m.createCollection())
                );

                let [order, other] = await Order.insertMany([
                { name: 'Bill' },
                { name: 'Ted' }
                ], { session });

                let fred = new Order({ name: 'Fred' });
                await fred.save({ session });

                let items = await OrderItems.insertMany(
                [
                { order: order._id, itemName: 'Cheese', price: 1 },
                { order: order._id, itemName: 'Bread', price: 2 },
                { order: order._id, itemName: 'Milk', price: 3 }
                ],
                { session }
                );

                // update an item
                let result1 = await OrderItems.updateOne(
                { order: order._id, itemName: 'Milk' },
                { $inc: { price: 1 } },
                { session }
                );
                log(result1);

                // commit
                await session.commitTransaction();

                // start another
                session.startTransaction();

                // Update and abort
                let result2 = await OrderItems.findOneAndUpdate(
                { order: order._id, itemName: 'Milk' },
                { $inc: { price: 1 } },
                { 'new': true, session }
                );
                log(result2);

                await session.abortTransaction();

                /*
                * $lookup join - expect Milk to be price: 4
                *
                */

                let joined = await Order.aggregate([
                { '$match': { _id: order._id } },
                { '$lookup': {
                'from': OrderItems.collection.name,
                'foreignField': 'order',
                'localField': '_id',
                'as': 'orderitems'
                }}
                ]);
                log(joined);


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

                })()


                So I would generally recommend calling the variable session in lowercase, since this is the name of the key for the "options" object where it is required on all operations. Keeping this in the lowercase convention allows for using things like the ES6 Object assignment as well:



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

                ...

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


                Also the mongoose documentation on transactions is a little misleading, or at least it could be more descriptive. What it refers to as db in the examples is actually the Mongoose Connection instance, and not the underlying Db or even the mongoose global import as some may misinterpret this. Note in the listing and above excerpt this is obtained from mongoose.connect() and should be kept within your code as something you can access from a shared import.



                Alternately you can even grab this in modular code via the mongoose.connection property, at any time after a connection has been established. This is usually safe inside things such as server route handlers and the like since there will be a database connection by the time that code is called.



                The code also demonstrates the session usage in the different model methods:



                let [order, other] = await Order.insertMany([
                { name: 'Bill' },
                { name: 'Ted' }
                ], { session });

                let fred = new Order({ name: 'Fred' });
                await fred.save({ session });


                All the find() based methods and the update() or insert() and delete() based methods all have a final "options block" where this session key and value are expected. The save() method's only argument is this options block. This is what tells MongoDB to apply these actions to the current transaction on that referenced session.



                In much the same way, before a transaction is committed any requests for a find() or similar which do not specify that session option do not see the state of the data whilst that transaction is in progress. The modified data state is only available to other operations once the transaction completes. Note this has effects on writes as covered in the documentation.



                When an "abort" is issued:



                // Update and abort
                let result2 = await OrderItems.findOneAndUpdate(
                { order: order._id, itemName: 'Milk' },
                { $inc: { price: 1 } },
                { 'new': true, session }
                );
                log(result2);

                await session.abortTransaction();


                Any operations on the active transaction are removed from state and are not applied. As such they are not visible to resulting operations afterwards. In the example here the value in the document is incremented and will show a retrieved value of 5 on the current session. However after session.abortTransaction() the previous state of the document is reverted. Note that any global context which was not reading data on the same session, does not see that state change unless committed.



                That should give the general overview. There is more complexity that can be added to handle varying levels of write failure and retries, but that is already extensively covered in documentation and many samples, or can be answered to a more specific question.





                Output



                For reference, the output of the included listing is shown here:



                Mongoose: orders.deleteMany({}, {})
                Mongoose: orderitems.deleteMany({}, {})
                Mongoose: orders.insertMany([ { _id: 5bf775986c7c1a61d12137dd, name: 'Bill', __v: 0 }, { _id: 5bf775986c7c1a61d12137de, name: 'Ted', __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                Mongoose: orders.insertOne({ _id: ObjectId("5bf775986c7c1a61d12137df"), name: 'Fred', __v: 0 }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                Mongoose: orderitems.insertMany([ { _id: 5bf775986c7c1a61d12137e0, order: 5bf775986c7c1a61d12137dd, itemName: 'Cheese', price: 1, __v: 0 }, { _id: 5bf775986c7c1a61d12137e1, order: 5bf775986c7c1a61d12137dd, itemName: 'Bread', price: 2, __v: 0 }, { _id: 5bf775986c7c1a61d12137e2, order: 5bf775986c7c1a61d12137dd, itemName: 'Milk', price: 3, __v: 0 } ], { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                Mongoose: orderitems.updateOne({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2") })
                {
                "n": 1,
                "nModified": 1,
                "opTime": {
                "ts": "6626894672394452998",
                "t": 139
                },
                "electionId": "7fffffff000000000000008b",
                "ok": 1,
                "operationTime": "6626894672394452998",
                "$clusterTime": {
                "clusterTime": "6626894672394452998",
                "signature": {
                "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
                "keyId": 0
                }
                }
                }
                Mongoose: orderitems.findOneAndUpdate({ order: ObjectId("5bf775986c7c1a61d12137dd"), itemName: 'Milk' }, { '$inc': { price: 1 } }, { session: ClientSession("80f827fe077044c8b6c0547b34605cb2"), upsert: false, remove: false, projection: {}, returnOriginal: false })
                {
                "_id": "5bf775986c7c1a61d12137e2",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Milk",
                "price": 5,
                "__v": 0
                }
                Mongoose: orders.aggregate([ { '$match': { _id: 5bf775986c7c1a61d12137dd } }, { '$lookup': { from: 'orderitems', foreignField: 'order', localField: '_id', as: 'orderitems' } } ], {})
                [
                {
                "_id": "5bf775986c7c1a61d12137dd",
                "name": "Bill",
                "__v": 0,
                "orderitems": [
                {
                "_id": "5bf775986c7c1a61d12137e0",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Cheese",
                "price": 1,
                "__v": 0
                },
                {
                "_id": "5bf775986c7c1a61d12137e1",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Bread",
                "price": 2,
                "__v": 0
                },
                {
                "_id": "5bf775986c7c1a61d12137e2",
                "order": "5bf775986c7c1a61d12137dd",
                "itemName": "Milk",
                "price": 4,
                "__v": 0
                }
                ]
                }
                ]






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 23 at 3:39









                Neil Lunn

                96.5k22169180




                96.5k22169180






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53435616%2fhow-to-use-mongodb-transaction-using-mongoose%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    What visual should I use to simply compare current year value vs last year in Power BI desktop

                    Alexandru Averescu

                    Trompette piccolo