How to update nested mongo object - javascript

I am trying to make a report function for my app
in the front end I make a put request :
.put(`http://localhost:3000/api/posts/report`, {
params: {
id: mongoId,
reportInfo: {
reported: true,
reportingUser: id
}
}
})
to this backend route
router.put('/report', (req, res, next) => {
postModel.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId(req.query.id) },
req.query,
{ new: true, useFindAndModify: false },
(error, returnedDocuments) => {
if (error) return next(error);
res.json(returnedDocuments);
}
);
});
for this model
const postSchema = new mongoose.Schema(
{
title: { type: String },
description: { type: String },
image: { type: String },
price: { type: String },
location: { type: String },
image: { type: Array },
author: {
type: String,
ref: 'User'
},
reportInfo: {
reported:{
type: Boolean,
default: false
},
reportingUser:{
type: String
}
}
},
{
timestamps: true
}
);
any ideas why it is not updating the reportInfo object , do I need to do something if there are some nested objects contained?
thanks

Your code tries to replace entires MongoDB document. Try to use $set instead of passing req.query directly:
{ $set: { reportInfo: req.query.reportInfo } }
I would also check if there should be req.query or req.body so just print the value to make sure that it gets deserialized properly.

Related

postman returning null with 200 status code

getting null value as response with 200 status code. i want to see the profile details as response but instead of that showing null value with no error status code in my postman i dont find any error on my code. why it shows like this ? i want to see profile details as response after sending
Router.post
router.post(
'/',
[
auth,
[
check('status', 'Status is required').not().isEmpty(),
check('skills', 'Skills cannot be empty').not().isEmpty(),
],
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
company,
website,
location,
bio,
status,
githubusername,
skills,
youtube,
twitter,
instagram,
linkedin,
} = req.body;
const profileFields = {};
profileFields.user = req.user.id;
if (company) profileFields.company = company;
if (website) profileFields.website = website;
if (location) profileFields.location = location;
if (bio) profileFields.bio = bio;
if (status) profileFields.status = status;
if (githubusername) profileFields.githubusername = githubusername;
if (skills) {
profileFields.skills = skills.split(',').map(skill => skill.trim());
}
// creating object for socila links
profileFields.social = {};
if (youtube) profileFields.social.youtube = youtube;
if (twitter) profileFields.social.twitter = twitter;
if (instagram) profileFields.social.instagram = instagram;
if (linkedin) profileFields.social.linkedin = linkedin;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile)
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
);
return res.json(profile);
// create
profile = new Profile(profileFields);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err);
res.status(500).send('server error');
}
}
);
here is profile schema looks like
const ProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
company: {
type: String,
},
website: {
type: String,
},
status: {
type: String,
required: true,
},
location: {
type: String,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
},
githubusername: {
type: String,
},
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: {
type: String,
required: true,
},
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model('profile',ProfileSchema)
your code logic has problem.
let profile = await Profile.findOne({ user: req.user.id }); if (profile) //update profile = await Profile.findOneAndUpdate( { user: req.user.id }, { $set: profileFields }, { new: true } ); return res.json(profile);
here, if you can't find the record in database, you still return and there is no value so that you got null response. i suggest you remoe the return res.json(profile); into if statement
Back to the basics, the if statement.
if (profile){
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
);
return res.json(profile);
}
You need to use brackets {}.
In your code, return res.json(profile); gets fired no matter if the response is null or not
the problem is with your syntax you need to add curly braces
if (profile){
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
)};

mongodb after updating document, returns old values

router.delete('/deletepost', (req, res) => {
// console.log(req.query.postid)
if (req.query.category === 'forsale') {
ForSalePosts.findById(req.query.postid)
// .then(post => console.log(post))
.deleteOne()
.catch(err => console.log(err))
AllPosts.updateOne({ user: req.query.userid },
{ $pull: { posts: { postid: req.query.postid } } })
.catch(err => console.log(err))
AllPosts.aggregate(
[
{ $match: { user: ObjectId(req.query.userid) } },
{ $unwind: '$posts' },
{ $sort: { 'posts.date': -1 } }
]
)
.then(posts => {
// console.log(posts)
res.json(posts)
})
.catch(err => res.status(404).json({ nopostfound: 'There is no posts' }))
}
})
this is my route. i am trying to delete an item in my document. the item is being deleted however it returns old values. for example :
Allposts has an array with posts:[postid:{type:String}, ...]
I am trying to delete a specific postid by using $pull,
postid is being deleted however when I aggregate the same model, .then(posts=> console.log(posts)) returns old values on first call, doesnt update the component.
EDIT: just realized sometimes it returns the right values but sometimes it returns the old values as well. does anyone know why and what can i do to solve it ?
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const AllPostsSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
posts: [{
postid: {
type: String
},
title: {
type: String
},
category: {
type: String
},
subcategory: {
type: String
}, category: {
type: String
},
description: {
type: String
},
name: {
type: String
},
price: {
type: Number
},
email: {
type: String
},
phonenumber: {
type: Number
},
language: {
type: String
},
make: {
type: String
},
model: {
type: Number
},
odometer: {
type: Number
},
condition: {
type: String
},
state: {
type: String
},
town: {
type: String
},
city: {
type: String
},
links: [{ type: String }],
date: {
type: Date,
default: Date.now
}
}]
})
module.exports = AllPosts = mongoose.model('allposts', AllPostsSchema)
REACT FUNCTION CALL :
deletePost = (category, postid) => {
const postinfo = {
category: category.toLowerCase(),
postid: postid,
userid: this.props.auth.user.id
}
this.props.deletePost(postinfo)
}
You need to add options parameter to delete like:
AllPosts.updateOne({ user: req.query.userid },
{
$pull: { posts: { postid: req.query.postid } }
},
{ new: true }
);
This will return the new object after performing the operation. Hope this works for you.
All the mongo queries return partial promise. You have to use .then in order to resolve each promises.
Here you are running all the queries in series without using .then or async-await. So whenever you $pull from AllPosts after that immediately you call the AllPosts aggregate query which sometimes get executed and sometimes it doesn't.
So in order to make it run one by one you have to use either .then or async-await.
router.delete("/deletepost", (req, res) => {
if (req.query.category === "forsale") {
ForSalePosts.findById(req.query.postid)
.deleteOne()
.then(() => {
AllPosts.updateOne(
{ "user": req.query.userid },
{ "$pull": { "posts": { "postid": req.query.postid } } }
)
.then(() => {
AllPosts.aggregate([
{ "$match": { "user": ObjectId(req.query.userid) } },
{ "$unwind": "$posts" },
{ "$sort": { "posts.date": -1 } }
]).then(posts => {
// console.log(posts)
res.json(posts);
});
})
.catch(err =>
res.status(404).json({ "nopostfound": "There is no posts" })
);
});
}
})

How to change boolean value within an object of a document sub-array in Mongoose?

I have a rooms model. Within it is an array of User's which has its own model.
Each user has a bunch of different attributes, some of them being boolean. Knowing the ID of the specific room and the specific user, I am attempting to change the boolean value within a specific User element within the sub array like this:
Room.findOne({_id: roomId, "users" : user}, { "$set" : { mutedAudio : false}})
.then(doc => {
console.log("Unmuted audio");
res.json(doc)
io.in(roomId).emit('userchange');
})
.catch(err => {
console.log(err);
})
(I'm using a user model instead of a user ID for seeking the user within the sub array. Could not get ID to work but can fetch object by comparing it to itself entirely.)
I get the error:
MongoError: Unsupported projection option: $set: { mutedAudio: true }
Anyone know the answer to this?
Thank you.
EDIT:
const RoomSchema = new Schema({
owner: {
id: {
type: String
},
username: {
type: String
}
},
roomname: {
type: String,
required: true
},
category: {
type: String,
required: true
},
password: {
type: String,
required: false
},
users: [UserSchema],
messages: [{
username: {
type: String
},
message: {
type: String
},
time: {
type: String
}
}],
date: {
type: Date,
default: Date.now
}
});
const UserSchema = new Schema({
id: {
type: String
},
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
},
micEnabled: {
type: Boolean,
default: false
},
mutedAudio: {
type: Boolean,
default: true
}
});
Model.findOne() takes 4 parameters, the second being "optional fields to return", that's why you're getting the error, mongoose is trying to select fields to return according to $set: { mutedAudio: true } which is being passed as a second parameter (therefore considered to be a projection option).
Use Model.findOneAndUpdate() which takes an update object as a second parameter, along with the positional operator $.
Room.findOneAndUpdate(
{ "_id": roomId, "users._id": userID },{ "$set": { "users.$.mutedAudio": false } } )
.then(doc => {
console.log("Unmuted audio");
res.json(doc)
io.in(roomId).emit('userchange');
})
.catch(err => {
console.log(err);
})
Original answer by #Neil Lunn in Mongoose find/update subdocument

Can't POST to nested array with express js

It's my first post here so please let me know if there's anything incomplete about my question, or if there's anything else that is missing :)
I'm trying to make a POST request to an array in my data structure called features:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CategorySchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
categoryname: {
type: String,
required: true
},
items: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
itemname: {
type: String,
required: true
},
features: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
firstfeature: {
type: String
},
date: {
type: Date,
default: Date.now
}
},
{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
secondfeature: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Category = mongoose.model('category', CategorySchema);
I don't have any issues with posting to the items array with the following code:
router.post(
'/item/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateItemInput(req.body);
// Check Validation
if (!isValid) {
// if any errors, send 400 with erros object
return res.status(400).json(errors);
}
Category.findById(req.params.id)
.then(category => {
const newItem = {
itemname: req.body.itemname,
user: req.user.id
};
// Add to item array
category.items.unshift(newItem);
// Save
category.save().then(category => res.json(category));
})
.catch(err =>
res.status(404).json({ categorynotfound: 'No category found' })
);
}
);
But I can't figure out what I need to change here in order to add data to the features array:
router.post(
'/feature/:id/:item_id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
Category.findById(req.params.id)
.then(category => {
const newFeature = {
firstfeature: req.body.firstfeature,
secondfeature: req.body.secondfeature,
user: req.user.id
};
// Add to item array
category.items.features.unshift(newFeature);
// Save
category.save().then(category => res.json(category));
})
.catch(err => res.status(404).json({ itemnotfound: 'Item not found'
}));
}
);
Issue solved with the following data structure:
features: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
price: {
type: String
},
size: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
]
And then simply make a post request for one feature at a time.

Mongodb POST response returns 0?

I got it to work on accident with out adding a res.json(doc) but I found that when I make a POST request I need to send a response and ever since I added that response I get 0 or null?
add: function(req, res) {
//var newPlayer = new models.Team({ _id: req.params.tid }, { players: req.body });
models.Team.update({ _id: req.params.tid }, { $addToSet: { players: req.body} }, function(err, newPlayer){
if (err) {
res.json({error: 'Error.'});
} else {
res.json(newPlayer);
}
});
},
Also tried with findOneAndUpdate but my POST request is showing 0 or null for the response.
I am updating an array of objects inside a collection, it's nested. Here is the SCHEMA just to be clear.
var Team = new Schema({
team_name: { type: String },
players: [
{
player_name: { type: String },
points: { type: Number },
made_one: { type: Number },
made_two: { type: Number },
made_three: { type: Number },
missed_one: { type: Number },
missed_two: { type: Number },
missed_three: { type: Number },
percentage: { type: Number },
assists: { type: Number },
rebounds: { type: Number },
steals: { type: Number },
blocks: { type: Number },
fouls: { type: Number },
feed: { type: String },
facebook_id: { type: Number }
}
]
});
So my question is does anyone have any idea why I am getting that response 0?
The update method does not return the document in the response. The callback arguments are (err, numAffected) where numAffected is the number of documents touched by the "update" statement, which can possibly do "bulk" ( or multi ) processing.
What you want is findByIdAndUpdate() or findOneAndUpdate(). These methods return the either the original document or the modified document, according to the arguments you give.
add: function(req, res) {
models.Team.findByIdAndUpdate(
req.params.tid,
{ $addToSet: { players: req.body } },
function(err, newPlayer){
if (err) {
res.json({error: 'Error.'});
} else {
res.json(newPlayer);
}
}
);
},

Categories

Resources