I have a problem testing my mongoose models
I have a structure like
app
models
Address
User
Organization
test
Both models User and Organization need to know the model Address. My models are structured like:
module.exports = function (mongoose, config) {
var organizationSchema = new mongoose.Schema({
name : {
type : String
},
addresses : {
type : [mongoose.model('Address')]
}
});
var Organization = mongoose.model('Organization', organizationSchema);
return Organization;
};
In my normal app i require Address before requiring User and Organization and everything is fine. I now wrote tests for User and Organization. In order to have the Address model registered i call require('../models/Address.js') This works fine if i run one test. But if i run all tests in a batch i get an error because i tried to register Address twice.
OverwriteModelError: Cannot overwrite Address model once compiled.
How do i solve this problem?
The problem is that you cant set mongoose model twice. The easiest way to solve your problem is to take advantage of node.js require function.
Node.js caches all calls to require to prevent your model from initializing twice. But you wrapping your models with functions. Unwrapping them will solve your problem:
var mongoose = require('mongoose');
var config = require('./config');
var organizationSchema = new mongoose.Schema({
name : {
type : String
},
addresses : {
type : [mongoose.model('Address')]
}
});
module.exports = mongoose.model('Organization', organizationSchema);
Alternative solution is to make sure that each model initialized only once. For example, you can initialize all you modules before running your tests:
Address = require('../models/Address.js');
User = require('../models/User.js');
Organization = require('../models/Organization.js');
// run your tests using Address, User and Organization
Or you can add try catch statement to your models to handle this special case:
module.exports = function (mongoose, config) {
var organizationSchema = new mongoose.Schema({
name : {
type : String
},
addresses : {
type : [mongoose.model('Address')]
}
});
try {
mongoose.model('Organization', organizationSchema);
} catch (error) {}
return mongoose.model('Organization');
};
Update: In our project we have /models/index.js file to handle everything. First, it calls mongoose.connect to establish connection. Then it requires every model in models directory and creates a dictionary of it. So, when we need some model (e.g. user) we requires it by calling require('/models').user.
Best solution (IMO):
try {
mongoose.model('config')
} catch (_) {
mongoose.model('config', schema)
}
This question already has an answer, but for a unique way to accomplish this check out https://github.com/fbeshears/register_models. This sample project uses a register_models.js that includes all models from an array of file names. It works really well and you end up with all your models pretty much wherever you need them. Keep in mind node.js's cache will cache objects in your project while it's running.
I use try/catch to tackle this problem and it works ok. But i think this not the best way of doing this.
try{
var blog = mongoose.model('blog', Article);
} catch (error) {}
I fixed this by patching r.js and making sure that all my modules used localRequire calls.
Checkout the thread here
https://github.com/jrburke/requirejs/issues/726
Related
It seems pretty clear that defining a model in one file but using it in another is common practice. I don't know why I'm having so much trouble getting it to work. I spent the morning rewriting my simple MongoDB app to follow what I thought was a dead simple example. I'm just structuring it like the top answer with the added convenience that the file where I define the Schema and Model and access the Model are in the same folder (because the project is so small and I'm just learning MongoDB). I think part of the problem as I tried researching solutions is that other examples really complex to follow. The DB connects as expected and I can work in the ManageDB.js file fine, but of course want to keep the project organized, even at my small scale. (The server.js file in the directory above connects before this code executes.)
What other things can I try to troubleshoot this MongoDB application?
// src/ManageDB.js
const mongoose = require('mongoose');
const devTweetRecordsModel = new mongoose.Schema({
time: Date,
text: String,
source: String,
positive: Number,
negative: Number,
});
var tweetdb = mongoose.model('DevTweetDB', devTweetRecordsModel);
module.exports = {
tweetdb: tweetdb
};
// src/TwitterAPI.js
const mongoose = require('mongoose');
var tweetdb = require('../src/ManageDB').tweetdb;
console.log(tweetdb); // Returns undefined
After defining a model-schema in mongoose you should assign it as mongoose.model
as you can see in the documentation.
https://mongoosejs.com/docs/models.html
const schema = new mongoose.Schema({ name: 'string', size: 'string' });
const Tank = mongoose.model('Tank', schema);
When you call mongoose.model() on a schema, Mongoose compiles a model for you.
and then you can use it by requiring it in another file.
const Model= require('./../models/modelName');
I solved it a way I hadn't seen suggested anywhere so I will post here for completeness. The solution was very sensitivity to how I exported it and brought it into the other JS file.
// Model.js
module.exports = mongoose.model('DevTweetDB', devTweetRecordsModel);
// TwitterAPI.js
var tweetdb = require('../models/tweetdb');
console.log(tweetdb);
// Returns the object as expected:
// Model { DevTweetDB }
I have a node.js project. In my project i want to use the collection( means model) name with dynamic name. In my code i will explain you about what my need actually is. This code is working fine.
User.findById(req.param('id'),function(err,user){
console.log('entered');
if(err){
res.json(HttpStatus.INTERNAL_SERVER_ERROR,{error:'unexpected error'});
return;
}
if(user == null){
res.json(HttpStatus.NOT_FOUND,{error:'user not found'});
return;
}
console.log("success");
Here, my collection(model) name is User. But i want to use the value of the client as "collection name" by using the code below
var temp = req.body.collectionName; // example: var temp ='User';
Can i use value of the temp variable in the place of "User".findById. If not, can i get any possible solutions for this functionality to happen?
Thanks in advance.
Looks like you're using Mongoose, in which case you can use mongoose.model(MODEL_NAME) to get a reference to a particular model:
var mongoose = require('mongoose');
...
var User = mongoose.model('User'); // or `req.body.collectionName`
User.find(...);
You haven't mentioned what framework and ORM you are using, so here's a simple way that you can use provided your model names are predetermined:
var collections = {
"User": User,// reference for model User
"Resource": Resource
};
var temp = req.body.collectionName;
if (collections[temp]) {
collections[temp].findById(req.param('id,), handler);
}
You should first try to find out if your ORM provides a list/mapping of models and use that if possible. Usually there is a hook or other inherent way of accessing them.
This code doesn't work for me. I'm using express framework with node.js. I'm using mongoose as my middleware.
var collections = {
"User": User,// reference for model User
"Resource": Resource
};
var temp = req.body.collectionName;
if (collections[temp]) {
collections[temp].findById(req.param('id,), handler);
}
I started a new project from scratch with ExpressJS.
Everything works fine but now I begin to have a dozen of 'app.get(....)' function and I need to give the project a structure.
What I have in mind is quite simple, it should have a folder named 'routes' containing a file such as 'module1.js', with all of the app.get related to that module. (like I've seen in many examples)
The issue is how to tell Express to route 'http://url/module1/' to that route file and how to pass it a param variable, containing for instance the mongodb connection.
what I tried is :
var params = {
db: myMongoConnection
};
var mod1 = require('routes/module1');
app.use('/module1', mod1);
but now I still miss the 'params'.
If I try to pass it as an argument to the require method i get an error saying it needs middleware.
Another issue is related to the fact that the myMongoConnection is valid in the connection callback, so I think i need to require and use the route.js inside the MongoClient connect callback.
Any idea?
thanks a lot
For custom modules, create a folder, call it modules
In its index.js, expose the modules that you need.
Something like,
var mods = [
'mod1',
'mod2',
];
function init() {
var expose = {};
var params = {
db: myMongoConnection
};
mods.forEach(mods, function (mod) {
expose[mod] = require('./' + mod)(params);
});
return expose;
}
// export init
module.exports = init;
In mod1.js, wrap the params
module.exports = function(params) {
// all your functions here will have access to params.
}
Then in, server/app.js, require this and set it in the app.
app.set('mods', require('path-to/modules'));
Now, you can access all your modules, using app.get('mods').moduleName.methodname
Knowing that Express.js pretty much leaves it to developer on deciding app structure, and after reading quite a few suggestions on SO (see link1 and link2 for example) as well as checking the example in official repo, I am still not sure if what I am doing is the best way forward.
Say I am using Redis extensively in my app, and that I have multiple "models" that require redis client to run query, would it be better to init redis client in the main app.js, like this:
var db = redis.createClient();
var models = require('./models')(db);
var routes = require('./controllers')(models);
or would it be better to just init redis in each model, then let each controller require models of interests?
The latter approach is what I am using, which looks less DRY. But is passing models instance around the best way? Note that I am loading multiple models/controllers here - I am not sure how to modify my setup to pass the redis client correctly to each models.
//currently in models/index.js
exports.home = require('./home.js');
exports.users = require('./user.js');
TL;DR, my questions are:
where best to init redis client in a MVC pattern app?
how to pass this redis client instance to multiple models with require('./models')(db)
Update:
I tried a different approach for index.js, use module.exports to return an object of models/controllers instead:
module.exports = function(models){
var routes = {};
routes.online = require('./home.js')(models);
routes.users = require('./user.js')(models);
return routes;
};
Seems like a better idea now?
Perhaps it's useful if I share how I recently implemented a project using Patio, a SQL ORM. A bit more background: the MVC-framework I was using was Locomotive, but that's absolutely not a requirement (Locomotive doesn't have an ORM and it leaves implementing how you handle models and databases to the developer, similar to Express).
Locomotive has a construct called 'initializers', which are just JS files which are loaded during app startup; what they do is up to the developer. In my project, one initializer configured the database.
The initializer established the actual database connection, also took care of loading all JS files in the model directory. In pseudocode:
registry = require('model_registry'); // see below
db = createDatabaseConnection();
files = fs.readDirSync(MODEL_DIRECTORY);
for each file in files:
if filename doesn't end with '.js':
continue
mod = require(path.join(MODEL_DIRECTORY, filename));
var model = mod(db);
registry.registerModel(model);
Models look like this:
// models/mymodel.js
module.exports = function(db ) {
var model = function(...) { /* model class */ };
model.modelName = 'MyModel'; // used by registry, see below
return model;
};
The model registry is a very simple module to hold all models:
module.exports = {
registerModel : function(model) {
if (! model.hasOwnProperty('modelName'))
throw Error('[model registry] models require a modelName property');
this[model.modelName] = model;
}
};
Because the model registry stores the model classes in this (which is module.exports), they can then be imported from other files where you need to access the model:
// mycontroller.js
var MyModel = require('model_registry').MyModel;
var instance = new MyModel(...);
Disclaimer: this worked for me, YMMV. Also, the code samples above don't take into account any asynchronous requirements or error handling, so the actual implementation in my case was a bit more elaborate.
I'm developing an express app that provides a REST api, it uses mongodb through mongoskin. I wanted a layer that splits routing from db acess. I have seen an example that creates a database bridge by creating a module file, an example models/profiles.js:
var mongo = require('mongoskin'),
db = mongo.db('localhost:27017/profiler'),
profs = db.collection('profiles');
exports.examplefunction = function (info, cb) {
//code that acess the profs collection and do the query
}
later this module is required in the routing files.
My question is: If I use this aproach for creating one module for each collection, will it be efficient? Do I have an issue of connecting and disconnecting multiple(unnecessary) times from mongo by doing that?
I was thiking that maybe exporting the db variable from one module to the others that handle each collection would solve the suposed issue, but I'm not sure.
Use a single connection and then create your modules passing in the shared db instance. You want to avoid setting up separate db pools for each module. One of doing this is to construct the module as a class.
exports.build = function(db) {
return new MyClass(db);
}
var MyClass = function(db) {
this.db = db;
}
MyClass.doQuery = function() {
}