Mongoose not saving changes to a document - javascript

I'm sorry if this might be a duplicate question but I'm quite having a hard time understanding Mongoose. I am working on a Node.js project that implements Mongoose and MongoDB. What I want to accomplish is to modify and save some users' data through a call from a specific endpoint.
Mongoose Schema looks like this
var UserSchema = new Schema({
isAdmin: {type: Boolean, default: false},
name: String,
surname: String,
nickname: { type: String },
email: { type: String, lowercase: true, required: true, trim: true, unique: true, dropDubs: true },
password: { type: String, required: true },
salt: { type: String },
verified: { type: Boolean, default: false },
bio: {
type: { type: String, enum: [0,1] }, // 0='Appassionato', 1='Giocatore'
birthday: String,
height: Number,
number: Number,
role: { type: String, enum: [0,1,2,3] }, // 0='Playmaker', 1='Ala', 2='Guardia', 3='Centro'
team: String,
city: String,
aboutMe: String,
},
newsletter: {type: Boolean, default: false},
lastCheckin: {type: mongoose.Schema.Types.ObjectId, ref: 'Checkin'},
follows: [{type: mongoose.Schema.Types.ObjectId, ref: 'Structure'}],
score: { type: Number, default: 0 },
profilePicture: String,
lastLogin: {type: Date},
facebook: {
id: String,
accessToken: String,
profileImage : String
}
}, {
collection: 'users',
retainKeyOrder: true,
timestamps: true,
}).plugin(mongoosePaginate);
Following is the code for when the endpoint gets interrogated
exports.updateUser = (req,res) => {
var userId = req.params.userId;
var updates = req.body;
User.findOneAndUpdate({_id: userId}, {$set: updates}, (err, saved) => {
if (!err) {
console.log("Ritorno questo: " + saved);
return res.status(202).json(saved);
} else {
return res.status(500).json(saved);
}
});
};
As far as I understood, the method findOneAndUpdate exposed by Mongoose should find the document I'm looking for and then modify it and save it. This doesn't happen though.
Through PostMan I'm sending this JSON
{"bio.aboutMe":"Hello this is just a brief description about me"}
But PostMan is responding with the non-modified object. What am I missing here?

What you need to do is to add {new:true}, it give you back the updated document.
In the documentation :
If we do need the document returned in our application there is
another, often better, option:
> Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true },
> function (err, tank) { if (err) return handleError(err);
> res.send(tank); });
This is something I don't really like as there is another option if we don't want to have the document → update
So what you need to do is :
User.findOneAndUpdate({_id: userId}, {$set: updates}, {new:true}.....

Related

findOneReplace replacment issue

I want to find a document in my db and replace it with a document that has a new name and new key.
Here is my Schema
const Schema = mongoose.Schema;
const vampireSchema = new Schema({
name: { type: String, required: true },
title: String,
hair_color: {type: String, default: "blonde" },
eye_color: String,
dob: Date,
loves: [String],
location: String,
gender: String,
victims: {type: Number, min: 0}
});
const Vampire = mongoose.model("Vampire", vampireSchema);
module.exports = Vampire;
Here is my executable code
Vampire.findOneAndReplace( { name: "Claudia" }, { name: "Eve", portrayed_by: "Tilda Swinton" }, (err, vamp) => {
if(err){
console.log(err)
}
else{
console.log(vamp)
}
db.close()
})
There are two issues that I can see.
First, you should pass null as the third argument in your findOneAndReplace call. This will set the options to null and should get your code running. This is in my opinion a strange behavior of mongoose.
Vampire.findOneAndReplace(
{ name: "Claudia" },
{ name: "Eve", portrayed_by: "Tilda Swinton" },
null,
(err, vamp) =>
{
if(err){
console.log(err)
}
else{
console.log(vamp)
}
db.close()
})
Secondly, I would recommend adding the portrayed_by to the schema, otherwise, that field will not be in the newly created document. Therefore, I would adjust your schema that way:
const vampireSchema = new Schema({
name: { type: String, required: true },
title: String,
hair_color: {type: String, default: "blonde" },
eye_color: String,
dob: Date,
loves: [String],
location: String,
gender: String,
victims: {type: Number, min: 0},
portrayed_by: String
});

Destructuring a mongoose document

Using mongoose in my project, I ran into a problem.
I want to find all documents that have such a key and value pair role: USER. I can get a list of documents, but I cannot get the values of specific fields from it, no matter how I try.
Here is my code:
const getUsersList = async () => {
const users = await userModel.find({ role: USER });
//also I truing:
//In each case, I get undefined
const users = await userModel.find({ role: USER }).userName;
////
const users = await userModel.find({ role: USER }).exec();
////
Document.prototype.toObject(users);
////
JSON.stringify(users).userName
}
The request definitely gets the document, because console.log(users) lists the documents.
[
{
_id: new ObjectId("618b1a587d57e9c8e78865e1"),
userName: 'Username1',
name: 'Fullname1',
email: 'email1#gmail.com',
password: 'Password1',
status: 'INVITED',
role: 'USER',
__v: 0
},
{
_id: new ObjectId("618b1a6e7d57e9c8e78865e5"),
userName: 'Username3',
name: 'Fullname2',
email: 'email2#gmail.com',
password: 'Password2',
status: 'INVITED',
role: 'USER',
__v: 0
}
]
Judging by the documentation of the mongoose, I am doing everything right. It is also advised to cast a document into an object using toObject(), but mongoose does not find such a method for request
Моя схема:
const userSchema = new Schema(
{
userName: { type: String, unique: true, required: true },
name: { type: String, required: true },
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
confirmationCode: { type: String, required: false },
status: { type: String, required: true, default: STATUS.INVITED },
role: { type: String, required: true, default: USER },
},
);
It's an array, so trying to get userName won't work. You need to get the specific element. Try this:
const userResponse = await userModel.find({ role: USER })
const firstUserName = userResponse[0].userName

How to optimize performance with CREATE, PUT, and DELETE requests on MongoDB?

I have a database named "reviews" with a 9.7GB size. It has a collection name products. I was able to optimize the READ request using indexing technical by running the command db.products.ensureIndex({product_name: 1}); When I run the following command db.products.find({product_name:"nobis"}).explain("executionStats"); in MongoDB terminal, it shows that my execution time reduces from 28334ms to 3301ms.
I have the following 2 questions:
1) How do I use explain("executionStats"); on CREATE, PUT and DELETE requests? For example, I got this following error [thread1] TypeError: db.products.insert(...).explain is not a function when I tried to use the following insert function
db.products.insert({"product_id": 10000002,"product_name": "tissue","review": [{"review_id": 30000001,"user": {"user_id": 30000001,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000001,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}, {"review_photo_id": 60000002,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}, {"review_id": 30000002,"user": {"user_id": 30000002,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000003,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}]}).explain("executionStats");
2) Is there any performance Optimization method I can use for the CREATE, PUT and DELETE requests? For example, I am able to use POSTMAN to get the response time of a DELETE request, but the response time takes 38.73seconds.
const deleteReview = (request, response) => {
const id = parseInt(request.params.id);
Model.ProductModel.findOneAndDelete({ "review.review_id": id}, (error, results) => {
if (error) {
response.status(500).send(error);
} else {
response.status(200).send(results);
}
});
};
This is my MongoDB schema:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/reviews', { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true });
const Schema = mongoose.Schema;
const productSchema = new Schema({
product_id: { type: Number, required: true, unique: true },
product_name: { type: String, required: true, unique: true },
review: [{
review_id: { type: Number, required: true, unique: true },
user: {
user_id: { type: Number },
firstname: { type: String },
lastname: { type: String },
gender: { type: String, enum: ['Male', 'Female', 'Other'] },
nickname: { type: String },
email: { type: String, required: true },
password: { type: String, required: true },
},
opinion: { type: String, required: true },
text: { type: String },
rating_overall: { type: Number, min: 1, max: 5, required: true },
doesRecommended: { type: Boolean, required: true },
rating_size: { type: String, enum: ['a size too small', '1/2 a size too small', 'Perfect', '1/2 a size too big', 'a size too big'], required: true },
rating_width: { type: String, enum: ['Too narrow', 'Slightly narrow', 'Perfect', 'Slightly wide', 'Too wide'], required: true },
rating_comfort: { type: String, enum: ['Uncomfortable', 'Slightly uncomfortable', 'Ok', 'Comfortable', 'Perfect'], required: true },
rating_quality: { type: String, enum: ['Poor', 'Below average', 'What I expected', 'Pretty great', 'Perfect'], required: true },
isHelpful: { type: Number, required: true, default: 0 },
isNotHelpful: { type: Number, required: true, default: 0 },
created_at: { type: Date, required: true },
review_photo_path: [{
review_photo_id: { type: Number },
review_photo_url: { type: String }
}]
}]
});
const ProductModel = mongoose.model('product', productSchema);
module.exports = { ProductModel };
If you do not have one, ensure you have an index of review.review_id on your products collection. You're using that to look up what to delete so it should be indexed.
I read your deleteReview function as deleting the product document that contains the review, not just removing the individual review -- is that what you expect?
You should be able to just $pull the review from the reviews array to get rid of it.
You can use explain on an update like so:
db.products.explain().update({...}, {...});
See: https://docs.mongodb.com/manual/reference/method/db.collection.explain/
You can explain:
aggregate()
count()
find()
remove()
update()
distinct()
findAndModify()

Querying sub document of sub document in mongoose

I wanted to save the data in "messageSchema" which is sub document of chatSchema by checking the "receiver" of chatSchema and "username" of userSchema.
like pseudoCode:-
if(userSchema.username == "Rahul" && userSchema.chatSchema.receiver){
then save the data in chatSchema.message;
}
Here is my Schema:-
var messageSchema = mongoose.Schema({
messageId: {type: String, unique: true, required: true},
created: {type: Date, default: Date.now},
messageContent: String
});
var chatSchema = mongoose.Schema({
message: [messageSchema],
receiver: {type: String, required: true}
});
var userSchema = mongoose.Schema({
username: { type: String, unique: true, required: true },
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
token: { type: String, required: false },
conversations: [chatSchema]
});
please suggest what should be code to save the message data.
tried below one that didn't work.
User.findOneAndUpdate({username: "rahul", "conversations.receiver": data.receiver },{$push: {"conversations.message": message}});
I think you need to use $elemMatch instead of the dot notation for matching properties within an array. Try this:
User.findOneAndUpdate(
{
username: "rahul",
conversations: {
$elemMatch: { receiver: data.receiver }
}
},
// or whatever your update is
{$push: {"conversations.message": message}
})

How To Create Mongoose Schema with Array of Object IDs?

I have defined a mongoose user schema:
var userSchema = mongoose.Schema({
email: { type: String, required: true, unique: true},
password: { type: String, required: true},
name: {
first: { type: String, required: true, trim: true},
last: { type: String, required: true, trim: true}
},
phone: Number,
lists: [listSchema],
friends: [mongoose.Types.ObjectId],
accessToken: { type: String } // Used for Remember Me
});
var listSchema = new mongoose.Schema({
name: String,
description: String,
contents: [contentSchema],
created: {type: Date, default:Date.now}
});
var contentSchema = new mongoose.Schema({
name: String,
quantity: String,
complete: Boolean
});
exports.User = mongoose.model('User', userSchema);
the friends parameter is defined as an array of Object IDs.
So in other words, a user will have an array containing the IDs of other users. I am not sure if this is the proper notation for doing this.
I am trying to push a new Friend to the friend array of the current user:
user = req.user;
console.log("adding friend to db");
models.User.findOne({'email': req.params.email}, '_id', function(err, newFriend){
models.User.findOne({'_id': user._id}, function(err, user){
if (err) { return next(err); }
user.friends.push(newFriend);
});
});
however this gives me the following error:
TypeError: Object 531975a04179b4200064daf0 has no method 'cast'
If you want to use Mongoose populate feature, you should do:
var userSchema = mongoose.Schema({
email: { type: String, required: true, unique: true},
password: { type: String, required: true},
name: {
first: { type: String, required: true, trim: true},
last: { type: String, required: true, trim: true}
},
phone: Number,
lists: [listSchema],
friends: [{ type : ObjectId, ref: 'User' }],
accessToken: { type: String } // Used for Remember Me
});
exports.User = mongoose.model('User', userSchema);
This way you can do this query:
var User = schemas.User;
User
.find()
.populate('friends')
.exec(...)
You'll see that each User will have an array of Users (this user's friends).
And the correct way to insert is like Gabor said:
user.friends.push(newFriend._id);
I'm new to Mongoose myself, so I'm not entirely sure this is right. However, you appear to have written:
friends: [mongoose.Types.ObjectId],
I believe the property you're looking for is actually found here:
friends: [mongoose.Schema.Types.ObjectId],
It may be that the docs have changed since you posted this question though. Apologies if that's the case. Please see the Mongoose SchemaTypes docs for more info and examples.
I would try this.
user.friends.push(newFriend._id);
or
friends: [userSchema],
but i'm not sure if this is correct.

Categories