Determine when async execution is finished - javascript

I have to process an array of entries that requires to perform to async tasks for each file entry: getFile and uploadResult both are async task. My question is how can I know when the array doc.entries is being processed using an async library like asyncjs. The code below is just an illustration of what I am trying to accomplish.
var doc = {
version: '1.7',
entries: [{
date: '11/11/10',
files: [{
name: 100,
executable: false
},
{
name: 101,
executable: false
}]
},
{
date: '11/12/10',
files: [{
name: 200,
executable: false
},
{
name: 201,
executable: false
}]
},
{
date: '11/13/10',
files: [{
name: 300,
executable: false
}]
},
{
date: '11/14/10',
files: [{
name: 400,
executable: false
}]
}]
};
doc.entries.map(function(entry){
entry.files.map(function(file){
getFile(file, function(err, result){
if(err){
throw Error(err)
}
uploadResult(result, function(err, status){
WriteOnDb(file.name, status, function(err, result){ ... });
});
})
});
});
How can I know when the last file is being store on the db and then do something else?
Thanks.

The easiest way is to use promises, or better observables, but you do it with callbacks too - for example you can count how many tasks are in total and how many was finished:
var total = doc.entries
.map(function (entry) {
return entry.files.length;
})
.reduce(function (x, acc) {
return acc + x
}, 0);
var finished = 0;
function finishCallback(err) {
if (err === null) {
/// all async tasks are finished;
}
}
doc.entries.map(function (entry) {
entry.files.map(function (file) {
getFile(file, function (err, result) {
if (err) {
finishCallback(err);
} else {
uploadResult(result, function (err, status) {
WriteOnDb(file.name, status, function (err, result) {
if (err) {
finishCallback(err);
} else {
finished += 1;
if (finished === total) finishCallback(null);
}
});
});
}
})
});
});

Related

How to use group by and sum in Mongoose?

I have a JSON array that contains objects like this one
{
"InvoiceNo": "FA 2019/1",
"Period": "01",
"DocumentTotals": {
"TaxPayable": "26.94",
"NetTotal": "117.16",
"GrossTotal": "144.10"
},
"WithholdingTax": {
"WithholdingTaxAmount": "0.00"
}
},
I want to sum the GrossTotal of the various objects and group it by Period.
I tried with the following code:
saftFileController.revenuePerMonth = function (req, res) {
Saft.find().exec(function (err, fileContent) {
if (err) {
console.log(err);
} else {
const JSONObject = JSON.parse(JSON.stringify(fileContent));
const sales = JSONObject[3].SalesInvoices.Invoice;
const revenuePerMonth = Saft.aggregate([
{
$group: {
Period: sales.Period,
revenue: {
$sum: "$GrossTotal",
},
},
},
]);
res.json({ revenue: revenuePerMonth });
}
});
};
But the output wasn´t the desired one. What I am doing wrong? This was the output:
{
"revenue": {
"_pipeline": [
{
"$group": {
"revenue": {
"$sum": "$GrossTotal"
}
}
}
],
"options": {}
}
}
Here are some printscreens of my Database
https://snipboard.io/QOfiYz.jpg
https://snipboard.io/72LSRC.jpg
Did this and now returns the Period but it is ignoring the sum because it is a string type.How can I convert?
saftFileController.revenuePerMonth = function (req, res) {
Saft.aggregate([
{
$group: {
_id: "$SalesInvoices.Invoice.Period",
revenue: {
$sum: "SalesInvoices.Invoice.DocumentTotals.GrossTotal",
},
},
},
]).exec(function (err, fileContent) {
if (err) {
console.log("Error: ", error);
} else {
res.json({ revenuePerMonth: fileContent });
}
});
};
You should pass the MongoDB expression what you want to group it by as _id in the $group stage (documentation). The $sum expression should contain the full path as well.
$group: {
_id: '$Period',
revenue: {
$sum: "$DocumentTotals.GrossTotal",
},
},
aggregate accepts a callback, or returns a Promise just like when you use Saft.find()
Saft.aggregate([/* ... */]).exec(function (err, result) {
/* use result here */
})

Mongoose $slice and get orginal size array

I'm currently trying to get the total amount of items in my News object, and return a slice of the items as objects.
I found out how to use the $slice operator in my query, but I don't know how to get the original size of the array of items.
The code I'm currently using in NodeJS:
if (req.query.limit) {
limit = 5;
}
News.findOne({ connected: club._id }, {items: {$slice: limit}}).exec(function (err, news) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else if (!news || news.items.length === 0) {
res.jsonp([]);
} else {
const returnObj = { items: [], totalNumber: 0 };
const items = news.items.sort(function (a, b) {
return b.date - a.date
});
res.jsonp({
items: items,
totalNumber: news.items.length
});
}
});
The Mongo model:
var mongoose = require('mongoose'),
validator = require('validator'),
Schema = mongoose.Schema;
var NewsSchema = new Schema({
connected: {
type: Schema.Types.ObjectId,
required: 'Gelieve een club toe te wijzen.',
ref: 'Club'
},
items: [{
userFirstName: String,
action: String,
date: Date,
targetName: String
}],
created: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('News', NewsSchema);
How would I do this efficiently?
Thanks!
EDIT: final code which works:
News.aggregate([{
$match: {
connected: club._id
}
}, {
$project: {
totalNumber: {
$size: '$items'
},
items: {
$slice: ['$items', limit]
}
}
}
]).exec(function (err, news) {
console.log(news);
if (!news || news[0].items.length === 0) {
res.jsonp([]);
} else {
res.jsonp(news[0]);
}
});
You cannot have both information at once using find and $slice.
The soluce you have :
Use aggregate to return the count and only the sliced values.
Like :
[{
$project: {
count: {
$size: "$params",
},
params: {
$slice: ["$params", 5],
},
},
}]
To help you out making aggregate, you can use the awesome mongodb-compass software and its aggregate utility tool.
Use a find without $slice, get the number of item there, and then slice in javascript the array before returning it.
EDIT :
[{
$sort: {
'items.date': -1,
},
}, {
$project: {
count: {
$size: "$items",
},
params: {
$slice: ["$items", 5],
},
},
}]

Promise.map returning a broken object

i'm using the Promise.map to make some operations in an array of objects, which object represents a file.
Inside i'm mapping this files, and when i finish, i want return a object just like this:
{
output: {
order_by: 'Competência',
output_data: 'TEST.dados.csv',
output_result: 'TEST.resultado.csv'
},
files: [{
full_name: 'Relat¢rio RE (1) 02.2014.pdf',
mime: 'application/pdf',
pages: [],
status: [],
text: ''
}, {
exists: false,
extension: 'pdf',
full_name: 'Relat¢rio RE (1) 02.2014.pdf',
mime: 'application/pdf',
pages: [],
status: [],
text: ''
}]
}
And when i use my code like this:
new Promise(function(resolve, reject) {
return Promise.map(files, function(file) {
var map;
result.push(mapping_files(file));
if (result.length === files.length) {
map = {
output: mapping_output(output),
files: result
};
return resolve(map);
}
}, {
concurrency: 3000
});
});
With resolve, reject.. the output will be PERFECT.. but if i delete the new Promise:
return Promise.map(files, function(file) {
var map;
result.push(mapping_files(file));
if (result.length === files.length) {
return map = {
output: mapping_output(output),
files: result
};
}
}, {
concurrency: 3000
});
The output will be like this:
[undefined, {
output: {
order_by: 'Competência',
output_data: 'TEST.dados.csv',
output_result: 'TEST.resultado.csv'
},
files: [
[Object],
[Object]
]
}]
Why this? I MUST return an array when i'm using the Promise.map?
Thanks.

Javascript variable scope when mongoose query

I'm working with node.js, mongoose and foursquare API.
foursquare.getVenues(params, function(err, venues) {
if(err) return res.json(JSON.stringify({status: 'error', returnData: err}));
// variable initialization
var rooms = [];
var vanueItem;
// iterate foursquare return list (venue item)
venues.response.venues.forEach(function(item) {
Room.aggregate(
[
{ "$group": {
"_id": '$mobileUser.genderType',
"genderTypeCount": { "$sum": 1 }
}}
],
function(err,result) {
if(err) return res.json(JSON.stringify({status: 'error', returnData: err}));
// build it to return after
vanueItem =
{
id: item.id,
name: item.name,
description: item.description,
contact: item.contact.formattedPhone,
lat: item.location.lat,
lng: item.location.lng,
distance: item.location.distance,
city: item.location.city
};
// insert it into venue array
rooms.push(vanueItem);
}
);
});
return res.json(JSON.stringify({ status: 'success', returnData: rooms }));
});
I'm having a problem with rooms array. When I remove the 'Room.aggregate' query, works fine (all rooms was ok), but when I use the aggregate, the return function gives me empty room.
I already tried remove var from 'var rooms = [];'
Room.aggregate is asynchronous function, if you want iterate over asynchronous function you can use async library, like this
var async = require('async');
foursquare.getVenues(params, function(err, venues) {
if (err) return res.json(JSON.stringify({
status: 'error',
returnData: err
}));
var rooms = [];
var vanueItem;
async.each(venues.response.venues, function (item, next) {
Room.aggregate(
[{
"$group": {
"_id": '$mobileUser.genderType',
"genderTypeCount": {
"$sum": 1
}
}
}],
function(err, result) {
if (err) {
return next(err);
}
// build it to return after
vanueItem = {
id: item.id,
name: item.name,
description: item.description,
contact: item.contact.formattedPhone,
lat: item.location.lat,
lng: item.location.lng,
distance: item.location.distance,
city: item.location.city
};
rooms.push(vanueItem);
next(null);
}
);
}, function (err) {
if (err) {
return res.json(JSON.stringify({
status: 'error',
returnData: err
}));
}
return res.json(JSON.stringify({
status: 'success',
returnData: rooms
}));
});
});

Mongodb POST response returns 0?

I got it to work on accident with out adding a res.json(doc) but I found that when I make a POST request I need to send a response and ever since I added that response I get 0 or null?
add: function(req, res) {
//var newPlayer = new models.Team({ _id: req.params.tid }, { players: req.body });
models.Team.update({ _id: req.params.tid }, { $addToSet: { players: req.body} }, function(err, newPlayer){
if (err) {
res.json({error: 'Error.'});
} else {
res.json(newPlayer);
}
});
},
Also tried with findOneAndUpdate but my POST request is showing 0 or null for the response.
I am updating an array of objects inside a collection, it's nested. Here is the SCHEMA just to be clear.
var Team = new Schema({
team_name: { type: String },
players: [
{
player_name: { type: String },
points: { type: Number },
made_one: { type: Number },
made_two: { type: Number },
made_three: { type: Number },
missed_one: { type: Number },
missed_two: { type: Number },
missed_three: { type: Number },
percentage: { type: Number },
assists: { type: Number },
rebounds: { type: Number },
steals: { type: Number },
blocks: { type: Number },
fouls: { type: Number },
feed: { type: String },
facebook_id: { type: Number }
}
]
});
So my question is does anyone have any idea why I am getting that response 0?
The update method does not return the document in the response. The callback arguments are (err, numAffected) where numAffected is the number of documents touched by the "update" statement, which can possibly do "bulk" ( or multi ) processing.
What you want is findByIdAndUpdate() or findOneAndUpdate(). These methods return the either the original document or the modified document, according to the arguments you give.
add: function(req, res) {
models.Team.findByIdAndUpdate(
req.params.tid,
{ $addToSet: { players: req.body } },
function(err, newPlayer){
if (err) {
res.json({error: 'Error.'});
} else {
res.json(newPlayer);
}
}
);
},

Categories

Resources