I'm currently using SailsJS framework, and i'm trying to update an array of elements using only one query in my MongoDB database.
Here is what I'm doing but records are not updated..
Incoming JSON:
{
"cars": [
{
"id": "5cb5cbd5c395a01b4c9d86da",
"latitude": "-5",
"longitude": "551"
},
{
"id": "5cb5cbd7c395a01b4c9d86db",
"latitude": "-4",
"longitude": "4421",
}
]
}
Controller:
async setLocationOfCars(req, res) {
try {
sails.log.info(controllerName + "::" + req.options.action + "() - called");
const carsLocationArray = req.body.cars;
let response = CarService.setLocationOfCars(carsLocationArray);
switch (response.status) {
case constants.RESOURCE_SUCCESSFULLY_UPDATED :
return HttpResponseService.json(200, res, constants.RESOURCE_SUCCESSFULLY_UPDATED, response.data);
default:
return HttpResponseService.internalServerError(res, response);
}
} catch(err) {
return HttpResponseService.internalServerError(res, err);
}
}
Service:
async setLocationOfCars(cars) {
try {
const arrayOfIdsUpdated = [];
_.forEach(cars, async function(car){
let attributesToUpdate = {};
if (car.hasOwnProperty("latitude")) {
attributesToUpdate.latitude = car.latitude;
}
if (car.hasOwnProperty("longitude")) {
attributesToUpdate.longitude = car.longitude;
}
await Car.updateOne({
id: car.id
}).set(attributesToUpdate);
arrayOfIdsUpdated.push(gateway.id)
});
return {
status: constants.RESOURCE_SUCCESSFULLY_UPDATED,
data : arrayOfIdsUpdated
};
} catch (err) {
return {
status : constants.DATABASE_ERROR,
name : err.name ? err.name : "",
message: err.message ? err.message: "",
stack : err.stack ? err.stack : "",
code : err.code ? err.code : "",
};
}
}
Make sure you are await
ing the response from your service.
let response = await CarService.setLocationOfCars(carsLocationArray);
I'd probably replace _.forEach
with a regular for
loop. In your case something like the following, ensuring that gateway.id
is actually defined & the value you want to return to the calling code (there's no reference to gateway
in your question).
for (let car of cars) {
let attributesToUpdate = {};
if (car.hasOwnProperty("latitude")) {
attributesToUpdate.latitude = car.latitude;
}
if (car.hasOwnProperty("longitude")) {
attributesToUpdate.longitude = car.longitude;
}
await Car.updateOne({
id: car.id
}).set(attributesToUpdate);
// Where does gateway.id come from?
arrayOfIdsUpdated.push(gateway.id)
}
That said, this will perform cars.length
amount of database queries, not "using only one query".
Using an async
callback inside forEach like doesn't look like it will do what you probably want it to do. It's likely that your arrayOfIdsUpdated
will be returned to your controller code empty, before any of the calls to updateOne
run.
Take this for example:
const _ = require('lodash');
function doNothing() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 0)
});
}
var foos = [1, 2, 3, 4, 5, 6]
_.forEach(foos, async (foo) => {
await doNothing();
console.log(`Finished #${foo}`);
});
console.log('Done!')
Running it gives the output
Done!
Finished #1
Finished #2
Finished #3
Finished #4
Finished #5
Finished #6
Note how the "Done!" gets logged to the console before the stuff await
ing a response in the forEach callback.