How to populate the User object with Mongoose and Node - node.js

I am trying to add a couple of attributes to the scaffolded MEAN.js User entity.
locationName: {
type: String,
trim: true
}
I also have created another entity Book connected with User. Unfortunately, I think I do not quite grasp the concept behind the populate method because I am not able to "populate" the User entity with the locationName attribute.
I tried the following:
/**
* List of Books
*/
exports.list = function(req, res) {
Book.find().sort('-created').populate('user', 'displayName', 'locationName').exec(function(err, books) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(books);
}
});
};
Unfortunately, I get the following error:
/home/maurizio/Workspace/sbr-v1/node_modules/mongoose/lib/connection.js:625
throw new MongooseError.MissingSchemaError(name);
^
MissingSchemaError: Schema hasn't been registered for model "locationName".
Any suggestion?
Thanks
Cheers

The error is clear, you should have a schema for the locationName.
If your location is just a string property in your user model and does not refer to separate model, you don't need and shouldn't use populate with it, it will simply be returned as a property of the returned user object from mongoose find() method.
If your want to make your location a stand alone entity (different mongodb document), you should have a mongoose model that defines your location object, aka have a file in your app\models name for example: location.server.model.js that contains something like:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var LocationSchema = new Schema({
_id: String,
name: String
//, add any additional properties
});
mongoose.model('Location', LocationSchema);
Note that the _id here replaces the auto generated objectId, so this has to be unique, and this the property you should refer to in your User object, meaning if you have a location like this:
var mongoose = require('mongoose'),
Location = mongoose.model('Location');
var _location = new Location({_id:'de', name:'Deutschland'});
you should refer to it in your User object like this:
var _user=new User({location:'de'});
//or:
var _user=new User();
_user.location='de';
then you should be able to populate your location object with your user, like this:
User.find().populate('location').exec(function(err, _user) {
if (err) {
//handle error
} else {
//found user
console.log(_user);
//user is populated with location object, makes you able to do:
console.log(_user.location.name);
}
});
I suggest you to further read in mongodb data modeling and mongoose Schemas, Models, Population.

Related

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)

Cannot read from database using mongoose

This is my GET request
var Catalog = mongoose.model('Catalog');
router.get('/catalog', function(req, res, next) {
Catalog.find(function(err, items){
if(err){
return next(err);
}
console.log(items);
res.json(items);
});
});
The model
var mongoose = require('mongoose');
var CatalogSchema = new mongoose.Schema({
title: String,
cost: String,
description: String
});
mongoose.model('Catalog', CatalogSchema);
The console.log gives me a [] but there is a collection named Catalog with the parameters filled.
If you have an existing collection, created outside of Mongoose, that you wish to query you need to configure your model to use that collection.
Otherwise, Mongoose will use a utility function to create a collection name by pluralizing and lowercasing the model name (which in your case would become catalogs).
To use Catalog as a collection, use the collection option:
var CatalogSchema = new mongoose.Schema({
title: String,
cost: String,
description: String
}, { collection : 'Catalog' });
you might check again the collection name in mongodb i.e whether it is 'Catalog' or 'Catalogs'. In my experience I found that if you name the model 'Catalog', then either mongoose creates the collection with a name 'Catalogs' if it is not already there or you have to create the collection first with a name 'Catalogs'.
You might check the following line also:
Catalog.find({}, function(err, items){
// your code goes here
}
Hope it helps..

How to get all nested document objects in mongoose

I am new to mongo DB. I am developing an application using MEAN stack. On my back-end I have two models - Feature & Project.
Project schema has an attribute called 'features' which is an array of Feature objects.
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ProjectSchema = new Schema({
name: {
type: String,
default: '',
trim: true
},
features:{
type: [Schema.ObjectId],
ref: 'Feature'
}
});
/**
* Statics
*/
ProjectSchema.statics.load = function(id, cb) {
this.findOne({
_id: id
})
.populate('features')
.exec(cb);
};
mongoose.model('Project', ProjectSchema);
Please note that I have separate files for Feature and Project schema. I am registering both schema as mongoose models.
I also have a controller as well for projects which exports the following middle-ware function:
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Project = mongoose.model('Project'),
Testcase = mongoose.model('Feature'),
_ = require('lodash');
/**
* Find project by id
*/
exports.project = function(req, res, next, id) {
Project.load(id, function(err, project) {
if (err) return next(err);
if (!project) return next(new Error('Failed to load project ' + id));
console.log(project.features.length);
req.project = project;
next();
});
};
I would have expected all details of Feature objects in the project object above since I have used ".populate('features')" in the static load function of Project schema. But it is not happening, it returns an empty array for features attribute. Could anyone please tell me what am I missing here?
Project schema has an attribute called 'features' which is an array of Feature objects.
Careful there. What you need is an array of ObjectIds that correspond to Feature documents.
I think you need to specify the project.features schema like this:
features: [{type: Schema.ObjectId, ref: 'Feature'}]
The populate function only works if both the code and the data are all 100% correct and it's very easy to make a mistake. Can you post the sample data of the Project document you are loading? We need to make sure features is really an array, really contains ObjectIds not strings or objects, etc.

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

What are Mongoose (Nodejs) pluralization rules?

I am a newbie to the Node.js, Mongoose and Expressjs. I have tried to create a table "feedbackdata" using the Mongoose in MongoDB via the following code. But it is created as "feedbackdata*s*". By Googling, I found that the Mongoose uses pluralization rules. Anyone please help me to remove the pluralization rules? or how my code should be for the "feedbackdata" table?
Below is my code:
app.post("/save",function(req,res){
mongoose.connect('mongodb://localhost/profiledb');
mongoose.connection.on("open", function(){
console.log("mongo connected \n");
});
// defining schemar variables
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
// define schema for the feedbackdata table
var feedback_schema = new Schema({
_id: String,
url:String,
username:String,
email:String,
subscribe:String,
types:String,
created_date: { type: Date, default: Date.now },
comments: String
});
// accessing feeback model object
var feedback_table = mongoose.model('feedbackdata', feedback_schema);
var tableObj = new feedback_table();
var URL = req.param('url');
var name = req.param('name');
var email = req.param('email');
var subscribe = req.param('subscribe');
var choices = req.param('choices');
var html = req.param('html');
var receipt = req.param('receipt');
var feedbackcontent = req.param('feedbackcontent');
tableObj._id = 3;
tableObj.url = URL;
tableObj.username = name;
tableObj.email = email;
tableObj.subscribe = subscribe;
tableObj.types = choices;
tableObj.comments = feedbackcontent;
tableObj.save(function (err){
if(err) { throw err; }else{
console.log("Saved!");
}
mongoose.disconnect();
})
res.write("<div style='text-align:center;color:green;font-weight:bold;'>The above values saved successfully! <br><a href='/start'>Go back to feedback form</a></div>");
res.end();
});
The pluralization rules are in this file: https://github.com/LearnBoost/mongoose/blob/master/lib/utils.js
You can add your schema name to the 'uncountables' list, then mongoose will not pluralize your schema name.
Provide the name for the collection in options while creating schema object, then Mongoose will not do pluralize your schema name.
e.g.
var schemaObj = new mongoose.Schema(
{
fields:Schema.Type
}, { collection: 'collection_name'});
For more Info: http://mongoosejs.com/docs/guide.html#collection
The pluralization rules is here to ensure a specific naming convention.
The collection name should be plural, all lowercase and without spacing.
What are naming conventions for MongoDB?
I think you should ask yourself if you want to follow the main rule (ensured by mongoose as a default behavior) or get rid of it.
What are the perks ? What are the good points ?
You design first what is an user (User model) and then you store users into a collection. It totally make sense.
Your call.
If you ask yourself how to get the final name of the collection after the pluralization :
const newName = mongoose.pluralize()('User');

Resources