Editing Large Collection in MongoDB - node.js

I am trying to edit an entire collection in my MongoDB Database.
The collection is about 12k documents in size.
I was trying to edit the files from my angular controller
let promises = [];
array.forEach(each => {
promises.push(this.commonService.postObject('editObject', each));
});
forkJoin(promises).subscribe(data =>{})
My node function
module.exports.editObject = (model) =>{
return function (req, res, next) {
model.findOneAndUpdate({
'_id': req.body._id
}, req.body, {
upsert: true
}, function (err, doc) {
if (err) return res.send(500, {
error: err
});
return res.send(req.body);
});
};
}
But I get the error Message
ERR_INSUFFICIENT_RESOURCES
Is there a smarter way to do that?

Related

Retrieving data from mongoDB using a field

I want to retrieve all the datas from my mongo db collection "Assets" whose "chapterName" is equal to the string sent through the url, How to get it?
app.get(`/api/assets/get_all/${chapterName}`, (req, res) => {
Assets.find({}, (err, assets) => {
if (err) return res.status(400).send(err);
res.status(200).send(assets);
});
});
the chapterName will be in params of request, req. let's say chaptername is present in mongoose schema.
app.get('/api/assets/get_all/:chapterName}', (req, res) => {
Assets.find({ chaptername: req.params.chapterName }, (err, assets) => {
if (err) return res.status(400).send(err);
res.status(200).send(assets);
});
});

Express app.put / app.post outputs null values

I'm really new to node/express/mongoDB coding, and I have a slight problem with adding/updating values into mongoDB via node/express.
app.post('/', (req, res, next) => {
let data = {
first_value: req.body.first_value,
second_value: req.body.second_value,
};
dbase.collection("testDB").insertOne(data, (err, result) => {
if (err) {
console.log(err);
}
res.send('data added successfully');
});
});
app.put('/:id', (req, res, next) => {
var id = { _id: new ObjectID(req.params.id) };
dbase.collection("testDB").updateOne({ _id: id }, {
$set: {
first_value: req.body.first_value
second_value: req.body.second_value,
}
}, (err, result) => {
if (err) {
throw err;
}
res.send('data updated sucessfully');
});
});
app.put does not alter the values in DB, and app.post only adds "null" into every section of the new entry when I'm trying them with Postman. When I add new values with html form, the data is added correctly.
What is the problem with my code?
For app.post , can you provide me a screen shot of the way you are entering data and in which format e.g. , application/raw , application/x-www-form-urlencoded etc .
For app.put you need to correct the following things . The corrected code is as below ,
app.put('/:id', (req, res, next) => {
var id = { _id: new ObjectID(req.params.id) };
dbase.collection("testDB").updateOne( id , { // put "id" instead of "{ _id: id }"
$set: {
first_value: req.body.first_value
second_value: req.body.second_value,
}
}, (err, result) => {
if (err) {
throw err;
}
res.send('data updated sucessfully');
});
});
Hope you can get the point and this works for you .

I want to increment many documents in mongodb

router.put('/stockAccepted/:id', (req, res, next) =>{
stockSchema.findOneAndUpdate({_id: req.params.id}, {
$set:{
stockAccepted: req.body.stockAccepted
}
},(err, result) => {
if(err) res.json(err);
else res.json(result);
});
let stockItem = req.body.stockItem;
stockItem.forEach((element) => {
console.log("counter")
productSchema.update({_id: element.productId}, {
$inc:{
productQuantity: element.productQuantity
}
}
,(err, result) => {
if(err) res.json(err);
else res.json(result);
});
});
});
what is the best approach to achieve it?
This code throws an error that "headers are already sent".
How can I implement this correctly?
The problem is you are trying to send a JSON response in each db update callback. You can only send a response once for a request. You can either use Promise.all to wait for the stock and product(s) to be updated, or use Mongoose's bulkWrite feature.
The bulkWrite is more efficient as it only involves one request to the DB rather than multiple.
Here is an (untested) example of how bulkWrite could work with your code:
router.put('/stockAccepted/:id', (req, res, next) => {
stockSchema.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
stockAccepted: req.body.stockAccepted
}
}).then((result) => {
let stockItem = req.body.stockItem;
let updates = []
stockItem.forEach((element) => {
updates.push({
updateOne: {
filter: {
_id: element.productId
},
update: {
$inc: {
productQuantity: element.productQuantity
}
}
}
})
})
return productSchema.bulkWrite(updates)
}).then((result) => {
res.json(result);
}).catch((err) => {
res.json(err);
})
});
I'm using the promise returned from a DB call in mongoose rather than the callbacks, as it reduces nesting.
Unlike your version, this code will wait for stock to be updated in DB. Then it will create a bulk operation and send that to the DB. Once this is completed, it will finally return the response from the bulk operation as JSON.
Note that the final catch handler will catch errors in both the stock update, or the bulk product update.
You are iterating over stockItem with a forEach loop, nothing wrong about that.
What you probably miss here is that after handling correctly the first element you use directly the res params which is why the next iteration when you process the same logic it fails with "headers are already sent" because you indeed, already fire an http response with res.
What I suggest is to aggregate your result during the loop and only fire the res with your aggregate results as json as below :
router.put('/stockAccepted/:id', (req, res, next) =>{
stockSchema.findOneAndUpdate({_id: req.params.id}, {
$set:{
stockAccepted: req.body.stockAccepted
}
},(err, result) => {
if(err) res.json(err);
else res.json(result);
});
let stockItem = req.body.stockItem;
const results = [];
const errors = [];
stockItem.forEach((element) => {
console.log("counter")
productSchema.update({_id: element.productId}, {
$inc:{
productQuantity: element.productQuantity
}
}
,(err, result) => {
if(err) errors.push(err);
else result.push(result);
});
});
res.json({
results,
errors
});
});

Query multiple mongodb collections in node app using Mongoose async

In my node.js app I want to query multiple mongodb collections in a series using mongoose with the async plugin based on result from the first callback in the series.
So far I have this working code but I'm sure there is a better way of doing it async:
router.route('/project/:projectId')
.get(function(req, res) {
var getProjectDetails = function(cb) {
models.Project.findById(req.params.projectId, function(err, project) {
if(err)
res.send(err);
models.Media.find({'project_code' : project.code }, function(err, media) {
cb(null, {'project' : project, 'media': media})
});
})
}
async.parallel([getProjectDetails], function(err, result) {
res.render('details', {data: result});
});
});
As you can see I want to find all entries from the Media collection where project_code equals code from Project collection.
How can I acvhieve this without nesting my mongoose-querys?
Why do you need async in this? I hope this will work.
router.route('/project/:projectId')
.get(function(req, res) {
models.Project.findById(req.params.projectId, function(err, project) {
if(err)
return res.send(err);
models.Media.find({'project_code' : project.code }, function(err, media) {
if(err)
return res.send(err);
return res.render('details', {data: {'project' : project, 'media': media}});
});
});
});

NodeJS / Mongoose Filter JSON

I am building a JSON API with ExpressJS, NodeJS and Mongoose:
Input -> id:
app.get('/folder/:id', function (req, res){
return Cars.find({reference: req.params.id}, function (err, product) {
if (!err) {
console.log(product);
return res.send(product);
} else {
return console.log(err);
}
});
});
It shows well the JSON:
[{"_id":"B443U433","date":"2014-08-12","reference":"azerty","file":"087601.png","
....:.
{"_id":"HGF6789","date":"2013-09-11","reference":"azerty","file":"5678.pnf","
...
I just want to display the _id in the JSON, so it is good when I have lots of data.
How I can do that? Something like a filter?
You can chain calls to select and lean to retrieve just the fields you want from the docs you're querying:
app.get('/folder/:id', function (req, res){
return Cars.find({reference: req.params.id}).select('_id').lean().exec(
function (err, product) {
if (!err) {
console.log(product);
return res.send(product);
} else {
return console.log(err);
}
});
});
You would have to iterate over your "products" object to obtain the ids
Something like this:
(Disclaimer: I haven't tested this)
app.get('/folder/:id', function (req, res){
return Cars.find({reference: req.params.id}, function (err, product) {
if (!err) {
console.log(product);
var ids = new Array();
for(var i = 0; i < product.length; i++){
ids.push(product[i]._id);
}
return res.send(JSON.stringify(ids));
} else {
return console.log(err);
}
});
});
--Edit
Also, "products" may already be a JSON string. You may want to parse it before looping.
product = JSON.parse(product);
Other answers are true but I think it's better to limit data in mongoose like this :(it's same as mongo shell commands)
app.get('/folder/:id', function (req, res){
Cars.find({reference: req.params.id} ,{ _id : true } ,function (err, product) {
if (!err) {
console.log(product);
} else {
console.log(err);
}
});
});

Resources