facing a small issue that whenever I try to update a 'comments' property it automatically overwrites old property, doesn't add next value to the array. Tried many options like adding $set parameter as option, removing it, adding overwrite: false, but not successfully. Been looking at the docs, but feels like I'm something missing and even docs can't help me.
My Model:
const mongoose = require("mongoose");
const TicketSchema = new mongoose.Schema({
title: {
type: String,
minlength: 5,
maxlength: 50,
required: [true, "Please validate title"]
},
description: {
type: String,
minlength: 10,
maxlength: 200,
required: [true, "Please validate description"]
},
comments: {
type: Array,
default: []
},
status: {
type: String,
enum: ["in progress", "resolved", "pending"],
default: "pending",
},
priority: {
type: String,
enum: ["regular", "medium", "high"],
default: "regular"
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: "User",
required: [true, "Please provide user"],
}
}, {
timestamps: true,
});
module.exports = mongoose.model('Ticket', TicketSchema);
My controller:
const updateUser = async (req, res) => {
const {
body: {username, email, firstname, lastname, country, password, comments},
params: {
id: employeeId
}
} = req;
const user = await User.findByIdAndUpdate(employeeId, req.body, {
$set: {
comments: req.body.comments
}
}, {
new: true, runValidators: true, upsert: true
}).select(["-password"]);
// Don't allow IT users to change credentials for IT and ADM users.
if (user.type.startsWith("IT") || user.type.startsWith("ADM")) throw new NotFoundError(`No user with id ${employeeId}`);
if (!user) throw new NotFoundError(`No user with id ${employeeId}`);
res.status(StatusCodes.OK).json({user});
};
You may want to use $push operator instead of $set.
Assuming req.body.comments is an array with comments, you could use $each to construct something like this:
const user = await User.findByIdAndUpdate(employeeId, req.body, {
$push: {
comments: { $each: req.body.comments }
}, {
new: true, runValidators: true, upsert: true
}
}).select(["-password"]);
Related
I have tried so many times to add new field to the existing MongoDB document but I failed. I tried following code to do the job but nothing happened.
Here is the User model.
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true }
);
Here is the code to add new field to the document.
const updateDocument = async () => {
const updatedUser = await User.findOneAndUpdate(
{ _id: "63eb30f466127f7a0f7a9b32" },
{
$set: { lastName: "syed" },
}
);
console.log(updatedUser);
};
updateDocument();
NOTE 1: lastName field does not exist in the MongoDB document and in the UserSchema. I want to add that field to the MongoDB document.
NOTE 2: The same code works when I update the existing field inside the document but it does not work when adding new field.
You need to pass strict:false as an option to findOneAndUpdate.
According to the mongoose doc:
The strict option, (enabled by default), ensures that values passed to our model constructor that were not specified in our schema do not get saved to the db.
const updatedUser = await User.findOneAndUpdate(
{ _id: "63eb30f466127f7a0f7a9b32" },
{
$set: { lastName: "syed" },
},
{ strict: false }
);
An alternative way is to pass this parameter when you defined the schema:
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true, strict: false }
);
I am trying to update an array, nested within an object in MongoDB document, using mongoose.
I have been over several similar questions, and only appeared to make a little success, then res.json return { acknowledged: false }, with no errors.
The goal is to push an object into the "likes" array inside reactions object
This is the document that I'm trying to update
_id: new ObjectId("63179b818ebed9da5b433ee0"),
thoughtText: "If Everything works out here, we're supposed to get a notification send to another guy by whomsoever leaves a comment or a like on this post.",
topic: 'Testing out the new notification codes for possible errors',
username: 'anotherguy',
userId: '63179a67849b0348e59d4338',
category: 'Secrets',
createdAt: 2022-09-06T19:12:01.345Z,
reactions: [
{
CommentLikeCount: 0,
mentions: 0,
reactionBody: 'Welcome to U-annon anotherGuy, this is your official first reaction on the site',
username: 'marryGold',
_id: new ObjectId("63179cd18ebed9da5b433ee8"),
reactionId: new ObjectId("63179cd18ebed9da5b433ee9"),
createdAt: 2022-09-06T19:17:37.829Z,
likes: []
},
Below is the query I'm currently using to update the document using updateOne.
EDIT: The schema.
// thought schema or post schema
const thoughtSchema = new Schema (
{
thoughtText: {
type: String,
required: true,
minlength: 1,
maxlength: 2000
},
topic:{
type: String,
required: true,
minlength: 1,
maxlength: 300
},
createdAt: {
type: Date,
default: Date.now,
get: createdAtVal => moment(createdAtVal).startOf('hour').fromNow()
},
username: {
type: String,
required: true,
},
userId:{
type: String,
required: true
},
category: {
type: String,
required: true,
},
reactions: [reactionSchema],
likes: [likeSchema],
},
{
toJSON: {
virtuals: true,
getters: true,
},
id: false,
}
The like schema is similar to the reaction schema,
//reaction schema
const reactionSchema = new Schema (
{
reactionId: {
type: Schema.Types.ObjectId,
default: () => new Types.ObjectId(),
},
reactionBody: {
type: String,
required: true,
},
username: {
type: String,
required: true,
},
userId:{
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
get: createdAtVal => moment(createdAtVal).startOf('second').fromNow()
},
mention: {
type: Object,
required: false
},
likes: [likeSchema],
CommentLikeCount: {
type: Number,
default: 0,
},
mentions: {
type: Number,
default: 0
}
},
{
toJSON: {
virtuals: true,
getters: true
},
id: false,
}
)
And this is the full controller function. including how I check to see if the same user already liked the comment
//Like comments
async likeComments(req, res){
console.log(req.body, req.params)
try {
const post = await Thought.findById(req.params.thoughtId)
const comment = post.reactions.find(x => x.reactionId.toString() === req.params.reactionId)
const liked = comment.likes.find(x => x.username === req.body.username)
if(!liked){
console.log('its open')
const data = await post.updateOne({"reactions.[reaction]._id": req.params.reactionId}, {$addToSet:{"reactions.$.likes": req.body}}, { runValidators: true, new: true })
console.log(data)
res.json(data)
}else{
console.log('already liked')
const data = await post.updateOne({"reactions.[reaction]._id": req.params.reactionId}, {$pull:{"reactions.$.likes": req.body}}, { runValidators: true, new: true } )
res.json(data)
}
} catch (error) {
console.log(error)
}
I've on this for the entire day, I'd really appreciate any help that I can get.
THIS PROBLEM IS A LITTLE LONGER. SO I TYPED BOLD THE CRITICAL INFORMATIONS FOR YOU.
I develop a project like stackoverflow. I have 4 databases which are:
problems
users
solutions
comments
I referrenced these schemas each other. Here is the Schemas:
Problem Schema
const problemSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'You have to enter a title']
},
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
slug: {
type: String
},
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
},
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
votes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
module.exports = mongoose.model('Problem', problemSchema)
User Schema:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: [true, 'You have to enter an email'],
unique: true,
match: [
/^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/,
'Please provide a valid email address.'
]
},
password: {
type: String,
required: [true, 'You have to enter a password'],
minlength: [6, 'Your password cannot be less than 6 character.'],
select: false
},
role: {
type: String,
default: 'user',
enum: ['user', 'admin']
},
createdAt: {
type: Date,
default: Date.now()
},
about: {
type: String
},
place: {
type: String
},
age: {
type: Number
},
blocked: {
type: Boolean,
default: false
},
problem: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
],
solution: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Solution'
}
],
comment: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
})
and Comments Schema:
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: [true, 'You have to enter a content']
},
createdAt: {
type: Date,
default: Date.now()
},
isFunctional: {
type: Boolean,
default: false
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
problem: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Problem'
},
})
module.exports = mongoose.model('Comment', commentSchema)
In my project, I send problems into MongoDB. Then I send comment. After save comments, I add these comments into problems and user DB with a function.
function that comments are saved in DB:
const Comment = require('../models/comment/Comment')
const Problem = require('../models/problem/Problem')
const User = require('../models/user/User')
const asyncErrorWrapper = require('express-async-handler')
const addCommentToProblem = asyncErrorWrapper(async (req, res, next) => {
const {content, problemId} = req.body
const newComment = await Comment.create({
content: content,
problem: problemId,
user: req.user.id,
})
const problemOfComment = await Problem.findByIdAndUpdate(problemId, {
$push: { comment: newComment._id }
})
const userOfComment = await User.findByIdAndUpdate(req.user.id, {
$push: { comment: newComment._id }
})
})
Okey everything is so far so good. The problem comes here. When I try to get a problem, I populate some fields for example user fields. So I can add user information in this detail of problem. When populate user and comment in problem schema, it sends me the data. Still, we're ok. But when I try to get user field in comments, it doesn't populate user. It turns just objectId of user information.
Here is the function that I get problem:
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate('comment') ==> THIS LINE WORKS
.populate('comment.user') ==> THIS LINE DOES NOT WORK
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})
Thanks for reading and your patience. Any help will be appreciated.
See doc at https://mongoosejs.com/docs/populate.html
And try this way.
const getAProblem = asyncErrorWrapper(async (req, res, next) => {
const {id} = req.params
const problems = null
await Problem.findByIdAndUpdate(id, {
$inc: { views: 1 }
}, { new: true })
.populate('user') ==> THIS LINE WORKS
.populate({
'path': 'comment',
'populate': {
'path':'user'
}
})
.exec(function(err, post) {
if(err) {
console.log(err)
}
res
.status(200)
.json({
success: true,
data: post
})
});
})
The database model is
const userSchema: Schema = new Schema(
{
email: {
confirmationCode: { type: String, unique: true, index: true },
confirmed: { type: Boolean, default: false },
sentAt: { type: Date },
},
password: {
hash: { type: String },
resetCode: { type: String },
sentAt: { type: Date },
},
shared: {
avatarId: { type: String },
email: { type: String, required: true, unique: true, index: true },
fullName: { type: String },
},
},
{
timestamps: true,
}
);
And I try and query the conformation code but always returns null
static confirmEmail = async (req: Request, res: Response) => {
try {
const { confirmationCode } = req.body;
console.log(confirmationCode); // logs the correct confirmation code as a string
const user = await userModel.findOne({ email: confirmationCode }).exec();
console.log(user); // Logs null
on a side question, is it any less efficient to query on a subdocument? Should I move the confirmation code to the top level or does it not matter?
Try like this:-
const user = await userModel.findOne({ 'email.confirmationCode': confirmationCode }).exec();
It's fine to query in sub document.
I have this problem. Basically, I have 2 schemas - a User schema and a Document schema. The Document schema has an owner which references the _id field of documents in the User collection.
The problem is that I am still able to save documents in the Document collection with owner ids that do not exist in the User collection which should not be so.
Here is my User schema and Document schema respectively
const UserSchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
validate: [{ validator: value => isEmail(value), msg: 'Invalid email.'
}],
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false,
},
}, {
timestamps: true,
});
const User = mongoose.model('User', UserSchema);
And the Document Schema
const DocumentSchema = new Schema({
title: {
type: String,
required: true,
},
text: {
type: String,
},
access: {
type: String,
enum: ['public', 'private'],
default: 'public',
},
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
}, {
timestamps: true,
});
const Document = mongoose.model('Document', DocumentSchema);
Any help will be appreciated thanks.
For that situation you can add pre save function in your Document schema that will call before save your Document.
const DocumentSchema = new Schema({
// ...
}, {
timestamps: true,
});
DocumentSchema .pre("save",function(next) {
var self = this;
if (self.owner) {
mongoose.models['User'].findOne({_id : self.owner }, function(err, existUser){
if(err){
return next(false, err);
}
if(!existUser)
return next(false, "Invalid user reference");
else
return next(true);
});
} else {
next(false, "Owner is required");
}
});
const Document = mongoose.model('Document', DocumentSchema);