Employing `mongoose transactions` for storing messages in separate collections within mongodb

When a student sends a message to a teacher, the message gets saved in both the 'teachers' and 'students' collections. Similarly, when a teacher sends a message to a student, it also gets saved in both the 'teachers' and 'students' collections. I am looking for guidance on how to implement a mongoose transaction in the code provided below to ensure that the message is successfully saved in both collections without any errors. I want to avoid a scenario where the message is only saved in one collection due to an error. Any help or hints would be greatly appreciated.

const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();

// Connect to Database
module.exports.db = mongoose
    .createConnection(process.env.DATABASE, { 
        useNewUrlParser: true, 
        useFindAndModify: false,
        useUnifiedTopology: true, 
        useCreateIndex: true
    }
);

// Controller
const db = require('./connection');

const session = db.startSession();
session.startTransaction();

module.exports.sendMessage = (req, res) => {
    
    let {sender, receiver, msg, role} = req.body;
    var hex = /[0-9A-Fa-f]{6}/g;

    sender = (hex.test(sender))? mongoose.Types.ObjectId(sender) : sender;
    receiver = (hex.test(receiver))? mongoose.Types.ObjectId(receiver) : receiver;
    console.log(sender, receiver, msg, 'send');

            if(role === 'tutor') {
                let teacherMessage =  new TeacherMessageSchema.TeacherMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    sender : sender
                })

                let studentMessage =  new TeacherMessageSchema.TeacherMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    receiver : receiver
                })
        
                db.db.collection('teachers').findOneAndUpdate( 
                    { _id : receiver },
                    { $push: { messages: teacherMessage } },
                    (err, updated) => {
                        console.log(updated, 'vvv');
        
                        updated.value.hashed_password = undefined;
                        updated.value.salt = undefined;
        
                        if(err) {
                            res.status(404).json(err);
                        }
        
                        if (updated) {
                            res.status(200).json(updated.value);

                            db.db.collection('students').findOneAndUpdate( 
                                { _id : sender },
                                { $push: { messages: studentMessage } },                              
                            );
                        }
                    }, session(session)
                )
        
              
            }
        
        
            if(role === 'student') {
                let studentMessage =  new StudentMessageSchema.StudentMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    receiver : receiver
                })

                let teacherMessage =  new TeacherMessageSchema.TeacherMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    sender : sender
                })
        
               
        
                db.db.collection('students').findOneAndUpdate( 
                    { _id : sender },
                    { $push: { messages: studentMessage } },
                    (err, updated) => {
                        console.log(updated, 'sss');
            
                        updated.value.hashed_password = undefined;
                        updated.value.salt = undefined;
            
                        if(err) {
                            res.status(404).json(err);
                        }
            
                        if (updated) {
                            res.json(updated.value);
                            db.db.collection('teachers').findOneAndUpdate( 
                                { _id : receiver },
                                { $push: { messages: teacherMessage } },                               
                            )
                        }
                    }, 
                ), session(session)
            }
}

Logged variable db

{
  db: NativeConnection {
    base: Mongoose {
      connections: [Array],
      models: [Object],
      modelSchemas: [Object],
      options: [Object],
      _pluralize: [Function: pluralize],
      plugins: [Array]
    },
    collections: {},
    models: {},
    config: { autoIndex: true },
    replica: false,
    options: null,
    otherDbs: [],
    relatedDbs: {},
    states: [Object: null prototype] {
      '0': 'disconnected',
      '1': 'connected',
      '2': 'connecting',
      '3': 'disconnecting',
      '99': 'uninitialized',
      disconnected: 0,
      connected: 1,
      connecting: 2,
      disconnecting: 3,
      uninitialized: 99
    },
    _readyState: 1,
    _closeCalled: false,
    _hasOpened: true,
    _listening: false,
    _connectionOptions: {
      useNewUrlParser: true,
      useFindAndModify: false,
      useUnifiedTopology: true,
      useCreateIndex: true,
      promiseLibrary: [Function: Promise]
    },
    client: MongoClient {
      _events: [Object: null prototype] {},
      _eventsCount: 0,
      _maxListeners: undefined,
      s: [Object],
      topology: [ReplSet],
      [Symbol(kCapture)]: false
    },
    name: null,
    '$initialConnection': Promise { [Circular] },
    db: Db {
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      s: [Object],
      serverConfig: [Getter],
      bufferMaxEntries: [Getter],
      databaseName: [Getter],
      [Symbol(kCapture)]: false
    }
  }
}

Answer №1

Including the code snippet to handle errors:

Execute await session.abortTransaction(); when there is an error.

For successful execution, commit the changes.

Invoke await session.commitTransaction();

The abort and commit functions are Promise-based, so I have modified the functions where they are used to be asynchronous.

const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();

// Establish connection with the Database and manage any connection failures
module.exports.db = mongoose
    .createConnection(process.env.DATABASE, { 
        useNewUrlParser: true, 
        useFindAndModify: false,
        useUnifiedTopology: true, 
        useCreateIndex: true
    }
);

//controller
const db = require('./connection');

module.exports.sendMessage = async (req, res) => {
    
    const session = await db.startSession();
    session.startTransaction();

    let {sender, receiver, msg, role} = req.body;
    var hex = /[0-9A-Fa-f]{6}/g;

    sender = (hex.test(sender))? mongoose.Types.ObjectId(sender) : sender;
    receiver = (hex.test(receiver))? mongoose.Types.ObjectId(receiver) : receiver;
    console.log(sender, receiver, msg, 'send');

            if(role === 'tutor') {
                let teacherMessage =  new TeacherMessageSchema.TeacherMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    sender : sender
                })

                let studentMessage =  new TeacherMessageSchema.TeacherMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    receiver : receiver
                })
        
                db.db.collection('teachers').findOneAndUpdate( 
                    { _id : receiver },
                    { $push: { messages: teacherMessage } },
                    async (err, updated) => {
                        console.log(updated, 'vvv');
        
                        updated.value.hashed_password = undefined;
                        updated.value.salt = undefined;
        
                        if(err) {
                            await session.abortTransaction();
                            res.status(404).json(err);
                            return
                        }
        
                        if (updated) {
                            res.status(200).json(updated.value);

                            db.db.collection('students').findOneAndUpdate( 
                                { _id : sender },
                                { $push: { messages: studentMessage } },
                               
                            );
                        }
                    }, session(session)
                )
        
              
            }
        
        
            if(role === 'student') {
                let studentMessage =  new StudentMessageSchema.StudentMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    receiver : receiver
                })

                let teacherMessage =  new TeacherMessageSchema.TeacherMessageSchema({
                    contentInfo : {
                        msg : msg
                    },
                    sender : sender
                })
        
               
        
                db.db.collection('students').findOneAndUpdate( 
                    { _id : sender },
                    { $push: { messages: studentMessage } },
                    async (err, updated) => {
                        console.log(updated, 'sss');
            
                        updated.value.hashed_password = undefined;
                        updated.value.salt = undefined;
            
                        if(err) {
                            await session.abortTransaction();
                            res.status(404).json(err);
                            return;
                        }
            
                        if (updated) {
                            res.json(updated.value);
                            db.db.collection('teachers').findOneAndUpdate( 
                                { _id : receiver },
                                { $push: { messages: teacherMessage } },
                               
                            )
                        }
                    }, 
                    session(session)
                )
            }

            await session.commitTransaction();
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips on developing a limited directive?

Currently, I am experimenting with AngularJS and NG-Table and facing a challenge that I cannot seem to resolve: My issue involves displaying a collection of User objects from a Django application in an NG-Table. One of the attributes of the model is a boo ...

Retrieve the JSON data based on a specific key after a specified period

Hello there, I am encountering an issue with a specific JSON key. Let's say I have an external file containing JSON data structured like this: { "key 1":[ { "linkName":"key name 1.1", "linkUrl":"key URL 1.1" }, ...

Ineffectiveness of Three.js camera's lookat function

I've been trying to modify the camera's focus point using a Vector3, but for some reason, the camera keeps looking at the scene's origin. I'm a bit perplexed as to why this is happening, especially since all the examples I've come ...

Is there a way to retrieve just the first element from JSON data?

I am trying to extract only the first element from a JSON array This is my JSON data: { id:"1", price:"130000.0", user:55, } { id:"2", price:"140000.0", user:55, } I need to retrieve the price of the first JSON element which is "13000.0" Th ...

Tips for retrieving a local variable from inside a callback and using it outside the function

Recently, I started utilizing npm plaid and I'm looking for a way to access the variable trans outside of the plaidClient.getTransactions function. Any suggestions would be greatly appreciated. var trans; var startDate = moment().subtract(30, &apo ...

What is the best way to display the contents of an array that contains another array within it?

I need help looping through this array and displaying the "roleName" value. I want to use the map method to iterate over it. { "userMenuDTO": { "menuId": "13", "menuName":"PruebaMenu900 ...

unable to render a vector layer in openlayers 6

Currently, I am facing an issue with displaying a vector layer on an openlayers map using the source of local geojson and gpx files in my Vuejs project. Unfortunately, even when testing outside of Vue.js, the vector layer is not being displayed. Here is t ...

Having Trouble Finding Vue Component Definition Using Vite Alias in Import Path

When I click on the Component within the code snippet: import Component from '@/components/Component.vue'; I am unable to navigate to its definition when using vite alias. Seeking a potential solution. ...

Troubleshooting error message: "Unsupported import of ESM Javascript file in CommonJS module."

My project relies solely on CommonJS modules and unfortunately, I cannot make any changes to it. I am attempting to incorporate a library that uses ESM called Got library (https://github.com/sindresorhus/got). This is the snippet of my code: const request ...

Using Angular to sort arrays based on the values in a separate array

I have a unique setup where there is a table containing select options in each row. The value of the select option changes based on the 'dataType' specified for that particular row. Here is an example of my Table Array: { 'name':' ...

Tips for skipping the execution of the following statement after using .then() in an Express Js Promise

Utilizing mysql2 with promise, my controller.js looks like this : exports.company_add = (req, res) => { company_model.company_add(admin_email, admin_info).then((result) => { ... }) .catch((err) => { ... }) } Here is the model.js code : ex ...

Issues with reloading when passing POST variables through Ajax requests

I have a button with the id #filter <input type="button" name="filter" id="filter" value="Filter" class="btn btn-info" /> Below is the Ajax script I am using: <script> $(document).ready(function(){ $('#filter').click(function() ...

What is the method for connecting to a JavaScript function?

Is there a way to have JavaScript code that creates a sliding div with expanded information when clicking on a text string, and then navigate to that specific page with the text already clicked on and expanded? If so, how could this be achieved? JSFidldle ...

Encounter a "syntax error Cannot GET /xyz" message using jQuery AJAX

When using $.ajax to request data from a node.js server, I encountered an error while debugging on the client side in Firefox. The console displayed: Syntax error Cannot GET /xyz, where /xyz represents the route for my server. Despite this error, the page ...

How to Include ".00" When Calculating Dollar Amount Totals Using Math.js

When working with my MongoDB and Node backend, I need to calculate total dollar amounts and send that information to the front end. It's important to note that I am only displaying totals and not making any changes to the values themselves. To ensure ...

Incorrectly colored buttons upon clicking

I am experiencing an issue with my website where the color of the container div is not changing to the correct color when a button representing a color is clicked. Instead, it seems to be displaying the next color in the array of color codes that I have se ...

Hovering over the child element, instead of the parent

I'm working on implementing a highlight feature for my website. The structure of the HTML looks something like this: <div> <!-- this is the main container --> <div> content ... </div><!-- a child element --> < ...

Exploring custom field grouping and projection in MongoDB

My current challenge involves MongoDB grouping and projecting custom fields within a specific collection: { "DescId" : "1", "Desc" : "Testing", "ParentId" : "null", "Order" : 1.0, "Type" : "A", "Parent" : null } { "Desc ...

I prefer for it to not display the database's _id

Currently working on a login page where successful logins return user information from the database. However, I'm looking to exclude the automatic _id allocation from the response. Does anyone know how to achieve this? Here is the function code: func ...

Body-parser is not returning any defined output

Seeking to transfer user input from the client side to the server side using body-parser in node.js, with express as the foundation of my application. Below is the server-side code snippet: app.post('/register', urlencodedParser, (req, res) => ...