MongodDB Mongoose Collection.findAndModify Upsert: ture - node.js

I'm trying to add a basic user-to-user messaging service for users of a NodeJS, Express, MongoDB app.
I have two MongoDB documents for the feature: a 'Messages' document which contains each individual message, and a 'Conversations' document, which will refer to all the 'Messages' which belong to it.
So once a message hits the server, I want to do a findAndModify for conversations belonging to both the sender and the recipient. If such a conversation exists, then update it with the new message. If the conversation doesn't exist, then create it and then add the message.
app.post('/messages', function(req, res){
var message = { // Create an object with the message data
sender: req.body.sender,
recipient: req.body.recipient,
messageContent: req.body.msgCont,
timeSent: Date.now()
};
Message.create(message, function(err, newMessage){ // Add the message to MongoDB
if(err){
console.log('Error Creating Message' + Err);
} else {
console.log("The New Message " + newMessage)
Conversation.findOneAndUpdate({ // Find a conversation with both the sender
$and: [ // and receiver as participants (there should
{$or: [ // only ever by one such conversatoin)
{"participants.user1.id" : req.body.sender},
{"participants.user1.id" : req.body.recipient}
]},
{$or: [
{"participants.user2.id" : req.body.sender},
{"participants.user2.id" : req.body.recipient}
]},
]}, {$setOnInsert : {
messages : message,
"participants.user1.id" : req.body.sender,
"participants.user2.id" : req.body.recipient
},
new : true,
upsert : true
}, function(err, convo){
if(err){
console.log(err + 'error finding conversation')
} else {
console.log("Convo " + convo)
}
});
}
});
res.redirect('/matches');
});
Adding the message to the database works fine, but something with the Conversation query isn't working. I get a console.log of Convo null, so it's not returning an error, but nothing is going into the conversation.
If anyone can see where I'm going wrong, I'd be super happy with some guidance!

MongoDB findOneAndUpdate method (documentation) doesn't have the option new, it's returnNewDocument instead. Also, you're missing the curly brackets out these options
{
returnNewDocument: true,
upsert: true
}

Related

findOneAndUpdate. Select array of messages, select message object and update likes array inside it

I use mongoose findOneAndUpdate method, but could't achieve the result. I always get the same object back. No errors. Here's my code.
User object:
{
"_id" : ObjectId("6273b64b607b1d228795f067"),
"username" : "User1",
"mail" : "user#inbox.com",
"password" : "$2b$12$NrJ9E8xSz1iCfmIPY0snC.e6x4B/ymqtH.at9uUWaCXTQUwJi1eem",
"messages" : [
{
"message" : "Brand new message",
"date" : 1652508162106.0,
"author" : "User1",
"likes" : []
}
],
"__v" : 16
}
Here's my backend code:
const {author} = req.params;
const {mssg, whoLiked} = req.body;
User.findOneAndUpdate({username: author, messages: mssg}, {$push: {likes: whoLiked}}, {new: true}, (err, user)=>{
err && console.log(err);
res.send(user)
})
})
Nothing udpates. Maybe I try to pick the message the wrong way... I know, it's possible to do this in one action. But I can't figure it out. What'd you say guys?
Ok, I finaly figured it out. Here's the code to push new person whoLiked into the likes array.
router.post("/:author/likeThePost", (req,res)=>{
const {author} = req.params;
const {mssg, whoLiked} = req.body;
console.log(mssg, whoLiked);
User.findOneAndUpdate({username: author, "messages.date": mssg.date}, {$push: {"messages.$.likes": whoLiked}}, {new: true}, (err, user)=>{
err && console.log(err);
res.send(user.messages)
})
})
Some comments:
I use mongoose findOneAndUpdate method. As far as I know there's the same node.js method, but the code might be different.
Let's break down the code a bit:
first you find the user who wrote the original message by username: "Some user name"
then you pick the message you need by some unique value. In my case this is just the timestamp: "messages.date": mssg.date . Same user just can't post two messages with the same Date.now() timestamp.
then you do what you need with the message've been picked:{$push: {"messages.$.likes": whoLiked}} Second dollar sign stands for picking right message. Looks like it's the index of it.

Selecting only modified subdocument from Mongo

I have a mongoose query like this:
var query = Events.findOneAndUpdate({ '_id': event._id,'participants._id':participant._id},{'$set': {'participants.$': participant}}, {upsert:false,new: true},function(err,result){
if(err){
return res.status(500).jsonp({
error: 'Unable to update participant'
});
}
console.log(result.participants[0]);
res.jsonp(result.participants[0]);
});
and the query works properly modifying the participants subdocument inside Events collection.
The problem:
I need only the modified participant to be returned as JSON and I am not in need of the entire participants array but I am not able to achieve this since I get all the participants when I do console.log(result.participants);
How do I get only the modified subdocument after the query?
You may have to use the native JS filter() method as in the following:
Events.findOneAndUpdate(
{ '_id': event._id, 'participants._id': participant._id },
{ '$set': { 'participants.$': participant } },
{ upsert: false, new: true },
function(err, result){
if(err){
return res.status(500).jsonp({
error: 'Unable to update participant'
});
}
var modified = result.participants.filter(function(p){
return p._id === participant._id
})[0];
console.log(modified);
res.jsonp(modified);
}
);

Update data in MongoDB with Mongojs using findAndModify()

Yet another first-timer problem here. This gets data from a database and displays it in some text fields (that part is not shown in the code below) and after the user edits it the data should be updated in the database via the findAndModify() method and I think this is where the issue lies. There are no errors, it just doesn't do anything. EDIT The following error is received: MongoError: Either an update or remove=true must be specified
server.js
MongoClient.connect("mongodb://user:secretPassword#aws-us-east-1-portal.7.dblayer.com:10712,aws-us-east-1-portal.10.dblayer.com:10316/database", function(err, db) {
if (err) throw err;
var contactList = db.collection("contactList");
app.put('/contactList/:id', function(req, res) {
var id = req.params.id;
console.log("edited: " + req.body.name); //works up until here
contactList.findAndModify({
query: {_id: mongojs.ObjectId(id)},
update: {$set: {name: req.body.name, email: req.body.email, number: req.body.number}},
new: true
}, function (err, doc) {
res.json(doc);
})
});
controller.js
$scope.update = function() {
$http.put('/contactList/' + $scope.contact._id, $scope.contact).success(function(response) {
refresh();
})
};
If this were me I would first do a couple of things:
Before your call to findAndModify just do a simple find using your query. Make sure you can actually find the object using your query. If that works you know that the 'find' part of the findAndModify is probably ok.
Do some console logging inside the callback handler of the findAndModify call. As it stands you do not do anything if an err is returned from the findAndModify call. It is possible your call is returning an error that you are just ignoring and it may provide some additional insight into your problem.
I would try these two first and see if it helps.
Update:
Example using native:
collection.findAndModify(
{ field: 'some value' },
[],
{ $set: { field2: 'some new value' } },
{ new:true },
function(err, doc) {
//handle err and doc
});

sails won't populate user (undefined)

I try to associate a chat message with an user. But the populate function gives me an user undefined
//message.js
module.exports = {
attributes: {
user:{
model: 'User'
},
message:{
type:'string',
required:true
}
}
};
I save an entry of a message and try to populate the messages with the user afterward
module.exports = {
test: function(req, res){
var data = {
user : "5533b9f00fe12ff020c67225",
message : 'auto generated'
};
Message.create(data, function messageCreated(err, message){
if(err) return res.negotiate(err);
Message.find().populate('user').exec(function(err, messages){
if(err) return res.negotiate(err);
res.json(messages);
});
});
}
};
But it always gives me user undefined back, when I try to populate.
What I have tried so far:
get one user and populate all his messages: works!
find one user according the found Message's associated userId the step before, to ensure the user exists: works!
change the Assosiation so another Message instead of an User and than populate the Messages with the one "master message" : works!
I don't understand why it works with other models but not the User model

Node.js : Mongodb - Concurrent Requests

In my node.js application, a route is executed on Ajax request and it always receives concurrent requests.
My requirement is to check for a similar document and if exist upsert the data to existing document. If no similar document then create a new record.
Refer the code given below. The problem is that most times the "findExistingDoc" returns false, but by the time when a call tries to create a new document, a similar document is already created by a previous call.
Please help with a optimal solution to solve this problem.
self.findExistingDoc(function(err, doc){ // Check if a similar doc already exist
if (!doc){
console.log ("NO existing doc");
self.save(function(err, doc) {
if (err) {
console.error ("DB Error while creating new doc : " + JSON.stringify(err));
} else {
console.log ("New document created");
}
});
} else {
console.log ("existing doc");
var qry = {"name": req.s_name, "class": req.s_class};
self.model("Collection").findOneAndUpdate (qry, {$addToSet: {"data": req.body.s_data}}, {safe: true, upsert: true}, function (err, album) {
if (err) {
console.error ("DB Error while adding data to existing doc : " + JSON.stringify(err));
} else {
console.log ("Data added to existing doc");
}
callback(err, output);
});
}
});
Solved my problem after some googling. Thanks to the new "$setOnInsert" operator that's introduced in mongodb v2.4.
var slug = getUniqueSlug("name");
var qry = {"name": req.s_name, "class": req.s_class};
var update = {"$addToSet": {"data": req.body.s_data}, "$setOnInsert": {"slug": slug}};
self.model("Collection").findAndModify ( qry, update, {"safe": true, "upsert": true} );

Resources