Output data from mongodb to an external variable - node.js

How to output data from mongodb to an external variable. For example
let variableArr = [];
const mongoClient = new MongoClient(url, { useUnifiedTopology: true });
mongoClient.connect(function(err, client){
const db = client.db("variabledb");
const collection = db.collection("variables");
if(err) return console.log(err);
collection.find().toArray(function(err, results){
results.forEach(item => variableArr.push(item))
client.close();
});
console.log(variableArr);
The console outputs an empty array. I understand that the problem is async, but how to implement this moment.
Thanks!

Related

How to print database names and their collections names in order using nodeJS MongoClient?

I'm trying to print list of database names following their corresponding collection names using a nested loop with MongoDB Node.js Driver.
Database A:
Collection a
Collection b
Database B:
Collection f
Collection g
Database C:
Collection h
Collection j
but instead I get this:
Database A:
Database B:
Database C:
Collection a
Collection b
Collection f
Collection g
Collection h
Collection j
What am I doing wrong? Is this because listDatabases() and listCollections() sharing the same MongoClient connection and they can't be executed simultaneously or it is just promise thing?
const mongo = require('mongodb').MongoClient;
const url = 'mongodb://127.0.0.1:27017';
mongo.connect(url, {
useNewUrlParser: true,
useUnifiedTopology: true
}, (err, client) => {
if (err) {
console.log(err)
return;
}
// print db names with their corresponding collections
client.db().admin().listDatabases({ nameOnly: true }, (err, result) => {
if (!err) {
result.databases.forEach((db) => {
console.log('Database: ' + db.name);
client.db(db.name).listCollections().toArray((err, collections) => {
collections.forEach((col) => {
console.log(col.name);
});
});
});
} else {
return console.log(err.message);
}
});
});
This is because client.db(db.name).listCollections() is async operation. To put it very plainly the result.databases.forEach is not going to wait for each listing of collections for the current db that just got printed above. Which is evident from the output.
That is how callbacks with async operations behave. This could be made nicer to look with async/await or promise/then syntaxes. If sticking to callback syntax, simply moving console of db inside would achieve the output.
// print db names with their corresponding collections
client
.db("test")
.admin()
.listDatabases({ nameOnly: true }, (err, results) => {
if (err) {
return console.error("Error::", err.message);
}
results.databases.forEach((item, index) => {
client
.db(item.name)
.listCollections()
.toArray((err, collections) => {
if (err) {
return console.error("Error::", err.message);
}
console.info("DATABASE: ", item.name);
collections.forEach(col => {
console.info("COLLECTION: ", col.name);
});
});
});
});
UPDATE: With async/await syntax
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost:27017";
const client = new MongoClient(url, {
useNewUrlParser: true,
useUnifiedTopology: true
});
const defaultDb = "test";
(async function() {
try {
await client.connect();
//fetching dbs
const dbs = await client
.db(defaultDb)
.admin()
.listDatabases({ nameOnly: true });
for (db of dbs.databases) {
console.info("Database::", db.name);
const collections = await client
.db(db.name)
.listCollections()
.toArray();
for (c of collections) {
console.info("Collection::", c.name);
}
}
client.close();
} catch (err) {
console.log(err.stack);
}
})();
Notice the for..of syntax with await because forEach doesn't behave the way intended Why?. And a plain for(let i=0 ... i++) would also work just fine with await.
try to convert it to promise and use the map to async await the results
thus https://flaviocopes.com/javascript-async-await-array-map/ may be helpfull

Connecting to MongoDB Successfully, But Having Issues with `getCollection()`

I am writing some tests for a Node/Mongo project, and in one of my tests I need to connect to the database, and then pull a document from my jobs collection. However, I am running into an issue. I can connect to the database successfully, but then get an error on my findOne(). The specific error is:
TypeError: db.getCollection is not a function
Here is the code:
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'sample_db';
// Create a new MongoClient
const client = new MongoClient(url);
client.connect(async function (err) {
assert.equal(null, err);
console.log("Connected successfully to server"); // I see this in the console
const db = await client.db(dbName);
let savedJobResult = await db.getCollection("jobs").findOne({
"name": "Agenda Job Test"
});
console.log('savedJobResult: ', savedJobResult);
client.close();
});
What am I missing here?
Try this query
let savedJobResult = await db.collection("jobs").findOne({
"name": "Agenda Job Test"
});
change getCollection to collection
const insertDocuments = function(db, callback) {
// Get the documents collection
const collection = db.collection('documents');
// Insert some documents
collection.insertMany([
{a : 1}, {a : 2}, {a : 3}
], function(err, result) {
assert.equal(err, null);
assert.equal(3, result.result.n);
assert.equal(3, result.ops.length);
console.log("Inserted 3 documents into the collection");
callback(result);
});
}

Cannot read property 'db' of null javascript with parcel

I tried to set up a mongodb system with my openlayers map, but it is not working : Uncaught TypeError: Cannot read property 'db' of null. My part of code about mongodb is :
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
var tapDB = db.db("tapDB"); //<-- here is the error
})
I suppose that this error is maybe because i am using npm start instear of node server.js, but I am not sure because i am a newbie. Mongodb is started via the cmd by doing the following command :"mongod" and then mongo on an other cmd.
UPDATE: For everyone having the same problem than me, i'd recommand deleting parcel. That's what I did and now it works fine
I believe you are currently providing the url in the wrong place - you need to provide the URL to MongoClient before calling .connect. As per MongoDB's Node.js Driver documentation it should look think this:
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'tapDB';
const client = new MongoClient(url);
client.connect(function(err) {
console.log("Connected successfully to server");
const db = client.db(dbName);
// use database connection here
client.close();
});
Have a look a the documentation here: http://mongodb.github.io/node-mongodb-native/3.2/tutorials/connect/
UPDATE:
You can also do the above using ES6 async/await which is in the long run simpler to use than a callback or native promises, this is our setup:
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'tapDB';
(async () => { // async/await function that will run immediately
let client;
try {
client = await MongoClient.connect(url);
} catch (err) { throw err; }
console.log("Connected successfully to server");
const db = client.db(dbName);
let res;
try {
res = await db.collection("markers").insertMany([{ test1: true, test2: "3/5" }]);
} catch (err) { throw err; }
try {
await client.close();
} catch (err) { throw err; }
});
Using Javascript Promises ES6 is code is clearer
Look my code
const {MongoClient} = require('mongodb');
MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }).then(client => {
console.log('Connected to MongoDB server')
const db = client.db('dbName')
// Here you can place your operations with the bd
client.close();
}, e => console.log('Error to connect', e))
I hope I've helped
Good luck!

NodeJS With Mongo: Callback Is Not A Function error

Im currently working on a NodeJS/Mongo project where I need to pull all the documents from a collection. I currently have the following code written:
var Db = require('mongodb').Db,
MongoClient = require('mongodb').MongoClient,
Server = require('mongodb').Server,
ReplSetServers = require('mongodb').ReplSetServers,
ObjectID = require('mongodb').ObjectID,
Binary = require('mongodb').Binary,
GridStore = require('mongodb').GridStore,
Grid = require('mongodb').Grid,
Code = require('mongodb').Code,
assert = require('assert');
var server = new Server('[server]', 27017);
var authDB = new Db('admin', server);
var DB1250 = new Db('1250', server);
var findDocuments = function (callback) {
authDB.authenticate("Username", "Password");
DB1250.open(function (error, db) {
if (error) {
console.log(error);
}
else {
console.log("successfully accessed: ", db);
callback;
var cursor = db.collection('Patients').find();
console.log('cursor ', cursor);
cursor.forEach(function (error, document) {
if (error) {
console.log('Document does not exist. Error: ', error);
}
else {
console.log('Document: ', document);
}
});
}
});
};
findDocuments(function (data) {
});
I am able to authenticate/connect to the server, connect to the DB, and connect to the collection. When I enter the forEach loop to iterate through all the documents, I keep getting the error "Callback is not a function". Can you guys see what I am doing wrong?
I believe the cursor you have there has not resolved into an array, so forEach is not a valid method. You might be looking for eachAsync, which will wait for the query to return before iterating.
Alternatively, you can wait for the query promise to resolve, ie:
cursor.then(docs => docs.forEach(callback));
which I personally find a little clearer.
Heres the solution I came up with after using Mongoose:
var Db = require('mongodb').Db,
MongoClient = require('mongodb').MongoClient,
Server = require('mongodb').Server,
ReplSetServers = require('mongodb').ReplSetServers,
ObjectID = require('mongodb').ObjectID,
Binary = require('mongodb').Binary,
GridStore = require('mongodb').GridStore,
Grid = require('mongodb').Grid,
Code = require('mongodb').Code,
Mongoose = require('mongoose');
assert = require('assert');
var findDocuments = function (callback) {
var options = { server: { socketOptions: { keepAlive: 1000 } } };
var connectionString = 'mongodb://username:password#server:27017/admin';
// Connected handler
Mongoose.connect(connectionString, function (err) {
var db = Mongoose.connection.useDb('db');
var collection = db.collection("collection");
collection.find().stream()
.on('data', function (document) {
console.log(document);
})
.on('error', function (err) {
// handle error
console.log(err);
})
.on('end', function () {
// final callback
});
});
// Error handler
Mongoose.connection.on('error', function (err) {
console.log(err);
});
// Reconnect when closed
Mongoose.connection.on('disconnected', function () {
self.connectToDatabase();
});
};
findDocuments(function () {
db.close();
});
cursor.forEach() is an asynchronous operation but returns nothing. So you shouldn't do db.close() or likewise below the line somethere synchronously.
Besides; I am not sure which version Node driver you are refering to but in the recent ones like v3+ cursor.forEach() doesn't take an error first type callback like the one that you use in your code. It will take an "iterator" and an "end" callback such as;
cursor.forEach(doc => console.log(doc),
err => err ? console.log(err)
: db.close())
So the above code will just work fine, iterating and processing the documents one by one as they appear without waiting up until all of them have been retrieved to the memory.

MongoDB - running parallel queries with nodejs driver and close the db connection when the last query completes

I need to run several queries against MongoDB in parallel using the node.js driver.
Currently I am using a counter which gets decreased any time a query gets completed. When the counter reached 0 it means that all queries have completed and then I close the db connection.
In a simple case with 2 queries running in parallel the code is the following
var mongodb = require('mongodb');
var MongoClient = require('mongodb').MongoClient;
var db;
MongoClient.connect("mongodb://localhost:27017/company", function(err, database) {
if(err) throw err;
db = database;
let collection = "resumes";
let numberOfParallelQueries = 2;
let result = [];
let finalCallback = (err, resp) => {
if (err) throw(err);
numberOfParallelQueries = numberOfParallelQueries -1;
result.push(resp);
if (numberOfParallelQueries == 0) {
console.log(result);
db.close()
};
}
db.collection(collection).find({"jobs": {$elemMatch: {"company": "CNA", position: "director"}}}).toArray(finalCallback);
db.collection(collection).find({$and: [{"jobs.company": "CNA"}, {"jobs.position": "director"}]}).toArray(finalCallback);
});
My question is whether there any more elegant solution. I am thinking about something in the line of forkJoin() method of Observable.
Thanks in advance
That's what Promises are for:
var mongodb = require('mongodb');
var MongoClient = require('mongodb').MongoClient;
var db;
MongoClient.connect("mongodb://localhost:27017/company", function(err, database) {
if(err) throw err;
db = database;
let collection = "resumes";
Promise.all([
queryPromise(collection, {"jobs": {$elemMatch: {"company": "CNA", position: "director"}}}),
queryPromise(collection, {$and: [{"jobs.company": "CNA"}, {"jobs.position": "director"}]})
]).then(function(result) {
// result is an array of responses here
db.close();
}).catch(function(err) {
console.log(err);
db.close();
});
function queryPromise(collection, query) {
return new Promise(function(resolve, reject) {
db.collection(collection).find(query).toArray(function(err, resp) {
if (err) {
reject(err);
} else {
resolve(resp);
}
});
})
}
});
using async/await is more simpler
var mongodb = require('mongodb');
var MongoClient = require('mongodb').MongoClient;
var db;
MongoClient.connect("mongodb://localhost:27017/company", function(err, database) {
if(err) throw err;
db = database;
let collection = "resumes";
let queries = [];
queries.push(async()=>await db.collection(collection).find({"jobs": {$elemMatch: {"company": "CNA", position: "director"}}}).toArray());
queries.push(async()=>await db.collection(collection).find({$and: [{"jobs.company": "CNA"}, {"jobs.position": "director"}]}).toArray());
Promise.all(
queries
).then(function(result) {
// result is an array of arrays of queries responses here
db.close();
}).catch(function(err) {
console.log(err);
db.close();
});
});

Resources