MongoDB connection ends prematurely - node.js

var MongoClient = require("mongodb").MongoClient;
MongoClient.connect("mongodb://localhost:27017/test",function(err,db){
if(err) throw err;
var query = {};
var projection = {State : 1,Temperature : 1,_id : 1};
var options = {
sort : [["State","asc"],["Temperature","desc"]]
};
var state = "";
var temp = 0;
db.collection("weather").find(query,projection,options).each(function(err,doc){
if(err) throw err;
if(doc){
// Check if we have found a new state
if(state !== doc.State){
state = doc.State;
doc["month_high"] = true;
db.collection("weather").save(doc,function(err,saved){
if(err) throw err;
});
}
}else{
return db.close();
}
});
});
This is my code where I have to update certain documents that meet a criteria. Instead of storing all the documents that need to be updated, and then updating them later, I am updating them as they come.
However, I get a MongoError: server localhost:27017 sockets closed. I know this is because I am trying to use a connection that has already been closed.
How do I properly sequence the closing of the database and updating of the documents?

It looks like your last db.save() is still being processed when you call db.close()
Check if you are processing the last item and then call db.close() in your db.save() callback.

Related

How to continuously query a mongodb database efficiently

I'm new to Node and MongoDB and I have a seemingly simple request. I've managed to connect to my database, and use a query to get my desired results. Now, I want to have this query continue indefinitely, since the end goal for my project is to plot data real time.
I would have thought a simple 'while (true)' loop would suffice, but that doesn't seem to be the case.
const MongoClient = require('mongodb').MongoClient;
// Connection URL
const url = 'mongodb://<username>:<password>#ds157614.mlab.com:57614/flight_data';
// Use connect method to connect to the Server
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("flight_data").collection("data");
while(true)
{
dbo.find().sort({_id: 1}).limit(1).toArray(function(err, result) {
if (err) throw err;
console.log("Temperature: " + result[0].data.temperature);
});
}
db.close();
});
I have found that the while loop is indeed running, but for some reason, the query just doesn't happen when inside the while loop. If you remove the while loop, the code functions fine. I just want it to continually print the results of the query being repeated.
Querying a DB continuously is inefficient and resource wasting, instead use change streams. It watches collection for any changes and will make the db call then only. Works only for Mongo 3.6+.
const MongoClient = require("mongodb").MongoClient;
// Connection URL
const url =
"mongodb://<username>:<password>#ds157614.mlab.com:57614/flight_data";
// Use connect method to connect to the Server
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
const collection = db.collection("data");
const changeStream = collection.watch();
changeStream.on("change", next => {
// process next document
collection
.find()
.sort({ _id: 1 })
.limit(1)
.toArray(function(err, result) {
if (err) throw err;
console.log("Temperature: " + result[0].data.temperature);
});
});
db.close();
});

How to efficiently close the db connection (mongodb and node)

I have a small program that reads each of the record and update each of the record. Given the async nature of node and callback. what is the efficient and the correct way to close the db connection?
Sample Program:
var MongoClient = require('mongodb').MongoClient;
var updateCount = 0;
MongoClient.connect('mongodb://localhost:27017/school', function(err, db) {
if(err) throw err;
var query = { };
// get all the students in the database
var cursor = db.collection('students').find(query);
cursor.each(function(err, doc) {
if(err) throw err;
if(doc == null) {
return;
}
// filter out only the homework scores
var homeworksOnly = doc.scores.filter(function(scores){
if (scores.type === "homework") return true;
return false;
})
// filter out the non homework scores
var notHomeWorks = doc.scores.filter(function(scores){
if (scores.type !== "homework") return true;
return false;
})
// sort the homework score to remove the min score from the list.
homeworksOnly.sort(function(a,b){
if (a.score > b.score) return 1;
if (b.score > a.score) return -1;
return 0;
});
console.log("Before removing the min score"+doc._id);
console.dir(homeworksOnly);
console.log("After removing the min score"+doc._id);
homeworksOnly.splice(0,1);
console.dir(homeworksOnly);
console.log("Merge the homework with other scores"+doc._id);
var newScores = homeworksOnly.concat(notHomeWorks);
console.dir(newScores);
console.log("*****");
// Now update the database for this student with the new scores
var search = {"_id":doc._id};
var operator = { '$set' : { 'scores' : newScores } };
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
});
});
});
Now the program works but I need to hit the Ctrl+C to terminate the program. Is there a way to know that all the callbacks have completed so that the program can be terminated?
There are better libaries you can integrate with nodejs to handle the callback flow better, but simply working with the basic driver as a dependency, all you need is the basic node stream interface which is already built in to the cursor.
This allows .pause() and .resume()for flow control when processing, and an "end" event when the cursor stream is complete:
var MongoClient = require('mongodb').MongoClient;
var updateCount = 0;
MongoClient.connect('mongodb://localhost:27017/school', function(err, db) {
if(err) throw err;
var query = { };
// get all the students in the database
var cursor = db.collection('students').find(query);
// called on errors
cursor.on("error",function(err) {
throw err;
});
// called on stream complete
cursor.on("end",function() {
db.close();
});
// process each document in the stream
cursor.on("data",function(data) {
cursor.pause(); // stops the cursor stream while processing
// filter out only the homework scores
var homeworksOnly = doc.scores.filter(function(scores){
if (scores.type === "homework") return true;
return false;
})
// filter out the non homework scores
var notHomeWorks = doc.scores.filter(function(scores){
if (scores.type !== "homework") return true;
return false;
})
// sort the homework score to remove the min score from the list.
homeworksOnly.sort(function(a,b){
if (a.score > b.score) return 1;
if (b.score > a.score) return -1;
return 0;
});
console.log("Before removing the min score"+doc._id);
console.dir(homeworksOnly);
console.log("After removing the min score"+doc._id);
homeworksOnly.splice(0,1);
console.dir(homeworksOnly);
console.log("Merge the homework with other scores"+doc._id);
var newScores = homeworksOnly.concat(notHomeWorks);
console.dir(newScores);
console.log("*****");
// Now update the database for this student with the new scores
var search = {"_id":doc._id};
var operator = { '$set' : { 'scores' : newScores } };
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
cursor.resume(); // restarts the stream processing now we are done
});
});
});
After the update statement is done use
db.collection('students').update(search, operator, function(err, updated) {
if(err) throw err;
updateCount++;
console.dir("Successfully updated " + updated + " document! count: "+updateCount);
});
db.close();

node js mongo find last inserted

I am having a hard time trying to find the last inserted element into mongo. I am using an example code I found and trying to make the query and display the item but I am getting an error. I understand I am suppose to do something like this.
db.collectionName.findOne({}, {sort:{$natural:-1}})
But this is what I have so far and it's not working.
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) {
if(err) { return console.dir(err); }
var collection = db.collection('test');
var doc1 = {'hello':'doc1'};
var doc2 = {'hello':'doc2'};
var lotsOfDocs = [{'hello':'doc3'}, {'hello':'doc4'}];
collection.insert(doc1);
collection.insert(doc2, {w:1}, function(err, result) {});
collection.insert(lotsOfDocs, {w:1}, function(err, result) {});
collection.find({}).toArray(function(err, docs) {
console.log(docs[0]);
});
db.close();
});
This is the error.
nodejs/node_modules/mongodb/lib/mongodb/connection/base.js:246
throw message;
^
TypeError: Cannot read property '0' of null
I checked to make sure the database is not empty so I am not sure why it's returning null.
I've found a possible solution(here) to your problem. It might be due to the fact that the database connection closes before the operations that you have issued finish.
You can fix it by including the db.close() call inside the find query.
collection.find({}).toArray(function(err, docs) {
console.log(docs[0]);
db.close();
});

How to get user info from mongodb in node.js

Totally new to mongo, I've been checking examples for hours, Trying to check if a user exists in this collection:
{ "name" : "chrispy", "pass" : "xxxx", "_id" : ObjectId("5221b29b69f9e9b11a000001") }
But cannot match name and get the results, i've tried numerous examples, and no luck.
Works well in the console:
mongo main
> db.users.findOne({name : 'chrispy'})
{
"name" : "chrispy",
"pass" : "xxxx",
"_id" : ObjectId("5221b29b69f9e9b11a000001")
}
>
Once I can match the name, I'll match the password. but cant even get as far as matching the user name. Help = 1000 thankyou's!
var name = 'chrispy';
var pass = '';
console.log("About to check for name and pw");
Mongo.connect('mongodb://127.0.0.1:27017/main', function(err, db) {
if(err) throw err;
var collection = db.collection('users');
// does user exist
var doc = collection.findOne({name : name}, function(err,doc){
if(err) throw err;
if(doc)
console.log("1 Found: "+name+", pass="+doc.pass);
else
console.log("1 Not found: "+name);
});
if(doc)
console.log("2 Found: "+name+", pass="+doc.pass);
else
console.log("2 Not found: "+name);
db.close();
});
Console Output:
About to check for name and pw
2 Not found: chrispy
It doesn't even seem to be going in to the findOne() function, external to the findOn() function it fails anyway.
So here are the changes before it started to work, removed var Doc = , and closed the db only after function within findOne() is fired, else it closes the DB before the result.
var name = 'chrispy';
var pass = '';
console.log("About to check for name and pw");
Mongo.connect('mongodb://127.0.0.1:27017/main', function(err, db) {
if(err) throw err;
var collection = db.collection('users');
// does user exist
collection.findOne({name : name}, function(err,doc){
if(err) throw err;
if(doc)
console.log("Found: "+name+", pass="+doc.pass);
else
console.log("Not found: "+name);
db.close();
});
});
Just to be clear, the various database calls are asynchronous actions that utilize Javascript Promises. The call to findOne returns a Promise object, not the found document. That is,
collection.findOne(
{name: name},
function(err,doc) {
/* handle err or process doc */
}
);
is functionally equivalent to
collection.findOne(
{name: name}
).then(
// resolved handler
function(doc) {
// process doc
},
// rejected handler
function(err) {
// handle err
}
);
Your code was starting the asynchronous findOne call and then closing the database before the call has finished (i.e. the promise resolved or rejected). If you need to close the db, you should do it in the callback, once you've obtained your document.

How to drop all collections via mongodb and node.js?

I am new to node.js and mongodb and I have the following problem:
I need to drop all collections from my mongodb from node.js file. I have such a function:
service.dropCollections = function(db, colls){
for(var i = 0; i < colls.length; i++){
var name = colls[i].name;
db.dropCollection(name, function(err) {
if(!err) {
console.log( name + " dropped");
} else {
console.log("!ERROR! " + err.errmsg);
}
});
}
}
And I am using it in the following function:
service.clearDB = function() {
var MongoClient = require('mongodb').MongoClient
, format = require('util').format;
MongoClient.connect('mongodb://127.0.0.1:27017/shiny_d', function(err, db){
if(err) throw err;
db.collectionNames(function(err, collections){
if(!err){
service.dropCollections(db, collections);
} else {
console.log("!ERROR! "+ err.errmsg);
}
service.showCollections();
});
});
}
As an output I have
!ERROR! ns not found
shiny_db.physicalinfos
Dunno what to do right now. I'll be very thankful for your help.
Ain't it more faster, easier and less error prone if you just drop the entire database?
db.dropDatabase();
At least from the Mongo CLI, whenever you access an inexistent DB, it'll be persisted the soon you create data. That's the same as dropping all collections from it.
I haven't tried MongoDB for anything except studying yet, so I don't know much about permissions. So, probably the only problem of dropping the entire DB would be the permissions of your users that would be lost (I believe).
If this script you're trying to create is not for production, then you're good to go with dropping the DB.
I found an answer. First of all I've made mistake in my connection it should be like following: 'mongodb://127.0.0.1:27017/shiny_db'. The second mistake was in the name of collection. It was like 'db_name.coll_name', that's why db.dropCollection(name, callback) couldn't find particular collection and because of it I had mistake ns not found. So I've used following mechanism to separate db_name from coll_name:
var name = colls[i].name.substring('shiny_db.'.length); and I've added checking for "system" collection.
Final code looks like following:
service.clearDB = function() {
var MongoClient = require('mongodb').MongoClient
, format = require('util').format;
MongoClient.connect('mongodb://localhost/shiny_db', function(err, db) {
if(err) throw err;
db.collectionNames(function(err, collections){
if(!err){
service.dropCollections(db, collections);
} else {
console.log("!ERROR! "+ err.errmsg);
}
});
});
}
service.dropCollections = function(db, colls){
for(var i = 0; i < colls.length; i++){
var name = colls[i].name.substring('shiny_db.'.length);
if (name.substring(0, 6) !== "system") {
db.dropCollection(name, function(err) {
if(!err) {
console.log( name + " dropped");
} else {
console.log("!ERROR! " + err.errmsg);
}
});
} else {
console.log(name + " cannot be dropped because it's a system file");
}
}
}
Hope it will help someone!
listCollections gives you an array of collection names as strings.
It looks like you may be confusing it with something that returns an array of collection objects like maybe db.collections()

Resources