How to change collection name with dynamic name in node.js - node.js

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);
}

Related

Why mongoose doesn't update document? [duplicate]

In previous versions of Mongoose (for node.js) there was an option to use it without defining a schema
var collection = mongoose.noSchema(db, "User");
But in the current version the "noSchema" function has been removed. My schemas are likely to change often and really don't fit in with a defined schema so is there a new way to use schema-less models in mongoose?
I think this is what are you looking for Mongoose Strict
option: strict
The strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db.
Note: Do not set to false unless you have good reason.
var thingSchema = new Schema({..}, { strict: false });
var Thing = mongoose.model('Thing', thingSchema);
var thing = new Thing({ iAmNotInTheSchema: true });
thing.save() // iAmNotInTheSchema is now saved to the db!!
Actually "Mixed" (Schema.Types.Mixed) mode appears to do exactly that in Mongoose...
it accepts a schema-less, freeform JS object - so whatever you can throw at it. It seems you have to trigger saves on that object manually afterwards, but it seems like a fair tradeoff.
Mixed
An "anything goes" SchemaType, its flexibility comes at a trade-off of
it being harder to maintain. Mixed is available either through
Schema.Types.Mixed or by passing an empty object literal. The
following are equivalent:
var Any = new Schema({ any: {} });
var Any = new Schema({ any: Schema.Types.Mixed });
Since it is a schema-less type, you can change the value to anything
else you like, but Mongoose loses the ability to auto detect and save
those changes. To "tell" Mongoose that the value of a Mixed type has
changed, call the .markModified(path) method of the document passing
the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
Mongoose Schema Types
Hey Chris, take a look at Mongous. I was having the same issue with mongoose, as my Schemas change extremely frequently right now in development. Mongous allowed me to have the simplicity of Mongoose, while being able to loosely define and change my 'schemas'. I chose to simply build out standard JavaScript objects and store them in the database like so
function User(user){
this.name = user.name
, this.age = user.age
}
app.post('save/user', function(req,res,next){
var u = new User(req.body)
db('mydb.users').save(u)
res.send(200)
// that's it! You've saved a user
});
Far more simple than Mongoose, although I do believe you miss out on some cool middleware stuff like "pre". I didn't need any of that though. Hope this helps!!!
Here is the details description: [https://www.meanstack.site/2020/01/save-data-to-mongodb-without-defining.html][1]
const express = require('express')()
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const Schema = mongoose.Schema
express.post('/', async (req, res) => {
// strict false will allow you to save document which is coming from the req.body
const testCollectionSchema = new Schema({}, { strict: false })
const TestCollection = mongoose.model('test_collection', testCollectionSchema)
let body = req.body
const testCollectionData = new TestCollection(body)
await testCollectionData.save()
return res.send({
"msg": "Data Saved Successfully"
})
})
[1]: https://www.meanstack.site/2020/01/save-data-to-mongodb-without-defining.html
Note: The { strict: false } parameter will work for both create and update.
Its not possible anymore.
You can use Mongoose with the collections that have schema and the node driver or another mongo module for those schemaless ones.
https://groups.google.com/forum/#!msg/mongoose-orm/Bj9KTjI0NAQ/qSojYmoDwDYJ

Mongoose some field are not inserted [duplicate]

In previous versions of Mongoose (for node.js) there was an option to use it without defining a schema
var collection = mongoose.noSchema(db, "User");
But in the current version the "noSchema" function has been removed. My schemas are likely to change often and really don't fit in with a defined schema so is there a new way to use schema-less models in mongoose?
I think this is what are you looking for Mongoose Strict
option: strict
The strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db.
Note: Do not set to false unless you have good reason.
var thingSchema = new Schema({..}, { strict: false });
var Thing = mongoose.model('Thing', thingSchema);
var thing = new Thing({ iAmNotInTheSchema: true });
thing.save() // iAmNotInTheSchema is now saved to the db!!
Actually "Mixed" (Schema.Types.Mixed) mode appears to do exactly that in Mongoose...
it accepts a schema-less, freeform JS object - so whatever you can throw at it. It seems you have to trigger saves on that object manually afterwards, but it seems like a fair tradeoff.
Mixed
An "anything goes" SchemaType, its flexibility comes at a trade-off of
it being harder to maintain. Mixed is available either through
Schema.Types.Mixed or by passing an empty object literal. The
following are equivalent:
var Any = new Schema({ any: {} });
var Any = new Schema({ any: Schema.Types.Mixed });
Since it is a schema-less type, you can change the value to anything
else you like, but Mongoose loses the ability to auto detect and save
those changes. To "tell" Mongoose that the value of a Mixed type has
changed, call the .markModified(path) method of the document passing
the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
Mongoose Schema Types
Hey Chris, take a look at Mongous. I was having the same issue with mongoose, as my Schemas change extremely frequently right now in development. Mongous allowed me to have the simplicity of Mongoose, while being able to loosely define and change my 'schemas'. I chose to simply build out standard JavaScript objects and store them in the database like so
function User(user){
this.name = user.name
, this.age = user.age
}
app.post('save/user', function(req,res,next){
var u = new User(req.body)
db('mydb.users').save(u)
res.send(200)
// that's it! You've saved a user
});
Far more simple than Mongoose, although I do believe you miss out on some cool middleware stuff like "pre". I didn't need any of that though. Hope this helps!!!
Here is the details description: [https://www.meanstack.site/2020/01/save-data-to-mongodb-without-defining.html][1]
const express = require('express')()
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const Schema = mongoose.Schema
express.post('/', async (req, res) => {
// strict false will allow you to save document which is coming from the req.body
const testCollectionSchema = new Schema({}, { strict: false })
const TestCollection = mongoose.model('test_collection', testCollectionSchema)
let body = req.body
const testCollectionData = new TestCollection(body)
await testCollectionData.save()
return res.send({
"msg": "Data Saved Successfully"
})
})
[1]: https://www.meanstack.site/2020/01/save-data-to-mongodb-without-defining.html
Note: The { strict: false } parameter will work for both create and update.
Its not possible anymore.
You can use Mongoose with the collections that have schema and the node driver or another mongo module for those schemaless ones.
https://groups.google.com/forum/#!msg/mongoose-orm/Bj9KTjI0NAQ/qSojYmoDwDYJ

Mongoose dynamic models, cannot use populate()

My CMS is written in Node.js + Express + Mongoose. It's a multisite CMS and each site has its own database. So I needed to make Mongoose to switch the connection at every HTTP request. I looked around for some solution, or someone who had my same condition, but without success. So this is my solution:
HTTP Request:
router.get('/site/pages', function (req, res) {
pagesBiz.list(req.session, function(err, list) {
//do stuff
});
});
BIZ component (pagesBiz):
module.exports = {
list: function(session, callback) {
Page(session.database).find(callback);
}
}
Model (Page)
var cached = {};
var getModel = function (database) {
if(! cached[database]) {
var conn = mongoose.createConnection('mongodb://localhost/' + database);
cached[database] = conn.model('Page', pageSchema);
}
return cached[database];
}
module.exports = function(database) {
return getModel(database);
}
So how does it works? When the user logs in a new user session is created and bound to the user via the session cookie (I use MongoStore + Express Session). The session contains the name of the database that is used to dynamically instantiate the Mongoose models.
It works perfectly, users of different sites read from their own databases without the risk of "collisions", on the other side I have to pass the session (or the database name) around the functions, but I guess this is how Node.js works in a multithreaded context.
The only problem is when I use the populate method(), I get this error:
f:\www\rudsjs\node_modules\mongoose\lib\connection.js:625
throw new MongooseError.MissingSchemaError(name);
^ MissingSchemaError: Schema hasn't been registered for model "User". Use mongoose.model(name, schema)
at NativeConnection.Connection.model (f:\www\rudsjs\node_modules\mongoose\lib\connection.js:625:11)
at populate (f:\www\rudsjs\node_modules\mongoose\lib\model.js:2136:24)
at Function.Model.populate (f:\www\rudsjs\node_modules\mongoose\lib\model.js:2101:5)
at Object.cb (f:\www\rudsjs\node_modules\mongoose\lib\query.js:1159:16)
at Object._onImmediate (f:\www\rudsjs\node_modules\mongoose\node_modules\mquery\lib\utils.js:137:16)
at processImmediate [as _immediateCallback] (timers.js:336:15)
Process finished with exit code 8
I tried to preload the model before the populate() call using this:
User(session.database);
but the problem seems to be related to the way Mongoose caches the models (that is not mine), I looked at connection.js
model = this.base.models[name];
if (!model) {
throw new MongooseError.MissingSchemaError(name);
}
so I'd need a way to insert my model inside this.base.models. Do you have any idea? Do you especially have a better solution to implement a multi-site/multi-database environment?
I found a solution. I was caching the models and not the connections, so I was spawning a new Mongoose connection for each model, that's why Mongoose wasn't able to load other models using populate().
My solution is: use a global Cache object that stores the connections that will be used to build all the models.
Cache global object
module.exports = {
Cache: {
database: {}
}
}
Model (Page)
module.exports = function(session) {
if(! Cache.database[session.database]) {
debug("Create connection to " + session.database);
var conn = mongoose.createConnection('mongodb://localhost/' + session.database);
Cache.database[session.database] = conn;
}
debug("[rud] " + session.database + " model created");
return Cache.database[session.database].model('Page', pageSchema);
}
As you can see all models now will share the same connection stored in Cache.database[session.database]

Mongoose model testing require models

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

efficiency of mongodb/mongoskin access by multiple modules approach?

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() {
}

Resources