fs.write & read not updating - javascript

Basically, my code here is saying that if a user sends a message !submit ___ then the file leaderboard.json will up their count by one.
This all works perfectly however say for example their count goes from 0 to 1, the next time that same person sends !submit, their count should go from 1 to 2 without me having to restart the script every time. This isn't happening unfortunately... I send !submit and my count goes from 0 to 1, but then I send it again and it stays going from 0 to 1.
Leaderboard.json:
{
"usercount<#386679122614681600>": 0
}
index.js:
client.on('message', msg => {
if (msg.content.startsWith("!submit ")){
var shoe = msg.content.substr("!submit ".length);
var fs = require('fs')
fs.readFile('leaderboard.json', 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
var user = msg.member;
var usercount = 'usercount'+user
var username = 'usercount'+user
var LEADERBOARD = require('./leaderboard.json');
var countvalue = LEADERBOARD[username]
var countvalue2 = countvalue+1
var replacetext = ('"'+usercount+'": '+countvalue).toString()
var newtext = ('"'+usercount+'": '+(countvalue2)).toString()
fs.writeFile('leaderboard.json', data.replace(replacetext, newtext),
'utf8', function () {
if (err) return console.log(err);
});
console.log('NEW SUBMISSION: '+replacetext+' >>>> '+newtext)
});
}
Here is what my console looks like after sending !submit twice:
When technically the second line should go from 1 to 2, without me having to close and restart the script.
I know this may seem a bit complicated but any help would be appreciated!

This is what I'd suggest:
const fs = require('fs')
client.on('message', msg => {
if (msg.content.startsWith("!submit ")) {
let shoe = msg.content.substr("!submit ".length);
// read leaderboard file and parse the JSON into a Javascript object
fs.readFile('leaderboard.json', 'utf8', function(err, data) {
if (err) {
console.log("Error reading leaderboard.json", err);
return;
}
let leaderboard;
try {
leaderboard = JSON.parse(data);
} catch(err) {
console.log("Error parsing leaderboard JSON", err);
return;
}
const user = msg.member;
const username = 'usercount' + user;
// make sure there's a count for this username
let cnt = leaderboard[username];
if (!cnt) {
cnt = 0;
}
// increment the cnt
++cnt;
// set the new count
leaderboard[username] = cnt;
// now write the data back to the file
fs.writeFile('leaderboard.json', JSON.stringify(leaderboard), 'utf8', function() {
if (err) {
console.log(err);
return;
}
console.log(`New Submission for ${username}, cnt = ${cnt}`);
});
});
}
});
Summary of changes:
Reads leaderboard.json only once using fs.readFile()
After reading the data, it converts it to JSON using JSON.parse().
Initializes user cnt if not already in the file
Updates cnt in the Javsacript object directly
Writes out the changed object using JSON.stringify() to convert the object back to JSON
Puts new submission console message in fs.writeFile() success handler
Switch to const and let from var
Issues not yet incorporated:
Concurrency issues if multiple message events can be "in-flight" at once and conflict.
More complete error handling besides just stopping processing when there's an error (I'm not sure what your application should be doing in that case as that is application-specific).
Your shoe variable is not being used anywhere, not sure what it's doing there.

Related

How to query mysql in nodejs in a loop? because you should wait until result of query is available else loop will run forever

In the following code I am generating a uuid and then making sure if it already doesn't exist but the problem is variable 'pid' is not working that is [pid] written after query is not working somehow and I don't why. now plz don't suggest y I am making sure if uuid already does not exist cuz they are unique all the time, just tell me the fact why js variabel var pid = uuid() is not being accepted by mySql connection.query
FIRST EDIT: The first connection.query is not even getting executed.
SECOND EDIT: After hours of debugging the problem still remains unsolved but now I know the cause which is not javascript variable but the result of the connection.query which is not readily available so the loop continues to run not waiting for the result. After reading articles I somehow know that promises may do my work but idk how to use them so see ya later until I learn them(also I am changing title of the question since error is not with js variable but with the returning of result of mysql connection.query function).
Title changed from:mySQL query is not selecting upon using Javascript variable nodejsTo: above title
//load user model and mysql
const mysql = require('mysql')
const configDB = require('../config/configDB')
const connection = mysql.createConnection(configDB.connection)
//load uuidv4 to generate user ids
const uuid = require('uuid')
module.exports = function(done){
var exist = true
do{
const pid = uuid.v4()
connection.query("SELECT * FROM PATIENT WHERE PATIENT_ID = ?",[pid], function(err, prows){
//query on the above line is not even getting executed and it is because of [pid] but don't know y
if(err)
return done(err)
if(prows.length){
exist = true
} else {
connection.query("SELECT * FROM DOCTOR WHERE DOCTOR_ID = ?",[pid], function(err, drows){
if(err)
return done(err)
if(drows.length){
exist = true
} else {
connection.query("SELECT * FROM ADMIN WHERE ADMIN_ID = ?",[pid], function(err, arows){
if(err)
return done(err)
if(arows.length){ //corrected from drows to arows after someone answered but the problem is something else and it is still unsolved
exist = true
} else {
exist = false
return pid
}
})
}
})
}
})
}while(exist)
}
In this line of your code
if(drows.length){
exist = true
}
Seems it should be
if(arows.length){ //it should be arows.length because arrows.length is never used
exist = true
}
Earlier my loop was keep generating new pids because all it was doing was generating a new pid then checking if that exist or not but while querying database it was not waiting for the result and since the result was not readily available so the loop was continuing to run till infinity(after debugging via vs code).
I used promise to wait for result of loop like this in generateUserId.js file I defined my function as follows
module.exports = generateUserId
async function generateUserId(){
let exist = true
do{
let pid = uuid.v4()
const id = await uuidExistOrNot(pid)
if(id===pid){
exist = false
return id
}
}while(exist)
}
function uuidExistOrNot(pid){
return new Promise((resolve, reject) => {
const selectPQuery = "SELECT * FROM PATIENT WHERE PATIENT_ID = ?"
const selectDQuery = "SELECT * FROM DOCTOR WHERE DOCTOR_ID = ?"
const selectAQuery = "SELECT * FROM HADMIN WHERE ADMIN_ID = ?"
connection.query(selectPQuery, [pid], (err, prows)=>{
if(!prows.length){
connection.query(selectDQuery, [pid], (err, drows)=>{
if(!drows.length){
connection.query(selectAQuery, [pid], (err, arows)=>{
if(!arows.length){
resolve(pid)
} else {
reject(err)
}
})
} else {
reject(err)
}
})
} else {
reject(err)
}
})
})
}
then when to use it I m importing in that file where I wanna use and then use it as:
generateUserId()
.then(function(id){
//do what u wanna do with id here that is being generated by generateUserId()
})
.catch(err=>console.log(err))

Undefined errors using Node.js, Mongoose, and Discord.js [Cannot read property of undefined]

I've been scouring similar problems but haven't seem to have found a solution that quite works on my end. So I'm working on a Discord bot that takes data from a MongoDB database and displays said data in the form of a discord embedded message using Mongoose. For the most part, everything is working fine, however one little section of my code is giving me trouble.
So I need to import an array of both all available users and the "time" data of each of those users. Here is the block of code I use to import said data:
for (i = 0;i < totalObj; i++){
timeArray[i] = await getData('time', i);
userArray[i] = await getData('user', i);
}
Now this for loop references a function I made called getData which obtains the data from MongoDB by this method:
async function getData(field, value){
var data;
await stats.find({}, function(err, result){
if(err){
result.send(err);
}else{
data = result[value];
}
});
if(field == "user"){
return data.user;
}else if (field == "time"){
return data.time;
}else{
return 0;
}
So that for loop is where my errors currently lie. When I try to run this code and display my data through a discord message, I get this error and the message does not get sent:
(node:13936) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'time' of undefined
Now the strange thing is, this error does not happen every time. If I continue calling the command that triggers this code from my discord server, it's almost like a 50/50 shot if the command actually shows the message or instead gives this error. It is very inconsistent.
This error is confounding me, as the undefined part does not make sense to me. The objects that are being searched for in the mongoDB collection are definitely defined, and the for loop never exceeds the number of objects present. My only conclusion is that I'm doing something wrong with my asynchronous function design. I have tried altering code to use the getData function less often, or to not use awaits or asynchronous design at all, however this leaves my final discord message with several undefined variables and an eventual crash.
If anyone has any advice or suggestions, that would be very much appreciated. Just for reference, here is the full function that receives the data, sorts it, and prepares a string to be displayed on the discord server (though the error only seems to occur in the first for loop):
async function buildString(){
var string = "";
var totalObj;
var timeArray = [];
var userArray = [];
var stopSort = false;
await stats.find({}, function(err, result){
if(err){
result.send(err);
}else{
totalObj = result.length;
}
});
for (i = 0;i < totalObj; i++){
timeArray[i] = await getData('time', i);
userArray[i] = await getData('user', i);
}
while(!stopSort){
var keepSorting = false;
for(i = 0; i < totalObj ; i++){
var target = await convertTime(timeArray[i]);
for(j = i + 1 ; j < totalObj ; j++){
var comparison = await convertTime(timeArray[j]);
if(target > comparison){
//Switch target time with comparison time so that the lower time is up front
var temp = timeArray[i];
timeArray[i] = timeArray[j];
timeArray[j] = temp;
//Then switch the users around so that the user always corresponds with their time
var userTemp = userArray[i];
userArray[i] = userArray[j];
userArray[j] = userTemp;
//The loop will continue if even a single switch is made
keepSorting = true;
}
}
}
if(!keepSorting){
stopSort = true;
}
}
//String building starts here
var placeArray = [':first_place: **1st', ':second_place: **2nd', ':third_place: **3rd', '**4th', '**5th', '**6th', '**7th', '**8th', '**9th', '**10th'];
for(i = 0; i < totalObj; i++){
string = await string.concat(placeArray[i] + ": " + userArray[i] + "** - " + timeArray[i] + " \n\n");
console.log('butt');
}
console.log("This String:" + string);
return string;
}
I think problem is you are trying to await function with callback, it will not work => access to data.time may run before data = result[value]. If you need await callback, you can use custom Promise (or use util.promisify, more info here)
Promise:
function findStats(options) {
return new Promise((resolve, reject) => {
return stats.find(options, function (err, result) {
if (err) {
return reject(err)
}
return resolve(result)
})
})
}
utils.promisify
const util = require('util');
const findStats = util.promisify(stats.find);
Now you can use await in your function
async function getData(field, value) {
try {
const result = await findStats({})
const data = result.value
if (field === 'user') {
return data.user
}
if (field === 'time') {
return data.time
}
return 0
} catch (error) {
// here process error the way you like
// or remove try-catch block and sanitize error in your wrap function
}
}

How can I return different values from a function depending on code inside an Axios promise? NodeJS - a

I have a block of code that calls an Api and saves results if there are differences or not. I would like to return different values for DATA as layed out on the code. But this is obviously not working since Its returning undefined.
let compare = (term) => {
let DATA;
//declare empty array where we will push every thinkpad computer for sale.
let arrayToStore = [];
//declare page variable, that will be the amount of pages based on the primary results
let pages;
//this is the Initial get request to calculate amount of iterations depending on result quantities.
axios.get('https://api.mercadolibre.com/sites/MLA/search?q='+ term +'&condition=used&category=MLA1652&offset=' + 0)
.then(function (response) {
//begin calculation of pages
let amount = response.data.paging.primary_results;
//since we only care about the primary results, this is fine. Since there are 50 items per page, we divide
//amount by 50, and round it up, since the last page can contain less than 50 items
pages = Math.ceil(amount / 50);
//here we begin the for loop.
for(i = 0; i < pages; i++) {
// So for each page we will do an axios request in order to get results
//Since each page is 50 as offset, then i should be multiplied by 50.
axios.get('https://api.mercadolibre.com/sites/MLA/search?q='+ term +'&condition=used&category=MLA1652&offset=' + i * 50)
.then((response) => {
const cleanUp = response.data.results.map((result) => {
let image = result.thumbnail.replace("I.jpg", "O.jpg");
return importante = {
id: result.id,
title: result.title,
price: result.price,
link: result.permalink,
image: image,
state: result.address.state_name,
city: result.address.city_name
}
});
arrayToStore.push(cleanUp);
console.log(pages, i)
if (i === pages) {
let path = ('./compare/yesterday-' + term +'.json');
if (fs.existsSync(path)) {
console.log("Loop Finished. Reading data from Yesterday")
fs.readFile('./compare/yesterday-' + term +'.json', (err, data) => {
if (err) throw err;
let rawDataFromYesterday = JSON.parse(data);
// test
//first convert both items to check to JSON strings in order to check them.
if(JSON.stringify(rawDataFromYesterday) !== JSON.stringify(arrayToStore)) {
//Then Check difference using id, otherwise it did not work. Using lodash to help.
let difference = _.differenceBy(arrayToStore[0], rawDataFromYesterday[0],'id');
fs.writeFileSync('./compare/New'+ term + '.json', JSON.stringify(difference));
//if they are different save the new file.
//Then send it via mail
console.log("different entries, wrote difference to JSON");
let newMail = mail(difference, term);
fs.writeFileSync('./compare/yesterday-' + term +'.json', JSON.stringify(arrayToStore));
DATA = {
content: difference,
message: "These were the differences, items could be new or deleted.",
info: "an email was sent, details are the following:"
}
return DATA;
} else {
console.log("no new entries, cleaning up JSON");
fs.writeFileSync('./compare/New'+ term + '.json', []);
DATA = {
content: null,
message: "There were no difference from last consultation",
info: "The file" + './compare/New'+ term + '.json' + ' was cleaned'
}
return DATA;
}
});
} else {
console.error("error");
console.log("file did not exist, writing new file");
fs.writeFileSync('./compare/yesterday-' + term +'.json', JSON.stringify(arrayToStore));
DATA = {
content: arrayToStore,
message: "There were no registries of the consultation",
info: "Writing new file to ' " + path + "'"
}
return DATA;
}
}
})
}
}).catch(err => console.log(err));
}
module.exports = compare
So I export this compare function, which I call on my app.js.
What I want is to make this compare function return the DATA object, so I can display the actual messages on the front end,
My hopes would be, putting this compare(term) function inside a route in app.js like so:
app.get("/api/compare/:term", (req, res) => {
let {term} = req.params
let data = compare(term);
res.send(data);
})
But as I said, Its returning undefined. I tried with async await, or returning the whole axios first axios call, but Im always returning undefined.
Thank you

Node.js mssql return query result to ajax

I'm new to learning Node.js, so I'm still getting used to asynchronous programming and callbacks. I'm trying to insert a record into a MS SQL Server database and return the new row's ID to my view.
The mssql query is working correctly when printed to console.log. My problem is not knowing how to properly return the data.
Here is my mssql query - in addJob.js:
var config = require('../../db/config');
async function addJob(title) {
var sql = require('mssql');
const pool = new sql.ConnectionPool(config);
var conn = pool;
let sqlResult = '';
let jobID = '';
conn.connect().then(function () {
var req = new sql.Request(conn);
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
jobID = result['recordset'][0]['JobID'];
conn.close();
//This prints the correct value
console.log('jobID: ' + jobID);
}).catch(function (err) {
console.log('Unable to add job: ' + err);
conn.close();
});
}).catch(function (err) {
console.log('Unable to connect to SQL: ' + err);
});
// This prints a blank
console.log('jobID second test: ' + jobID)
return jobID;
}
module.exports = addJob;
This is my front end where a modal box is taking in a string and passing it to the above query. I want it to then receive the query's returned value and redirect to another page.
// ADD NEW JOB
$("#navButton_new").on(ace.click_event, function() {
bootbox.prompt("New Job Title", function(result) {
if (result != null) {
var job = {};
job.title = result;
$.ajax({
type: 'POST',
data: JSON.stringify(job),
contentType: 'application/json',
url: 'jds/addJob',
success: function(data) {
// this just prints that data is an object. Is that because I'm returning a promise? How would I unpack that here?
console.log('in success:' + data);
// I want to use the returned value here for a page redirect
//window.location.href = "jds/edit/?jobID=" + data;
return false;
},
error: function(err){
console.log('Unable to add job: ' + err);
}
});
} else {
}
});
});
And finally here is the express router code calling the function:
const express = require('express');
//....
const app = express();
//....
app.post('/jds/addJob', function(req, res){
let dataJSON = JSON.stringify(req.body)
let parsedData = JSON.parse(dataJSON);
const addJob = require("../models/jds/addJob");
let statusResult = addJob(parsedData.title);
statusResult.then(result => {
res.send(req.body);
});
});
I've been reading up on promises and trying to figure out what needs to change here, but I'm having no luck. Can anyone provide any tips?
You need to actually return a value from your function for things to work. Due to having nested Promises you need a couple returns here. One of the core features of promises is if you return a Promise it participates in the calling Promise chain.
So change the following lines
jobID = result['recordset'][0]['JobID'];
to
return result['recordset'][0]['JobID']
and
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
to
return req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
and
conn.connect().then(function () {
to
return conn.connect().then(function () {
You may need to move code around that is now after the return. You would also be well served moving conn.close() into a single .finally on the end of the connect chain.
I recommend writing a test that you can use to play around with things until you get it right.
const jobId = await addJob(...)
console.log(jobId)
Alternatively rewrite the code to use await instead of .then() calls.

Node/Express - How to wait until For Loop is over to respond with JSON

I have a function in my express app that makes multiple queries within a For Loop and I need to design a callback that responds with JSON when the loop is finished. But, I'm not sure how to do this in Node yet. Here is what I have so far, but it's not yet working...
exports.contacts_create = function(req, res) {
var contacts = req.body;
(function(res, contacts) {
for (var property in contacts) { // for each contact, save to db
if( !isNaN(property) ) {
contact = contacts[property];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err) { console.log(err) };
}); // .save
}; // if !isNAN
}; // for
self.response();
})(); // function
}; // contacts_create
exports.response = function(req, res, success) {
res.json('finished');
};
There are a few problems with your code besides just the callback structure.
var contacts = req.body;
(function(res, contacts) {
...
})(); // function
^ you are redefining contacts and res in the parameter list, but not passing in any arguments, so inside your function res and contacts will be undefined.
Also, not sure where your self variable is coming from, but maybe you defined that elsewhere.
As to the callback structure, you're looking for something like this (assuming contacts is an Array):
exports.contacts_create = function(req, res) {
var contacts = req.body;
var iterator = function (i) {
if (i >= contacts.length) {
res.json('finished'); // or call self.response() or whatever
return;
}
contact = contacts[i];
var newContact = new Contact(contact);
newContact.user = req.user.id
newContact.save(function(err) {
if (err)
console.log(err); //if this is really a failure, you should call response here and return
iterator(i + 1); //re-call this function with the next index
});
};
iterator(0); //start the async "for" loop
};
However, you may want to consider performing your database saves in parallel. Something like this:
var savesPending = contacts.length;
var saveCallback = function (i, err) {
if (err)
console.log('Saving contact ' + i + ' failed.');
if (--savesPending === 0)
res.json('finished');
};
for (var i in contacts) {
...
newContact.save(saveCallback.bind(null, i));
}
This way you don't have to wait for each save to complete before starting the next round-trip to the database.
If you're unfamiliar with why I used saveCallback.bind(null, i), it's basically so the callback can know which contact failed in the event of an error. See Function.prototype.bind if you need a reference.

Categories