Node.js : Mongodb - Concurrent Requests - node.js

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} );

Related

MongoDB: findOne returns null but document exists in collection

I'm trying to send an email and password server-side and check if a document with those values exists (which it does), but when I console log the results from the query it's null.
Here's the document in the users collection:
{
"_id" : ObjectId("580bcf9874ae28934705c0fc"),
"email" : "johndoe#gmail.com",
"password" : "pass"
}
Here's what I'm sending server-side:
{"email":"johndoe#gmail.com","password":"pass"}
Here's my code (updated):
mongo.connect('mongodb://localhost:27017', function (err, db) {
if (err) {
console.log("error: " + err); // logs nothing
} else {
var users = db.collection("users");
var tasks = db.collection("tasks");
app.post("/login", function(req, res) {
var emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var userInDb;
var userEmail = req.body.email;
var userPassword = req.body.password;
console.log(req.body.email); // logs "johndoe#gmail.com"
console.log(req.body.password); // logs "pass"
if (!userEmail || !userPassword) {
return res.sendStatus(403);
} else if ( !emailRegex.test(userEmail)) {
return res.sendStatus(403);
} else {
users.findOne( { "email": userEmail, "password": userPassword }, function(err, results) {
console.log(results); // logs "null"
if(err) {
console.log("error: " + err); // logs nothing
res.sendStatus(403);
} else {
console.log("here"); // logs "here"
res.sendStatus(200);
}
});
}
});
}
});
each time you pass a callback that has an error parameter, it's your responsibility to check if an error was passed, and if so, deal with it.
in your code, you have two such callbacks:
mongo.connect('mongodb://localhost:27017', function (err, db)
users.findOne( { "email": userEmail, "password": userPassword }, function(err, results)
either one of them can return an error object that might explain the issue.
add the following to the first line of each callback:
if (err) {
return console.log("error: " + err);
}
This one worked for me.
I had to call toArray() method.
I don't remember how I found that solution, cuz in MongoDB manuals they don't call to array method
users.findOne( { "email": userEmail, "password": userPassword }).toArray()
I faced a simular problem in one of my project. It is all because I stored the collection and the document in a database which is different from which my app is connected to. Check that once.
It is really mysterious, I think MongoDB client should make a fix on it.
MongoDB is not very reliable. Often get lose connection in 1/10 of requests. But the very annoying is, it returns an empty array instead of an error in connection, that makes us impossible to catch connection error.
Because I use the existence of documents in DB to reinitialize the project, I really get annoyed of it. CouchDB will not have this problem.
users.findOne({'email' : userEmail , 'password':userPassword }, function(err, result) {
console.log("result:"+result);
});

MongodDB Mongoose Collection.findAndModify Upsert: ture

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
}

MongoDB Upserts Variable name as a key (Node.JS)

Node.JS, MONGODB, not using Mongoose.
I have a document I'm saving. When I use UPSERT, it structures the data as so :
{ "_id" : ObjectId("573a123f55e195b227e7a78d"),
"document" :
[ {"name" : "SomeName" } ]
}
When I use INSERT it inserts it all on the root level :
{ "_id" : ObjectId("573a123f55e195b227e7a78d"), "name" : "SomeName" }
This is obviously going to lead to lots of inconsistencies. I have tried various things. I've tried various methods such as findOneAndReplace, Update with Upsert, I've tried Replace, $setOnInsert. It all does the same thing when Upsert is involved it seems.
I have tried to use document[0] to access the first block of the array, but that throws an error... 'Unexpected Token ['
I have tried various methods and dug for hours through the various documentation, and have searched high and low for someone else having this problem, but it doesn't seem to be well documented issue for anyone else.
Anyone have any recommendations to make sure that all the fields are on the ROOT level, not nested under the variable name? Relevant code below.
findReplace: function(db, document, collection) {
var dbCollection = db.collection(collection);
var filter = document.name;
return new Promise(function(resolve, reject) {
dbCollection.updateOne({
"name" : filter
}, {
document
}, {
upsert: true
}, function(err, result) {
if (err) {
console.log('error', err)
reject(err);
}
console.log("Found Document and Upserted replacement Document");
resolve([{'status' : 'success'}]);
});
});
}
When you do this:
{
document
}
You are creating an object containing a document property and the variable's value as its value:
{
document: [ {"name" : "SomeName" } ]
}
This is new functionality from ES6. If you want to access the first item of the document variable, don't create a new object:
return new Promise(function(resolve, reject) {
dbCollection.updateOne({
"name" : filter
}, document[0], { // <========
upsert: true
}, function(err, result) {
if (err) {
console.log('error', err)
reject(err);
}
console.log("Found Document and Upserted replacement Document");
resolve([{'status' : 'success'}]);
});
});

Fetch entries from mongodb using mongoose

I am using mongoose to operate mongodb in node. And now I have to query entries from Post table where tags doesn't contain any tag like inc:___, inc:gdc, exc:abc, exc:57uyht7, all others tags are allowed like(test,card,check).
PostModel.Post.find({
$where:this.activeFlag==true && (this.tags!= null && this.tags != /^inc:/ && this.tags !=/^exc:/)
}), function(err, stack) {
if (!err) {
logger.debug('Posts found successfully.');
} else {
logger.error('Problem in finding posts, error :' + err);
}
});
But this is not working.. My query fetches entries with inc:dbgh also.
Can anyone help me?
Thanks in advance.
According to Mongo docs, you should pass either a string or javascript function to $where, and you pass javascript expression, which gets evaluated in place.
Also, your query can be simplified to
PostModel.Post.find({
activeFlag: true,
tags: {
$exists: true,
$not: /^(inc:|ecx:)/
}
}, function(err, stack) {
if (!err) {
logger.debug('Posts found successfully.');
} else {
logger.error('Problem in finding posts, error :' + err);
}
});

MongoDB node native driver creating duplicate documents

I'm getting a duplicate document when using the mongodb-native-driver to save an update to a document. My first call to save() correctly creates the document and adds a _id with an ObjectID value. A second call creates a new document with a text _id of the original ObjectID. For example I end up with:
> db.people.find()
{ "firstname" : "Fred", "lastname" : "Flintstone", "_id" : ObjectId("52e55737ae49620000fd894e") }
{ "firstname" : "Fred", "lastname" : "Flintstone with a change", "_id" : "52e55737ae49620000fd894e" }
My first call correctly created Fred Flinstone. A second call that added " with a change" to the lastname, created a second document.
I'm using MongoDB 2.4.8 and mongo-native-driver 1.3.23.
Here is my NodeJS/Express endpoint:
app.post("/contacts", function (req, res) {
console.log("POST /contacts, req.body: " + JSON.stringify(req.body));
db.collection("people").save(req.body, function (err, inserted) {
if (err) {
throw err;
} else {
console.dir("Successfully inserted/updated: " + JSON.stringify(inserted));
res.send(inserted);
}
});
});
Here is the runtime log messages:
POST /contacts, req.body: {"firstname":"Fred","lastname":"Flintstone"}
'Successfully inserted/updated: {"firstname":"Fred","lastname":"Flintstone","_id":"52e55737ae49620000fd894e"}'
POST /contacts, req.body: {"firstname":"Fred","lastname":"Flintstone with a change","_id":"52e55737ae49620000fd894e"}
'Successfully inserted/updated: 1'
Why doesn't my second update the existing record? Does the driver not cast the _id value to an ObjectID?
What you are posting back the 2nd time contains a field named "_id", and it's a string. That is the problem.
Look at the document, what the save method does is a "Simple full document replacement function". I don't use this function quit often so here's what I guess. The function use the _id field to find the document and then replace the full document with what you provided. However, what you provided is a string _id. Apparently it doesn't equal to the ObjectId. I think you should wrap it to an ObjectId before passing to the function.
Besides, the save method is not recommended according to the document. you should use update (maybe with upsert option) instead
I don't exactly know why a second document is created, but why don't you use the update function (maybe with the upsert operator)?
An example for the update operation:
var query = { '_id': '52e55737ae49620000fd894e' };
db.collection('people').findOne(query, function (err, doc) {
if (err) throw err;
if (!doc) {
return db.close();
}
doc['lastname'] = 'Flintstone with a change';
db.collection('people').update(query, doc, function (err, updated) {
if (err) throw err;
console.dir('Successfully updated ' + updated + ' document!');
return db.close();
});
});
And now with the upsert operator:
var query = { '_id': '52e55737ae49620000fd894e' };
var operator = { '$set': { 'lastname': 'Flintstone with a change' } };
var options = { 'upsert': true };
db.collection('people').update(query, operator, options, function (err, upserted) {
if (err) throw err;
console.dir('Successfully upserted ' + upserted + ' document!');
return db.close();
});
The difference is that the upsert operator will update the document if it exist, otherwise it will create a new one. When using the upsert operator you should keep in mind that this operation can be underspecified. That means if your query does not contain enough information to identify a single document, a new document will be inserted.

Resources