How to combine models using Mongoose Population - node.js

At the moment I'm defining different models in different files depending on where I use them but there are relations between them and I find myself constantly fetching one, then the other using an ID from the first. E.g.:
File 'models/users.js'
var mongoose = require('mongoose');
var db = mongoose.connection;
var UserSchema = mongoose.Schema({
// fields
companyId: {
type: String
}
});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
File 'models/company.js'
var mongoose = require('mongoose');
var db = mongoose.connection;
var CompanySchema = mongoose.Schema({
// fields
});
// module export and functions etc.
So in that example the User companyId is the ID of one of the companies. Then in my routes I'll import both models, get a User, find the company ID and then fetch that company data:
var Promise = require('bluebird');
var User = Promise.promisifyAll(require('../models/user'));
var Company = Promise.promisifyAll(require('../models/company'));
User.getUserByIdAsync()
.then(function(user){
return [user, Company.getCompanyByIdAsync(user.companyId)];
}).spread(function(user,company){
// Do stuff
}).catch(function(err)... etc });
I've been reading about population on the Mongoose docs here: http://mongoosejs.com/docs/populate.html but in the examples everything is in one file. I'm trying to keep models separate for maintainability (and my sanity) so any calls to other models will throw errors.
Is there a way to join the models or reference other external models so I can do this? Is the schema even right?...
I'm using Express and Mongoose.

For reference, the fix is pretty simple. The models being in different files makes no difference.
Change the user model so that the data type is an ObjectID and the ref field references the correct model like so:
companyId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Company'
}
And use it:
User.find({}).populate('companyId').exec(callback);
Really simple in the end..

You can save company reference in Users Model and hence While fetching details you can get company details as well :
var getUserWithPopulate = function (criteria, projection, options, callback) {
Models.Users.find(criteria, projection, options).populate([
{path: 'company', select:'companyId, companyName'}
]).exec(callback);
};

Related

How to implement more than one collection in get() function?

My model university.js
const mongoose = require('mongoose');
const UniversitySchema = mongoose.Schema({
worldranking:String,
countryranking:String,
universityname:String,
bachelorprogram:String,
masterprogram:String,
phdprogram:String,
country:String
},{collection:'us'});
const University =module.exports = mongoose.model('University',UniversitySchema);
My route.js
const express = require('express');
const router = express.Router();
const University = require('../models/university');
//retrieving data
//router.get('/universities',(req,res,next)=>{
// University.find(function(err,universities){
// if(err)
// {
// res.json(err);
//}
// res.json(universities);
//});
//});
router.get('/usa',function(req,res,next){
University.find()
.then(function(doc){
res.json({universities:doc});
});
});
module.exports= router;
How to implement multiple collections in this get() function? I put my collection name in the model. Please help me with a solution to call multiple collections in get() function.
Here is Example to use multiple collection names for one schema:
const coordinateSchema = new Schema({
lat: String,
longt: String,
name: String
}, {collection: 'WeatherCollection'});
const windSchema = new Schema({
windGust: String,
windDirection: String,
windSpeed: String
}, {collection: 'WeatherCollection'});
//Then define discriminator field for schemas:
const baseOptions = {
discriminatorKey: '__type',
collection: 'WeatherCollection'
};
//Define base model, then define other model objects based on this model:
const Base = mongoose.model('Base', new Schema({}, baseOptions));
const CoordinateModel = Base.discriminator('CoordinateModel', coordinateSchema);
const WindModel = Base.discriminator('WindModel', windSchema);
//Query normally and you get result of specific schema you are querying:
mongoose.model('CoordinateModel').find({}).then((a)=>console.log(a));
In Short,
In mongoose you can do something like this:
var users = mongoose.model('User', loginUserSchema, 'users');
var registerUser = mongoose.model('Registered', registerUserSchema, 'users');
This two schemas will save on the 'users' collection.
For more information you can refer to the documentation: http://mongoosejs.com/docs/api.html#index_Mongoose-model or you can see the following gist it might help.
Hope this may help you. You need to modify according to your requirements.

Issue with MongoDB, Node and Express-looking up ID in Mongo

I am currently working on a simple MEAN project and I am working on pulling one item out of my mongo db and show it on the web page. Whenever I go to the site
http://localhost:3000/api/events/5782b1dbb530152d0940a227 to see the information on the object, I get null displayed. I am, like I said, suppose to see information on this object. Here is what my code looks like:
controllers:
var mongoose = require('mongoose');
var Eve = mongoose.model('Info');
var sendJSONresponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.eventsReadOne = function(req, res) {
Eve
.findById(req.params.eventid)
.exec(function(err, info){
sendJSONresponse(res, 200, info)
});
//sendJSONresponse(res, 200, {"status" : "success"});
};
Models:
var mongoose = require( 'mongoose' )
var eventSchema = new mongoose.Schema({
activity: String,
address: String,
});
mongoose.model('Info', eventSchema);
routes:
var express = require('express');
var router = express.Router();
var ctrlEvents = require('../controllers/events');
//events
router.get('/events/:eventid', ctrlEvents.eventsReadOne);
module.exports = router;
Now, one thing that may be noticed is that I call my mongo collection events. However, I forgot that event is a key word in JS so I tried "changing" it to Info which you will see on the last line of my model. Like I said, if I go to the site http://localhost:3000/api/events/5782b1dbb530152d0940a227, where the last number is the obj _id then I should see all of the data on it. Instead, all that I see is null. Any help would be great, thank you!
my other model file, db.js, has the connection:
var mongoose = require('mongoose');
var dbURI = 'mongodb://localhost/mission';
mongoose.connect(dbURI)
require('./events');
Mongoose will use the model name to determine which MongoDB collection it should use. The default scheme is to take the model name, lowercase and pluralize it, and use the result as the collection name, which in your case (using Info as model name) would be infos:
// This is where the model is created from the schema, and at this point
// the collection name is decided.
mongoose.model('Info', eventSchema);
If you want it to use a different collection, you have to explicitly tell Mongoose what collection to use by setting the collection option for your schema:
var eventSchema = new mongoose.Schema({
activity : String,
address : String,
}, { collection : 'events' });
To fix the error you're stating in the comments (Schema hasn't been registered for model "mission"), you need to make sure that you change all occurrences of mongoose.model():
// To create the model:
mongoose.model('Mission', eventSchema);
// Later on, to access the created model from another part of your code:
var Eve = mongoose.model('Mission');
(although it seems to be that "mission" is the name of your database; because your collection is called events I would think that a model name Event seems much more appropriate)

Mongoose schemas and collections

Hi everyone this is the first time I ask a question in here,
I'm very new into the MEAN stack and by now I'm trying to develop an application using it. As I understand in mongodb an Schema (Database) can have one or more collections (Tables(?)), when I'm using mongoose I define a Song model and an Artist model:
For Song:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var songSchema = new Schema({
songName: { type: String },
songArtist: [{type : Schema.Types.ObjectId, ref : 'Artist'}]
});
module.exports = mongoose.model('Song', songSchema);
For artist:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var artistSchema = new Schema({
artistName: { type: String }
});
module.exports = mongoose.model('Artist', artistSchema);
My app.js looks like this:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/song', function(err, res) {
if(err) throw err;
console.log('Connected to Database');
});
var models = require('./models/song')(app, mongoose);
The issue with this is, as I understand and saw, that I'm creating 2 databases/schemas while I want to Create one database/schema and have this two collections in there:
SongDatabase:
--- Song
--- Artist
How should I do it in this case with my mongoose models/controllers and my app.js? Thanks in advance :)
I solved all my doubts taking a look into this tutorial, as you can see in here they implement two collections within one database (My inicial concern). Once you are connected to the database and you perform a post to the collection you want, in this case thread or post, it will create the collection into mongodb.
The issue there is that your connection string is mongodb://localhost/song
Song becomes your DB, and within Song you should be able to see two collections
- SongS
- ArtistS
Posible solutions: Flush the database, drop all. Start clean and check what your application is doing. Use another name for the DB

Mongoose referencing models in other models

I am working on an express/node app, using mongodb and mongoose to model users, players, teams, etc. Between these models I am using references with ObjectIDs, so between players and teams there is a connector model RosterSpot which holds a team_id and player_id, both with the Schema type of ObjectID.
Anyways, I am running into problems trying to reference RosterSpots inside of the Team and Player models. I would like to have a method for Players that grabs all their teams, and vice versa for Teams. First, given this code in roster_spot.js
// Synchronously load model dependecies, so foreign model calls can be made
var fs = require('fs');
var models_path = __dirname;
fs.readdirSync(models_path).forEach(function (file) {
if (~file.indexOf('.js')) require(models_path + '/' + file);
})
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var Team = mongoose.model('Team');
var Player = mongoose.model('Player');
var RosterSpotSchema = new Schema({
team_id: {type: ObjectId, required: true},
player_id: {type: ObjectId, required: true}
});
....
RosterSpotSchema.statics.getTeamsForPlayer = function(player_id, callback) {
this.getTeamIdsForPlayer(player_id, function(ids){
Team.find({ _id: { $in: ids } }, function(err, teams){
callback(teams);
});
});
};
...
this works perfectly fine, as this RosterSpot model calls the Team.find() and Player.find() methods without a problem.
The problem comes when I try to make that call in the Player or Team model in the following method in player.js:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var RosterSpot = mongoose.model('RosterSpot');
var PlayerSchema = new Schema({...});
...
PlayerSchema.methods.getTeams = function (callback) {
RosterSpot.getTeamsForPlayer(this._id, function(teams){
callback(teams);
})
};
When I have the var RosterSpot = mongoose.model('RosterSpot'); in there, it throws a "MissingSchemaError: Schema has not been registered for 'RosterSpot'."
I've tried loading in the models via require and async stuff but nothing is working.
The crazy thing is, though, that I've been able to call these sorts of methods for users and players, using the connector model Family and that all works fine. I don't even have to initialize a variable for Family in users.js, which is weird but it works...
I have thought that maybe it is my testing environment that might mess with loading files, but when I start the server it also throws that error. Would be helpful to know if this is even possible, and any insight to this problem would be much appreciated.

Dynamically create collection with Mongoose

I want to give users the ability to create collections in my Node app. I have really only seen example of hard coding in collections with mongoose. Anyone know if its possible to create collections dynamically with mongoose? If so an example would be very helpful.
Basically I want to be able to store data for different 'events' in different collections.
I.E.
Events:
event1,
event2,
...
eventN
Users can create there own custom event and store data in that collection. In the end each event might have hundreds/thousands of rows. I would like to give users the ability to perform CRUD operations on their events. Rather than store in one big collection I would like to store each events data in a different collection.
I don't really have an example of what I have tried as I have only created 'hard coded' collections with mongoose. I am not even sure I can create a new collection in mongoose that is dynamic based on a user request.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
var schema = mongoose.Schema({ name: 'string' });
var Event1 = mongoose.model('Event1', schema);
var event1= new Event1({ name: 'something' });
event1.save(function (err) {
if (err) // ...
console.log('meow');
});
Above works great if I hard code 'Event1' as a collection. Not sure I create a dynamic collection.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
...
var userDefinedEvent = //get this from a client side request
...
var schema = mongoose.Schema({ name: 'string' });
var userDefinedEvent = mongoose.model(userDefinedEvent, schema);
Can you do that?
I believe that this is a terrible idea to implement, but a question deserves an answer. You need to define a schema with a dynamic name that allows information of 'Any' type in it. A function to do this may be a little similar to this function:
var establishedModels = {};
function createModelForName(name) {
if (!(name in establishedModels)) {
var Any = new Schema({ any: Schema.Types.Mixed });
establishedModels[name] = mongoose.model(name, Any);
}
return establishedModels[name];
}
Now you can create models that allow information without any kind of restriction, including the name. I'm going to assume an object defined like this, {name: 'hello', content: {x: 1}}, which is provided by the 'user'. To save this, I can run the following code:
var stuff = {name: 'hello', content: {x: 1}}; // Define info.
var Model = createModelForName(name); // Create the model.
var model = Model(stuff.content); // Create a model instance.
model.save(function (err) { // Save
if (err) {
console.log(err);
}
});
Queries are very similar, fetch the model and then do a query:
var stuff = {name: 'hello', query: {x: {'$gt': 0}}}; // Define info.
var Model = createModelForName(name); // Create the model.
model.find(stuff.query, function (err, entries) {
// Do something with the matched entries.
});
You will have to implement code to protect your queries. You don't want the user to blow up your db.
From mongo docs here: data modeling
In certain situations, you might choose to store information in
several collections rather than in a single collection.
Consider a sample collection logs that stores log documents for
various environment and applications. The logs collection contains
documents of the following form:
{ log: "dev", ts: ..., info: ... } { log: "debug", ts: ..., info: ...}
If the total number of documents is low you may group documents into
collection by type. For logs, consider maintaining distinct log
collections, such as logs.dev and logs.debug. The logs.dev collection
would contain only the documents related to the dev environment.
Generally, having large number of collections has no significant
performance penalty and results in very good performance. Distinct
collections are very important for high-throughput batch processing.
Say I have 20 different events. Each event has 1 million entries... As such if this is all in one collection I will have to filter the collection by event for every CRUD op.
I would suggest you keep all events in the same collection, especially if event names depend on client code and are thus subject to change. Instead, index the name and user reference.
mongoose.Schema({
name: { type: String, index: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true }
});
Furthermore I think you came at the problem a bit backwards (but I might be mistaken). Are you finding events within the context of a user, or finding users within the context of an event name? I have a feeling it's the former, and you should be partitioning on user reference, not the event name in the first place.
If you do not need to find all events for a user and just need to deal with user and event name together you could go with a compound index:
schema.index({ user: 1, name: 1 });
If you are dealing with millions of documents, make sure to turn off auto index:
schema.set('autoIndex', false);
This post has interesting stuff about naming collections and using a specific schema as well:
How to access a preexisting collection with Mongoose?
You could try the following:
var createDB = function(name) {
var connection = mongoose.createConnection(
'mongodb://localhost:27017/' + name);
connection.on('open', function() {
connection.db.collectionNames(function(error) {
if (error) {
return console.log("error", error)
}
});
});
connection.on('error', function(error) {
return console.log("error", error)
});
}
It is important that you get the collections names with connection.db.collectionNames, otherwise the Database won't be created.
This method works best for me , This example creates dynamic collection for each users , each collection will hold only corresponding users information (login details), first declare the function dynamicModel in separate file : example model.js
/* model.js */
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function dynamicModel(suffix) {
var addressSchema = new Schema(
{
"name" : {type: String, default: '',trim: true},
  "login_time" : {type: Date},
"location" : {type: String, default: '',trim: true},
}
);
return mongoose.model('user_' + suffix, addressSchema);
}
module.exports = dynamicModel;
In controller File example user.js,first function to create dynamic collection and second function to save data to a particular collection
/* user.js */
var mongoose = require('mongoose'),
function CreateModel(user_name){//function to create collection , user_name argument contains collection name
var Model = require(path.resolve('./model.js'))(user_name);
}
function save_user_info(user_name,data){//function to save user info , data argument contains user info
var UserModel = mongoose.model(user_name) ;
var usermodel = UserModel(data);
usermodel.save(function (err) {
if (err) {
console.log(err);
} else {
console.log("\nSaved");
}
});
}
yes we can do that .I have tried it and its working.
REFERENCE CODE:
app.post("/",function(req,res){
var Cat=req.body.catg;
const link= req.body.link;
const rating=req.body.rating;
Cat=mongoose.model(Cat,schema);
const item=new Cat({
name:link,
age:rating
});
item.save();
res.render("\index");
});
I tried Magesh varan Reference Code ,
and this code works for me
router.post("/auto-create-collection", (req, res) => {
var reqData = req.body; // {"username":"123","password":"321","collectionName":"user_data"}
let userName = reqData.username;
let passWord = reqData.password;
let collectionName = reqData.collectionName;
// create schema
var mySchema = new mongoose.Schema({
userName: String,
passWord: String,
});
// create model
var myModel = mongoose.model(collectionName, mySchema);
const storeData = new myModel({
userName: userName,
passWord: passWord,
});
storeData.save();
res.json(storeData);
});
Create a dynamic.model.ts access from some where to achieve this feature.
import mongoose, { Schema } from "mongoose";
export default function dynamicModelName(collectionName: any) {
var dynamicSchema = new Schema({ any: Schema.Types.Mixed }, { strict: false });
return mongoose.model(collectionName, dynamicSchema);
}
Create dynamic model
import dynamicModelName from "../models/dynamic.model"
var stuff = { name: 'hello', content: { x: 1 } };
var Model = await dynamicModelName('test2')
let response = await new Model(stuff).save();
return res.send(response);
Get the value from the dynamic model
var Model = dynamicModelName('test2');
let response = await Model.find();
return res.send(response);

Resources