Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating 2 mongoose schemas in an api call

Currently I'm trying to update Two different User Schema's in an api call.

The first schema is logged in user schema, we give it a name = Tom The second schema is other users who signup for the app, we give it a name = John

The schema code

schema.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');



var UserSchema = new Schema({
    name: String,
    username: { type: String, required: true, index: { unique: true }},
    password: { type: String, required: true, select: false },
    followers: [{ type: Schema.Types.ObjectId, ref: 'User'}],
    following: [{ type: Schema.Types.ObjectId, ref: 'User'}],
    followersCount: Number,
    followingCount: Number

});


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

The api name is '/follow/:user_id', what I want to achieve is . Whenever user Tom follows other user's like John, Tom's following field will be updated as well as John's follower field.

My current attempt (req.decoded.id is the logged in user)

api.js

// The first way

apiRouter.post('/follow/:user_id', function(req, res) {
    User.findOneAndUpdate(
    {   

        _id: req.decoded.id, 
        following: { $ne: req.params.user_id }
    }, 

    { 
        $push: { following: req.params.user_id},
        $inc: { followingCount: 1}

    },
    function(err, currentUser) {
        if (err) {
            res.send(err);
            return;
        }
        console.log(currentUser);

    });
    User.findOneAndUpdate(
    {

        _id: req.params.user_id,
        followers: { $ne: req.decoded.id } 

    },

    {
        $push: { followers: req.decoded.id },
        $inc: { followersCount: 1}

    }, function(err, user) {
        if(err) {
            res.send(err);
            return;
        }
        res.json({
            message: "Successfully followed"
        });
    }
    )
});


//Second way

apiRouter.post('/follow/:user_id', function(req, res) {

    // find a current user that has logged in
        User.update(
            {   
                _id: req.decoded.id, 
                following: { $ne: req.params.user_id } 
            }, 

            { 
                $push: { following: req.params.user_id},
                $inc: { followingCount: 1}

            },
            function(err) {
                if (err) {
                    res.send(err);
                    return;
                }

                User.update(
                    {
                        _id: req.params.user_id,
                        followers: { $ne: req.decoded.id }
                    },

                    {   
                        $push: { followers: req.decoded.id },
                        $inc: { followersCount: 1}

                    }

                ), function(err) {
                    if(err) return res.send(err);

                    res.json({ message: "Successfully Followed!" });
                }

        });
});

Both have problems,

The first way: The problem is, 'Can't set headers that already sent', because of the two separate mongoose query in one api call, it response twice that's why I get that error.

The second way: The problem is, the following field of logged in user(Tom) gets updated while the other user's followers field (John) return null. I console log both value and as well test it with POSTMAN chrome app.

Lend me your thoughts fellas!

like image 715
sinusGob Avatar asked Feb 12 '15 10:02

sinusGob


People also ask

What is the difference between schema and model in Mongoose?

A Mongoose schema defines the structure of the document, default values, validators, etc., whereas a Mongoose model provides an interface to the database for creating, querying, updating, deleting records, etc.

Can I use MongoDB and Mongoose at the same time?

Yes, we're actually using multiple drivers, in a production application. We need connections to multiple databases, and mongoose is only able to connect to one DB. So we use MongoDB for the connections to the secondary databases. It should be the same using MongoJS instead.

Does Mongoose sanitize input?

If you prefer explicitly calling a function rather than setting an option, Mongoose also exports a sanitizeFilter() function. The sanitizeFilter() function lets you sanitize input against query selector injections yourself.


2 Answers

The second way it is correct (could be improved running both of them in parallel) I guess the problem is in another place. I don't know which framework you are using but i guess the field _id is from mongoDB and is an ObjectId and looks like that the decoded.id can be an objectId while the one that comes from the request is of course just a string. So I guess it is empty because it does not find any user with that string.

Try do make it an objectId out of that string ( reffering to req.params.user_id in the second query)

like image 156
DevAlien Avatar answered Nov 01 '22 07:11

DevAlien


The first route you took seems to be fine.

However, as @cdbajorin mentioned, the error "can't send headers that already sent" has nothing to do with mongoose but the fact that you're trying to set the header after sending a response to the client already. (see this lovely answer)

My suggestion would be to make sure that both database calls are successful before you send a response.

You may also want to look into a two phase commit in this situation, as MongoDB does not support traditional DB transactions and you're updating two documents, one at a time. If for some reason either database call fails, a procedure to recover to a stable state should be taken.

like image 1
zedjay72 Avatar answered Nov 01 '22 08:11

zedjay72