I'm trying to take some random values from Mongo using mongoose and push it to an array.
But the array is empty outside the function:
exports.Run = (req, res) => {
var response = {}
var you = "you"
response[you] = [];
Model.estimatedDocumentCount().exec(function (err, count) {
for (let i = 0; i < 8; i++) {
let random = Math.floor(Math.random() * count)
Model.findOne()
.skip(random)
.exec( function (err, result) {
response[you].push(result);
console.log(response); // Array is increased each iteration
})
}
})
console.log(response); // Array is empty here
res.status(200).send(response);
};
Please, how to fix that?
Thanks in advance.
Check this way. Hope it should work
exports.Run =async (req, res) => {
var response = {}
var you = "you"
response[you] = [];
var result = await Model.estimatedDocumentCount().exec(function (err, count) {
for (let i = 0; i < 8; i++) {
let random = Math.floor(Math.random() * count)
await Model.findOne()
.skip(random)
.exec( function (err, result) {
response[you].push(result);
console.log(response);
})
}
return response;
})
console.log(result); // Array is empty here
res.status(200).send(result);
};
I found the solution and it works for me:
exports.Run = (req, res) => {
ex1( function(rp){
console.log(rp);
res.status(200).send(rp);
});
}
function ex1(callback) {
var response = {}
var you = "you"
response[you] = [];
Model.estimatedDocumentCount().exec(async function (err, count) {
for (let i = 0; i < 8; i++) {
let random = Math.floor(Math.random() * count)
var doc = await Model.findOne().skip(random).exec();
response[you].push(doc);
}
callback(response);
})
}
Related
This is the async function:
async function getpackages(conn, childId, callback) {
var childId = childId;
var request6 = new sql.Request(conn);
var packageQuery = "select OrderId,ChildID from dbo.tbl_Scheduler where NoOfMealsLeft>0 and ChildId=" + childId;
await request6.query(packageQuery, function (err, packagelist) {
if (!err && packagelist.recordsets.length > 0) {
console.log("Error:" + err + "Result:" + util.inspect(packagelist.recordsets[0]));
var orderdetail_ = [];
for (i = 0; i < packagelist.recordsets[0].length; i++) {
orderdetail_.push(packagelist.recordsets[0][i].OrderId);
}
console.log("-->" + orderdetail_);
callback(null, packagelist.recordsets[0]);
} else if (packagelist.recordsets.length < 1) {
callback("Not a valid id input", null);
}
});
};
I need to call the orderdetails_ array in the query. The array contains four data and I need to iterate over 4 data one by one, using the or in the SQL query.
module.exports.newscheduledmeal = function (req, res, next, callback) {
let entered_date = req.query.date;
let childId = req.query.id;
let current_date = new Date().toISOString().slice(0, 10);
if (entered_date < current_date) {
return callback('Please enter date more than or equal to current date.', null);
} else
var conn = new sql.ConnectionPool(dbConfig);
try {
conn.connect().then(function () {
var request = new sql.Request(conn);
getpackages(conn, childId, function (err, orderid) {
if (err) {
callback(err, null);
} else
var PackageidQuery = "select PackageId from dbo.tbl_Order where OrderId=";
request.query(PackageidQuery, function (err, packagelist) {
if (!err) {
conn.close();
callback(null, packagelist.recordsets);
} else {
conn.close();
callback("Error", null);
}
});
});
});
} catch (err) {
console.log("Exception occured:" + err);
conn.close();
callback(err, null);
}
};
I want to get the details of the array which is in getpackages to be used in the module section and specifically in the SQL query section.
I am trying to read some data from a file and store it in a database.
This is part of a larger transaction and I need the returned ids for further steps.
async parseHeaders(mysqlCon, ghID, csv) {
var self = this;
var hIDs = [];
var skip = true;
var idx = 0;
console.log("Parsing headers");
return new Promise(async function(resolve, reject) {
try {
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream(csv)
});
await lineReader.on('close', async function () {
console.log("done: ", JSON.stringify(hIDs));
resolve(hIDs);
});
await lineReader.on('line', async function (line) {
line = line.replace(/\"/g, '');
if (line.startsWith("Variable")) { //Variable,Statistics,Category,Control
console.log("found variables");
skip = false; //Ignore all data and skip to the parameter description.
return; //Skip also the header line.
}
if (!skip) {
var data = line.split(",");
if (data.length < 2) { //Variable section done return results.
console.log("Found sub?",line);
return lineReader.close();
}
var v = data[0];
var bidx = data[0].indexOf(" [");
if (bidx > 0)
v = data[0].substring(0, bidx); //[] are disturbing mysql (E.g.; Air temperature [�C])
var c = data[2];
hIDs[idx++] = await self.getParamID(mysqlCon, ghID, v, c, data);//, function(hID,sidx) { //add data in case the parameter is not in DB, yet.
}
});
} catch(e) {
console.log(JSON.stringify(e));
reject("some error occured: " + e);
}
});
}
async getParamID(mysqlCon,ghID,variable,category,data) {
return new Promise(function(resolve, reject) {
var sql = "SELECT ID FROM Parameter WHERE GreenHouseID="+ghID+" AND Variable = '" + variable + "' AND Category='" + category + "'";
mysqlCon.query(sql, function (err, result, fields) {
if(result.length === 0 || err) { //apparently not in DB, yet ... add it (Acronym and Machine need to be set manually).
sql = "INSERT INTO Parameter (GreenHouseID,Variable,Category,Control) VALUES ("+ghID+",'"+variable+"','"+category+"','"+data[3]+"')";
mysqlCon.query(sql, function (err, result) {
if(err) {
console.log(result,err,this.sql);
reject(err);
} else {
console.log("Inserting ",variable," into DB: ",JSON.stringify(result));
resolve(result.insertId); //added, return generated ID.
}
});
} else {
resolve(result[0].ID); //found in DB .. return ID.
}
});
});
}
The functions above are in the base class and called by the following code:
let headerIDs = await self.parseHeaders(mysqlCon, ghID, filePath);
console.log("headers:",JSON.stringify(headerIDs));
The sequence of events is that everything in parseHeaders completes except for the call to self.getParamID and control returns to the calling function which prints an empty array for headerIDs.
The console.log statements in self.getParamID are then printed afterward.
What am I missing?
Thank you
As you want to execute an asynchronous action for every line we could define a handler to do right that:
const once = (target, evt) => new Promise(res => target.on(evt, res));
function mapLines(reader, action) {
const results = [];
let index = 0;
reader.on("line", line => results.push(action(line, index++)));
return once(reader, "close").then(() => Promise.all(results));
}
So now you can solve that easily:
let skip = false;
const hIDs = [];
await mapLines(lineReader, async function (line, idx) {
line = line.replace(/\"/g, '');
if (line.startsWith("Variable")) { //Variable,Statistics,Category,Control
console.log("found variables");
skip = false; //Ignore all data and skip to the parameter description.
return; //Skip also the header line.
}
if (!skip) {
var data = line.split(",");
if (data.length < 2) { //Variable section done return results.
console.log("Found sub?",line);
return lineReader.close();
}
var v = data[0];
var bidx = data[0].indexOf(" [");
if (bidx > 0)
v = data[0].substring(0, bidx); //[] are disturbing mysql (E.g.; Air temperature [�C])
var c = data[2];
hIDs[idx] = await self.getParamID(mysqlCon, ghID, v, c, data);
}
});
I'm trying to make something for a little community I'm in. But I'm not very well versed in JavaScript and NodeJS yet. The script I'm making, is supposed to first find all possible combinations of a list of users in groups of 6.
Then I need to take each of those groups skill rating average, and compare so that I can find the two that matches the closest. This way we get two teams that can play against each other, and be somewhat balanced at least.
But my first issue is that I seem to be unable to even print out anything from the array that I'm making, and I don't understand why.
var filePath = 'data.txt';
function readFile() {
var data = [];
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream(filePath)
});
lineReader.on('line', function(line) {
var splitString = line.split(',');
var arr = {
sr: splitString[0],
role: splitString[1],
discord: splitString[3]
};
data.push(arr);
console.log(arr);
});
lineReader.on('close', () => {
return data;
});
}
function balance() {
var data = readFile();
for(var i = 0; i < data.length; i++) {
console.log(data[i]);
}
}
balance();
The output is always undefined. What am I doing wrong?
What you can do is not to change the nature of the reading function, instead change your approach.
I will give you two options.
Using callback:
var filePath = 'data.txt';
function readFile(callback) {
var data = [];
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream(filePath)
});
lineReader.on('line', function (line) {
var splitString = line.split(',');
var arr = {
sr: splitString[0],
role: splitString[1],
discord: splitString[3]
};
data.push(arr);
console.log(arr);
});
lineReader.on('close', () => {
callback(data);
});
}
function balance() {
readFile(function (data) {
for (var i = 0; i < data.length; i++) {
console.log(data[i]);
}
});
}
balance();
Using Promise:
var filePath = 'data.txt';
function readFile() {
return new Promise(function (resolve, reject) {
var data = [];
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream(filePath)
});
lineReader.on('line', function (line) {
var splitString = line.split(',');
var arr = {
sr: splitString[0],
role: splitString[1],
discord: splitString[3]
};
data.push(arr);
console.log(arr);
});
lineReader.on('close', () => {
resolve(data);
});
});
}
function balance() {
readFile().then(function (data) {
for (var i = 0; i < data.length; i++) {
console.log(data[i]);
}
}, function (error) {
});
}
balance();
Using async await
const filePath = './data.txt';
async function readFile() {
return new Promise((resolve) => {
const data = [];
const lineReader = require('readline').createInterface({
input: require('fs').createReadStream(filePath),
});
lineReader.on('line', (line) => {
const [sr, role, discord] = line.split(',');
const arr = {
sr,
role,
discord,
};
data.push(arr);
});
lineReader.on('close', () => {
resolve(data);
});
});
}
async function balance() {
const data = await readFile();
for (let i = 0; i < data.length; i += 1) {
console.log(data[i]);
}
return 1;
}
balance().then(() => { });
Using observer pattern
const { EventEmitter } = require('events');
const fs = require('fs');
class FileParser extends EventEmitter {
constructor(file) {
super();
this.file = file;
}
parse() {
const self = this;
const data = [];
const lineReader = require('readline').createInterface({
input: require('fs').createReadStream(self.file),
});
lineReader.on('line', (line) => {
const [sr, role, discord] = line.split(',');
const arr = {
sr,
role,
discord,
};
data.push(arr);
});
lineReader.on('close', () => {
self.emit('done', data);
});
return this;
}
}
const fileParser = new FileParser('./data.txt');
fileParser
.parse()
.on('done', (data) => {
for (let i = 0; i < data.length; i += 1) {
console.log(data[i]);
}
});
Refactored a bit to use the latest ES6 syntax;
The function works, gives me the required object, but only after the second request to the server. At the first request, returns an empty array, then fills it, and at the second request already issues
'use strict';
const user2 = require('../models/base');
var array = [];
var i =0;
exports.getEda = email =>
new Promise((resolve,reject) => {
user2.count().exec(function(err, count){
var random = Math.floor(Math.random() * count);
var calories = 2500;
test(calories);
function test(calories, random) {
user2.findOne().skip(random).exec(
function (err, result) {
random = Math.floor(Math.random() * count);
var stringify = JSON.stringify(result);
var jsonContent = JSON.parse(stringify);
calories = calories - jsonContent.calories;
console.log(calories);
if (calories > 0){
test(calories, random);
}
array[i] = result;
i++;
});
}
console.log(array);
})
.then(eda => resolve(array))
.catch(err => reject({ status: 500, message: 'Internal Server Error !' }))
});
You forget about return:
...
return new Promise((resolve,reject) => {
...
I'm trying to get multiple documents from MongoDB and send all the data in an array, but I'm having serious trouble understanding how this can be done with the event-driven Node.js.
The problem is that at the time dataArray.push(tempObject) is being executed, the tempObject["data"] = tempDataArray still has not been performed.
My code looks like this:
app.post('/api/charts', function(req, res) {
var names = req.body.names;
var categories = req.body.categories;
var dataArray = [];
for (i = 0; i < names.length; i++) {
var tempObject = {};
tempObject["name"] = names[i];
Company.find({ name : names[i] }, function(err, result) {
if (err) {
throw err;
}
var tempDataArray = [];
for (k = 0; k < categories.length; k++) {
var tempDataObject = {};
tempDataObject["name"] = categories[k];
tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"];
tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"];
tempDataArray.push(tempDataObject);
}
tempObject["data"] = tempDataArray;
});
dataArray.push(tempObject);
}
res.send(dataArray);
});
Any suggestions on how to properly achieve the desired result would be appreciated.
Use this library
https://github.com/caolan/async
And Using this code, your code will look like this:
var async = require("async");
app.post('/api/charts', function(req, res) {
var names = req.body.names;
var categories = req.body.categories;
var dataArray = [];
async.forEach(names, function(name, callback){
var tempObject = {};
tempObject["name"] = name;
Company.find({ name : name }, function(err, result) {
if (err) {
callback(err);
} else {
var tempDataArray = [];
for (k = 0; k < categories.length; k++) {
var tempDataObject = {};
tempDataObject["name"] = categories[k];
tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"];
tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"];
tempDataArray.push(tempDataObject);
}
tempObject["data"] = tempDataArray;
dataArray.push(tempObject);
callback();
}
});
}, function(err){
if(err){
res.send(err);
} else {
res.send(dataArray);
}
});
});
The Company.find() method takes a callback function as it's second parameter. This callback is to be called after the company data is retrieved from the database. This means it could be anywhere between a few milliseconds and a few hundered milliseconds until it is called after calling the Company.find() method. But the code directly after Company.find() will not be delayed; it will be called straight away. So the callback delay is why dataArray.push(tempObject) is always called before tempObject["data"] = tempDataArray.
On top of this the outer for loop will run synchronously and on each iteration a separate DB call will be made. This isn't ideal so we want to get this for loop into the callback. So we can do something like:
app.post('/api/charts', function(req, res) {
var names = req.body.names;
var categories = req.body.categories;
// we just do one DB query where all the data we need is returned
Company.find({ name : names }, function(err, result) {
if (err) {
throw err;
}
var dataArray = [];
// we iteratre through each result in the callback, not outside it since
// that would cause blocking due to synchronous operation
for (i = 0; i < result.length; i++) {
var tempObject = {};
tempObject["name"] = result[i].name;
var tempDataArray = [];
for (k = 0; k < categories.length; k++) {
var tempDataObject = {};
tempDataObject["name"] = categories[k];
tempDataObject["numbers"] = result[i]["data"][categories[k]]["numbers"];
tempDataObject["dates"] = result[i]["data"][categories[k]]["dates"];
tempDataArray.push(tempDataObject);
}
tempObject["data"] = tempDataArray;
dataArray.push(tempObject);
}
res.send(dataArray);
});
});
There are many approaches to abstract Nodes event driven nature such as Promises (which can be accessed either in ECMA Script 6 or a Promise library such as Bluebird, Async, etc.). But the above is a basic callback approach that is typically used in the likes of Express applications.
Simply change this :
tempObject["data"] = tempDataArray;
});
dataArray.push(tempObject);
To:
tempObject["data"] = tempDataArray;
dataArray.push(tempObject);
});