Push new Object inside a document array - javascript

I have this schema for my user
const userSchema = mongoose.Schema({
firstName: {
type: String,
},
notifications : [{
description: String,
status: {
type: String,
enum : ['read', 'unread'],
default: 'unread'
},
dateAdded:{
type: Date,
default: Date.now
}
}],
})
supposedly I want to find the user _id first then insert a new object inside the new notification array. and it should look like this
{
_id: ObjectId('123')
firstName: 'John Doe'
notifications:[
{
description: 'somedescription'
status: 'unread'
},
{
description: 'somedescription2'
status: 'unread'
}
]
}
How can I achieve this, assuming that the notification property is non existent in the user document first, i need to check if notification property is present else add the notification property and push the new object
User.updateOne(
{ _id: userId },
{ $push: { notifications: {description: 'new notifications'} } }
)
this code is not working for me

Use $addToSet operator to achieve that
User.updateOne(
{ _id: userId },
{ $addToSet: { notifications: {description: 'new notifications'} } }
)
If that doesn't work try to add the default value too, and then that must work
User.updateOne(
{ _id: userId },
{ $addToSet: { notifications: {description: 'new notifications',
'status': 'unread'} } }
)

Related

findoneandupdate mongodb for new field that not exist

so i try to udpdate new field that not exist on my mongodb models, i try to update it every time user login , but its not push any new fields on the mongodb, can someone tell me where did i do wrong here, here is mycode:
const userLog = await User.findOneAndUpdate(
{ NIK: req.body.NIK },
// { loginDate: { $exists: false } },
{ $push: { loginDate: toJSONLocal(date) } },
{ new: true }
);
console.log("====================================");
console.log(userLog);
console.log("====================================");
here is the response :
_id: new ObjectId("6368e40d1f58fd76efb27957"),
fullname: 'Ja lie',
password: '$2b$10$DEWgxY/hth/yYIVGOvZDt..RpxKCKm58NbrrDFQgITLB8.cHrHHvG',
NIK: 'MT220',
status: 'active',
department: 'Logistic',
position: 'Trainee',
Group_Shift: 'Non Shift',
role: 'admin',
createdAt: 1667818509,
updatedAt: 1669188767,
__v: 0
When you create your MongoDB model, you must create a model with the key loginDate

How to add an object to an array of object, using addToSet, or push operators in mongodb

I have an array of reviews, I want to add a review using addToSet that will check if user is present in the array, then we do not want to add since one user can only review once.
My schema looks like this:
const sellerSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
reviews: [
{
by: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
title: {
type: String,
},
message: {
type: String,
},
rating: Number,
imagesUri: [{ String }],
timestamp: {
type: Date,
default: Date.now,
},
},
],
});
I might be doing the query wrong, but can't figure out how to add a review and check if current user has not reviewed before.
Here is the query where I add the review:
router.post("/review/:_id/", async (req, res) => {
try {
const stylist_id = mongoose.Types.ObjectId(req.params._id);
const review = {
by: req.user._id,
title: req.body.title,
message: req.body.message,
rating: parseFloat(req.body.rating),
};
if (req.body.imagesUri) {
//if there is images, we need to set it up
review.imagesUri = req.body.imagesUri;
}
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
return res.status(200).send(true);
}catch(err){
res.status(502).send({
error: "Error creating a review.",
});
}
});
I'm thinking of checking for seller's id and also check that no review is by current user, but it is not working.
const userID = req.user._id;
await Seller.updateOne(
{ _id: seller_id, reviews: { $elemMatch: { by: { $ne: { userID } } } } },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
ANSWER:
I was able to solve the issue, in case other people have same issue. I did this:
await Seller.updateOne(
{
_id: seller_id,
"reviews.by": { $nin: [req.user.id] },
//knowing req.user._id is a mongoose.Types.ObjectId.
//You can also use [id1, id2, ...] to the array to check for other id's
},
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
Here is the documentation for $nin operator: https://www.mongodb.com/docs/manual/reference/operator/query/nin/
You are pushing the review object inside an object.
Instead do this:
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } }
);

Mongoose populate returns an empty array | multiple levels of embedded documents

I am trying to populate my ChatRoom model with the User reference. However, it returns a ChatRoom object with only _ids where I expected usernames, as if I never applied populate on it.
Here is an extract of my ChatRoom model :
const ChatRoom = mongoose.model("ChatRoom", {
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
roomname: { type: String, default: "new room" },
messages: [
{
messages: {
type: mongoose.Schema.Types.ObjectId,
ref: "Message",
},
meta: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
delivered: Boolean,
read: Boolean,
},
],
},
],
participants: [
{
user: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
},
],
isPrivate: { type: Boolean, default: "false" },
});
My User model :
const User = mongoose.model("User", {
username: { required: true, unique: true, type: String },
avatar: Object,
token: String,
hash: String,
salt: String,
chatroom: {
type: mongoose.Schema.Types.ObjectId,
ref: "ChatRoom",
},
});
As this seems to be a recurrent issue, I tested several StackOverflow answers for my populate code :
Using populate('participants.user') and 'model: user' or just populate('participants.user'), same solution here:
const chatroom = await ChatRoom.findById(req.params.id)
.populate([
{
path: "participants.user",
model: "User",
},
])
.exec((err, user) => {
if (err) {
console.log("error", err);
} else {
console.log("Populated User " + user);
}
});
The console.log returns :
Populated User { _id: new ObjectId("62262b342e28298eb438d9eb"),
sender: new ObjectId("6225d86c9340237fe2a3f067"), roomname:
'Hosmeade', participants: [ { _id: new
ObjectId("6225d86c9340237fe2a3f067") } ], isPrivate: false,
messages: [], __v: 0 }
As if no populate method was ever applied. On the client side, I get an empty string.
Checking my documents aren't empty, this link mentions that Mongoose get problems with detecting referring model across multiple files but the solution doesn't work for me :
_id:6225d86c9340237fe2a3f067 username:"Berlioz" token:"rTCLAiU7jq3Smi3B"
hash:"wvJdqq25jYSaJjfiHAV4YRn/Yub+s1KHXzGrkDpaPus="
salt:"hZdiqIQQXGM1ryYK" avatar:Object
__v:0
If I remove the .exec(...) part, I get the info on the client side, but participants is still filled with only id :
chatroom response : Object { _id: "62262bb14e66d86fb8a041e8",
sender: "6225d86c9340237fe2a3f067", roomname: "Very secret room",
participants: (1) […], isPrivate: false, messages: [], __v: 0 }
I also tried with select: 'username' and get the same result as above :
const chatroom = await ChatRoom.findById(req.params.id).populate({
path: "participants.user",
select: "username",
});
Populating it "as an array"
Changing type of participants.user in my ChatRoom model into an Object (nothing changes)
If needed hereafter are my repos:
Client side and Backend
I run out of ideas on how to debbug my code. Any help would be great !

MongoDB findOne using $and & $elemMatch not working?

I am trying to check if there is an existing conversation between two users before creating another one.
My Conversation object stores the conversation participants in an array, I need to return a conversation object that has BOTH participants (senderId & recId) that exists in my database but I am unable to build to correct MongoDB query to get it.
Please see the queries I have tried below because I have tried all manner of using $and & $elemMatch but can't get it to work.
Thank you
Conversation Model
const conversationSchema = mongoose.Schema(
{
participants: [participantSchema],
},
{timestamps: true}
)
const participantSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: `User`,
},
username: {
type: String,
required: true,
}
}
)
Conversation Object
{
_id: 61cb6316asas4b54e09168234,
participants: [
{
userId: 61b777ea6815a69a625b,
username: 'johnsmith'
},
{
userId: 61bc0dcbe7181ccfd806,
username: 'testuser'
}
],
createdAt: 2021-12-28T19:18:46.673Z,
updatedAt: 2021-12-28T23:41:12.364Z
}
Queries I have tried that ARE NOT what I need or don't work
// null - no convo found when the convo definitely exists in db
const existingConvo = await Conversation.findOne({
$and: [{ userId: senderId }, { userId: recId }],
})
// works but only checks for ONE id property
// if I make an array: "Query filter must be an object, got an array"
const existingConvo = await Conversation.findOne({
participants: { $elemMatch: { userId: senderId } },
})
// "Unknown operator $and"
const existingConvo = await Conversation.find({
participants: {
$and: [{ userId: senderId }],
},
})
// returns empty array when it should have the convo object
const existingConvo = await Conversation.find({
participants: { $all: [{ userId: senderId }, { userId: recId }] },
})

Cant set headers after they are sent ALSO CASTError: Cast to [ObjectId] failed

I seem to be getting 2 errors.....
Here is the Route:
router.post("/event", isLoggedIn, function (req,res){
// get data from form and add to events array
var title = req.body.title;
var date = req.body.date;
var description = req.body.description;
var venue = req.body.venue;
var photo = req.body.photo;
var category = req.body.category;
//get user data to save to the event.
var owner = {
id: req.user._id,
username: req.user.username
};
var newEvent = {category: category, title: title, date: date, description: description, venue: venue, photos:{link: photo,date: date}, owner: owner};
//Create the event itself in the database, event will return the actual database event.
Event.create(newEvent, function(err, event){
if (err) {
console.log(err)
} else {
//This takes the event owner ID and saves it into the Event model
console.log(event);
event.owner.id = req.user._id;
//This takes the event username and saves it into the Event model
event.owner.username = req.user.username;
event.save();
//Save the event ID in the user document
console.log(event);
User.findByIdAndUpdate(
req.user._id,
{$push: {events:{"ObjectId": event._id}}},
{save: true, upsert: true, new: true},
function (err,newEventData){
if(err){
console.log("error at saving the id ..." + err)
res.redirect("/dashboard");
} else {
console.log(newEventData);
}
}
);
//Add the Event ID to the User model
console.log (owner);
};
});
res.redirect('events');
});
Here is the output of the console.log of the returned value from Mongoose and also the error.
The id of the user 583f30b1e5e7e376502762f5
Below are all the events pulled{ _id: 583f30b1e5e7e376502762f5,
username: 'asdf',
__v: 0,
favoriteMoments: [],
favoriteEvents: [],
likeEvents: [],
likeMoments: [],
friends: [],
moments: [],
events: [],
categories: [] }
{ __v: 0,
title: 'asdf',
description: 'asdf',
_id: 583f3175b6a3b376a515c146,
comments: [],
photos: { link: '', date: null },
moments: [],
category: [ '' ],
owner: { id: 583f30b1e5e7e376502762f5, username: 'asdf' } }
{ __v: 0,
title: 'asdf',
description: 'asdf',
_id: 583f3175b6a3b376a515c146,
comments: [],
photos: { link: '', date: null },
moments: [],
category: [ '' ],
owner: { id: 583f30b1e5e7e376502762f5, username: 'asdf' } }
{ id: 583f30b1e5e7e376502762f5, username: 'asdf' }
error at saving the idCastError: Cast to [ObjectId] failed for value "[{"ObjectId":"583f3175b6a3b376a515c146"}]" at path "events"
events.js:141
throw er; // Unhandled 'error' event
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/var/www/familysite.com/node_modules/express/lib/response.js:719:10)
at ServerResponse.location (/var/www/familysite.com/node_modules/express/lib/response.js:836:15)
at ServerResponse.redirect (/var/www/familysite.com/node_modules/express/lib/response.js:874:18)
at /var/www/familysite.com/routes/eventRoute.js:67:29
at /var/www/familysite.com/node_modules/mongoose/lib/model.js:3388:16
at /var/www/familysite.com/node_modules/mongoose/lib/model.js:3388:16
at /var/www/familysite.com/node_modules/kareem/index.js:207:48
at /var/www/familysite.com/node_modules/kareem/index.js:127:16
at nextTickCallbackWith0Args (node.js:419:9)
at process._tickCallback (node.js:348:13)
Here is the schema of the Users Model:
const userSchema = new mongoose.Schema({
username: String,
password: String,
nickname: String,
firstName: String,
middleName: String,
lastName: String,
address: String,
city: String,
state: String,
phone: Number,
birthday: Date,
birthplace: String,
userCover: String,
categories: Array,
events: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Event"
}],
moments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Moments"
}],
friends: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}],
likeMoments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Moments"
}],
likeEvents: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Event"
}],
favoriteEvents: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Event"
}],
favoriteMoments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Moments"
}]
})
I have been getting no where with this Cast issue and now I have 2 errors which seems odd... getting very frustrated at this point and unsure where to go.
In the end, I have route that needs to create an event, save it event ID to the user that created it and then go to the /event page and display the data for each event.
If you look at the first block of code about 3/4 the way down...
this line:
{$push: {events:{"ObjectId": event._id}}},
Should look like this:
{$push: {events:{_id: event._id}}},
Thats it! so _id is how you tell it to be an ID.

Categories

Resources