Unable to update the database using findAndModify - node.js

I'm using Node.js with mongodb.
var mongo = require("mongodb").MongoClient;
var url = process.env.MLAB_URI;
mongo.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
//HURRAY!! We are connected. :)
console.log('Connection established');
var sequence = db.collection('sequence')
//Find and modify the sequence counter.
var obj = sequence.findAndModify({
query: {_id: 1},
update: { $inc: {seq: 1}},
new: true
});
console.log(obj);
sequence.find({_id:1}).toArray(function(err,res){
if(err)
console.log(err);
else
console.log(res)
});
db.close();
}
});
But the above code is not updating the database and the output of obj that is returned is Promise { <pending> } .
The complete output is
Connection established
Promise { <pending> }
[ { _id: 1, seq: 0 } ]
I need to update the value and retrieve the object . Is there any way to do that?
Thanks in advance!

Please change the findAndModify as mentioned below.
var obj = sequence.findAndModify(
{_id: 1},
{},
{ $inc: {"seq": 1}},
{new:true, upsert:true}
);
Second option to avoid promise:-
Comment the console.log(obj);
var obj = sequence.findAndModify(
{_id: "1"},
{},
{ $inc: {"seq": 1}},
{new:true, upsert:true}, function (err, doc) {
if(err)
console.log(err);
else
console.log(doc);
}
);
You are referring the Mongo shell version and try implementing the same using NodeJS version. The NodeJS findAndModify version is little different.
In order to get the old value of seq, you can set the new flag to false and doc.value.seq gives you the old value (i.e. value before update).
sequence.findAndModify(
{_id: "1"},
{},
{ $inc: {"seq": 1}},
{new:false, upsert:true}, function (err, doc) {
if(err)
console.log(err);
else {
console.log(doc);
console.log(doc.value.seq);
}
});

change to
var obj = sequence.findOneAndModify({
_id: 1,
update: { $inc: {seq: 1}},
upsert: true
});

Related

Node.js MongoDB Find with projection to exclude _id still returns it

Trying to follow the examples here to filter by using a projection to exclude _id. The _id still returns:
Code
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/db1";
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbase = db.db("db1"); //here
dbase.collection("customers").find(
{},
{
_id: 0
}
).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
Result still return as follows:
[ { _id: 5a2bb2d6ee48575cb54c4365,
name: 'John',
address: 'Highway 71' }, { _id: 5a2bb2d6ee48575cb54c436d,
name: 'Susan',
address: 'One way 98' }, .... { _id: 5a2bb2d6ee48575cb54c4371,
name: 'Chuck',
address: 'Main Road 989' }, { _id: 5a2bb2d6ee48575cb54c4372,
name: 'Viola',
address: 'Sideway 1633' } ]
Theoretically _id should not be part of what is returned. What is wrong here?
To limit the fields you have to use fields option( dont know about new updates):
dbase.collection("customers").find({}, {
fields: { _id: 0 }
}).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
UPDATE:
For version > 3 you have to use projection option instead:
dbase.collection("customers").find({}, {
projection:{ _id: 0 }
}).toArray(function(err, result) {
if (err) throw err;
console.log(result);
db.close();
});
In version 3 of the MongoDB API, the fields option has been deprecated. You should now use the projection option instead.
For example:
dbase.collection('customers').find({}, {
projection: {
_id: 0
}
}).toArray(function (err, result) {
if (err) {
throw err
}
console.log(result)
db.close()
})
The full list of supported options can be found here: http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#find
Starting in version 3.4, there is now an option for adding .project() outside of find().
Using ES8 async,await.
Ex:
async function connectDB(url) {
try {
const db = await MongoClient.connect(url);
const dbase = db.db("db1"); //here
const results = await dbase.collection("customers").find().project({_id:0}).toArray();
console.log(result);
db.close();
}
catch(err) {
throw err;
}
}
Docs here and another example here.

Mongo Update Record With Values Set In the Same Query

I have the following code for an API that aims to update a document in Mongo. It uses collection.update to insert values into a section and then another collection.update to push these values into an array within the document.
However, when I run this code, I keep getting nan in events.eventTime and events.endDate = null. How can I modify this to work properly? That is, I first insert values into runtime. and subsequently use those to push into events array.
router.get('/stop/:id', function(req,res){
var collection = db.get('Activity');
//Important to use findOne here to get an object back instead of an array
collection.findOne({_id : req.params.id }, function(err, activity){
if (err) throw err;
res.json(activity);
//console.log(activity);
collection.update({
_id: activity._id
},
{
$set: {
"runtime.started": false,
"runtime.endDate": new Date()
}
},
function(err, activity){
if (err) throw err;
//res.json(activity);
console.log(activity);
}
);
collection.update({
_id: activity._id
},
{
$push: {events: {
eventTime: ((activity.runtime.endDate - activity.runtime.startDate) / 1000),
startDate: activity.runtime.startDate,
endDate: activity.runtime.endDate
}
},
},
function(err, activity){
if (err) throw err;
//res.json(activity);
console.log(activity);
}
);
});
});
I guess either you should use promise or nested callback, javascript is asynchronous, so even though it looks like the second update call will be called after first, in reality they are being called at the same time and thus I guess the weird behavior.
It's better to wrap your DB call in promise and apply chaining. maybe you want to look at $q promises which is impressive. If you don't want to use promises, then call the second update method inside the function of the first update call
router.get('/stop/:id', function(req,res){
var collection = db.get('Activity');
//Important to use findOne here to get an object back instead of an array
collection.findOne({_id : req.params.id }, function(err, activity){
if (err) throw err;
res.json(activity);
//console.log(activity);
collection.update({
_id: activity._id
},
{
$set: {
"runtime.started": false,
"runtime.endDate": new Date()
}
},
function(err, activity){
if (err) throw err;
//res.json(activity);
console.log(activity);
collection.update({
_id: activity._id
},
{
$push: {events: {
eventTime: ((activity.runtime.endDate - activity.runtime.startDate) / 1000),
startDate: activity.runtime.startDate,
endDate: activity.runtime.endDate
}
},
},
function(err, activity){
if (err) throw err;
//res.json(activity);
console.log(activity);
}
);
}
);
}); });
Yes looks pretty messy, that's why promises are good.
Update, maybe you can try merging both the update call into one
collection.findOne({_id : req.params.id }, function(err, activity){
if (err) throw err;
res.json(activity);
//console.log(activity);
var date = new Date();
var duration = (date - activity.runtime.startDate)/1000;
collection.update({
_id: activity._id
},
{
$set: {
"runtime.started": false,
"runtime.endDate": date
},
$push: {events: {
eventTime: duration,
startDate: activity.runtime.startDate,
endDate: date
}
}
},
function(err, activity){
if (err) throw err;
//res.json(activity);
console.log(activity);
});
});

How to use findOneAndUpdate in MongoDB in Node

Say previously I had inserted a document into a mongo collection.
MongoClient.connect(url, function(err,db){
if(err) {throw err;}
else {
document = {action: "alert",
protocol: "udp",
port: "80",
_id: "12" }
var collection = db.collection("connections");
collection.insertOne(document, function(err,result){
if (err) {throw err;}
else {
console.log("Successful")
db.close();
}
}
}
Now I want to update the protocol field. What I have with no luck so far is
MongoClient.connect(url, function(err,db){
if (err) { throw err; }
else {
var collection = db.collection("connections");
collection.findOneAndUpdate({_id: "12"}, {$set: {protocol: "http"}}, {new: true}, function(err,doc) {
if (err) { throw err; }
else { console.log("Updated"); }
});
}
});
Am I passing the wrong parameters to the findOneAndUpdate method? I connect to the database correctly.
I think you should try
MongoClient.connect(url, function(err,db){
if (err) { throw err; }
else {
var collection = db.collection("connections");
collection.findOneAndUpdate({_id: "12"}, {$set: {protocol: "http"}}, {upsert: true}, function(err,doc) {
if (err) { throw err; }
else { console.log("Updated"); }
});
}
});
If "upsert" set to true, creates a new document when no document matches the query criteria.
=== Aug 2021
Here is an example of using findOneAndUpdate and getting back the updated document:
With the release of v4 of the node.js client, it seems the old solution of returnOriginal: false (which was awful anyway) is no longer the correct answer.
To see the list of available options for the node.js findOneAndUpdate method: https://mongodb.github.io/node-mongodb-native/4.0/interfaces/findoneandupdateoptions.html
But in short, this should work:
const doc = await <Collection>.findOneAndUpdate(
{ ... search },
{
$set: {
field1: 'value 1',
field2: ['value 2'],
etc.
},
},
{
upsert: true,
returnDocument: 'after', // this is new !
}
)
your third {new: true} argument is not valid
MongoClient.connect(url, function(err,db){
if (err) { throw err; }
else {
var collection = db.collection("connections");
collection.findOneAndUpdate({"_id": "12"}, {$set: {"protocol": "http"}}, function(err,doc) {
if (err) { throw err; }
else { console.log("Updated"); }
});
}
});
Here to update the record,it is not needed to use {upsert: true} and {new: true}. This solution works better.Try it once and suggest me if any errors in the code.
insert the update document if nothing matches the filter then use upsert.
MongoClient.connect(url, function(err,db){
if (err) { throw err; }
else {
var collection = db.collection("connections");
collection.findOneAndUpdate({_id: "12"}, {protocol: "http"}, {new: true}, function(err,doc) {
if (err) { throw err; }
else { console.log("Updated"); }
});
}
});

toArray undefined in NodeJS and MongoDB

I am trying to run an aggregation MongoDB and Nodejs, but I have some difficulties in running the project.
When I enter the following command in the MongoDB shell:
db.data.aggregate([{$match: {}},{$group: {'_id': '$State', 'total': {'$sum': 1}} }]).toArray()
then I am getting the expected output.
However, when I use the following little Nodejs program
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/weather', function(err, db) {
if(err) throw err;
console.log("Connected correctly to server");
var col=db.collection('data');
col.aggregate([{$match: {}},{$group: {'_id': '$State', 'total': {'$sum': 1}} }])
.toArray(function(err, result) {
if(err) throw err;
console.log(result);
});
db.close();
});
then I am getting the error message:
'TypeError: Cannot read property 'toArray' of undefined'
Could somebody please help me?
Many thanks in advance, Andi
As #ExplosionPills correctly pointed out, your code won't work as the logging is done asynchronously and after the connection is closed so you could try removing the db.close() line or create a function that makes use of a callback function to returns the aggregation results:
var aggregateStates = function(db, callback) {
db.collection('data').aggregate(
[
{ $group: { "_id": "$State", "total": { $sum: 1 } } }
]
).toArray(function(err, result) {
console.log(result);
callback(result);
});
};
Call the aggregateStates function:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/weather', function(err, db) {
aggregateStates(db, function() {
db.close();
});
});
According to mongo-native driver doc, aggregate() returns null. Therefore, toArray() cannot be called from what it returns. However, aggregate() takes a callback which has the result of aggregate() if successful. So here is the revised code:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/weather', function(err, db) {
if(err) throw err;
console.log("Connected correctly to server");
var col=db.collection('data');
col.aggregate([
{$match: {}},
{$group: {'_id': '$State', 'total': {'$sum': 1}} }
], function(err, result) {
if(err) {
db.close();
throw err;
}
console.log(result);
db.close();
});
});

use variable as key name in mongo db with modifier

I am trying to insert a bunch of arrays into mongoDB.
The data looks something like:
var places = {
city: ['london', 'york', 'liverpool'],
country: ['uk', 'france']
};
Each time I add an array, some of the entries could already be there, so I want to ensure there are no duplicates.
db.collection('places').ensureIndex({'city': 1}, {unique: true }, function(){ });
db.collection('places').ensureIndex({'country': 1}, {unique: true }, function(){ });
I then need to loop through the places object and add each item:
for(var key in places){
db.collection('places').update({_id: 1}, { $addToSet: { key: { $each places[key] } } }, function(err, docs){
if(err){
console.log(err);
}else{
console.log('success');
}
});
}
Here I need key to be the actual keys: 'city', 'country'. Rather than literally being 'key'. But I cant see how this can be achieved with with the modifiers in place.
You need to build up your $addToSet value programmatically:
for(var key in places){
var addToSet = {};
addToSet[key] = { $each: places[key] };
db.collection('places').update({_id: 1}, {$addToSet: addToSet}, function(err, docs){
if(err){
console.log(err);
}else{
console.log('success');
}
});
}
But you can do this more efficiently by combining both updates into one:
var addToSet = {};
for(var key in places){
addToSet[key] = { $each: places[key] };
}
db.collection('places').update({_id: 1}, {$addToSet: addToSet}, function(err, docs){
if(err){
console.log(err);
}else{
console.log('success');
}
});

Resources