Mongoose delete other objects after update - javascript

I'm having trouble with my update query on mongoose. I'm not sure why other objects get deleted after I update a specific object. the code works when I update but after that, the rest of the objects inside the array are getting deleted/removed. Literally, all of the remaining objects get deleted after the update request.
export const updateProduct = async (req,res) => {
const { id } = req.params;
try {
if(!mongoose.Types.ObjectId.isValid(id)) return res.status(404).json({ message: 'Invalid ID' });
await OwnerModels.findOneAndUpdate({'_id': id, store:{$elemMatch: {productname: req.body.store[0].productname }}},
{$set:
{
store:
{
productname: req.body.store[0].productname,
price: req.body.store[0].price,
quantity: req.body.store[0].quantity,
categoryfilter: req.body.store[0].categoryfilter,
description: req.body.store[0].description,
timestamp: req.body.store[0].timestamp
}
}
}, // list fields you like to change
{'new': true, 'safe': true, 'upsert': true});
} catch (error) {
res.status(404).json(error)
} }

I'm not sure why other objects get deleted after I update a specific object.
Because you are updating the whole object and it will replace the existing store array of object in the database,
You need to use arraFilters, and upsert is not effective in array of object updated, so i have commented,
await OwnerModels.findOneAndUpdate(
{
'_id': id,
store:{
$elemMatch: {
productname: req.body.store[0].productname
}
}
},
{
$set: {
store: {
"store.$[s].productname": req.body.store[0].productname,
"store.$[s].price": req.body.store[0].price,
"store.$[s].quantity": req.body.store[0].quantity,
"store.$[s].categoryfilter": req.body.store[0].categoryfilter,
"store.$[s].description": req.body.store[0].description,
"store.$[s].timestamp": req.body.store[0].timestamp
}
}
},
{
'arrayFilters': [
{ "s.productname": req.body.store[0].productname }
],
'new': true,
'safe': true,
// 'upsert': true
}
);

Related

How to add multiple scores to user in mongodb

I have built a quiz game where when a player finishes their score is submitted to mongodb as an object for e.g:
{
_id: "b7db3e12161567"
name: "james"
score: 5
}
Now what I want to do is to create multiple quizzes, not just one. I've created multiple quizzes which all have a unique name i.e quiz1, quiz2, quiz3 etc.
I want to use this quizId and give each player a score for the specific quiz they play instead of just one singular score. I was thinking something like:
{
_id: "b7db3e12161567"
name: "james",
quiz1: 5,
quiz2: 6
}
Here is my current code for the version which just the single score:
try {
const { _id, name, score, quizId } = req.body;
const users = await mongodb.getDb()
.db('database')
.collection('users')
.updateOne(
{ _id },
{ $set: { _id, name }, $max: { score } },
{ upsert: true }
);
res.json(users);
} catch(err) {
console.log(err);
throw new Error('Cannot add user score');
}
How would I implement what I suggested with mongo? And or would there be a better way to implement what I suggested?
Thanks
One option is to use an update with pipeline query like:
.updateOne(
{ _id, name }
[
{$set: {
quiz1: {$max: ["$quiz1", 3]},
quiz2: {$max: ["$quiz1", 7]}
}}
],
{ upsert: true }
)
Which works like this
You can create it dynamically of course with something like:
const setPart = {}
for (const quiz_name of Object.keys(quizes)) {
setPart[quiz_name] = `{$max: ["$${quiz_name}", ${quizes[quiz_name]}]}`
}
const users = await mongodb.getDb()
.db('database')
.collection('users')
.updateOne(
{ _id },
{ $set: setPart },
{ upsert: true }
);

Mongodb, Delete one object from an array of objects using javascript

I am trying to remove one object from an array of my collection, which looks like this. It s a collection in Mongodb
Before deleting a specific object based on chartId, I need to check the userId and the name of the array. Then I need to delete the object.
I have written this code, but its not working. someone will tell me what exactly I am missing in this code.
delChartObj.updateOne(
{ 'userId': userId },
{ $pull: { "Color": { "chartId": req_chart_id } } },
{ safe: true, multi: true}, function (err, obj) {
if (err) { res.send.err }
res.status(200).send({ msg: "Deleted Sucessfully" });
});
In my case, userId = ADAM, array = "Color" and chartID = time
I am using mongoose for performing action
delChartObj is an object of model
const UserSchema = mongoose.Schema({
userId: { type: String, required: true, unique: true },
charts: { type: Object },
});
You should do findOneAndUpdate, the syntax will be something like:
Model.findOneAndUpdate(
< condition>,
{ $pull: { "Color.$.chartId": req_chart_id } } }, // The actual Query
{ new: true }
)
try this in pull
{ $pull: { "Chart.Color.$.chartId": req_chart_id } } },

how do I increment field in array of objects after finding it (using findOne()) and before saving it?

I want to update an object inside an array of schemas without having to do two requests to the database. I currently am incrementing the field using findOneAndUpdate() if the object already exists and it works fine. but in case the object does not exist then I am having to make another request using update() to push the new object and make it available for later increments.
I want to be able to do only one request (e.g. findOne()) to get the user and then increment the field only if object exists in the array and if not I would like to push the new object instead. then save the document. this way I am only making one read/request from the database instead of two.
this is the function now:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } }
);
if (itemInDb) return true;
const updated = await Model.update(
{ _id: userId },
{ $push: { cart: body } }
);
if (updated.ok !== 1)
return createError(500, 'something went wrong in userService');
return true;
}
what I would like to do is:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOne(
{
_id: userId,
'cart.productId': body.productId,
}
);
if (itemInDb) {
/**
*
* increment cart in itemInDb then do itemInDb.save() <<------------
*/
} else {
/**
* push product to itemInDb then save
*/
}
Thank you!
You can try findOneAndUpdate with upsert.
upsert: true then create data if not exists in DB.
Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } },
{
upsert: true,
}
)
Use $set and $inc in one query.
try {
db.scores.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $set: { "cart.$.productName" : "A.B.C", "cart.$.productPrice" : 5}, $inc : { "cart.$.count" : 1 } },
{ upsert:true, returnNewDocument : true }
);
}
catch (e){
//error
}
reference Link : here
You can use upsert.
upsert is defined as an operation that creates a new document when no document matches the query criteria and if matches then it updates the document. It is an option for the update command. If you execute a command like below it works as an update, if there is a document matching query, or as an insert with a document described by the update as an argument.
Example: I am just giving a simple example. You have to change it according to your requirement.
db.people.update(
{ name: "Andy" },
{
name: "Andy",
rating: 1,
score: 1
},
{ upsert: true }
)
So in the above example, if the people with name Andy is found then the update operation will be performed. If not then it will create a new document.

delete object from document array in mongodb collection using mongoose

I try to remove an element from an array attribute of my object.
This is my schema :
const userSchema = new mongoose.Schema({
userID: {
type: Number
},
name: {
type: String
},
names: [
{
text: { type: String, required: true },
order: {
type: Number,
required: true
}
}
]
});
this is my mongoose function :
User.findOne({ userID: Number(req.params.id) })
.then((user) => {
user.names.remove({text: "john", order: 3});
recipe.save(() => {
res.json(recipe);
});
})
I don't understand why it's not good :/
As per documentation of mongoose remove method remove operation is only executed when a callback is passed. To force execution without a callback, you must first call remove() and then execute it by using the exec() method.
Since you are trying to delete from array of objects then better would be to use pull operator. You don't have to do find and remove, you can simply use update method.
As per documentation of $pull operator you can either specify a value or a condition
i.e.
{ $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
In your scenario you need to either specify complete value of one or more names item object or an condition that matches one or more names item
Add the condition where you match id of names item or if you don't know that then you can use elemMatch to match on few fields i.e.
Use following pull condition to solve the issue:
User.update(
{ _id: Number(req.params.id) },
{ $pull: { 'names': { $elemMatch: { 'text': "john", 'order': 3 }} } },
(error, success) => {
if (error) console.log(error);
console.log(success);
}
);
To Remove Element from array in document please follow as below
User.update(
{
userID: Number(req.params.id),
},
{
$pull: { names: { $elemMatch: { text: "john", order: 3 } } }
},
{
multi: false
}
).lean().then((Status) => {
console.log("Status-->", Status);
res.json('Removed Successfully');
})
Refer $pull operator at link

Mongoose: updating array in document not working

I'm trying to update an array in document by adding object if it doesn't exist, and replacing the object in array otherwise. But nothing ($push, $addToSet) except the $set parameter does anything, and $set works as expected - overwrites the whole array.
My mongoose schema:
var cartSchema = mongoose.Schema({
mail: String,
items: Array
});
The post request handler:
app.post('/addToCart', function(req, res) {
var request = req.body;
Cart.findOneAndUpdate({
"mail": request.mail
}, {
$addToSet: {
"items": request.item
}
}, {
upsert: true
},
function(err, result) {
console.log(result);
}
);
res.send(true);
});
The data that I'm sending from the client:
{
"mail":"test#gmail.com",
"item":{
"_id":"59da78db7e9e0433280578ec",
"manufacturer":"Schecter",
"referenceNo":"Daemon-412",
"type":"Gitare",
"image":"images/ba9727909d6c3c26412341907e7e12041507489988265.jpeg",
"__v":0,
"subcategories":[
"Elektricne"
]
}
}
EDIT:
I also get this log when I trigger 'addToCart' request:
{ MongoError: The field 'items' must be an array but is of type object in
document {_id: ObjectId('5a19ae2884d236048c8c91e2')}
The comparison in $addToSet would succeeded only if the existing document has the exact same fields and values, and the fields are in the same order. Otherwise the operator will fail.
So in your case, request.item always need to be exactly the same.
I would recommend creating a model of "item". Then, your cart schema would be like:
var cartSchema = mongoose.Schema({
mail: String,
items: [{
type: ObjectId,
ref: 'item',
}],
});
And let MongoDB determine if the item exist.
this should work you just need to implement objectExits function that test if the item is that one you're looking for :
Cart.findOne({ "mail": request.mail })
.exec()
.then(cart => {
var replaced = cart.items.some((item, i) => {
if (item._id == request.item._id)) {
cart.items[i] = request.item;
return true;
}
})
if (!replaced) {
cart.items.push(request.item);
}
cart.save();
return cart;
})
.catch(err => {
console.log(err)
});

Categories