updating subdocument array in mongoose express - node.js

So I have a schema, which is like this:
const playerSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
paid : {type: Boolean, default: false}
}
)
const tournamentSchema = new Schema(
{
name: {
type: String,
required: true
},
player:[ playerSchema])
so in the tournament model i get this as return:
{
"_id": "5fbf3afe1ecd92296c746db6",
"name": "1st Testing Tournament",
"player": [
{
"paid": false,
"_id": "5fbf3cfe6347784028da8181",
"user": "5fbf3b4c1ecd92296c746dcd"
}
]}
in the API I will have only the user ID and the tournament ID. I want to update paid in players array from false to true. Here is what I tried:
exports.put_confirmPayment= async(req, res)=>{
const uid = req.params.user_id
const tid = req.params.tid
const findData= {"_id": tid, "player.user": uid }
const changeData = {"player.$.paid": "true"}
try {
await Tournament.findOneAndUpdate( findData, {$set: {changeData}} )
const tDB = await Tournament.findById(tid)
res.status(200).json(tDB)
} catch (error) {
console.log(error)
res.status(500).json(error.message)
}
}
Where I am going wrong? and what should my approach be?

convert string id from string to object type using mongoose.Types.ObjectId
change "true" string to boolean true
return updated result using returnOriginal: false, or new: true both will return new updated result
have removed extra constant variables, don't create too much variables
exports.put_confirmPayment = async(req, res) => {
try {
const tDB = await Tournament.findOneAndUpdate(
{
_id: mongoose.Types.ObjectId(req.params.tid),
"player.user": mongoose.Types.ObjectId(req.params.user_id)
},
{ $set: { "player.$.paid": true } },
{ returnOriginal: false }
);
res.status(200).json(tDB);
} catch (error) {
console.log(error);
res.status(500).json(error.message);
}
}
Playground
For more information refer mongoose findoneandupdate documentation.

Related

Mongoose Could not find path "${filterPath}" in schema by updating a subdocument

In this Items object:
{
"items": [
{
"_id": "63a48f12a9731cfd8a64b0b1",
"item_name": "addidas shoes",
"__v": 0,
"rating": [
{
"_id": "63a48fd51fb70775d216eb87",
"rate": 1,
"user_id": "6398a1a157d6146413b23b43"
}
]
}
]
}
I'm trying to update the rating property if a user_id inside of it already exists, else, add a new object into it.
const addRating = async (req, res) => {
const { rate, user_id, item_id } = req.body;
// item_id = 63a48f12a9731cfd8a64b0b1 user_id = 6398a1a157d6146413b23b43 rate = 6
// Adding ratings to the selected item
const test = await itemDB.item.updateOne(
{ _id: item_id, rating: { user_id: user_id } },
{ $push: { "items.rating.$[i].rate": rate } },
{ arrayFilters: [{ "i.user_id": user_id }], upsert: true }
);
console.log(test);
res.json({ message: "success" });
};
I wanted to change something in the rating property so I set the filter as above but it gives me this error when hitting the endpoint:
\node_modules\mongoose\lib\helpers\update\castArrayFilters.js:74
throw new Error(`Could not find path "${filterPath}" in schema`);
^
Error: Could not find path "items.rating.0.user_id" in schema
This is my Items Schema:
const mongoose = require("mongoose");
const RateSchema = mongoose.Schema({
rate: {
type: Number,
required: true,
},
user_id: {
type: mongoose.ObjectId,
},
item_id: {
type: mongoose.ObjectId,
},
});
const ItemSchema = mongoose.Schema({
item_name: {
type: String,
required: true,
},
rating: {
type: [RateSchema],
},
});
module.exports = mongoose.model("Items", ItemSchema);
It looks like it is not noticing that items is also an array when applying the array filter to rating.
Try using the all-positional operator like:
{ $push: { "items.$[].rating.$[i].rate": rate } }

Mongoose - findByIdAndUpdate

All,
I can seem to figure out why the record in the database will not update. I am not 100% sure where my error is but this isn't really providing me a great error message. Can someone please take a look at this for me?
I believe that I am calling the mongoose request properly. Thank you in advance!
$ npm mongoose -v
8.15.0
const mongoose = require("mongoose");
const CartSchema = new mongoose.Schema(
{
owner: {
type: String,
unique: true,
required: true,
},
discount: {
type: Number,
},
total: {
type: Number,
},
items: [
{
itemId: {
type: Number,
},
sku: {
type: Number,
},
quantity: {
type: Number,
},
price: {
type: Number,
},
},
],
},
{ timestamps: true }
);
const Cart = mongoose.model("Cart", CartSchema);
module.exports = Cart;
Record in Database
{"_id":{"$oid":"630689708997a6589635986c"},
"owner":"611afa8b9069c9126cff3357",
"total":{"$numberInt":"0"},
"items":[],
"createdAt":{"$date":{"$numberLong":"1661372784844"}},
"updatedAt":{"$date":{"$numberLong":"1661372784844"}},
"__v":{"$numberInt":"0"}}
exports.add = async (req, res, next) => {
const { id, product } = req.body;
const addItem = { itemId: product._id, sku: product.sku, quantity: 1, price: product.price };
console.log(addItem);
try {
const updateCart = Cart.findByIdAndUpdate(id, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" });
if (!updateCart) return next(new ErrorResponse("Unable to update the cart record", 404));
console.log(updateCart);
if (updateCart) {
return sendRes(updateCart, 200, res);
} else {
return sendRes(updateCart, 201, res);
}
} catch (error) {
console.log(error);
next(error);
}
};
This issue was caused by me using an ASYNC Function without the AWAIT on the DB Call.
Please try once with this:
Cart.findByIdAndUpdate(id, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" });
The first obvious mistake is that you're searching for a document with the wrong field:"id", Kindly change that to "_id: id"
Also you might need to convert the _id string you have to MongoDB Object ID, like this:
const ObjectId = require('mongodb').ObjectId;
Cart.updateOne({_id: new ObjectId(id)}, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" });
For other update method, you need to specify the field, and also convert it to a MongoDB ID
OR
Cart.findByIdAndUpdate(id, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" })
You do not to specify the field in findByIdAndUpdate, just pass the id to it.

Why this code is creating two similar object in mongodb?

Node js
The method used to post the comment data
const { type, movieId, userId, review, timestamp } = req.body;
const movie = await Movie.findOneAndUpdate(
{ movieId: movieId, type: type },
{
$push: {
movieReview: { ... },
},
},
{ upsert: true, new: true },
(err, info) => {
...
}
);
Reactjs
The method used to submit the comment
const submit_comment = async () => {
....
const file = {
movieId: id,
type: type,
userId: userInfo._id,
comment: comment,
};
if (!commentFlag) {
const { data } = await axios.post("/api/movie/add-comment", file, config);
...
};
Mongoose Schema
const movieSchema = new mongoose.Schema({
movieId: String,
type: String,
...
comment: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
});
After I run my submit function it posts two objects with the same object _id in mongoDB

MongoDB array of objects update

I'm trying to update array of user information objects in mongoose.
I've stored the user core information in the login process, I want to update some of user information when user tries to make an order.
Here is the code for the model
const mongoose = require('mongoose');
const { ObjectId } = mongoose.Schema;
const userSchema = new mongoose.Schema(
{
name: String,
email: {
type: String,
required: true,
index: true,
},
role: {
type: String,
default: 'subscriber',
},
info: [
{ country: String },
{ city: String },
{ address: String },
{ phone: String },
{ birthdate: Date },
{ gender: { type: String, enum: ['Male', 'Female'] } },
],
// wishlist: [{ type: ObjectId, ref: "Product" }],
},
{ timestamps: true }
);
module.exports = mongoose.model('User', userSchema);
In my controller I'm getting the data from front-end react app as JSON format, I want to push some data to info which is an array of objects in the users model above.
exports.createOrder = async (req, res) => {
// Here I constract the data
const { plan, service, fullName, country, city, address } = req.body.order;
const { user_id } = req.body;
// This the method I tried
try {
const user = await User.updateOne(
{
_id: user_id,
},
{
$set: {
'info.$.country': country,
'info.$.city': city,
'info.$.address': address,
},
},
{ new: true }
);
if (user) {
console.log('USER UPDATED', user);
res.json(user);
} else {
res.json((err) => {
console.log(err);
});
}
const newOrder = await new Order({
orderPlan: plan,
orderService: service,
orderUser: user_id,
}).save();
console.log(newOrder);
console.log(req.body);
} catch (error) {
console.log(error);
}
};
I tired other solutions like
const user = await User.updateOne(
{
_id: user_id,
info: { $elemMatch: { country, city, address } },
},
{ new: true }
);
So do I need to reformat my model or there is a way to update this array of objects?
Option 1
Use $[]
db.collection.update(
{},
{ $set: { "info.$[i].country": "a1" }} ,
{ arrayFilters: [ { "i.country": "a" } ] }
)
Demo - https://mongoplayground.net/p/UMxdpyiKpa9
Option 2
if you know the index
Demo - https://mongoplayground.net/p/41S7qs6cYPT
db.collection.update({},
{
$set: {
"info.0.country": "a1",
"info.1.city": "b1",
"info.2.address": "c1",
"info.3.phone": "d1"
}
})
Suggestions -
Change info schema to object instead of an array

updating document inside array in mongoose

I want to update my answer object inside answers array. I am using following schema
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const questionSchema = new mongoose.Schema(
{
postedBy: {
type: ObjectId,
required: true,
ref: "User",
},
question: {
type: String,
required: true,
},
photo: {
data: String,
required: false,
},
answers: [
{
userId: { type: ObjectId, ref: "User" },
answer: String,
},
],
questionType: {
data: String,
required: false,
},
},
{ timeStamps: true }
);
module.exports = mongoose.model("Question", questionSchema);
I am using updateOne method to update my answer in my db. Can anyone explain what is missing here. I am been trying to solve this since hours
exports.updateAnswer = (req, res) => {
const questionId = req.body.questionId;
const answerId = req.body.answerId;
Question.findOne({ _id: questionId }).exec((err, question) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
return;
}
if (!question) {
res.status(400).json({
error: "question not found",
});
return;
}
});
Question.updateOne(
{ _id: answerId },
{
$set: {
"answers.$.answer": "This is update answer. My name is Ravi Dubey",
},
},
{ new: true },
(err, success) => {
if (err) {
res.status(400).json({
error: errorHandler(err),
});
}
res.json({
msg: "answer updated successfully",
success,
});
}
);
};
My result is coming successful but answer is not updating in db.
I am confused on Question.updateOne method.
Any help appreciated.
If you trying to query based on id of one of the documents in the answers array then instead of {_id: answerId} you need to provide {'answers._id': answerId}. And also if you need the updated document as result then you should use the findOneAndUpdate method.
Question.findOneAndUpdate(
{ "answers._id": answerId },
{ $set: { "answers.$.answer": "some answer" } },
{ new: true },
(err, data) => {
// handle response
}
);

Resources