I have a "Place your order" button that calls /orderRegistration, which in turn updates the quantity of product in stock according to the order and sends the confirmed order to the email
const orderStatus = ['Confirmed', 'Not confirmed'];
router.post('/orderRegistration', (req, res) => {
if (req.session.successAuthentication === true &&
req.session.isWorker === false) {
conn.query(`SELECT orders.id,
products.product_id,
products.product_name,
products.product_amount,
order_product.count
FROM orders INNER JOIN order_product
ON orders.id = order_product.order_id INNER JOIN products
ON order_product.product_id = products.product_id
WHERE orders.id IN(
SELECT id
FROM orders
WHERE user_id=${req.session.userId}
AND status = '${orderStatus[1]}')
AND orders.status = '${orderStatus[1]}';`, (err, selProductId) => {
if (err) {throw err;}
if (selProductId.length > 0) {
let dateNow = new Date();
let prepDate = {
day: (dateNow.getDate() < 10) ? `0${dateNow.getDate()}` : dateNow.getDate(),
month: ( dateNow.getMonth() + 1 < 10) ? `0${dateNow.getMonth() + 1}` : dateNow.getMonth() + 1,
year: dateNow.getFullYear(),
hours: (dateNow.getHours() < 10) ? `0${dateNow.getHours()}` : dateNow.getHours(),
minutes: (dateNow.getMinutes() < 10) ? `0${dateNow.getMinutes()}` : dateNow.getMinutes()
};
let orderDate = `${prepDate.day}.${prepDate.month}.${prepDate.year} ${prepDate.hours}:${prepDate.minutes}`;
let productsInOrderHTML = '';
let totalAmount = 0;
for (let i = 0; i < selProductId.length; i++) {
conn.query(`UPDATE products
SET products.product_count_stock = products.product_count_stock - ${selProductId[i].count}
WHERE products.product_id = ${selProductId[i].product_id}`, err => {
if (err) {throw err;}
productsInOrderHTML += `<tr>
<td>
${selProductId[i].product_name}
</td>
<td>
${selProductId[i].count}
</td>
<td>
${selProductId[i].product_amount}
</td>
</tr>`;
totalAmount += selProductId[i].count *
selProductId[i].product_amount;
if(i === selProductId.length - 1) {
console.log('totalAmount: ' + totalAmount);
}
});
}
} else {
res.send('error');
}
});
} else {
res.send('error');
}
});
But because the calls are asynchronous, sometimes the loop does not have time to update all the products and occurs
if(i === selProductId.length - 1) {
console.log('totalAmount: ' + totalAmount);
}
that is, sometimes totalAmount may have time to update all products, and sometimes it does not, and it turns out that totalAmount will not be equal to the cost that the user ordered the product for.
How do I rewrite the query or refactor it so that this doesn't happen again
P.S. Sorry for the English, I translated this through a translator, because I speak Russian.I may also have missed something, so correct me if necessary
The queries to the database are asynchronous which means the eventloop of nodejs will pass them to the queue and the loop will continue so you have to await for them
try this
const orderStatus = ['Confirmed', 'Not confirmed'];
router.post('/orderRegistration', async (req, res) => {
if (req.session.successAuthentication === true &&
req.session.isWorker === false) {
await conn.promise().query(`SELECT orders.id,
products.product_id,
products.product_name,
products.product_amount,
order_product.count
FROM orders INNER JOIN order_product
ON orders.id = order_product.order_id INNER JOIN products
ON order_product.product_id = products.product_id
WHERE orders.id IN(
SELECT id
FROM orders
WHERE user_id=${req.session.userId}
AND status = '${orderStatus[1]}')
AND orders.status = '${orderStatus[1]}';`, async (err, selProductId) => {
if (err) {
throw err;
}
if (selProductId.length > 0) {
let dateNow = new Date();
let prepDate = {
day: (dateNow.getDate() < 10) ? `0${dateNow.getDate()}` : dateNow.getDate(),
month: (dateNow.getMonth() + 1 < 10) ? `0${dateNow.getMonth() + 1}` : dateNow.getMonth() + 1,
year: dateNow.getFullYear(),
hours: (dateNow.getHours() < 10) ? `0${dateNow.getHours()}` : dateNow.getHours(),
minutes: (dateNow.getMinutes() < 10) ? `0${dateNow.getMinutes()}` : dateNow.getMinutes()
};
let orderDate = `${prepDate.day}.${prepDate.month}.${prepDate.year} ${prepDate.hours}:${prepDate.minutes}`;
let productsInOrderHTML = '';
let totalAmount = 0;
for (let i = 0; i < selProductId.length; i++) {
await conn.promise().query(`UPDATE products
SET products.product_count_stock = products.product_count_stock - ${selProductId[i].count}
WHERE products.product_id = ${selProductId[i].product_id}`, err => {
if (err) {
throw err;
}
productsInOrderHTML += `<tr>
<td>
${selProductId[i].product_name}
</td>
<td>
${selProductId[i].count}
</td>
<td>
${selProductId[i].product_amount}
</td>
</tr>`;
totalAmount += selProductId[i].count *
selProductId[i].product_amount;
if (i === selProductId.length - 1) {
console.log('totalAmount: ' + totalAmount);
}
});
}
} else {
res.send('error');
}
});
} else {
res.send('error');
}
});
Related
I have a function called parsedata in my node.js file which is called when a user logs in. After parsedata() is called, the server switches to a new screen. However, this only works every other time. I put an asynchronous wait in between, which made it work about 90% of the time but I am just wondering why it is doing this. I believe it has something to do with all of the helper functions which are being used but I am not completely sure. Any info or help would be greatly appreciated!
app.post("/login.html", urlencodedParser, async (req, res) => {
await parseData();
//await sleep(750);
res.redirect(__dirname + "/homescreen.html");
});
async function parseData() {
let dates = await findCommon();
let maxStreak = await getMaxStreak(dates);
}
async function findCommon() {
var dates = new Set();
var data = await fs.readFile(__dirname + "/mem.txt", "utf8", (err, data) => {
if (err) {
console.error(err);
return;
}
return data;
});
for (let i = 0; i < data.length; i++) {
if (data[i] === "*" && i + mostRecentName.length < data.length) {
if (data.slice(i + 1, i + mostRecentName.length + 1) == mostRecentName) {
while (data[i] != "\n") {
i++;
}
if (i < data.length - 1) {
i++;
}
while (data[i] != "*" && i < data.length) {
let curr = "";
let count = 10;
while (count > 0) {
count--;
curr += data[i];
i++;
}
while (data[i] != "\n") {
i += 1;
}
if (i < data.length - 1) {
i++;
}
dates.add(curr);
}
}
}
}
dates = Array.from(dates);
dates = await bubbleSort(dates);
return dates;
}
async function getMaxStreak(dates) {
let today = new Date();
let year = today.getFullYear().toString();
let month = (today.getMonth() + 1).toString();
let day = today.getDate().toString();
if (month.length == 1) {
month = "0" + month;
}
if (day.length == 1) {
day = "0" + day;
}
let testDate = year + "-" + month + "-"+ day;
if (!(testDate in dates)) {
dates.push(testDate);
}
let streak = 1;
for (let i = dates.length - 1; i > 0; i--) {
let options;
if (i == dates.length - 1) {
options = await convert(testDate);
} else {
options = await convert(dates[i]);
}
if (dates[i - 1] == options[0] || dates[i - 1] == options[1] || dates[i - 1] == options[2]) {
streak++;
} else {
return streak;
}
}
return streak;
}
async function convert(date) {
let option1Day = (parseInt(date.slice(8, 10)) - 1).toString();
if (option1Day.length == 1) {
option1Day = "0" + option1Day;
}
let option2Month = (parseInt(date.slice(5, 7)) - 1).toString();
if (option2Month.length == 1) {
option2Month = "0" + option2Month;
}
let option2Day = "30";
let option3Day = "31";
let option1 = date.slice(0, 8) + option1Day;
let option2 = date.slice(0, 5) + option2Month + "-" + option2Day;
let option3 = date.slice(0, 5) + option2Month + "-" + option3Day;
return [option1, option2, option3];
}
It has something with the macro and micro tasks.Your code has the same result with the following codes:
new Promise((resolve, reject) => {
findCommon().then(dates => {
getMaxStreak(dates).then(maxStreak => {})
})
resolve()
})
.then(() => {
res.redirect(__dirname + "/homescreen.html")
})
res.redirect will be added into the micro task queue;
then, getMaxStreak will be added into the micro task queue too.
finally, we will take out the first task of the micro task queue to execute, yes, it's res.redirect, not getMaxStreak.
Here are two variables whose values are unexpected for me I can't get a way around it. Whenever I try to push message object in array inside a loop even after the variable being in global scope of the loop the value of variable currentUsersMessages is an empty array, similarly targetUserName is returning undefined however I can access value inside the callback function.
let AccountsCollection = require('../models/account')
let {PrivateChat} = require('../models/chat.model')
let messageFetchingLimit = 10;
function standardMessage(message){
let developedMessage = {
content : message.content,
content_received : message.content_received,
content_seen : message.content_seen,
sending_user : message.sending_user,
receiving_user : message.receiving_user,
delivery_date : message.delivery_date,
delivery_time : message.delivery_time,
}
if(message.attachment !== undefined){
developedMessage.attachment_url = `http://localhost:5000/chats/media/${message.attachment.file_name}`
developedMessage.attachment_type = message.attachment.file_type
}
return developedMessage;
}
router.get('/' , (req, res) => {
let targetUserName;
// Here We Will Find Target User Name to whom the logged in user is talking
AccountsCollection.findOne({identifier : {publicKey : req.query.targetUserPublicKey}} , (err,success) => {
if(success){
targetUserName = success.name
}
else {
return null;
}
})
// Here we will find and send previous chats of these chatting users
const previousChats = []
let currentUserMessages = []
PrivateChat.findOne({ $and : [{'template.users' :{$in : req.query.userPublicKey}} , {'template.users' : {$in : req.query.targetUserPublicKey}}]}, async (err,usersChatObject) => {
if (usersChatObject) {
const userPreviousChats = usersChatObject.template.messages
let previousMessageCount = parseInt(req.query.nfmr)
if(userPreviousChats.length > 0){
//? IF Messages Are Less Than 10
if((userPreviousChats.length - previousMessageCount) <= messageFetchingLimit ){
console.log('messages are less than 10')
for(let index = userPreviousChats.length - (previousMessageCount + 1) ; index >= 0; index--){
let message = await standardMessage(userPreviousChats[index])
currentUserMessages.push(message)
console.log(message)
}
console.log(currentUserMessages)
}
//? IF Messages are not available
else if (userPreviousChats.length < 1 || userPreviousChats.length == -1){
res.json({messageCount : 0})
}
//? Otherwise if Messages are available and more than or equals to 10
else {
let numberOfMessages = userPreviousChats.length - 1
let messageLeftToShow = numberOfMessages - previousMessageCount
if(messageLeftToShow <= 10){
for (let index = messageLeftToShow; index >= 0; index--) {
let message = standardMessage(userPreviousChats[index])
currentUserMessages.push(message)
}
}
else {
// If messages are more than 10
console.log(messageLeftToShow - 10);
for (let index = messageLeftToShow; index > messageLeftToShow - 10; index--) {
let message = standardMessage(userPreviousChats[index])
currentUserMessages.push(message)
}
}
}
previousChats.push({
userPublicKey : req.query.userPublicKey,
targetUserPublicKey : req.query.targetUserPublicKey,
targetName : targetUserName,
serverSentMessageCount : `${previousMessageCount + (currentUserMessages.length)}`,
messages : currentUserMessages
})
console.log(previousChats)
res.json(previousChats)
}
else {
res.json({error : "No Previous Messages Are Available"})
}
}
else {
console.log(err);
res.status(404).json({ error: 'Messages Record Is Not Available'})
}
})
})
The problem here are that both AccountsCollection.findOne and PrivateChat.findOne are asyncronous
it's easier if you handle it with async/await instead of callback like this
let AccountsCollection = require('../models/account')
let {
PrivateChat
} = require('../models/chat.model')
let messageFetchingLimit = 10;
router.get('/', async(req, res) => {
let targetUserName;
// Here We Will Find Target User Name to whom the logged in user is talking
try {
targetUserName = await AccountsCollection.findOne({
identifier: {
publicKey: req.query.targetUserPublicKey
}
})
} catch (e) {
targetUsername = null
}
// Here we will find and send previous chats of these chatting users
const previousChats = []
let currentUserMessages = []
try {
const userChatObject = await PrivateChat.findOne({
$and: [{
'template.users': {
$in: req.query.userPublicKey
}
}, {
'template.users': {
$in: req.query.targetUserPublicKey
}
}]
})
const userPreviousChats = usersChatObject.template.messages
let previousMessageCount = parseInt(req.query.nfmr)
if (userPreviousChats.length > 0) {
//? IF Messages Are Less Than 10
if ((userPreviousChats.length - previousMessageCount) <= messageFetchingLimit) {
console.log('messages are less than 10')
for (let index = userPreviousChats.length - (previousMessageCount + 1); index >= 0; index--) {
let message = await standardMessage(userPreviousChats[index])
currentUserMessages.push(message)
console.log(message)
}
console.log(currentUserMessages)
}
//? IF Messages are not available
else if (userPreviousChats.length < 1 || userPreviousChats.length == -1) {
res.json({
messageCount: 0
})
return;
}
//? Otherwise if Messages are available and more than or equals to 10
else {
let numberOfMessages = userPreviousChats.length - 1
let messageLeftToShow = numberOfMessages - previousMessageCount
if (messageLeftToShow <= 10) {
for (let index = messageLeftToShow; index >= 0; index--) {
let message = standardMessage(userPreviousChats[index])
currentUserMessages.push(message)
}
} else {
// If messages are more than 10
console.log(messageLeftToShow - 10);
for (let index = messageLeftToShow; index > messageLeftToShow - 10; index--) {
let message = standardMessage(userPreviousChats[index])
currentUserMessages.push(message)
}
}
}
previousChats.push({
userPublicKey: req.query.userPublicKey,
targetUserPublicKey: req.query.targetUserPublicKey,
targetName: targetUserName,
serverSentMessageCount: `${previousMessageCount + (currentUserMessages.length)}`,
messages: currentUserMessages
})
console.log(previousChats)
res.json(previousChats)
} else {
res.json({
error: "No Previous Messages Are Available"
})
}
} catch (err) {
console.log(err);
res.status(404).json({
error: 'Messages Record Is Not Available'
})
}
})
I have a firebase cloud function that is unable to finish executing. I suspect my code can be dramatically improved but I'm not quite sure how.
I've made the query as specific as possible to try and reduce the number of documents required to iterate through but that didn't solve the issue.
I get a Deadline Exceeded error which I suspect is due to the fact that I'm iterating through so many documents and trying to update them.
I increased the timeout (9 minutes)and memory allocation (2GB) in Google cloud console but that didn't help either.
exports.updatePollWinner = functions.runWith(runtimeOpts).firestore.document('triggerAccuracyCalculation/{id}').onCreate(trigger => {
const week = trigger.get('week');
const scoringTags = ["STD", "0.25PPR", "0.5PPR", "PPR", "0.10PPC", "0.25PPC", "0.5PPC", "4PTPASS", "5PTPASS", "6PTPASS", "-2INT", "TEPREMIUM"]
let winningChoiceIds = [];
let totalPollIds = [];
return db.collection("polls").where("sport", "==", 1).where("week", "==", week).where("pollType", "==", "WDIS").get()
.then((querySnapshot) => {
console.log("A");
querySnapshot.forEach((doc) => {
totalPollIds.push(doc.id);
let pollData = doc.data();
// extract relevant scoring tags
let tags = pollData.tags.filter(tag => scoringTags.includes(tag.code)).map(tag => tag.code);
// if no scoring setting is tagged, then use STD - determine what STD is
// extract player from each option
let winner = {score: 0, choice: {}, choiceId: null};
let cnt = 0;
pollData.choices.forEach((choice) => {
let choiceId = choice.id
let mappedChoices = choice.players.map(player => {
return { displayName: player.displayName, playerId: player.playerId, position: player.position, team: player.team }
});
// ToDo: What happens if someone posts a poll with two players in one option? This poll should be ignoree from accuracy calculation
// ignmore if option has more than one player
// if (mappedChoices.length > 1) return;
const player = mappedChoices[0]
// We can't score defense
if (player.position === "DEF") {
return;
}
const playerId = player.playerId;
// Make FFN API call to retrieve stats for that player in that weekconst statsEndpoint = `https://www.fantasyfootballnerd.com/service/player/json/${functions.config().ffnerd.key}${req.url}`;
const statsEndpoint = `https://www.fantasyfootballnerd.com/service/player/json/${functions.config().ffnerd.key}/${playerId}`;
const json = {"Stats": {"2019": ""}, "Player": {}};
https.get(statsEndpoint, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
const weekString = week.toString();
const fetchedStats = JSON.parse(data).Stats
if (!fetchedStats) return;
const response = fetchedStats["2019"]
if (!response) return;
// TODO SCORE KICKERS AND DEFENSES
const stats = response[weekString];
let score = 0;
stats["recYards"] ? score += parseInt(stats["recYards"]) / 10 : false
stats["recTD"] ? score += parseInt(stats["recTD"]) * 6 : false
stats["rushYards"] ? score += parseInt(stats["rushYards"]) / 10 : false
stats["rushTD"] ? score += parseInt(stats["rushTD"]) * 6 : false
stats["xpMade"] ? score += parseInt(stats["xpMade"]) : false
stats["fgMade"] ? score += parseInt(stats["fgMade"]) * 3 : false
stats["kickoffRet"] ? score += parseInt(stats["kickoffRet"]) / 10 : false
stats["SackYards"] ? score -= parseInt(stats["SackYards"]) / 10 : false
stats["fumbleLost"] ? score -= parseInt(stats["fumbleLost"]) * 2 : false
// Determine winner
// ToDo: handle ties
if (score > winner.score) {
winner.score = score;
winner.choiceId = choiceId;
winner.choice = choice;
}
if (cnt>=pollData.choices.length-1){
// Save player object on the poll Document (include choice ID)
winningChoiceIds.push(winner.choiceId);
const pollDoc = db.doc(`polls/${doc.id}`);
pollDoc.update({winner: winner});
}
cnt++;
});
}).on("error", (err) => {
console.log("Error: ", err.message);
});
});
});
console.log("B");
return false;
}).then(() => {
console.log("C");
let dateToQueryAfter = new Date(new Date("08/22/19").setHours(0,0,0,0))
return db.collection("users").where("recentVote", ">", dateToQueryAfter).get()
})
.then((querySnapshot) => {
console.log("D");
const promises = [];
querySnapshot.forEach((doc) => {
const p = db.collection("votes").where("uid", "==", doc.id).where("week", "==", week).where("pollType", "==", "WDIS").get()
promises.push(p)
});
return Promise.all(promises)
})
.then((querySnapshots) => {
console.log("E");
querySnapshots.forEach((querySnapshot) => {
if (querySnapshot.docs.length <= 0) return;
const uid = querySnapshot.docs[0].data().uid
const retrieveUserDoc = db.doc(`users/${uid}`);
let correctVotes = 0;
let cnt = 0;
let totalVotes = 0;
let pollVoteIds = [];
let pollVoteIdsCorrect = [];
querySnapshot.docs.forEach((doc) => {
const voteData = doc.data();
if (totalPollIds.includes(voteData.poll)) {
pollVoteIds.push(voteData.poll)
totalVotes++;
if (winningChoiceIds.includes(voteData.choice)) {
pollVoteIdsCorrect.push(voteData.poll)
correctVotes++;
}
}
if (cnt>=querySnapshot.size-1){
console.log("Updating user ID: ", uid);
retrieveUserDoc.update({
['accuracyWeeks.week'+week]: true,
['accuracy.week'+week]: {
totalVotes: totalVotes,
correct: correctVotes,
accuracy: correctVotes/totalVotes,
correctVoteIds: pollVoteIdsCorrect,
totalVoteIds: pollVoteIds
}
});
}
cnt++;
})
});
console.log("F");
return false;
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
});
I'm trying to scan through an entire tree in my database, looking for two properties ('title' and 'id') for every item, and then I need to check if there's a linked database table below the current table, and if so, I need to perform the same actions on that table.
Once I get the whole tree, I need to insert those into a master variable that I can then import elsewhere throughout my web app.
(I'm at the point where I'm pretty sure I'm reinventing the wheel. I've searched the Sequelize documentation, but if there's a simple solution, I didn't see it.)
Here's the relevant portions of my current code (it's not fully working yet, but should give the idea).
(You can find the full project repository by clicking this link here.)
```
const buildTopicTreeFromCurrentDatabase = (callback) => {
let topicTree = [];
let isFinished = false;
const isSearchFinished = new Emitter();
console.log(`emitter created`);
isSearchFinished.on('finished', () => {
console.log(`the search is done`);
if (isFinished = true) {
callback(null, this.primaryTopicsShort)
};
});
/* need to go back and refactor -- violates DRY */
this.primaryTopicsShort = [];
PrimaryTopic.all()
.then((primaryTopics) => {
if (primaryTopics.length === 0) {
return callback('no Primary Topics defined');
}
for (let i = 0; i < primaryTopics.length; i++) {
this.primaryTopicsShort[i] = {
title: primaryTopics[i].title,
id: primaryTopics[i].id,
secondaryTopics: []
};
PrimaryTopic.findById(this.primaryTopicsShort[i].id, {
include: [{
model: SecondaryTopic,
as: 'secondaryTopics'
}]
})
.then((currentPrimaryTopic) => {
if (currentPrimaryTopic.secondaryTopics.length !== 0) {
for (let j = 0; j < currentPrimaryTopic.secondaryTopics.length; j++) {
this.primaryTopicsShort[i].secondaryTopics[j] = {
title: currentPrimaryTopic.secondaryTopics[j].title,
id: currentPrimaryTopic.secondaryTopics[j].id,
thirdTopics: []
};
SecondaryTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].id, {
include: [{
model: ThirdTopic,
as: 'thirdTopics'
}]
})
.then((currentSecondaryTopic) => {
if (currentPrimaryTopic.secondaryTopics.length - 1 === j) {
isSearchFinished.emit('finished');
}
if (currentSecondaryTopic.thirdTopics.length !== 0) {
for (let k = 0; k < currentSecondaryTopic.thirdTopics.length; k++) {
this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k] = {
title: currentSecondaryTopic.thirdTopics[k].title,
id: currentSecondaryTopic.thirdTopics[k].id,
fourthTopics: []
};
ThirdTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].id, {
include: [{
model: FourthTopic,
as: 'fourthTopics'
}]
})
.then((currentThirdTopics) => {
if (currentThirdTopics.fourthTopics.length !== 0) {
for (let l = 0; l < currentThirdTopics.fourthTopics.length; l++) {
this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k] = {
title: currentThirdTopics.fourthTopics[l].title,
id: currentThirdTopics.fourthTopics[l].id,
fifthTopics: []
}
FourthTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[l].id, {
include: [{
model: FifthTopic,
as: 'fifthTopics'
}]
})
.then((currentFourthTopics) => {
if (currentFourthTopics.fifthTopics.length !== 0) {
for (let m = 0; m < currentFourthTopics.fifthTopics.length; m++) {
this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k].fifthTopics[m] = {
title: currentFourthTopics.fifthTopics[m].title,
id: currentFourthTopics.fifthTopics[m].id
}
}
}
})
.catch((err) => {
callback(err);
});
}
}
})
.catch((err) => {
callback(err)
});
}
}
})
.catch((err) => {
callback(err);
})
}
}
})
.catch((err) => {
callback(err);
})
}
})
.catch((err) => {
callback(err);
});
};
There are a few problems with this code.
First, I need to be using a DRY and recursive solution, since the database structure may change in the future.
Second, I've played around a lot with the 'finished' emitter, but I haven't figured out yet how to place it so that the event is emitted at the end of searching the database, and also so that I don't cycle back through the database multiple times.
I've been working on the following recursive solution, but the hours keep stretching by and I don't feel like I'm getting anywhere.
const buildDatabaseNames = (DatabaseNameStr, callback) => {
let camelSingular = DatabaseNameStr.slice(0,1);
camelSingular = camelSingular.toLowerCase();
camelSingular = camelSingular + DatabaseNameStr.slice(1, DatabaseNameStr.length);
let camelPlural = DatabaseNameStr.slice(0,1);
camelPlural = camelPlural.toLowerCase();
camelPlural = camelPlural + DatabaseNameStr.slice(1, DatabaseNameStr.length) + 's';
let databaseNameStr = `{ "capsSingular": ${"\"" + DatabaseNameStr + "\""}, "capsPlural": ${"\"" + DatabaseNameStr + 's' + "\""}, "camelSingular": ${"\"" + camelSingular + "\""}, "camelPlural": ${"\"" + camelPlural + "\""} }`;
let names = JSON.parse(databaseNameStr);
return callback(names);
};
const isAnotherDatabase = (DatabaseName, id, NextDatabaseName) => {
DatabaseName.findById({
where: {
id: id
}
})
.then((res) => {
if (typeof res.NextDatabaseName === undefined) {
return false;
} else if (res.NextDatabaseName.length === 0) {
return false;
} else {
return true;
}
})
.catch((err) => {
console.error(err);
process.exit();
});
};
const searchDatabase = (first, i, current, next, list, callback) => {
if (typeof first === 'string') {
first = buildDatabaseNames(first);
current = buildDatabaseNames(current);
next = buildDatabaseNames(next);
}
if (first === current) {
this.first = current;
let topicTree = [];
const isSearchFinished = new Emitter();
console.log(`emitter created`);
isSearchFinished.on('finished', () => {
console.log(`the search is done`);
callback(null, this.primaryTopicsShort);
});
}
current.CapsSingular.all()
.then((res) => {
current.camelPlural = res;
if (if current.camelPlural.length !== 0) {
for (let j = 0; j < currentParsed.camelPlural.length; j++) {
if (first === current) {
this.first[i].current[j].title = current.camelPlural[j].title,
this.first[i].current[j].id = current.camelPlural[j].id
next.camelSingular = []
} else {
this.first[i]..current[j].title = current.camelPlural[j].title,
this.first[i].id = current.camelPlural[j].id,
next.camelSingular = []
}
let isNext = isAnotherDatabase(current.)
searchDatabase(null, j, next, list[i + 1], list, callback).bind(this);
}
} else {
callback(null, this.first);
}
})
.catch((err) => {
callback(err);
});
};
I decided to stop and ask for help, as I just realized that in order to make the properties (this.first[i].current[j].title = current.camelPlural[j].title) on each recursive iteration accurate, I'll have to do a JSON.stringify, alter the string for the next iteration, place all the required iterations of itinto a variable, pass it into the next recursion, and then do JSON.parse again afterwards. It seems like I'm making this ridiculously complicated?
Any help is appreciated, thank you.
While I wasn't able to make a recursive, generic solution, I did at least find some result.
const buildTopicTreeFromCurrentDatabase = (callback) => {
let topicTree = [];
const isSearchFinished = new Emitter();
isSearchFinished.on('finished', () => {
if (isFinished === 0) {
callback(null, this.primaryTopicsShort);
};
});
/* need to go back and refactor -- violates DRY */
this.primaryTopicsShort = [];
let isFinished = 0;
isFinished = isFinished++;
PrimaryTopic.all()
.then((primaryTopics) => {
isFinished = isFinished + primaryTopics.length;
if (primaryTopics.length === 0) {
return callback('no Primary Topics defined');
}
for (let i = 0; i < primaryTopics.length; i++) {
if (i === 0) {
var isLastPrimary = false;
};
this.primaryTopicsShort[i] = {
title: primaryTopics[i].title,
id: primaryTopics[i].id,
secondaryTopics: []
};
PrimaryTopic.findById(this.primaryTopicsShort[i].id, {
include: [{
model: SecondaryTopic,
as: 'secondaryTopics'
}]
})
.then((currentPrimaryTopic) => {
isFinished = isFinished - 1 + currentPrimaryTopic.secondaryTopics.length;
if (i === primaryTopics.length - 1) {
isLastPrimary = true;
};
if (currentPrimaryTopic.secondaryTopics.length === 0 && isLastPrimary) {
isSearchFinished.emit('finished');
} else if (currentPrimaryTopic.secondaryTopics.length !== 0) {
for (let j = 0; j < currentPrimaryTopic.secondaryTopics.length; j++) {
if (j === 0) {
var isLastSecondary = false;
}
this.primaryTopicsShort[i].secondaryTopics[j] = {
title: currentPrimaryTopic.secondaryTopics[j].title,
id: currentPrimaryTopic.secondaryTopics[j].id,
thirdTopics: []
};
SecondaryTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].id, {
include: [{
model: ThirdTopic,
as: 'thirdTopics'
}]
})
.then((currentSecondaryTopic) => {
isFinished = isFinished - 1 + currentSecondaryTopic.thirdTopics.length;
if (j === currentPrimaryTopic.secondaryTopics.length - 1) {
isLastSecondary = true;
}
if (currentSecondaryTopic.thirdTopics.length === 0 && isLastPrimary && isLastSecondary) {
isSearchFinished.emit('finished');
} else if (currentSecondaryTopic.thirdTopics.length !== 0) {
for (let k = 0; k < currentSecondaryTopic.thirdTopics.length; k++) {
if (k === 0) {
var isLastThird = false;
};
this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k] = {
title: currentSecondaryTopic.thirdTopics[k].title,
id: currentSecondaryTopic.thirdTopics[k].id,
fourthTopics: []
};
ThirdTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].id, {
include: [{
model: FourthTopic,
as: 'fourthTopics'
}]
})
.then((currentThirdTopic) => {
isFinished = isFinished - 1 + currentThirdTopic.fourthTopics.length;
if (k === currentSecondaryTopic.thirdTopics.length - 1) {
isLastThird = true;
};
if (currentThirdTopic.fourthTopics.length === 0 && isLastPrimary && isLastSecondary && isLastThird) {
isSearchFinished.emit('finished');
} else if (currentThirdTopic.fourthTopics.length !== 0) {
for (let l = 0; l < currentThirdTopic.fourthTopics.length; l++) {
if (l = 0) {
var isLastFourth = false;
}
this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k] = {
title: currentThirdTopic.fourthTopics[l].title,
id: currentThirdTopic.fourthTopics[l].id,
fifthTopics: []
}
FourthTopic.findById(this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[l].id, {
include: [{
model: FifthTopic,
as: 'fifthTopics'
}]
})
.then((currentFourthTopics) => {
isFinished = isFinished - 1 + currentFourthTopics.fifthTopics.length;
if (l = currentThirdTopic.fourthTopics.length - 1) {
isLastFourth = true;
}
if (currentFourthTopic.fifthTopics.length === 0 && isLastPrimary && isLastSecondary && isLastThird && isLastFourth) {
isSearchFinished.emit('finished');
} else if (currentFourthTopic.fifthTopics.length !== 0) {
for (let m = 0; m < currentFourthTopic.fifthTopics.length; m++) {
if (m = 0) {
var isLastFifth = false;
}
if (m === currentFourthTopic.fifthTopics.length - 1) {
isLastFifth = true;
}
this.primaryTopicsShort[i].secondaryTopics[j].thirdTopics[k].fourthTopics[k].fifthTopics[m] = {
title: currentFourthTopic.fifthTopics[m].title,
id: currentFourthTopic.fifthTopics[m].id
};
if (isLastPrimary === true && isLastSecondary === true && isLastThird === true && isLastFourth === true && isLastFifth === true) {
isFinished = isFinished - 1;
isSearchFinished.emit('finished');
};
}
}
})
.catch((err) => {
callback(err);
});
}
}
})
.catch((err) => {
callback(err)
});
}
}
})
.catch((err) => {
callback(err);
})
}
}
})
.catch((err) => {
callback(err);
});
}
})
.catch((err) => {
callback(err);
});
};
I eventually learned that the root of the problem I face was in the asynchronous nature of the database calls.
To prevent the final callback from getting fired before all the calls were complete, I used something called a semaphore. It's a thing used often in other languages, but not often used in JavaScript (so I hear).
You can see how it works by looking at the variable isFinished. The value starts at zero and goes up for every entry it retrieves from the database. When it finishes processing the data, the code subtracts 1 from the total value. The final callback event (in the emitter), can only go off once the isFinished value returns to zero.
This isn't the most elegant solution. As #Timshel said, it probably would have been wiser not to design this database portion not have so many different tables.
But this solution will do for now.
Thanks.
i'm having bad time with nodejs (v6.14.0) at this moment, because i made a post request that have to do some work until return a valid response. The problem is that i get an empty array as response (if i rerun more than once the request the response will be valid), i have researched and need to manage how te promises are resolved and returned but i can't see what i need to do because i see this a bit more complex that the examples that i found, i need a more deeper advice or guide please. This is my code:
ru.post('/route', (request, response) => {
let rBody = request.body;
let weekDay = getDay(rBody.date); // Return day => "Monday"
let cRef = adminDb.database().ref('/fields/');
return cRef.orderByChild('field_size').equalTo(rBody.type).once('value').then((snap) => {
let arrResult = new Array;
snap.forEach(fields => {
infoField = fields.val();
let idFi = infoField.id_fi;
let idCen = infoField.id_cen;
let id_rsvp = idFi + "_" + rBody.date + "_" + idCen;
let id_rsvp2 = idFi + "_" + rBody.date + "_" + idCen + "_" + rBody.hour;
let resRef = adminDb.database().ref('/rsvp/' + id_rsvp + '/' + id_rsvp2);
resRef.on('value', snap => {
if (snap.val() === null) {
if (infoField.act === "true") {
let cenRef = adminDb.database().ref('/cen/' + idCen);
cenRef.on('value', snap => {
let infoCen = snap.val();
if (infoCen.act === "true") {
values = infoField.ft;
daySelected = weekDay;
dayCheck = values[daySelected];
hourSelected = rBody.hour;
hourCheck = dayCheck[hourSelected];
if (hourCheck !== "" && hourCheck !== "0") {
infoField.lat = infoCen.lat;
infoField.lon = infoCen.long;
if (rBody.lat !== undefined && rBody.long !== undefined) {
infoField.dGet = true;
} else {
infoField.dGet = false;
}
infoField.address = infoCen.address;
uRef = adminDb.database().ref('/users/');
uRef.child(rBody.userid).child('/fav/').orderByChild('id_fi').equalTo(idFi).on('value', snap => {
if (snap.exists() === true) {
infoField.fav = true
} else {
infoField.fav = false
}
arrResult.push(infoField); //how to get this arrResult (completed) to use on the next .then() on first try?
})
}
}
})
}
}
})
})
return arrResult;
}).then((res) => {
console.log("check",res);
return response.status(200).json(res); // Fist response is [], if it a new request with the same values it works...
}).catch(err => {
return response.status(err.status >= 100 && err.status < 600 ? err.code : 500).send(err.message);
})});
Resulting array must be something like this at fist try:
[{
//Values from first snapshot return (infoField) plus:
lat: infoCen.lat,
lon: infoCen.long,
dGet: true/false,
address: infoCen.address,
fav: true/false,
}]
I only get that after second running, at first one it keeps as empty []
I made some edits to your code, you are not handling promises correctly:
ru.post('/route', (request, response) => {
let rBody = request.body;
let weekDay = getDay(rBody.date); // Return day => "Monday"
let cRef = adminDb.database().ref('/fields/');
return cRef.orderByChild('field_size').equalTo(rBody.type).once('value').then((snapshot) => {
let arrResult = [];
snapshot.forEach(fields => {
let infoField = fields.val();
let idFi = infoField.id_fi;
let idCen = infoField.id_cen;
let id_rsvp = idFi + "_" + rBody.date + "_" + idCen;
let id_rsvp2 = idFi + "_" + rBody.date + "_" + idCen + "_" + rBody.hour;
let resRef = adminDb.database().ref('/rsvp/' + id_rsvp + '/' + id_rsvp2);
return resRef.on('value', snap => {
if (snap.val() === null) {
if (infoField.act === "true") {
let cenRef = adminDb.database().ref('/cen/' + idCen);
return cenRef.on('value', snap => {
let infoCen = snap.val();
if (infoCen.act === "true") {
let values = infoField.ft;
let daySelected = weekDay;
let dayCheck = values[daySelected];
let hourSelected = rBody.hour;
let hourCheck = dayCheck[hourSelected];
if (hourCheck !== "" && hourCheck !== "0") {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/lat').set(infoCen.lat));
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/lon').set(infoCen.long));
if (rBody.lat !== undefined && rBody.long !== undefined) {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/dGet').set(true));
} else {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/distanciaGet').set(false));
}
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/address').set(infoCen.address));
const uRef = adminDb.database().ref('/users/');
uRef.child(rBody.userid).child('/fav/').orderByChild('id_fi').equalTo(idFi).on('value', snap => {
if (snap.exists() === true) {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/fav').set(true));
} else {
arrResult.push(adminDb.database().ref('/fields/' + fields.key + '/fav').set(false));
}
});
}
}
});
}
}
});
});
return Promise.all(arrResult);
}).then((res) => {
console.log("check", res);
return response.status(200).json(res); // Fist response is [], if it a new request with the same values it works...
}).catch(err => {
return response.status(err.status >= 100 && err.status < 600 ? err.code : 500).send(err.message);
});
});