fail to update documents with cursor.forEach - mongodb

This Meteor client code does not update the documents found as expected. The console.log(res) prints '0' when there are documents to be updated.
Why and how to fix it? Thanks
MyCollection.find({
class: 'check-filter'
}).forEach((obj) => {
MyCollecction.update({
obj
}, {
$set: {
class: ''
}
}, (err, res) => {
if (!err) {
console.log(res);
}
});
});

Change your selector to use the object's _id:
MyCollection.find({ class: 'check-filter' }).forEach(obj => {
MyCollection.update(obj._id, { $set: { class: '' }}, (err, res) => {
if (!err) {
console.log(res);
}
});
});
Also you have a typo where you're trying to do MyCollecction.update instead of MyCollection.update

Related

Mongo and mongoose $match _id in array

I have a frontend in React and a backend in express and node.
From FE i am calling an API on the server:
const { data: autotaskItems } = useApiCall({
url: `api/endpoint`,
method: 'post',
payload: {
filter: {
_id: {
$in: ["id1","id2"],
},
},
},
});
on the server:
router.post('/config-items/find', async (req, res) => {
const { filter } = req.body
// ConfigItem.find({ ...filter })
// .then(result => {
// res.status(200).json({ success: true, data: result });
// })
ConfigItem.aggregate([
{ $match: { ...filter }
}])
.then(result => {
res.status(200).json({ success: true, data: result });
})
But this doesn't work. I have found that aggregate doesn't "support" automatic conversion of ObjectId to string. If I have used find() and spread filter like above this will work just fine. However, I do need to use aggregate as I have a couple of lookups there too.
Anyone can help, please?
Also, if possible i would like to keep structure with spreading the filter object for match
Thank you
As per #Martinez's answer, this was resolved by the following:
Nice and simple :-)
ConfigItem.aggregate([{
"$addFields": {
"_id": {
"$toString": "$_id"
}
}
},
//rest of the query

findByIdAndUpdate do not update document

I am trying to update a field to the document with findByIdAndUpdate. The field I am trying to update is defined in the Bar Model. And I can also assure that req.body.bookId has a valid id.
Here's how my request looks,
app.patch("/foo", async (req, res) => {
try {
await validateId(req.body.bookId);
let doc = await Bar.findByIdAndUpdate(
req.body.bookId,
{ DateT: Date.now() },
{ new: true }
);
res.send(doc);
} catch (err) {
console.log(err);
}
});
Bar schema,
const mongoose = require("mongoose");
const barSchema = mongoose.Schema({
bookId: {
type: String,
unique: true,
},
DateT: {
type: Date,
default: null,
},
});
module.exports = mongoose.model("Bar", barSchema);
use updateOne, when you use async don't use .then() use try/catch
test it:
app.patch("/foo", async (req, res) => {
try {
let doc = await Bar.updateOne(
{ bookId : req.body.bookId },
{ DateT: Date.now() },
{ new: true }
);
res.send(doc);
} catch (error) {
console.log(error);
}
});
app.patch("/foo", async (req, res) => {
await Bar.findByIdAndUpdate(
req.body.bookId,
{ DateT: Date.now()},
(err, docs) => {
if (err) {
console.log(err);
} else {
res.send(docs);
}
}
);
});

Getting a $set error even though i am setting it?

chats.post('/approveUser', (req, res) => {
// let regex = new RegExp(req.body.requestId)
Chat.updateOne(
{ 'requests._id': req.body.requestId },
{ $set: {'approved': true} },
{ upsert: true }
)
.then(res => {
console.log('hi')
if (!res) {
res.status(404).send()
} else {
res.status(200).send(res)
}
})
.catch(err => {
console.log(err)
res.status(500).send(err)
})
})
Does anyone see why i'm getting an error? It says:
errmsg: '\'$set\' is empty. You must specify a field like so: {$set: {<field>: ...}}'
req.body.requestId is not undefined. This is the layout of each mongoDB document:
{
"_id":"5cd1f4aceb05c12298779345",
"title":"test 3",
"participants":[
{
"_id":"5cd1f4aceb05c12298779347",
"userEmail":"test#gmail.com"
},
{
"_id":"5cd1f4aceb05c12298779346",
"userEmail":"adam2.cole#northumbria.ac.uk"
}],
"chatType":"publicGroup",
"messages":[
{
"_id":"5cd254b591a0eb1de4a0863c",
"text":"hi",
"from":"test#gmail.com",
"dateTimeSent":"2019-05-08T04:01:57.732Z"
},
{
"_id":"5cd254b591a0eb1de4a0863b",
"text":"hi",
"from":"test#gmail.com",
"dateTimeSent":"2019-05-08T04:01:57.764Z"
}
],
"__v":0,
"requests":[
{
"approved":false,
"_id":"5cd2467891a0eb1de4a08631",
"userEmail":"test2#gmail.com"
}
]
}
The intention is to turn approved of any request to true. I'm failing to see why it won't work. Does anyone else see why?
Thanks.

update and retrieve the updated document mongodb

I am trying to update a document with nested subdocuments, but i always retrieve the previus document.
i tried
{ returnOriginal: false }
but it is not working...
this is my code in nodejs
almacenCtrl.updateAlmacen = async (req, res) => {
almacen = await almacenModel.findOneAndUpdate(req.params.id, { $set: req.body }, { returnOriginal: false }, function (err, updated) {
res.json(updated)
})
}
what am i doing wrong?
//After update i check with mongoshell and the update was updated successfully
Use {new : true} as given below:
almacenCtrl.updateAlmacen = async (req, res) => {
almacen = await almacenModel.findOneAndUpdate(req.params.id, { $set: req.body }, { new: true }, function (err, updated) {
res.json(updated)
})
}

Mongoose update only the values that have changed

I have a PUT route to update value. I am hitting this route from two places. One is sending information about details and one about completed. The problem is that mongoose is updating booth even though it gets value from only one.
So if I send information about completed that it is true and latter I hit this route with new details (that dont have completed value) it will update completed also to false. How do I update just the value that was changed?
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
foundObject.details = req.body.details
foundObject.completed = req.body.completed
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
EDIT:
Thanks to Jackson hint I was managed to do it like this.
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
if(req.body.details !== undefined) {
foundObject.details = req.body.details
}
if(req.body.completed !== undefined) {
foundObject.completed = req.body.completed
}
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
const updateQuery = {};
if (req.body.details) {
updateQuery.details = req.body.details
}
if (req.body.completed) {
updateQuery.completed = req.body.completed
}
//or
Todo.findOneAndUpdate({id: req.body.id}, updateQuery, {new: true}, (err, res) => {
if (err) {
} else {
}
})
//or
Todo.findOneAndUpdate({id: req.body.id}, {$set: updateQuery}, {new: true}, (err, res) => {
if (err) {
} else {
}
})
Had a function similar to this my approach was this
const _ = require('lodash');
router.put('/update/:id',(req,res, next)=>{
todo.findById({
_id: req.params.id
}).then(user => {
const obj = {
new: true
}
user = _.extend(user, obj);
user.save((error, result) => {
if (error) {
console.log("Status not Changed")
} else {
res.redirect('/')
}
})
}).catch(error => {
res.status(500);
})
};
Taking new : true as the value you updating
It gets kinda ugly as the fields to be updated get increased. Say 100 fields.
I would suggest using the following approach:
try {
const schemaProperties = Object.keys(Todo.schema.paths)
const requestKeys = Object.keys(req.body)
const requestValues = Object.values(req.body)
const updateQuery = {}
// constructing dynamic query
for (let i = 0; i < requestKeys.length; i++) {
// Only update valid fields according to Todo Schema
if ( schemaProperties.includes(requestKeys[i]) ){
updateQuery[requestKeys[i]] = requestValues[i]
}
}
const updatedObject = await TOdo.updateOne(
{ _id:req.params.idd},
{ $set: updateQuery }
);
res.json(updatedObject)
} catch (error) {
res.status(400).send({ message: error });
}