Individual nested subdocument Mongoose - node.js

I'm Trying to embed a subdocument into my main document,like this:
This is the main document.js
var mongoose = require('../../db/mongodb.connector'),
Schema = mongoose.Schema;
require('./document.model');
var Document= mongoose.model('Document');
require('./alert.model');
var Alert = mongoose.model('Alert');
var userSchema = new Schema({
name: { type: String }
created: { type: Date, default: Date.now()},
alerts: {type: Schema.ObjectId,ref: 'Alert'},
documents: [{type: Schema.ObjectId,ref: 'Document'}],
});
module.exports = mongoose.model('User', userSchema);
This is the embed document.js
var mongoose = require('../../db/mongodb.connector'),
Schema = mongoose.Schema;
var alertsSchema = new Schema({
push: {type: String, default: "true"},
email: {type: String, default: "false"},
sms: {type: String, default: "false"}
});
module.exports = mongoose.model('Alert', alertsSchema);
When I Insert a new User document like this:
exports.insertUser = function (userData, res) {
var user = new User({
name: userData.name,
alerts: {push: "true", email:"false", sms: "false"}
});
user.save...
...
The returned data is this:
{ name: 'name',
documents: [],
created: 2017-04-14T10:22:05.612Z
}
The problem is that I don't know if I'm doing correctly the sintax of embed document because the insert doesn't return any error but the alerts object doesn't appear into the inserted new document.
What would be wrong?

You are doing it wrong. You need to first save the alert document and then use its id in the user document.
let alertDoc = await new Alert({push: "true", email:"false", sms: "false"}).save();
// now use the id in the user doc
await new User({name: userData.name,alerts: alertDoc._id }).save()
In case you want to embed the whole document instead of just storing the ref. You could modify schema of user model. Define your schema like this.
var alertsSchema = new Schema({
push: {type: String, default: "true"},
email: {type: String, default: "false"},
sms: {type: String, default: "false"}
});
....
var userSchema = new Schema({
name: { type: String }
created: { type: Date, default: Date.now()},
alerts: alertsSchema,
documents: [{type: Schema.ObjectId,ref: 'Document'}],
});
....
// now this should work
var user = new User({
name: "<some name>",
alerts: {push: "true", email:"false", sms: "false"}
});

There is a small issue in userSchema. From your schema definition, it looks like you want to store only references to alerts and documents. The right syntax here would be alerts: {type: Schema.Types.ObjectId,ref: 'Alert'}. Please notice that extra Types in it.
Another issue here is, you are trying to store complete alert object inside user document. Mongoose can't allow that, as in your schema, you have told mongoose to save only references to the alert document. So what you need to do here is, create an alert document, get it's _id and then store it in alert field of user document.
Whenever you want to fetch the complete user schema, you can just populate alert and documents.
Hope this answer improves your understanding of how mongoose schema works.

Related

How to dynamically populate array of objects in mongoose?

I am trying to dynamically populate array of objects in mongoose. On my user model I want an array that contains all posts that user made. The problem is that I have multiple types of posts.
Different post models:
const ImagePost = mongoose.model('ImagePost', new Schema({ url: String }))
const TextPost = mongoose.model('TextPost', new Schema({ text: String }))
My user model looks like this:
const userSchema = new Schema({
userName: {
type: String,
required: true
},
posts: [{
postId: {
type: Schema.Types.ObjectId,
required: true,
refPath: "postModel"
},
postModel: {
type: String,
required: true,
enum: ['ImagePost', 'TextPost']
}
}]
})
const User = mongoose.model('User', userSchema)
How can I get the user from my database and automatically populate the posts the user made?
The whey I think it should work is this but for some reason it doesn't do anything:
User.findById('5d302c7caf1b8906ccb611b6').populate('posts.postId')
Changing your refPath from postModel to posts.postModel may solve your problem.

How to show relationship in mongoose?

I have two mongoose schemas 'user' and 'project' and i want to show relationship between these schemas like in mysql. How to do this?
And how can i pass user while creating a project?
User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const Schema = mongoose.Schema();
const UserSchema = mongoose.Schema({
fullname: {type: String},
username : {type:String},
password: {type:String}
});
UserSchema.methods.encryptPassword = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10), null);
};
UserSchema.methods.comparePassword = function(userPassword, cb) {
bcrypt.compare(userPassword, this.password, (err, isMatch) => {
if(err) throw err;
cb(null, isMatch);
});
}
module.exports = mongoose.model('User', UserSchema);
project.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema();
const User = require('./user');
const ProjectSchema = mongoose.Schema({
title: {type: String, required: true},
description: {type:String},
created_at: { type: Date, default: Date.now },
publish : { type: Boolean, default: false}
});
module.exports = mongoose.model('Project', ProjectSchema);
Creating schema in Mongoose isn't like creating schema in Relational DBMS, such as MySQL, PostGreSQL.
You can use objectId, bro.
If one project just can be handled by one user, you can use something like this.
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
But if one project is handled by multi users
users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}]
Hope it will help
Schema Change
To create a relationship in Mongoose (MongoDB), create a property on your schema with the following properties
1. type: mongoose.Schema.Types.ObjectId
2. ref: "string name of the Collection this reference lives in"
For example, if you wanted a project to contain a reference to the users inside of it, you could do the fallowing
const mongoose = require('mongoose');
const Schema = mongoose.Schema();
const User = require('./user');
const ProjectSchema = mongoose.Schema({
title: {type: String, required: true},
description: {type:String},
created_at: { type: Date, default: Date.now },
publish : { type: Boolean, default: false},
users: [{type: Schema.Types.ObjectId, ref: 'User'}]
});
module.exports = mongoose.model('Project', ProjectSchema);
Example Opertaion
Given the above Schema, if you wanted to create a new Project and add users to it during creation, you would need to have those users' _id properties on hand (i.e. cached). That could mean making a prior query to the db to get all the users who will be a part of this project or having some client send you the user ids.
A much better option would be to create a project, and update its' users property as users are added or removed.
Here is a quick example
const Project = require('./models/Project.js');
let userIds = // get the user _ids some how
Project.create({
title: 'A sample',
description: 'The usual',
publish: true,
users: userIds
})
.then(callback);

Newly entered field does not inserted in already created collection after updating mongoose schema

I have already created one MongoDB collection using mongoose schema from express web service after some days I found a requirement of a new field in mongoose table so added that field in mongoose schema and try to insert a value of newly added field along with existing fields but it's not get inserted in the newly added document.
So what I have done as a troubleshooting process, I deleted my existing collection totally and then inserted my new document in a collection, Then I found It started working properly. My newly inserted field get inserted into the collection. So Is there any other way to add a new field in MongoDB using mongoose without deleting the whole collection.
My existing user collection schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
var User = mongoose.model('User', userSchema);
module.exports = User;
When I inserted a new document with username and password In above collection It works fine.
My new user collection schema after adding new field (name):
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
name: String,
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
var User = mongoose.model('User', userSchema);
module.exports = User;
But After updating my user schema When I inserted a new document with name, username and password In above collection only insert username and password, name field does not get inserted.
Just do one simple step, add default values to every field you create.
Example
var userSchema = new Schema({
name: {type: String , default: ""},
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
Happy Coding
Name should be also be an object in the schema like so
var userSchema = new Schema({
name: {type: String},
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
});

Mongoose - don't allow documents to have identical arrays

I want to create a database schema where a document cannot have an array that is identical to that of another document. So, say I have the schema conversations:
var ConversationSchema = new Schema({
name: String,
participants: {
type: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
}
});
Now if I create two conversations with the same participants, how can I validate this so that the second one will fail, but the third will not?
var conversation1 = new Conversation({
name: "Hello",
participants: ['12345', '09876']
});
var conversation2 = new Conversation({
name: "World",
participants: ['12345', '09876']
});
var conversation3 = new Conversation({
name: "Group chat",
participants: ['12345', '09876', '13579']
});
conversation1.save(); // Valid
conversation2.save(); // Invalid - conversation already exists
conversation3.save(); // Valid
I guess you could use some custom Mongoose validation before saving your data.
But this is not really a schema thing, as Kevin said in his comment, since you will need to make a database query to compare already existing array with the new one.
Something like this (not tested):
function checkArray(arr) {
// here make a call to the db to compare existing array with arr
}
var ConversationSchema = new Schema({
name: String,
participants: {
type: [{
type: Schema.Types.ObjectId,
ref: 'User',
validate: checkArray
}]
}
});
No better idea for now.

How to create an embedded document that follows a model with Mongoose?

I have two models, Post and Comment:
My Post model (models/post.js):
var mongoose = require('mongoose');
var Comment = require('../models/comment');
var Schema = mongoose.Schema;
module.exports = mongoose.model('Post', new Schema({
text: {type: String, trim: true},
postedBy: String,
comments: [Comment]
}));
My Comment model (models/comment.js):
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports = mongoose.model('Comment', new Schema({
user: String,
comment: {type: String, trim: true},
created: {type: Date, default: Date.now(), select: false}
}));
When I attempt to create a new post without any comments, the post is created perfectly fine.
Although when I try to $push a comment to the post after creation, nothing happens.
Post.findOneAndUpdate(
{"_id": req.params.id},
{$push: {comments: {
comment: "Hello World",
user: "933ujrfn393r"
}}
}).exec(function(err, post) {
console.log(post);
res.json({success: true});
});
Why is this failing to push the comment to the post? My console.log(post) line simply logs undefined, so not too sure what is happening here. I tried a simple test of Post.findOne({"_id": req.params.id}) and it returned the post successfully, so there is no problem with the find query.
Embedded sub documents
Your usage implies an embedded sub document inside the model which only requires a schema definition for the sub document. This will store both schema's in a single document in a single collection in MongoDB
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
user: String,
comment: {type: String, trim: true},
created: {type: Date, default: Date.now(), select: false}
});
var PostSchema = new Schema({
text: {type: String, trim: true},
postedBy: String,
comments: [CommentSchema]
});
module.exports = mongoose.model('Post', PostSchema);
Then create comments as you were.
Post.findOneAndUpdate(
{"_id": req.params.id},
{$push: {comments: {
comment: "Hello World",
user: "933ujrfn393r"
}}
}).then(function (post) {
console.log(post);
res.json({success: true});
});
Document references
If you want to keep the two models then you would need to use a reference in your Post schema instead. This will create seperate documents in seperate collections in MongoDB and use the _id to look up the second document.
var PostSchema = new Schema({
text: {type: String, trim: true},
postedBy: String,
comments: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
});
Then comments need to be created before you can reference them in the Post model.
c = new Comment({ comment: 'x' })
c.save().then(function (result) {
return Post.findOneAndUpdate(
{ _id: req.params.id },
{ $push: { comments: result._id } }
);
}).then(function (result) {
console.log('updated post');
});
Population can be used to easily retrieve the "foreign" documents.
Based on this question, I believe your problem is that you're embedding the Comment Model instead of the Comment Schema.
Try changing post.js from:
var Comment = require('../models/comment');
to:
var Comment = require('../models/comment').schema;
This also makes sense after looking at the example on the Mongoose docs regarding sub-docs.
P.S.
What helped me investigate this was outputting the err object of the exec callback to see what was actually going on...

Resources