Schema referencing stores the whole value instead of ObjectId alone - node.js

Lately, I've been going through a course named The Web Developer Course.
In which the final project is based on Camps.
In the project, the comment database and the campground database are referenced, that is, the ObjectIds of the comments which are posted in a campground is stored in an array. This is what to be happened actually.
But in my case, the exact scenario's changed..When I try to Add a new comment what actually happens is that the total object gets stored in the comments array instead of the ObjectId of the comment.
I've almost gone through Stackoverflow seeking solution for my problem but failed.
I just wanted the ObjectId to be stored in the comments array instead it stores the whole Object which brings me problem in updating and deleting a comment. When I delete or update a comment the operation does happen in the Comments database but doesn't reflect in the Campgrounds database. Please help me with this issue. If anyone's taking the same course, Please give me solutions if you've experienced something like this already. The Schema are as given below
Campground Schema:
var mongoose = require("mongoose");
var campgroundSchema = mongoose.Schema({
campGroundName: String,
campGroundImage: String,
description: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
],
addedBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Campground", campgroundSchema);
Comment Schema:
var mongoose = require("mongoose");
var commentSchema = mongoose.Schema({
text: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Comment", commentSchema);
Post request for creating a comment:
router.post("/", middleware.isLoggedIn, function(req, res) {
Comment.create(req.body.comment, function(err, createdComment) {
if(err) {
console.log(err);
} else {
createdComment.author.id = req.user._id;
createdComment.author.username = req.user.username;
createdComment.save(); Campground.findById(req.params.id).populate("comments").exec(function(err, foundCampground){
foundCampground.comments.push(createdComment);
foundCampground.save();
req.flash("success" , "Comment created successfully");
res.redirect("/index/" + req.params.id);
});
}
});
});
The whole source code is below,
https://1drv.ms/u/s!AmISAco3PGaPhQl_Riu8nroCom5h
Please help me fix this issue!

you have this line:
foundCampground.comments.push(createdComment)
which is telling mongodb to store the whole comment in the array.
it should be this instead:
foundCampground.comments.push(createdComment._id)
which will only push the id property of the comment into the array.

The version seemed to have a bug in it.
The problem was fixed when I updated it. Fixed version is 5.0.3

Related

how can i get data from an db object which is store id of another db in mongoDB

hi there I am not getting data from a DB object which is storing the id of another DBS, actually, this is a blog website so I am posting comments and acting comments but there are problems I am getting the same comments on all posts but I want, every post should have their own comment.
here is post schema
const blogSchema = new mongoose.Schema({
title: String ,
content: String,
image:{data: Buffer,contentType: String},
comment:[
{
type:mongoose.Schema.Types.ObjectId, ref:'Comment'
}
]
});
here is the comment schema
var commentSchema = new mongoose.Schema({
name:{
type:String,
required: "this field is required"
},
comment:{
type:String,
required:"this filed is required"
},
blog:{
type:mongoose.Schema.Types.ObjectId,
ref: 'Blog'
}
},{
timestamps: true
})
node js router which is getting post but not comment
pp.get("/post/:postname",(req,res)=>{
// const requesttitle = _.lowerCase(req.params.postname);
const requesttitle = req.params.postname;
Blog.findOne({_id: requesttitle} ,(err ,got)=>{
if(err){
console.log(err)
}else{
const data= got.comment.find({})
console.log(data)
res.render('post',{post:got });
}
})
})
I believe the problem lays in your Schema. In your blogSchema you have references to many Comment documents, and in your commentSchema you have a reference to a single "Blog" ( I suggest not naming it "blog" but "post" since that is what it is ) . This duplicative referencation is not necessary in most cases.
Since in your setup a single comment can only be a child of one specific post, this would be the reference I would go for. The post document itself doesn't really need to know directly what comments are included since that information is already hold in the Comment document.
For your post I would suggest the following schema :
// Post Schema
const postSchema = new mongoose.Schema({
title: String ,
content: String,
image: { data: Buffer, contentType: String }
});
For your comment I would suggest the following schema :
// Comment Schema
const commentSchema = new mongoose.Schema({
name: {
type: String,
required: "this field is required"
},
comment: {
type: String,
required: "this filed is required"
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
});
Now sure the next step depends on your whole frontend part is setup up, but having schemas like this would let you do something along the lines of :
pp.get("/post/:id", async (req,res) => {
const id = req.params.id;
const post = await Post.findOne({ _id: id });
const comments = await Comment.find({ post: id });
res.render('post', {
post: post,
comments: comments
});
});
Pros
one-directional relation means less work if a comment is created or deleted.
possibility to just get comment and/or post or both in one api call.
Cons
Requires 2 database calls if post and comments both are requested.
Alternative: Subdocuments
As an alternative to using referenced Documents you can use Subdocuments.
For your commentSchema that means you won't need to create a seperate Model out of it. However your postSchema would need to look like this:
const commentSchema = new mongoose.Schema({
message : { type : String }
});
const postSchema = new mongoose.Schema({
comments : [commentSchema]
});
👆 This would by default include all comments of the post if you retrieve the post from the database. However it would also require a different code for interacting with those comments (adding, deleting, ...) but you can read about it in the docs I am sure.

Mongoose populate not working. How to resolve?

I want to do mongoose populate but it not working below is given my schema. How can I solve this issue? Why is it not working I working many solution on stackoverflow but all are not working even I read out doc that also not worked for me. Now someone can please help me How can I sort it?
Auth
const authSchema = mongoose.Schema({
first_name: {
type: String
},
last_name: {
type: String
},
});
const auth = mongoose.model('auth', authSchema);
Project
const teamSchema = mongoose.Schema({
auth_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'auth'
}
});
const projectSchema = mongoose.Schema({
team: {
type: [ teamSchema ]
}
});
const project = mongoose.model('project', projectSchema);
Query:
project.find({}).populate(team.$.auth_id).exec((err, result) => {
if(err) rej(err);
res(result);
});
Based on the documentation you should do something like:
project.find({}).populate('team').exec..... or project.find({}).populate('auth_id').exec.....
If you want to filter an specific auth_id the criteria should be in the find not in the populate, populate apparently receives key name to be populated
https://mongoosejs.com/docs/populate.html
Hope this help
For me, the verison of mongoose 6.* don't work populate.
I stay hour to descover.
try downgrade your mongoose to versions 5.*
commands:
yarn remove mongoose
yarn add mongoose#5.13.14
I think you need to create a virtual.
something like:
userSchema.virtual('car', {
ref: 'Car',
localField: '_id',
foreignField: 'owner',
});

Mongoose - inserting subdocuments

I have a user model, and a log model. The log model is a subdocument of user model. So in my user model I have:
var mongoose = require('mongoose');
var Log = require('../models/log');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
Log
]
});
Then in my 'Log' model I have:
var mongoose = require('mongoose');
var logSchema = new mongoose.Schema({
logComment: {
type: String,
},
});
module.exports = mongoose.model('Log', logSchema);
So upon creation of a 'user', the 'logsHeld' always begins empty. I want to know how to add subdocuments to this user model.
I've tried doing this POST method:
router.post('/createNewLog', function(req, res) {
var user = new User ({
logssHeld: [{
logComment: req.body.logComment
}]
});
user.save(function(err) {
if(err) {
req.flash('error', 'Log was not added due to error');
return res.redirect('/home');
} else {
req.flash('success', 'Log was successfully added!');
return res.redirect('/home');
}
});
});
But this doesn't work. It also includes a 'new User' line, which I don't think I need given this would be for an existing user.
You need to use the logSchema instead of the Log model as your subdocument schema in User model. You can access the schema as follows:
var mongoose = require('mongoose');
/* access the Log schema via its Model.schema property */
var LogSchema = require('../models/log').schema; // <-- access the schema with this
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [LogSchema]
});
Picking up from your comments in another answer where you are facing another issue
WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key
error index: testDB.users.$email_1 dup key:
you are getting this because there's already a document in your users collection that has most probably a null value on the email field. Even though your schema does not explicitly specify an email field, you may have an existing old and unused unique index on users.email.
You can confirm this with
testDB.users.getIndexes()
If that is the case and manually remove the unwanted index with
testDB.users.dropIndex(<index_name_as_specified_above>)
and carry on with the POST to see if that has rectified the error, I bet my $0.02 that there is an old unused unique index in your users collection which is the main issue.
Try using logSchema which references only the subdocument schema, Log refers to the entire contents of ../models/log
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
logSchema
]
});
Documentation: http://mongoosejs.com/docs/subdocs.html
Try push to insert item in array in mongoose
var user = new User;
user.logssHeld.push({
logComment: req.body.logComment
});
user.save(function(err, doc) {
//DO whatever you want
});
see the docs here

Not able to persist array of objects in mongo using mongoose

I'm trying to persist an array of objects in a document using mongoose. I have tried multiple times but it's not persisting array in document. It places an empty array in document.
Following is my Schema:
var ProfileSchema = new Schema({
name: String,
PagesData: [{
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
}]
});
module.exports = mongoose.model('Profile', ProfileSchema);
I'm trying to save a document with an array of objects using following query:
var newProfile = new Profile();
newProfile.name = "someName";
newProfile.PagesData = [ { pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ];
newProfile.save(function(err, result, numAffected){
if(err) {
console.log(err);
res.send(500, "Error");
}
console.log(result);
res.send(200, "Success");
});
I tried debugging the mongo commands using
require('mongoose').set('debug', true)
On Debug logs it shows, empty array during insert command execution.
Can anyone please tell me how can I store this array of object in my schema ?
Thanks,
Update:
It's been too long and I'm still not able to figure out the root cause of the problem. There is a long thread going on github for this.
https://github.com/Automattic/mongoose/issues/3249
I would like other experts to please take a look and suggest me some way by which I can solve the issue. I'm really stuck at this.
Update 2:
None of the solution worked for me so far, so I decided to modify the schema only to meet my requirements. This resulted in a different problem:
I want to create a map with a objectId as key and an array of string values as its value. The closest that I can get is:
var schema = new Schema({
map: [{myId: {type:mongoose.Schema.Types.ObjectId, ref: 'MyOtherCollection'}, values: [String]}]
});
But somehow this is not working for me. When I perform an update with {upsert: true}, it is not correctly populating the key: value in the map. In fact, I'm not even sure if I have declared the schema correctly.
Can anyone tell me if the schema is correct ? Also, How can I perform an update with {upsert: true} for this schema?
Also, if above is not correct and can;t be achieved then how can I model my requirement by some other way. My use case is I want to keep a list of values for a given objectId. I don't want any duplicates entries with same key, that's why picked map.
Please suggest if the approach is correct or should this be modelled some other way?
Thanks
I tried the exact code you have provided here and it's working for me. I am not sure what is causing the issue for you. Until and unless we get the same issue, it's very difficult to rectify it.
Here are few suggestions which you might try:
Create a simple schema and try storing the object, that way you can
figure it out if it has to do something with the schema.
You can try out your schema in a sample app to find if some
dependency is causing the problem.
Once you know where exactly the problem is, you would be able to figure out a solution too. I hope it helps.
I tested this and the insert works for me using the below:
(I had to remove the get: decryptText, set: encryptText)
var n = { name: "Testing for mongoose", PagesData : [{ pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ] }
Profile.create(n, function (err) {
if (!err) {
return 'records saved successfully';
}
else {
return error on save:' + err;
}
});
To create multiple pageDatas you can use it as an embedded collection instead of using arrays.
The Schema will be as follows:
var PagesDataSchema = new Scheme({
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
})
var ProfileSchema = new Schema({
name: String,
PagesData: [PagesDataSchema]
});
module.exports = mongoose.model('Profile', ProfileSchema);
Reference: http://mongoosejs.com/docs/subdocs.html
For Saving the document you can use like.
exports.save = function(req,res){
var test = new ProfileSchema; // new object for ProfileSchema domain.
test.name= req.body.name;
if(req.body.PagesData){
req.body.PagesData.forEach(function(page){ // For every element of pageData from client.
test.PagesData.push(page) // This pushes each and every pagedata given from the client into PagesData.
})
}
test.save(function (saveErr, saved) { // Saves the new document into db.
if (saveErr) {
console.log(saveErr)
return;
}
res.status(HttpStatus.OK).json(saved);
});
};
Hope this helps.
Have you tried
Profile.create({
name: "someName",
PagesData: [
{
pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms: [
'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT'
]
}
]
}, function(err, profile) {
// do your stuff
})
?

How do I update a property with the current date in a Mongoose schema on every save?

In my database collections, I want to update a 'lastChanged' field every time the record is updated with the current datetime. I want it to be in the same format as mongoose's default date like:
ISODate("2011-10-06T14: 01: 31.106Z")
Any words of wisdom?
If you just want an ISO String use:
new Date().toISOString()
One way of accomplishing this is to use Mongoose Middleware and update the field pre-save.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//schema
var SomethingSchema = new Schema({
text: {type: String},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now}
});
//middle ware in serial
SomethingSchema.pre('save', function preSave(next){
var something = this;
something.updatedAt(Date.now());
next();
});
It seems, however, that the middleware is not always invoked:
Notes on findAndUpdate()
pre and post are not called for update operations executed directly on the database, including Model.update,.findByIdAndUpdate,.findOneAndUpdate, .findOneAndRemove,and .findByIdAndRemove.order to utilize pre or post middleware, you should find() the document, and call the init, validate, save, or remove functions on the document. See explanation.
Update: See this question "add created_at and updated_at fields to mongoose schemas"
In a few days Mongo is going to announce new 2.6 version (currently you can download experimental 2.5.x version). Among many other features you can use $currentDate which is going to do exactly the thing you want:
db.users.update(
<criteria>,
{
$currentDate: { yourField: true},
}
)
The middleware function is a good approach, however, it should be
SomethingSchema.pre('save', function preSave(next){
var something = this;
something.updatedAt = Date.now();
next();
});
Since something.updateAt is not a function.
I added updated: new Date to fix a similar problem. Here is how I used it.
update: (req, res) => {
let userId = req.params.id;
let userParams = {
name: {
first: req.body.first,
last: req.body.last
},
email: req.body.email,
password: req.body.password,
updated: new Date
};
db.User.findOneAndUpdate(userId, { $set: userParams })
.then(upUser => res.json(`Profile Updated for: ${upUser.fullName}`))
.catch(err => res.status(422).json(err));
}

Resources