Mongoose Model within Model is undefined - node.js

I'm writing a clean up function for when an object is delete. This is being called as normal.
My problem is; the other model isn't being populated as normal. (It works else where)
Within ModelGame.js
var mongoose = require('mongoose');
var ModelUser = require(process.env.root + '/models/ModelUser');
// Some schema code
// Some schema code
// Some schema code
// Remove ids to users.
gameScheme.post('remove', function(game) {
game.removeGameIdsOnUsers().then(function(){
next();
})
.catch(function(err){
next(err);
})
;
});
gameScheme.methods.removeGameIdsOnUsers = function () {
var self = this;
return this.users.reduce(function(promise, user) {
return ModelUser.findOne({id: user}).exec()
.then(function(foundUser) {
return foundUser.removeGameId(self)
.catch(function(err) {
console.log('Error removing game id to user.' + user);
console.log(err);
Promise.reject(err);
}),
Promise.resolve();
});
});
};
I get the following
TypeError: ModelUser.findOne is not a function
When logging the ModelUser it's just {}

I'm guessing that this is due to circular includes (ModelUser requires ModelGame that requires ModelUser).
You can get a reference to ModelUser at runtime using mongoose.model():
gameScheme.methods.removeGameIdsOnUsers = function () {
const ModelUser = mongoose.model('ModelUser');
...
};

Related

How to access data from dynamically created collection using Node Express and MongoDB

Here is the code. I am creating dynamically collection but cannot access the created collection to access data. Any help would be appreciated.
exports.dynamicCollection = async (req, res) => {
try {
var stuff = {
any: req.body.any,
};
var Model = createModelForName(req.body.name); // Create the model.
var model = Model(stuff); // Create a model instance.
model.save(function (err) {
if (err) {
return res.status(500).json(err);
}
return res.status(200).json('success');
});
} catch (err) {
return res.json(err);
}
};
var establishedModels = {};
function createModelForName(name) {
if (!(name in establishedModels)) {
var Any = new Schema({
any: {},
});
establishedModels[name] = mongoose.model(name, Any);
}
return establishedModels[name];
}
Well i got the answer how to access dynamically created collections by R&D. Below is the code to access that,
const document = await createModelForName("test").find();
In place of test you can add name of the collection and you will get the data.

How to get data from mongodb for dynamically created schema model

I am trying to get data from mongodb but could not. Because below the error is the reason.I have inserted data into mongodb by dynamically creating schema model.So I am not able to get data from mongodb.How to get data from mongodb for (dynamically creating schema) collection? Please help anyone.I have searched in google but no use.
MissingSchemaError: Schema hasn't been registered for model "Tea".
Use mongoose.model(name, schema) at Mongoose.model
createschema.js
const db = mongoose.createConnection(
"mongodb://localhost:27017/products", {
useNewUrlParser: true,
useUnifiedTopology: true
}
);
function dynamicModel(suffix) {
var collsName = suffix.charAt(0).toUpperCase() + suffix.slice(1);
var collsSmall = suffix.toLowerCase();
var newSchema = new Schema({
pid: {
type: String
},
product_name: {
type: String
},
product_price: {
type: Number
}
}, {
versionKey: false,
collection: collsSmall
});
try {
if (db.model(collsName)) return db.model(collsName);
} catch (e) {
if (e.name === 'MissingSchemaError') {
return db.model(collsName, newSchema, collsSmall);
}
}
}
module.exports = dynamicModel;
data.controller.js:
const mongoose = require('mongoose');
module.exports.getCollectionData = (req, res, next) => {
let collection = req.query.collection;
let tabledata = mongoose.model(collection); //Got MissingSchemaError
tabledata.find({}, function(err, docs) {
if (err) {
console.log(err);
return;
} else {
res.json({ data: docs, success: true, msg: 'Products data loaded.' });
}
})
}
//Create model
module.exports.newCollection = (req, res, next) => {
var collectionName = req.query.collectionName;
var NewModel = require(path.resolve('./models/createschema.model.js'))(collectionName);
NewModel.create({ }, function(err, doc) {});
}
db.js:
const mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URI, (err) => {
if (!err) { console.log('MongoDB connection succeeded.'); } else { console.log('Error in MongoDB connection : ' + JSON.stringify(err, undefined, 2)); }
});
require('./createschema.model');
api call:
http://localhost:3000/api/getCollectionData?collection='Tea'
Try db.model instead of mongoose.model in function getCollectionData
Since you created the collection on that particular connection, you have to use the same connection to get the model as well.
const db = mongoose.createConnection(
"mongodb://localhost:27017/products", {
useNewUrlParser: true,
useUnifiedTopology: true
}
);
module.exports.getCollectionData = (req, res, next) => {
let collection = req.query.collection;
let tabledata = db.model(collection); //Change here
tabledata.find({}, function(err, docs) {
if (err) {
console.log(err);
return;
} else {
res.json({ data: docs, success: true, msg: 'Products data loaded.' });
}
})
}
You fundamentally misuse mongoose.model() / connection.model().
This function can ONLY be used to CREATE a NEW mongoose model using
required model name and schema deffinition parameters.
In createschema.js when you have try {if (db.model(collsName))
return db.model(collsName);} catch you are NOT checking if model
already exists. db.model(collsName) will always throw an error
because you are not providing second required parameter with schema
definition.
I presume you are trying to check if model already exists and if so
return it. Please see documentation for
Connection.prototype.models. the fragment above should
therefore be:
try {
// db is an instance of mongoose.Connection
let existingModel = db.models[collsName];
if (existingModel) return existingModel;
} catch
Likewise in data.controller.js getCollectionData currently you are
calling a method to create a new mongoose model 'mongoose.model()',
but don't provide a schema (hence why you get MissingSchemaError)
you need to EITHER
get the model from mongoose instance (if already registered as you seem to imply)
let tabledata = mongoose.connection.models[collection];
OR
create a new mongoose model
const dynamicModel = require('./createschema.js');
let tabledata =
dynamicModel(collection)

How to store a variable outside the scope of a mongodb.connect() function, and how does the cursor work? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 3 years ago.
For some reason, I can't get the following to work return a document queried from my mongodb database using the node.js driver.
function findSomething(){
const database = "learning";
const collection = "stuff";
var str;
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db(database);
dbo.collection(collection).findOne({}, function(err, result) {
if (err) throw err;
str = result.name;
db.close();
});
});
return str;
}
console.log(findSomething());
This outputs
undefined
However, if I modify my above code to include the following
console.log(str);
right after
str = result.name;
It will result in the following output:
undefined
Company Inc //sample data from my document
The two things I don't understand are;
Why I can't assign str outside the .connect() function, and why the value it is assigned is lost once the connection is closed
Why the .connect() function executes after a value is returned by the findSomething() function.
I understand that my second concern will answer my first, and I am trying to do this so I can modularize my mongodb crud functions so as to make everything cleaner in my express app.
Are you looking something like this code below:
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
class MongoDB {
async connection(connection, options = {}) {
try {
const client = await MongoClient.connect(connection.URI, options);
const db = await client.db(connection.DB);
MongoDB.db = db;
MongoDB.client = client;
} catch(ex) {
console.log(ex.message);
}
}
}
MongoDB.db = {};
MongoDB.client = {};
class MongoModel extends MongoDB {
constructor(collectionName) {
super();
this.collectionName = collectionName;
}
get collection() {
return MongoModel.db.collection(this.collectionName)
}
}
// get mongodb, for an example: import from another file
const mongodb = new MongoDB();
// connect to mongodb from 'server.js' or 'app.js'
mongodb.connection({ URI: 'mongodb://localhost:27017', DB: 'blog_dev'}, { useUnifiedTopology: true });
const somethingModel = new MongoModel('something');
// an example another collection
const anotherModel = new MongoModel('anotherCollection');
// model find
async function findSomething() {
try {
const result = await somethingModel.collection.find({}).toArray();
return result;
} catch(ex) {
console.log(ex.message);
}
}
// model create
async function createSomething(payload) {
try {
const result = await somethingModel.collection.insert(payload);
return result.ops[0];
} catch(ex) {
console.log(ex.message);
}
}
// get controller
app.get('/', async (req, res) => {
try {
const result = await findSomething();
console.log(result);
res.status(200).send(result);
} catch(ex) {
console.log(ex.message);
}
});
// create controller
app.post('/', async(req, res) => {
try {
const result = await createSomething(req.body);
res.status(200).send(result);
} catch(ex) {
console.log(ex.message);
}
})
app.listen(3000, () => console.log('Server is up on port 3000'));
There's an example simple express server using mongodb. For Advance MongoModel, you can install and learn that code in node-modules.
I hope it can help you to get a new idea.

Mongoose not calling callbacks of save() and find()

I'm not getting into the callbacks for either the save() or find() methods. It's also not getting into the callback for mongoose.connect. Any idea why?
Here's my code:
mongoose.connect("mongodb://localhost/blah", {
useMongoClient: true,
}, function (err) {
console.log('connect to database'); // Not called
if (err) {
throw err;
}
});
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
var u = new MyUserModel();
u.author = 'authorname';
u.save(function(err){
console.log("saved") // not called
if (err) console.log(err);
});
MyUserModel.find({}, function (err,docs) {
console.log("found"); // not called
console.log(docs);
});
You need to "await" the connection before doing anything. It is an "async" function, and like all async functions you need to await their resolution before continuing other code that depends on the result. In this case the "connection" is going to be needed by later actions with the database.
Best done in modern environments with async/await:
// setup part
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
// async parts - wrapped in a self executing closure with the async keyword
(async function() {
try {
const conn = await mongoose.connect("mongodb://localhost/blah", {
useMongoClient: true,
});
console.log('database connected');
// Add a user and wait for it to save
var u = new MyUserModel();
u.author = 'authorname';
await u.save();
console.log("saved");
// find the documents and await that too
let docs = await MyUserModel.find();
console.log(JSON.stringify(docs,undefined,2));
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})();
Or by chaining promises in older nodejs versions:
// setup part
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
// Async part
mongoose.connect("mongodb://localhost/blah", { useMongoClient: true })
.then( () => {
var u = new MyUserModel();
u.author = 'authorname';
return u.save();
})
.then( () => MyUserModel.find() )
.then( docs => {
console.log(JSON.stringify(docs,undefined,2));
mongoose.disconnect();
})
.catch( e => console.error(e) );
Bottom line is that you need to wait for completion of all async calls and .connect() is now an async call in modern mongoose releases. It really should have always been handled like this, but now you "must" handle like this.

Is it a good idea to program in a non-event driven way in nodejs?

In order to separate the business logic and data access layer , I have separated the files and folders in a non-event driven way. Here is an example of the User signup process I have taken , Here is my Business logic layer file.
var userDA = require('./../DataAccess/UserDA');
module.exports = {
signUpUser: function(userGiven)
{
//Some bookeeping
userGiven.userType = "admin";
return userDA.save(tutorGiven);
}
}
and here is my data access file
"use strict";
var mongoose = require('mongoose');
if(!mongoose.connection)
mongoose.connect('mongodb://localhost/test');
var User = require('./../models/User');
module.exports ={
save : function(UserGiven){
var pass = true;
var user = new User(UserGiven);
user.save(function (err) {
if(err) {
console.log(err);
pass = false;
}
});
return pass;
},
getUser: function (email) {
var user = null;
user.findOne({email:email},function(err,foundUser){
if(err)
console.log(err);
else
user = foundUser;
});
return user;
}
}
This a for a real project of medium-large size , and as a newcomer in nodejs I was wondering and in need of an expert advice if It would be any problem if I take this kind of designing approach?
You are running into a classic pitfall of those new to asynchronous programming in node. For example, consider this code:
setTimeout(function () {
console.log('timer');
}, 200);
console.log('done');
which will output the following:
done
timer
The reason for this is that setTimeout is scheduling asynchronous work for later. What actually happens is the setTimeout function returns immediately, console.log('done') is called, then ~200 ms later the callback function is called and console.log('timer') is called.
A more proper approach would be to use callbacks to indicate when your work is complete. Something like this:
"use strict";
var mongoose = require('mongoose');
if(!mongoose.connection)
mongoose.connect('mongodb://localhost/test');
var User = require('./../models/User');
module.exports ={
save : function(UserGiven, callback){
var pass = true;
var user = new User(UserGiven);
user.save(function (err) {
if(err) {
console.log(err);
return callback(err);
}
return callback();
});
},
getUser: function (email, callback) {
user.findOne({email:email},function(err,foundUser){
if(err) {
console.log(err);
return callback(err);
}
return callback(null, foundUser);
});
}
}
This follows the convention of error-first callbacks, where a callback's first parameter is an error, the second is the data. Your first code segment would then look like this:
var userDA = require('./../DataAccess/UserDA');
module.exports = {
signUpUser: function(userGiven, callback) {
//Some bookeeping
userGiven.userType = "admin";
userDA.save(tutorGiven, callback);
}
}
Now, you would call signUpUser like so:
var userRepo = require('<PATH TO FILE>');
var someUser = {};
userRepo.signUpUser(someUser, function(err, user) {
if (err) {
// oops!
}
// do whatever you need to accomplish after the user is signed up
});

Resources