I'm doing a Node.js project that contains sub projects. One sub project will have one Mongodb database and Mongoose will be use for wrapping and querying db. But the problem is
Mongoose doesn't allow to use multiple databases in single mongoose instance as the models are build on one connection.
To use multiple mongoose instances, Node.js doesn't allow multiple module instances as it has caching system in require(). I know disable module caching in Node.js but I think it is not the good solution as it is only need for mongoose.
I've tried to use createConnection() and openSet() in mongoose, but it was not the solution.
I've tried to deep copy the mongoose instance (http://blog.imaginea.com/deep-copy-in-javascript/) to pass new mongoose instances to the sub project, but it throwing RangeError: Maximum call stack size exceeded.
I want to know is there anyways to use multiple database with mongoose or any workaround for this problem? Because I think mongoose is quite easy and fast. Or any other modules as recommendations?
According to the fine manual, createConnection() can be used to connect to multiple databases.
However, you need to create separate models for each connection/database:
var conn = mongoose.createConnection('mongodb://localhost/testA');
var conn2 = mongoose.createConnection('mongodb://localhost/testB');
// stored in 'testA' database
var ModelA = conn.model('Model', new mongoose.Schema({
title : { type : String, default : 'model in testA database' }
}));
// stored in 'testB' database
var ModelB = conn2.model('Model', new mongoose.Schema({
title : { type : String, default : 'model in testB database' }
}));
I'm pretty sure that you can share the schema between them, but you have to check to make sure.
Pretty late but this might help someone. The current answers assumes you are using the same file for your connections and models.
In real life, there is a high chance that you are splitting your models into different files. You can use something like this in your main file:
mongoose.connect('mongodb://localhost/default');
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('connected');
});
which is just how it is described in the docs. And then in your model files, do something like the following:
import mongoose, { Schema } from 'mongoose';
const userInfoSchema = new Schema({
createdAt: {
type: Date,
required: true,
default: new Date(),
},
// ...other fields
});
const myDB = mongoose.connection.useDb('myDB');
const UserInfo = myDB.model('userInfo', userInfoSchema);
export default UserInfo;
Where myDB is your database name.
One thing you can do is, you might have subfolders for each projects. So, install mongoose in that subfolders and require() mongoose from own folders in each sub applications. Not from the project root or from global. So one sub project, one mongoose installation and one mongoose instance.
-app_root/
--foo_app/
---db_access.js
---foo_db_connect.js
---node_modules/
----mongoose/
--bar_app/
---db_access.js
---bar_db_connect.js
---node_modules/
----mongoose/
In foo_db_connect.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/foo_db');
module.exports = exports = mongoose;
In bar_db_connect.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/bar_db');
module.exports = exports = mongoose;
In db_access.js files
var mongoose = require("./foo_db_connect.js"); // bar_db_connect.js for bar app
Now, you can access multiple databases with mongoose.
As an alternative approach, Mongoose does export a constructor for a new instance on the default instance. So something like this is possible.
var Mongoose = require('mongoose').Mongoose;
var instance1 = new Mongoose();
instance1.connect('foo');
var instance2 = new Mongoose();
instance2.connect('bar');
This is very useful when working with separate data sources, and also when you want to have a separate database context for each user or request. You will need to be careful, as it is possible to create a LOT of connections when doing this. Make sure to call disconnect() when instances are not needed, and also to limit the pool size created by each instance.
Mongoose and multiple database in single node.js project
use useDb to solve this issue
example
//product databse
const myDB = mongoose.connection.useDb('product');
module.exports = myDB.model("Snack", snackSchema);
//user databse
const myDB = mongoose.connection.useDb('user');
module.exports = myDB.model("User", userSchema);
A bit optimized(for me atleast) solution. write this to a file db.js and require this to wherever required and call it with a function call and you are good to go.
const MongoClient = require('mongodb').MongoClient;
async function getConnections(url,db){
return new Promise((resolve,reject)=>{
MongoClient.connect(url, { useUnifiedTopology: true },function(err, client) {
if(err) { console.error(err)
resolve(false);
}
else{
resolve(client.db(db));
}
})
});
}
module.exports = async function(){
let dbs = [];
dbs['db1'] = await getConnections('mongodb://localhost:27017/','db1');
dbs['db2'] = await getConnections('mongodb://localhost:27017/','db2');
return dbs;
};
I have been using this method and it works great for me until now.
const mongoose = require('mongoose');
function makeNewConnection(uri) {
const db = mongoose.createConnection(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
db.on('error', function (error) {
console.log(`MongoDB :: connection ${this.name} ${JSON.stringify(error)}`);
db.close().catch(() => console.log(`MongoDB :: failed to close connection ${this.name}`));
});
db.on('connected', function () {
mongoose.set('debug', function (col, method, query, doc) {
console.log(`MongoDB :: ${this.conn.name} ${col}.${method}(${JSON.stringify(query)},${JSON.stringify(doc)})`);
});
console.log(`MongoDB :: connected ${this.name}`);
});
db.on('disconnected', function () {
console.log(`MongoDB :: disconnected ${this.name}`);
});
return db;
}
// Use
const db1 = makeNewConnection(MONGO_URI_DB1);
const db2 = makeNewConnection(MONGO_URI_DB2);
module.exports = {
db1,
db2
}
Related
I'm new to Node, along with Mongoose and MongoDB. I'm trying to test inserting data into one of the collections in a database on MongoDB Atlas. However, the code somehow inserts the data into the wrong database. I intend to insert data into the 'test' collection in the 'quizzard' database. However, a new collection called 'tests' was created within quizzard where the data was placed. When I tried it again, it started inserting data into another database called 'test' and created a collection called 'tests', where the data is still being placed.
var link = "mongodb+srv://<user>:<password>#quizzard-dp0b2.mongodb.net/test?retryWrites=true&w=majority";
// changed to <user> and <password> for privacy reasons
mongoose.connect(link, {
useNewUrlParser: true,
useUnifiedTopology: true
});
mongoose.connection.on('connected', () => {
console.log('Connected');
});
const Schema = mongoose.Schema;
const TestSchema = new Schema({
_id: Number,
data: String
});
//TestSchema.set('database', 'test');
//TestSchema.set('collection', 'test');
const Test = mongoose.model('Test', TestSchema);
const data = {
_id: 11,
data: "why???"
};
const newTest = new Test(data);
newTest.save((error) => {
if(error){
console.log("An error has occured");
} else {
console.log("Action performed");
}
});
You need to change the link; after the first slash, you choose which DB you want to use.
Examples
// DB NAME youinsertinheredbname
var link = "mongodb+srv://<user>:<password>#quizzard-dp0b2.mongodb.net/youinsertinheredbname?retryWrites=true&w=majority";
// DB NAME stackoverflow
var link = "mongodb+srv://<user>:<password>#quizzard-dp0b2.mongodb.net/stackoverflow?retryWrites=true&w=majority";
I'm doing a Node.js project that contains sub projects. One sub project will have one Mongodb database and Mongoose will be use for wrapping and querying db. But the problem is
Mongoose doesn't allow to use multiple databases in single mongoose instance as the models are build on one connection.
To use multiple mongoose instances, Node.js doesn't allow multiple module instances as it has caching system in require(). I know disable module caching in Node.js but I think it is not the good solution as it is only need for mongoose.
I've tried to use createConnection() and openSet() in mongoose, but it was not the solution.
I've tried to deep copy the mongoose instance (http://blog.imaginea.com/deep-copy-in-javascript/) to pass new mongoose instances to the sub project, but it throwing RangeError: Maximum call stack size exceeded.
I want to know is there anyways to use multiple database with mongoose or any workaround for this problem? Because I think mongoose is quite easy and fast. Or any other modules as recommendations?
According to the fine manual, createConnection() can be used to connect to multiple databases.
However, you need to create separate models for each connection/database:
var conn = mongoose.createConnection('mongodb://localhost/testA');
var conn2 = mongoose.createConnection('mongodb://localhost/testB');
// stored in 'testA' database
var ModelA = conn.model('Model', new mongoose.Schema({
title : { type : String, default : 'model in testA database' }
}));
// stored in 'testB' database
var ModelB = conn2.model('Model', new mongoose.Schema({
title : { type : String, default : 'model in testB database' }
}));
I'm pretty sure that you can share the schema between them, but you have to check to make sure.
Pretty late but this might help someone. The current answers assumes you are using the same file for your connections and models.
In real life, there is a high chance that you are splitting your models into different files. You can use something like this in your main file:
mongoose.connect('mongodb://localhost/default');
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('connected');
});
which is just how it is described in the docs. And then in your model files, do something like the following:
import mongoose, { Schema } from 'mongoose';
const userInfoSchema = new Schema({
createdAt: {
type: Date,
required: true,
default: new Date(),
},
// ...other fields
});
const myDB = mongoose.connection.useDb('myDB');
const UserInfo = myDB.model('userInfo', userInfoSchema);
export default UserInfo;
Where myDB is your database name.
One thing you can do is, you might have subfolders for each projects. So, install mongoose in that subfolders and require() mongoose from own folders in each sub applications. Not from the project root or from global. So one sub project, one mongoose installation and one mongoose instance.
-app_root/
--foo_app/
---db_access.js
---foo_db_connect.js
---node_modules/
----mongoose/
--bar_app/
---db_access.js
---bar_db_connect.js
---node_modules/
----mongoose/
In foo_db_connect.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/foo_db');
module.exports = exports = mongoose;
In bar_db_connect.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/bar_db');
module.exports = exports = mongoose;
In db_access.js files
var mongoose = require("./foo_db_connect.js"); // bar_db_connect.js for bar app
Now, you can access multiple databases with mongoose.
As an alternative approach, Mongoose does export a constructor for a new instance on the default instance. So something like this is possible.
var Mongoose = require('mongoose').Mongoose;
var instance1 = new Mongoose();
instance1.connect('foo');
var instance2 = new Mongoose();
instance2.connect('bar');
This is very useful when working with separate data sources, and also when you want to have a separate database context for each user or request. You will need to be careful, as it is possible to create a LOT of connections when doing this. Make sure to call disconnect() when instances are not needed, and also to limit the pool size created by each instance.
Mongoose and multiple database in single node.js project
use useDb to solve this issue
example
//product databse
const myDB = mongoose.connection.useDb('product');
module.exports = myDB.model("Snack", snackSchema);
//user databse
const myDB = mongoose.connection.useDb('user');
module.exports = myDB.model("User", userSchema);
A bit optimized(for me atleast) solution. write this to a file db.js and require this to wherever required and call it with a function call and you are good to go.
const MongoClient = require('mongodb').MongoClient;
async function getConnections(url,db){
return new Promise((resolve,reject)=>{
MongoClient.connect(url, { useUnifiedTopology: true },function(err, client) {
if(err) { console.error(err)
resolve(false);
}
else{
resolve(client.db(db));
}
})
});
}
module.exports = async function(){
let dbs = [];
dbs['db1'] = await getConnections('mongodb://localhost:27017/','db1');
dbs['db2'] = await getConnections('mongodb://localhost:27017/','db2');
return dbs;
};
I have been using this method and it works great for me until now.
const mongoose = require('mongoose');
function makeNewConnection(uri) {
const db = mongoose.createConnection(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
db.on('error', function (error) {
console.log(`MongoDB :: connection ${this.name} ${JSON.stringify(error)}`);
db.close().catch(() => console.log(`MongoDB :: failed to close connection ${this.name}`));
});
db.on('connected', function () {
mongoose.set('debug', function (col, method, query, doc) {
console.log(`MongoDB :: ${this.conn.name} ${col}.${method}(${JSON.stringify(query)},${JSON.stringify(doc)})`);
});
console.log(`MongoDB :: connected ${this.name}`);
});
db.on('disconnected', function () {
console.log(`MongoDB :: disconnected ${this.name}`);
});
return db;
}
// Use
const db1 = makeNewConnection(MONGO_URI_DB1);
const db2 = makeNewConnection(MONGO_URI_DB2);
module.exports = {
db1,
db2
}
So I have this schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TreeSchema = new Schema({
}, { collection: 'treeLocations' });
var TreeDetailsSchema = new Schema({
}, { collection: 'treeInfo' });
module.exports = mongoose.model('Tree', TreeSchema);
module.exports = mongoose.model('TreeDetail', TreeDetailsSchema, "treeInfo");
And I am calling by ID like this:
var TreeDetails = require('./app/models/tree').model('TreeDetail');
router.route('/api/trees/:tree_id')
.get(function(req, res) {
TreeDetails.findById(req.params.tree_id, function(err, treedetail) {
if (err)
res.send(err);
res.json(treedetail);
});
});
For some reason - http://localhost:3000/api/trees/5498517ab68ca1ede0612d0a which is a real tree, is returning null
Something that might help you help me:
I was following this tutorial: https://scotch.io/tutorials/build-a-restful-api-using-node-and-express-4
The only thing I can think of that changed is that I have a collection name. Might that be it?
The step that I don't see is how you actually connect to MongoDB and after that, how you get the Model from the connection.
// connect to MongoDB
var db = mongoose.createConnection('mongodb://user:pass#host:port/database');
// now obtain the model through db, which is the MongoDB connection
TreeDetails = db.model('TreeDetails');
This last step is how you associate your model with the connected mongo database.
More info on Mongoose.model
There are several ways to establish a connection to MongoDB with mongoose, the tutorial uses:
mongoose.connect('mongodb://node:node#novus.modulusmongo.net:27017/Iganiq8o');
(Personally I prefer the more explicit mongoose.createConnection as shown in the example)
(I used mongoose 4.3.1 for this example)
My steps to reproduce, in order to provide a working example (without creating a webservice for it):
var mongoose = require('mongoose'),
TreeDetails, db;
// create the model schema
mongoose.model('TreeDetails', mongoose.Schema({
// .. your field definitions
}, {collection: 'treeInfo'}));
db = mongoose.createConnection('mongodb://user:pass#host/example');
TreeDetails = db.model('TreeDetails');
TreeDetails.findById('5671ac9217fb1730bb69e8bd', function(error, document) {
if (error) {
throw new Error(error);
}
console.log(document);
});
Instead of:
var TreeDetails = require('./app/models/tree').model('TreeDetail');
try:
var mongoose = require('mongoose'),
TreeDetails = mongoose.model('TreeDetail');
Defining the collection name shouldn't give you any issues. It's just what the collection will be called in the database / when using the mongo shell.
And just to be sure, try logging req.params.tree_id before calling findById to make sure it's coming through as you suspect.
I am getting start with mongodb and mongoose but am having problems querying a database. There are a number of tutorials online for what I am trying to do but it just doesn't seem to work for me. My problem is that the .find() function is not even being called and the collection is not being displayed. I have a collection called Subjects in which I know there are some values (I manually entered them in the mongodb command line). I only tried including pertinent code but let me know if anything else is needed. Thanks in advance.
app.js file
require('./models/model.js');
var conn = mongoose.createConnection('mongodb://localhost/test');
var Subject = mongoose.model('Subjects');
Subject.find( { }, function (err, subjects) {
if(err) console.log("Error"); // There is no "Error"
console.log("Made it"); // There is no "Made it"
console.log(subjects);
});
model.js file
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var SubjectsSchema = new Schema ({
subject: { type: String }
});
module.exports = mongoose.model('Subjects', SubjectsSchema);
Call mongoose.connect instead of mongoose.createConnnection to open the default connection pool that will be used by models created using mongoose.model:
mongoose.connect('mongodb://localhost/test');
I'm writing a Node module which whould perform queries to MongoDB.
My module should take as parameter (when I init it with new MyModule(db)) the MongoDB connection and use it inside it.
I was using standard MongoDB without any NPM module and I was passing in my db variable the connection to MongoDB. Now I'm switching to Mongoose and I can't find a way to pass the Mongoose connection to my module.
I don't want to initialize the Mongoose connection inside my module because I want to share it with my tests and with other modules.
How can I do? I've tried passing mongoose to my module but it doesn't work "is not a function".
Edit:
After reading the reply of #Neil Lunn I'm posting this sample of my module:
(function () {
"use strict";
/**
* various requires
*/
function TopicManager(dbURI) {
if (!(this instanceof TopicManager)) { return new TopicManager(dbURI); }
mongoose.connect(dbURI);
}
TopicManager.prototype.save = function (topics, done) {
var Topic = new mongoose.schema(
{
title: { type: String },
slug: { type: String, index: { unique: true } }
},
{collection : "topics"}
);
/**
* Use monguurl on "slug"
*/
mongoose.update(
{title: topic.title},
{upsert: true},
function (err, numberAffected, raw) {
if (err) { return done(err); }
return done(null, raw);
}
);
};
module.exports = TopicManager;
})();
It doesn't work because when I use it I get undefined is not a function when it runs new mongoose.
Generally speaking you don't do this. The mindset is a little different with mongoose than working with the native driver in it's raw form, and there are plenty of things helping under the hood to make things work a bit more seamlessly without diving into the gory details.
So the basic approach is when you define your "schema" and "models", these are bound to the default connection instance. Unless you have a specific reason for binding to another connection, this is what you should follow:
So you would have a Schema and model definition:
var mySchema = new Schema({
"name": String
});
module.exports = mongoose.model( "Model", mySchema, "collection" )
Where the "collection" part is optional otherwise the "model" name in the first argument is put to standard rules, usually lowercase and pluralized.
Then in your other code listing, you pull this in with require:
var Model = require('./models/mymodel.js');
And use your model objects as mongoose permits:
Model.find({ "field": "name"}, function(err,models) {
});
So it allows a bit more abstract handling than is done with the basic driver as the "models" themselves know how to bind to connections, or are otherwise explicitly bound to the connection you want as an optional parameter.
You can create a default Mongoose connection:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
var db = mongoose.connection;
If you want to open multiple connections you can use createConnection:
var dbconnection = mongoose.createConnection ('uri,uri');
This connection object can then be used for creating/retrieving models that are scoped only to this connection.
The Mongoose connection object has multiple events on which you attach handlers. You should check out the documentation for the full list of handlers you can use.
To get you started, a basic approach would be:
// log connection errors
db.on('error', console.error.bind(console, 'connection error:'));
// wait for opening the connection
db.once('open', function () {
// do something once connection to the MongoDB is open
});
If you open your connection using createConnection, you need to use it when creating models:
// dbconnection is created using createConnection
var MyModel = dbconnection.model('MyModel', MyModelSchema);