MongoDB error while retrieving collection - node.js

I'm having a problem getting collection from the database in my Node.js application. I'm using Mongodb 3.6.
That's how I set it up:
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
/*
===========================================================================
DB setup
===========================================================================
*/
var state = {
db: null,
}
function get() {
return state.db;
}
exports.connect_database = function(done) {
if (state.db) return done()
MongoClient.connect(process.env.DATABASE_URL, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
/* some other functions ... */
exports.return_collection = function(collection_name, callback) {
var result_array = [];
var collection = get().getCollection(collection_name);
var result = collection.find()
result.forEach(function(res) {
result_array.push(res);
}, function(error) {
console.log("error: ")
console.log(error);
if (!error)
callback(result_array);
});
}
In the main file I do:
'use strict';
// LIB IMPORTS
var env = require('node-env-file');
env(__dirname + '/.env');
// LOCAL IMPORTS
var aux = require('./modules/aux.js');
var scheduler = require('./modules/scheduler.js');
var Bot = require('./modules/bot.js');
/*
===========================================================================
DB setup
===========================================================================
*/
aux.connect_database((err) => {
if (err) {
console.log('Unable to connect to Mongo.')
process.exit(1)
} else {
console.log('Connected to db.');
}
})
I can see in the log the Connected to db. prompt, so the connection works fine. After that I try to call some function to add/retrieve data from the db and i get the error:
TypeError: get(...).getCollection is not a function
at Object.exports.return_collection
If I try to print the state.db variable I get the following result:
MongoClient {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
s:
{ url: 'mongodb://localhost:27017/BotDb',
options:
{ socketOptions: {},
read_preference_tags: null,
readPreference: [Object],
dbName: 'slackBotDb',
servers: [Object],
server_options: [Object],
db_options: [Object],
rs_options: [Object],
mongos_options: [Object],
socketTimeoutMS: 360000,
connectTimeoutMS: 30000,
promiseLibrary: [Function: Promise] },
promiseLibrary: [Function: Promise],
dbCache: {},
sessions: [] },
topology:
Server {
domain: null,
_events:
{ serverOpening: [Function],
serverDescriptionChanged: [Function],
serverHeartbeatStarted: [Function],
serverHeartbeatSucceeded: [Function],
serverHeartbeatFailed: [Function],
serverClosed: [Function],
topologyOpening: [Function],
topologyClosed: [Function],
topologyDescriptionChanged: [Function],
joined: [Function],
left: [Function],
ping: [Function],
ha: [Function],
authenticated: [Function],
error: [Function],
timeout: [Function],
close: [Function],
parseError: [Function],
open: [Object],
fullsetup: [Object],
all: [Object],
reconnect: [Function] },
_eventsCount: 22,
_maxListeners: undefined,
clientInfo:
{ driver: [Object],
os: [Object],
platform: 'Node.js v7.10.0, LE' },
s:
{ coreTopology: [Object],
sCapabilities: null,
clonedOptions: [Object],
reconnect: true,
emitError: true,
poolSize: 5,
storeOptions: [Object],
store: [Object],
host: 'localhost',
port: 27017,
options: [Object],
sessionPool: [Object],
promiseLibrary: [Function: Promise] } } }
What am I missing? In the mongo console everything looks fine:
> db.getCollection("users");
BotDb.users

I can't find any function named getCollection in the API documentation for the Node.js MongoDB native driver. Collections are usually fetched with collection('mycoll'). So you can rewrite this line:
var collection = get().getCollection(collection_name);
to
var collection = get().collection(collection_name);
Note that if you use v3.0 or later of the driver you have to modify the connect function as well. There were some changes done to the connection functions (see here). The callback now returns a client object rather than the db object. So you'll have to change your function to:
exports.connect_database = function(done) {
if (state.db) return done()
MongoClient.connect(process.env.DATABASE_URL, function(err, client) {
if (err) return done(err);
state.db = client.db('database_name');
done();
})
}
Note the 'database_name' string. It should be the name of your database.

Related

MongoClient - Works on one machine but not the other. How to Output Configuration/Version Information

So I have two machines running in the cloud (both Ubuntu 18.04/Node 13/NPM 6.4) and on one machine I am running into issues with Mongo - I cannot figure out why.
Here is my simple script:
const MongoClient = require('mongodb').MongoClient
const url = 'mongodb://localhost:27017/mydb';
MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, async (err, client) => {
if(err) throw err;
console.log('Mongo Client: ', client)
const db = client.db('mydb');
console.log('Mongo DB Connected', db)
const userCount = await db.collection('users').countDocuments()
console.log('# of Users: ', userCount)
});
This works as expected on one machine, but on the other I get the following error:
(node:22716) UnhandledPromiseRejectionWarning: TypeError: db.collection(...).countDocuments is not a function
I am hoping there is a way to have MongoClient spit out it's configuration/version information in the script so that I can see some difference between the two??
Right now when the script runs and I log out "client" I see this on the working machine:
Mongo Client: MongoClient {
_events: [Object: null prototype] { newListener: [Function (anonymous)] },
_eventsCount: 1,
_maxListeners: undefined,
s: {
url: 'mongodb://localhost:27017/mydb',
options: {
servers: [Array],
caseTranslate: true,
useNewUrlParser: true,
useUnifiedTopology: true,
checkServerIdentity: true,
sslValidate: true,
dbName: 'mydb',
socketTimeoutMS: 0,
connectTimeoutMS: 10000,
retryWrites: true,
useRecoveryToken: true,
readPreference: [ReadPreference],
promiseLibrary: [Function: Promise]
},
promiseLibrary: [Function: Promise],
dbCache: Map {},
sessions: Set {},
writeConcern: undefined,
readPreference: ReadPreference {
mode: 'primary',
tags: undefined,
hedge: undefined
},
namespace: MongoDBNamespace { db: 'admin', collection: undefined }
},
topology: NativeTopology {
...
This is different than what I see on the non-working machine:
Mongo Client: Db {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
s: {
databaseName: 'mydb',
dbCache: {},
children: [],
topology: Server {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
s: [Object],
bson: [Getter],
isMasterDoc: [Getter],
poolSize: [Getter],
autoReconnect: [Getter],
host: [Getter],
port: [Getter],
emitOpen: false,
connectTimeoutMS: 30000,
socketTimeoutMS: 0
},
options: {
read_preference_tags: null,
readPreference: [ReadPreference],
url: 'mongodb://localhost:27017/mydb',
promiseLibrary: [Function: Promise],
native_parser: true
},
logger: Logger { className: 'Db' },
bson: BSON {},
authSource: undefined,
readPreference: ReadPreference {
_type: 'ReadPreference',
mode: 'primary',
tags: undefined
},
...
Any guidance would be greatly appreciated.
When all else fails, reboot it. In my case I deleted Mongo and re-installed on the machine not working and now it works.

Mongoose/MongoDB won't connect from Node.js

So i'm new to MongoDB/Mongoose, I am following many different tutorials online about how to connect my node.js server to mongodb using mongoose, however none of them seem to mention the issue i'm having. I have mongo server running on my machine, I can manipulate it find from the command line, but when connecting from node i run into a few problems. Mainly, its that It still says waiting for connections on Port 27017 but the connection is never made, the db I define in mongoose is never created, but also, I don't get any errors.
I do get some deprecation warnings regarding both body-parser and URL string parser. Not sure what to do about that. But also, i'm trying to log either a connection confirmation or an error if it can't connect. Instead it just logs a monster mongoose(?) object. See below
body-parser deprecated bodyParser: use individual json/urlencoded middlewares server.js:15:9 body-parser deprecated undefined extended: provide extended option node_modules\body-parser\index.js:105:29 (node:12152) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect. connected to [object Promise] + NativeConnection { base: Mongoose { connections: [ [Circular] ], models: { users: [Function] }, modelSchemas: { users: [Schema] }, options: { pluralization: true }, _pluralize: [Function: pluralize], Schema: { [Function: Schema] reserved: [Object], Types: [Object], ObjectId: [Function] }, model: [Function], plugins: [ [Array], [Array], [Array], [Array] ] }, collections: { users: NativeCollection { collection: [Collection], opts: [Object], name: 'users', collectionName: 'users', conn: [Circular], queue: [], buffer: false, emitter: [EventEmitter] } }, models: { users: { [Function: model] hooks: [Kareem], base: [Mongoose], modelName: 'users', model: [Function: model], db: [Circular], discriminators: undefined, events: [EventEmitter], '$appliedMethods': true, '$appliedHooks': true, _middleware: [Kareem], schema: [Schema], collection: [NativeCollection], Query: [Function], '$__insertMany': [Function], '$init': [Promise], '$caught': true, [Symbol(mongoose#Model)]: true } }, config: { autoIndex: true }, replica: false, options: null, otherDbs: [], relatedDbs: {}, states: [Object: null prototype] { '0': 'disconnected', '1': 'connected', '2': 'connecting', '3': 'disconnecting', '99': 'uninitialized', disconnected: 0, connected: 1, connecting: 2, disconnecting: 3, uninitialized: 99 }, _readyState: 1, _closeCalled: false, _hasOpened: true, '$internalEmitter': EventEmitter { _events: [Object: null prototype] { dropDatabase: [Function] }, _eventsCount: 1, _maxListeners: 0 }, _listening: false, _connectionOptions: { promiseLibrary: [Function: Promise], useNewUrlParser: false }, name: 'financeDB', host: 'localhost', port: 27017, user: undefined, pass: undefined, client: MongoClient { _events: [Object: null prototype] { left: [Function] }, _eventsCount: 1, _maxListeners: undefined, s: { url: 'mongodb://localhost:27017/financeDB', options: [Object], promiseLibrary: [Function: Promise], dbCache: [Object], sessions: [] }, topology: Server { _events: [Object], _eventsCount: 26, _maxListeners: Infinity, clientInfo: [Object], s: [Object] } }, '$initialConnection': Promise { <pending> }, db: Db { _events: [Object: null prototype] { reconnect: [Function], close: [Function], timeout: [Function] }, _eventsCount: 3, _maxListeners: undefined, s: { databaseName: 'financeDB', dbCache: {}, children: [], topology: [Server], options: [Object], logger: [Logger], bson: BSON {}, readPreference: [ReadPreference], bufferMaxEntries: -1, parentDb: null, pkFactory: undefined, nativeParser: undefined, promiseLibrary: [Function: Promise], noListener: false, readConcern: undefined }, serverConfig: [Getter], bufferMaxEntries: [Getter], databaseName: [Getter] } }
So why is this printing and what am I doing wrong re connection? Here is my server.js:
const express = require('express'),
path = require('path'),
bodyParser = require('body-parser'),
cors = require('cors'),
mongoose = require('mongoose');
config = require('.DB');
mongoose.Promise = global.Promise;
mongoose.connect(config.DB, { useNewUrlParser: true}).then(
()=> {console.log('Database is connected')},
err=>{console.log('Can not connect to the database' + err)}
);
const app = express();
let port = process.env.PORT || 4000;
const server = app.listen(function(){
console.log('Listening on port ' + port);
});
and my DB.js is just the uri
module.exports = {
DB: 'mongodb://localhost:27017/ng7crud'
};

How to connect to MongoDB using Node.js written in TypeScript?

I'm working on a Node.js server, connecting to MongoDB and written with TypeScript. When I try to use MongoDB it doesn't create a connection, however when I check the mongo output it does appear to create a connection.
What am I missing in my code to define the connection in Node.js?
My connection string is 'mongodb://localhost:27017'
My connection method:
connect() {
console.log('connecting to mongo') //this is called
MongoClient.connect(this.connectionString, {useNewUrlParser: true})
.then(client => {
console.log('setting client'); //this doesn't get called
this.client = client;
console.log(this.client);
})
.catch(error => {
console.log('error during connecting to mongo: '); //this also doesn't get called
console.error(error);
});
}
Mongo output:
2018-11-08T23:06:24.106+0100 I NETWORK [listener] connection accepted from 127.0.0.1:51345 #11 (2 connections now open)
2018-11-08T23:06:24.107+0100 I NETWORK [conn11] received client metadata from 127.0.0.1:51345 conn11: { driver: { name: "nodejs", version: "3.1.9" }, os: { type: "Darwin", name: "darwin", architecture: "x64", version: "17.7.0" }, platform: "Node.js v8.9.3, LE, mongodb-core: 3.1.8" }
My repository is at https://github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/db/mongo.db.ts for the full code.
console.log(db) as asked by shkaper
MongoClient {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
s:
{ url: 'mongodb://localhost:27017',
options:
{ servers: [Array],
caseTranslate: true,
useNewUrlParser: true,
socketTimeoutMS: 360000,
connectTimeoutMS: 30000,
promiseLibrary: [Function: Promise] },
promiseLibrary: [Function: Promise],
dbCache: {},
sessions: [] },
topology:
Server {
domain: null,
_events:
{ serverOpening: [Function],
serverDescriptionChanged: [Function],
serverHeartbeatStarted: [Function],
serverHeartbeatSucceeded: [Function],
serverHeartbeatFailed: [Function],
serverClosed: [Function],
topologyOpening: [Function],
topologyClosed: [Function],
topologyDescriptionChanged: [Function],
commandStarted: [Function],
commandSucceeded: [Function],
commandFailed: [Function],
joined: [Function],
left: [Function],
ping: [Function],
ha: [Function],
authenticated: [Function],
error: [Function],
timeout: [Function],
close: [Function],
parseError: [Function],
open: [Object],
fullsetup: [Object],
all: [Object],
reconnect: [Function] },
_eventsCount: 25,
_maxListeners: Infinity,
clientInfo:
{ driver: [Object],
os: [Object],
platform: 'Node.js v8.9.3, LE' },
s:
{ coreTopology: [Object],
sCapabilities: null,
clonedOptions: [Object],
reconnect: true,
emitError: true,
poolSize: 5,
storeOptions: [Object],
store: [Object],
host: 'localhost',
port: 27017,
options: [Object],
sessionPool: [Object],
sessions: [],
promiseLibrary: [Function: Promise] } } }
The issue is because mongo.connect is async code and controller call it wrongly. so the subsequent line mongo.getDb() will be executed without having this.client initiated properly.
Since you use Typescript, we can use async/await for cleaner code.
async connect() { // add async
console.log('connecting to mongo');
try {
if (!this.client) { // I added this extra check
console.log('setting client');
this.client = await MongoClient.connect(this.connectionString, { useNewUrlParser: true })
console.log(this.client);
}
} catch(error) {
console.log('error during connecting to mongo: ');
console.error(error);
}
}
And in controller code
async get(req: Request, res: Response) { // add async
const mongo = new MongoDb();
await mongo.connect(); // add await
const db = mongo.db();
// ....
}
I tried your repo with the changes and got this response
Hope it helps
Based on what you said about neither error nor success callbacks being called, I think the problem is not only in code here: https://github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/db/mongo.db.ts
But also e.g. here: https://github.com/FrisoDenijs/MEAN-ToDo/blob/master/server/src/controllers/to-do.controller.ts
When you run mongo.connect there should be an option to wait for the promise returned from MongoClient.connect. So I would change code in mongo.db.ts to for example sth like this (it depends how you want to handle this promise):
connect() {
console.log('connecting to mongo')
return MongoClient.connect(this.connectionString, {useNewUrlParser: true})
.then(client => {
console.log('setting client');
this.client = client;
console.log(this.client);
})
.catch(error => {
console.log('error during connecting to mongo: ');
console.error(error);
});
}
Then in to-do.controller.ts you can await this or use then:
get(req: Request, res: Response) {
const mongo = new MongoDb();
mongo.connect().then(() => {
const db = mongo.getDb();
const collection = db.collection('todo', (error, collection) => {
if (error) {
res.json(error);
res.statusCode = HttpStatus.BAD_REQUEST
return;
}
collection.find().toArray((error, result) => {
if (error) {
res.json(error);
res.statusCode = HttpStatus.BAD_REQUEST
}
res.json(result);
res.statusCode = HttpStatus.OK;
})
});
mongo.close();
});
}
I suppose that your code just does not "wait" for connection to be established and then fails.
The above solutions do work but there is another method that I like a bit better. It more resembles a standard HTTP call (I've gotten it to work with and without the async-await, but it's a good idea to leave it in):
private async connect(){
this.mongo = new mongodb.MongoClient(this.url, {useNewParser: true});
await this.mongo.connect((err, client) => {
if(client.isConnected()){
console.log('setting client');
this.client = client;
console.log(this.client);
}
else{
console.log('error during connecting to mongo: ');
console.error(error);
}
});
}
This is based off the Node.js driver api v3.2 that you can look over here:
https://mongodb.github.io/node-mongodb-native/3.2/api/index.html
It might just be a different way of shaving a cat but I figure it's worth sharing for anyone else who ends up here later.

Assertion Error: 1 == 0 for MongoDB deleteOne Command

I'm trying to workout some of the CRUD operations for a MongoDB/NodeJS/Express application I'm working on, and I'm having difficult with the deleteOne command, presumably because it's not recognizing my query, although I don't know why.
I lifted the mongoDB code from the NodeJS driver documentation, and my code block looks like this:
DELETE REQUEST:
router.delete('/formula-list/:id', function(req, res){
var db = req.db.collection('formulas');
var id = req.params.id;
var query = { "_id": id };
db.deleteOne(query, function(err, r){
assert.equal(null, err);
assert.equal(1, r.deletedCount);
db.close();
});
res.end();
});
When I run this it returns the error AssertionError: 1 == 0, which I take to mean that the program doesn't have a document to delete.
However, if I do a console.log(req.params.id) I get 587f6ff4824d0a085c2b57bf, which is an _id for a document in my database:
console.log(db) returns the following:
console.log(db):
{ s:
{ pkFactory:
{ [Function: ObjectID]
index: 858167,
createPk: [Function: createPk],
createFromTime: [Function: createFromTime],
createFromHexString: [Function: createFromHexString],
isValid: [Function: isValid],
ObjectID: [Circular],
ObjectId: [Circular] },
db:
EventEmitter {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
s: [Object],
serverConfig: [Getter],
bufferMaxEntries: [Getter],
databaseName: [Getter] },
topology:
EventEmitter {
domain: null,
_events: [Object],
_eventsCount: 7,
_maxListeners: undefined,
clientInfo: [Object],
s: [Object] },
dbName: 'formulas',
options: { promiseLibrary: [Function: Promise], readConcern: undefined },
namespace: 'formulas.formulas',
readPreference:
{ _type: 'ReadPreference',
mode: 'primary',
tags: undefined,
options: undefined },
slaveOk: true,
serializeFunctions: undefined,
raw: undefined,
promoteLongs: undefined,
promoteValues: undefined,
promoteBuffers: undefined,
internalHint: null,
collectionHint: null,
name: 'formulas',
promiseLibrary: [Function: Promise],
readConcern: undefined } }
I take this to mean that the Mongo collection itself is also being recognized since it's not returning undefined.
Any advice on what I'm getting wrong would be greatly appreciated.
Native mongodb library needs _id to be ObjectId
End result would look more-less like this:
var mongo = require('mongodb');
// Other code lines
router.delete('/formula-list/:id', function(req, res){
var db = req.db.collection('formulas');
var query = { "_id": new mongo.ObjectId(req.params.id) };
db.deleteOne(query, function(err, r){
assert.equal(null, err);
assert.equal(1, r.deletedCount);
db.close();
});
res.end();
});

What's proper way to insert a document using node API for MongoDB?

I'm using node-mongo for querying and inserting documents to a mongoDB database.
This is what I'm assuming for inserting:
collection.insert({}, function(err,insertedDocuments){
});
However for insertedDocuments I expect them to be the actual documents, but this is what insertedDocuments is always looking like:
{"ok":1,"n":1}
The important code to explain what I'm doing is:
var MongoClient = require('mongodb').MongoClient;
var ObjectID = require('mongodb').ObjectID;
MongoClient.connect('mongodb://127.0.0.1:27017/places', function (err, db) {
if (err) throw err;
console.log("Connected to database ... Will work with default places collection".inverse);
places = db.collection('places');
strings = db.collection('strings');
app.route('/places').post(newPlaceController);
app.listen(6190, function() {
console.log('Express listening'.inverse);
});
});
function newPlaceController (request,response) {
console.log(request.body);
var latitude = request.body.lat;
var longitude = request.body.long;
var name = request.body.name;
var newPlace = getNewPlaceObject(name,latitude,longitude); // This returns the object/document to be inserted
places.insert(newPlace, function (err,createdPlace) {
if (err)
response.send(JSON.stringify(err),500);
else {
console.log(createdPlace);
response.send(JSON.stringify(createdPlace),200);
}
});
}
Strangely, the log of createdPlace looks like this:
{ result: { ok: 1, n: 1 },
connection:
{ domain: null,
_events:
{ close: [Object],
error: [Object],
timeout: [Object],
parseError: [Object],
connect: [Function] },
_maxListeners: 10,
options:
{ socketOptions: {},
auto_reconnect: true,
host: '127.0.0.1',
port: 27017,
cursorFactory: [Object],
reconnect: true,
emitError: true,
size: 5,
disconnectHandler: [Object],
bson: {},
messageHandler: [Function],
wireProtocolHandler: {} },
id: 2,
logger: { className: 'Connection' },
bson: {},
tag: undefined,
messageHandler: [Function],
maxBsonMessageSize: 67108864,
port: 27017,
host: '127.0.0.1',
keepAlive: true,
keepAliveInitialDelay: 0,
noDelay: true,
connectionTimeout: 0,
socketTimeout: 0,
domainSocket: false,
singleBufferSerializtion: true,
serializationFunction: 'toBinUnified',
ca: null,
cert: null,
key: null,
passphrase: null,
ssl: false,
rejectUnauthorized: false,
responseOptions: { promoteLongs: true },
flushing: false,
queue: [],
connection:
{ _connecting: false,
_handle: [Object],
_readableState: [Object],
readable: true,
domain: null,
_events: [Object],
_maxListeners: 10,
_writableState: [Object],
writable: true,
allowHalfOpen: false,
onend: null,
destroyed: false,
bytesRead: 56,
_bytesDispatched: 215,
_pendingData: null,
_pendingEncoding: '',
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
pipe: [Function],
addListener: [Function: addListener],
on: [Function: addListener],
pause: [Function],
resume: [Function],
read: [Function],
_consuming: true },
writeStream: null,
buffer: null,
sizeOfMessage: 0,
bytesRead: 0,
stubBuffer: null },
ops: [ { name: 'OXXO', loc: [Object], _id: 553723a2a2c10c273605309a } ] }
What am I doing wrong?
The new record is at createdPlace.ops[0]. What you are calling createdPlace is not the new document, but a wrapper object the API docs call result. It contains metadata about the operation and then under the ops property array you can find the new documents.
Here's an excerpt from the documentation
The insert command will return a results object that contains several fields that might be useful.
result Contains the result document from MongoDB
ops Contains the documents inserted with added _id fields
connection Contains the connection used to perform the insert
You might consider a more-convenient module such as monk which will return the document to you. (Also recommended because in general the mongodb native API is so bad it's practically developer-hostile).

Resources