MongoDB/Mongoose- How to use nested objects/documents or subdocuments - mongodb

I am creating simple RestApi using MongoDB, Mongoose, Node.js, Express.js and In which there will be multiple users and many more users can sign up and there will be an entry for each user in user collection(database). And my user Schema(users.js) will be like
var mongoSchema = mongoose.Schema;
var userSchema ={
mobileno: {
type:String
},
firstname: {
type:String
},
lastname: {
type:String
},
facebookid: {
type:String
},
userimage: {
type:String
}
}
module.exports = mongoose.model('user', userSchema);
Now each user can save one or more products as follows
Now which one would be the best solution:
Should I use separate collection (products collection with one field
like mobile no for reference)
Or Should I use subdocument or nested objects
My personal choice is second one but I am new in MongoDB and Mongoose environment. Please help me what I need to change in users schema.

You can do it like this :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema{
mobileno: {
type:String
},
firstname: {
type:String
},
lastname: {
type:String
},
facebookid: {
type:String
},
userimage: {
type:String
}
}
var productSchema = new Schema{
_user:{type: Schema.ObjectId, ref: 'user'}
prod_name: {
type:String
},
prod_cost: {
type:String
}
}
module.exports = mongoose.model('user', userSchema);
module.exports = mongoose.model('products', productSchema);
You can reference user to product table (i,.e, just like joins in mysql)
And populate while fetching the profile :
Models.products.find(criteria, projection, options).populate([
{path: '_user', select: 'prod_name, prod_cost'},
]).exec(callback);
Thanks & Regards

The second choice is generally preferred. The schema for the product key would look like -
products: [productname: String, productdesc: String, ]
However, in some cases the first option to create a separate product collection makes more sense. This would happen if the collection depends on some external source and is fairly dynamic in nature. Here you would find it easier to update at one place rather than a lot of places. In this case, you can have a schema like
products: [productSchema]
Here for getting the product details, you would need to do $lookup operations

I think, the better way is other way around.
Save the product as a collection and user as another collection.
In product collection, save the reference of user.
Then use populate() to populate user in product, if you want.

After reading existing stack overflows answers I think following approach would be right. Please suggest or correct me if I am wrong.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var productSchema = new Schema{
prod_name: {
type:String
},
prod_cost: {
type:String
}
}
var userSchema = new Schema{
mobileno: {
type:String
},
firstname: {
type:String
},
lastname: {
type:String
},
facebookid: {
type:String
},
userimage: {
type:String
},
products: [productSchema]
}
module.exports = mongoose.model('user', userSchema);
module.exports = mongoose.model('products', productSchema);

Related

Mongoose Populate with Express, not working in production (Heroku)

This is a MERN app, hosted on github, and it works perfectly on localhost. Unfortunately, it does not work on Heroku.
The issue is the API request, it must return an object and populate an array of OIDs (see Department Model). API request is working. I'm getting the data from MLab, but it doesn't populate... instead returns: "surveys":[]
API File
router.get('/department_data/:d_oid', function(req, res) {
Department.findOne({_id: req.params.d_oid}).populate("surveys").exec(function(err,doc){
if(err) throw(err)
res.send(doc)
})
});
Department Model
**Department Model**
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Create the survey schema
var departmentSchema = new Schema({
department_name: {
type: String,
trim: true,
required: true
},
surveys: [{
type: Schema.Types.ObjectId,
ref: 'Surveys'
}],
participants: [{
type: String
}],
create_date: {
type: Date,
default: Date.now
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'Created_By'
},
});
departmentSchema.index({ department_name: 1, created_by: 1}, {unique: true});
const Department = mongoose.model('Departments', departmentSchema);
module.exports = Department;
Survey Model
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Create the survey schema
var surveySchema = new Schema({
survey_name: {
type: String,
trim: true,
required: true
},
questions: [{
type: Schema.Types.ObjectId,
ref: 'Questions'
}],
created_date: {
type: Date,
default: Date.now
}
});
const Survey = mongoose.model('Surveys', surveySchema);
module.exports = Survey;
Solved.
The problem was in the database: the ref OIDs got scrambled with a previous update, so when it was trying to populate, Mongoose couldn't find any matching OIDs.
Solution: we had to purge and re-seed. When the correct OID references exist, this code works as expected in localhost & Heroku.

Club childschema Under parent on mongoose

I am using mongoose module
I have two Schema file
//First User File
var mongoose = require('mongoose');
const UserActivitySchema = require('./useractivity')
//User Schema
var UserSchema = new mongoose.Schema({
username: {
type: String
},
activity: [UserActivitySchema]
});
var User = module.exports = mongoose.model('User', UserSchema, 'User');
I already Tried to Create a subdocument but not able to acheive it
//Second Schema i.e supposed to be child is on useractivity.js file
var mongoose = require('mongoose');
//User Activity Schema....011217
var UserActivitySchema = mongoose.Schema({
message_count:{
type: Number,
default: 0
}
});
exports.UserActivitySchema = UserActivitySchema;
By this, It will create the Entry like this:
{
"_id" : ObjectId("5ab38941ffbb87124c673862"),
"username" : "peter",
"activity" : []
}
What I want is to like this:
{
"_id" : ObjectId("5ab38941ffbb87124c673862"),
"username" : "peter",
"activity" : {
"message_count" : 0
}
}
Any help is really Appreciated..
Mongoose populate can help you to get the required functionalities. You can learn more about populate here. You can do it in this way,
Create UserActivitySchema
var mongoose = require('mongoose');
var UserActivitySchema = mongoose.Schema({
message_count:{
type: Number,
default: 0
}
});
module.exports = mongoose.model('UserActivity', UserActivitySchema, 'UserActivity');
Now create UserSchema like this
var mongoose = require('mongoose');
const UserActivitySchema = require('./useractivity')
//User Schema
var UserSchema = new mongoose.Schema({
username: {
type: String
},
activity: {
type: mongoose.Schema.Types.ObjectId,
ref: 'UserActivity',
}
});
var User = module.exports = mongoose.model('User', UserSchema, 'User');
Now, while you are saving a user save the _id of the UserActivity in activity
When you are making your query to get user you can do like this
User.findOne({_id: 5ab38941ffbb87124c673862})
.populate('UserActivity')
.exec(function(err, user) {
// do stuff with returned user
});

Using Mongoose Populate Virtuals on Arrays

Here is my Follow model
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let FollowSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
followers: [{
type: Schema.Types.ObjectId,
ref: 'Card'
}],
following: [{
type: Schema.Types.ObjectId,
ref: 'Card'
}]
});
module.exports = mongoose.model('Follow', FollowSchema);
Here's my Card model
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let CardSchema = new Schema({
title: String,
content: String,
likes: Number,
createdById: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
module.exports = mongoose.model('Card', CardSchema);
A typical Follow Model in DB will have something like this:
{
"_id" : ObjectId("59f0eef155ee5a5897e1a66d"),
"user" : ObjectId("59e3e617149f0a3f1281e849"),
"following" : [
ObjectId("59e21942ca5652efc4ca30ab"),
ObjectId("59e13b2dca5652efc4ca2cf5")
]
}
Here's what I'm trying to do:
To get all post from those you follow, find user in Follow model, and for each ObjectId in the following array, pull all documents from Card model, matching createdById field.
Using Mongoose Populate Virtuals, how do I do the schema relationship and populate query eventually?
Or, how would I go about creating and querying such a relationship with or without virtuals?

How can I restructure my database?

I have a collection with embedded documents. This worked fine when updating a document within a document, but when I had to update a document within a document within a document problems arose. To make this work I would need to use a double '$' and this is not possible.
This works:
{$push: {
"progress.$.exercise":{
"exercise":exercise
}
}
This is where the problem comes in:
{$push: {
"progress.$.exercise.$.workout":{
"_set":_set,
"reps":reps,
"weight":weight
}
}
Since dobbel $ can't be done and there seems to be no way (that I can find) to do it with elemMatch. I can only come to the conclusion that the way I have structured my Database is wrong....
Here are my Schemas:
var Workout = new mongoose.Schema({
_set : {
type: String
},
reps: {
type: String
},
weight: {
type: String
}
});
var Exercise = new mongoose.Schema({
exerciseName : {
type: String
},
workout : [Workout]
});
var Progress = new mongoose.Schema({
date : {
type: String
},
name : {
type: String
},
exercise : [Exercise]
});
var UserSchema = mongoose.Schema({
username: {
type: String,
index:true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
progress : [Progress]
});
var User = module.exports = mongoose.model('User', UserSchema);
It seems I need to restructure my database, if so how should I do this?
Do I need to make more collections?
I'm used to relational databases and if I where to make collections out of all of them it would start to look like that.

Mongoose populate array

I can't get mongoose to populate an array of objects.
The schema is as follows:
var topOrganisationsForCategorySchema = new mongoose.Schema({
category: String,
topOrganisations: [{
organisation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'organisation'
},
model: mongoose.Schema.Types.Mixed
}]
});
module.exports = mongoose.model('topOrganisationsForCategory', topOrganisationsForCategorySchema);
I would like all of the objects in this collection populated with an array of organisations.
Here is what i have tried
TopOrganisationsForCategory
.find()
.exec(function(err, organisation) {
var options = {
path: 'topOrganisations.organisation',
model: 'organisation'
};
if (err) return res.json(500);
Organisation.populate(organisation, options, function(err, org) {
res.json(org);
});
});
var organisationSchema = new mongoose.Schema({
name: String,
aliases: [String],
categories: [String],
id: {
type: String,
unique: true
},
idType: String
});
organisationSchema.index({
name: 'text'
});
module.exports = mongoose.model('organisation', organisationSchema);
You're close but a couple notes:
The following code assumes you also have a schema/model declaration for Oranisation.
I am not sure if the model property is meant as an option (which would be invalid) or actually is a property of topOrganisations.
So, I left model in as it shouldn't cause any issues but be aware that if you were using it as an option it is not doing what you might think it is.
// Assuming this schema exists
var organisationSchema = new mongoose.Schema({...});
var topOrganisationsForCategorySchema = new mongoose.Schema({
category: String,
topOrganisations: [{
organisation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Organisation' // Model name convention is to begin with a capital letter
}
// Is `model` supposed to be for the ref above? If so, that is declared in the
// Organisation model
model: mongoose.Schema.Types.Mixed
}]
});
// Assuming these model definitions exist
var Organisation = mongoose.model('Organisation', organisationSchema);
var TopOrganisationsForCategory = mongoose.model('TopOrganisationsForCategory', TopOrganisationsForCategorySchema);
// Assuming there are documents in the organisations collection
TopOrganisationsForCategory
.find()
// Because the `ref` is specified in the schema, Mongoose knows which
// collection to use to perform the population
.populate('topOrganisations.organisation')
.exec(function(err, orgs) {
if (err) {
return res.json(500);
}
res.json(orgs);
});