How to send JavaScript object/array to front-end - node.js app - javascript

I am trying to send data to front end of my app to create a graph. I attach the array in the response object but this doesn't work, although I can use the user value that is also sent in the response object on the front end.
var scoreQuery = "SELECT q1, q2, q3, q4, q5, q6, q7, q8, q1, q2, q3, q4 FROM table WHERE id = user.id";
var scoreArray;
module.exports = function(app, passport){
app.get('/profile', isLoggedIn, function (req, res) {
connection.query(scoreQuery, function (err, result, fields) {
if (err) throw err;
getData(result);
console.log(scoreArray);
}, res.render('profile.ejs', {
user:req.user,
data: scoreArray
})
);
});
};
function getData(result){
Object.keys(result).forEach(function(key) {
const values = result[key];
scoreArray = Object.values(values);
});
});
Below is the public/javascripts/scripts.js file where I'm creating the graph.
/*Scripts*/
var quizCategory1 = data.scoreArray.slice(0,7);
var quizCategory2 = data.scoreArray.slice(8,11);
var cat1Total = totalScore(category1);
var cat2Total = totalScore(category2);
function totalScore(categoryScore){
return categoryScore = scoreArray.reduce((accum,scoreArray) =>
{
const splitValues = scoreArray.split('/');
return {
score:accum.score + parseInt(splitValues[0]),
maxScore:accum.maxScore + parseInt(splitValues[1]),
}
},{score:0,maxScore:0}
);
}
var ctx = document.getElementById("myChart").getContext('2d');
var barTotalCategoryScores = [cat1Total.score, cat2Total.score];
var labels = ["Java & Design", "Build & Versioning"];
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: barTotalCategoryScores
}
}
});
the scoreArray prints out to the console whenever a user logsin so the sql query and getData() function are working. But when I try to use the data.scoreArray in my scripts.js file it doesn' work.

server :
// ... your routes
// + new route
app.get('/profile/:id', (req,res) => {
let id = req.params.id // e.g /profile/3 -> req.params.id = 3
let scoreArray;
// your query (it's very bad to set param in your query like this (injection SQL) )
let scoreQuery = `SELECT q1, q2, q3, q4, q5, q6, q7, q8, q1, q2, q3, q4 FROM
table WHERE id = ${id}`; // `... ${foo}` => https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Litt%C3%A9raux_gabarits
connection.query(scoreQuery, function (err, result, fields) {
if (err) throw err;
getData(result);
console.log(scoreArray);
}, res.status(200).json({
user:req.user,
data: scoreArray
})
);
})
// ... your method
function getData(result){
Object.keys(result).forEach(function(key) {
const values = result[key];
scoreArray = Object.values(values);
});
});
client :
/* code to get data
let id_test = 1;
fetch(`http://localhost:<your_node_server_port>/profil/${id_test}`)
.then(response => response.json())
.then(json => console.log(json)) // return profil for id 1
*/
// e.g page is loaded
window.onload = () => {
let id_test = 1;
fetch(`http://localhost:<your_node_server_port>/profil/${id_test}`)
.then(response => response.json())
.then(json => console.log(json)) // return profil for id 1
}

Add new route:
app.get('/profile/:id', (req,res) => {
// your_array = query DB with param id (req.params.id) store in your array
res.status(200).json({data: <your_array>})
})
Then on your client make a GET query on /profile/:id (https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest)
when the page loading (window.onload)
For MySQL, you should using :
http://docs.sequelizejs.com/
http://docs.sequelizejs.com/manual/getting-started.html#installing
http://docs.sequelizejs.com/manual/dialects.html#mysql
http://docs.sequelizejs.com/manual/querying.html

Thanks a million Sylvain, your explanation got me on the right track. I did the following...
//routes.js
app.get('/profile', function (req, res) {
let id = req.user.id;
getData(id, function (score) {
res.status(200).render('profile.ejs',{user: req.user, score})
});
});
I put my sql statement within the getData() function and used a '?' in place of id in my sql statement and send id parameter as value to insert into the query method. This has has worked and I can now get the score object on the client-side ejs file using <?= score.cat1Total.score ?>.
My problem now is still trying to use score.cat1Total.score in my public/javascripts/scripts.js file and it is undefined... and I'm not sure how to correctly implement your window.onload() method
// /public/javascripts/scripts.js
/* code to get data?
window.onload = () => {
fetch(`http://localhost:8080/profile)
.then(response => response.json())
.then(json => console.log(json))
}
*/
var ctx = document.getElementById("myChart").getContext('2d');
//This is where I am trying to get score objects values.
var barTotalCategoryScores = [score.cat1Total.score, cat2Total.score];
var labels = ["Java & Design", "Build & Versioning"];
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: barTotalCategoryScores
}
}
});
FYI below is the getData(id, callback) method I used in routes
// routes.js
function getData(id, callback) {
let scoreQuery = "SELECT c1q1, c1q2, c1q3, c1q4, c1q5, c1q6, c1q7, c1q8, c2q1, c2q2, c2q3, c2q4 FROM nodejs_login.assessment_score AS a JOIN nodejs_login.users as u ON a.respondent_id = u.user_respondent_id WHERE a.respondent_id = ?";
connection.query(scoreQuery, [id],function (err, result) {
if (err) throw err;
Object.keys(result).forEach(function(key) {
let values = result[key];
let scoreArray = Object.values(values);
let category1 = scoreArray.slice(0,7);
let category2 = scoreArray.slice(8,11);
//parsing and accumlating each category score
var cat1Total = totalScore(category1);
var cat2Total = totalScore(category2);
//function to parse the strings into numbers and accumulate them
function totalScore(categoryScore){
return categoryScore.reduce((accum,scoreArray) =>
{
const splitValues = scoreArray.split('/');
return {
score:accum.score + parseInt(splitValues[0]),
maxScore:accum.maxScore + parseInt(splitValues[1]),
}
},{score:0,maxScore:0}
);
}
let categories = {cat1Total, cat2Total};
callback(categories);
});
})
}

Related

Updating Yaml File through Patch method not working

I have a Yaml file(layouts.yaml) of this format from which i want to perform crud Operations through REST Api:
Layouts:
-
Name: Default Layout
LayoutId : 1
ConfiguredSegments:
LiveA :
Height : 100
Id : LiveA
Ref1A :
Height : 100
Id : Ref1A
My controller Function to update a layout based on layout Id(I tried 2 ways which wont work):
1st way: //This does not seem to work
const raw = fs.readFileSync("layouts.yaml", 'utf8');
const layoutData = YAML.load(raw);
//function to update specific layout based on LayoutId
export const updateSpecificLayout = (req, res)=>{
const { id } = req.params;
const { ConfiguredSegments } = req.body;
const getLayoutList = JSON.parse(JSON.stringify(layoutData));
getLayoutList.forEach(element => {if(element.LayoutId == id) element.ConfiguredSegments =
ConfiguredSegments
});
let yaml = YAML.dump(getLayoutList);
fs.writeFileSync("layouts.yaml", yaml, function (err,file){
if(err) throw err;
console.log(`Layout with the id:${id} has been updated`);
})
}
2nd way://This does not seem to work as well
const raw = fs.readFileSync("layouts.yaml", 'utf8');
const layoutData = YAML.load(raw);
//function to update specific layout based on LayoutId
export const updateSpecificLayout = (req, res)=>{
const { id } = req.params;
const { ConfiguredSegments } = req.body;
const getLayout = JSON.parse(JSON.stringify(layoutData));
const foundLayout = getLayout.Layouts.find((layout) => layout.LayoutId == id);
if(ConfiguredSegments)foundLayout.ConfiguredSegments = ConfiguredSegments;
console.log(`Layout with the id:${id} has been updated`);
}
Through Postman i am testing my api with patch request with the following body:
{
"ConfiguredSegments": {
"Ref2A": {
"Height": 100,
"Id": "LiveA"
},
"Ref3A": {
"Height": 100,
"Id": "Ref1A"
}
}
}
But the yaml file is not getting updated.Any other ways to achieve this ?
You can try using this method.
Define a function which will be able to find and replace the object you are looking for.
Your controller function:
export const updateSpecificLayout = (req, res)=>{
const { id } = req.params;
const { ConfiguredSegments } = req.body;
const getLayoutList = JSON.parse(JSON.stringify(layoutData));
const layoutToBeUpdated = getLayoutList.Layouts.find((layout) => layout.LayoutId == id );
findAndReplace(getLayoutList.Layouts,layoutToBeUpdated.ConfiguredSegments,ConfiguredSegments)
let yaml = YAML.dump(getLayoutList);
fs.writeFileSync("layouts.yaml", yaml, function (err,file){
if(err) throw err;
console.log(`Layout with the id:${id} has been updated`);
})
}
The helper function which can find and replace the data.
// Helper function to update layout data
function findAndReplace(object, value, replacevalue) {
for (var x in object) {
if (object.hasOwnProperty(x)) {
if (typeof object[x] == 'object') {
findAndReplace(object[x], value, replacevalue);
}
if (object[x] == value) {
object["ConfiguredSegments"] = replacevalue;
break;
}
}
}
}

Storing MongoDB document inside a Node.js array and return it

I try to get specific documents from MongoDB with Node.js and insert them into array.
const getStockComments = async (req) => {
const stockname = req.params.stockName;
var comments = [];
var data = [];
const stock = await stockModel.findOne({ name: stockname });
comments = stock.comments;
comments.forEach(async (commentId) => {
const comm = await commentModel.findOne({ _id: commentId });
data.push(comm);
console.log(data); // This returns the data in loops, because its inside a loop.
});
console.log(data); // This not returns the data and i don't know why.
return data;
};
The first console.log(data) returns the same data a lot of times because its inside a loop.
But the second console.log(data) dosen't returns the data at all.
What I'm doing wrong?
Instead of using loop , you can use $in operator to simplify things .
const getStockComments = async (req) => {
const stockname = req.params.stockName;
var comments = [];
var data = [];
const stock = await stockModel.findOne({ name: stockname });
comments = stock.comments;
commentModel.find({ _id: { $in: comments } }, (err, comments) => {
data = comments;
});
console.log(data);
return data;
};

Mongoose nested schema

I want to make a system of topics. each topic may have subtopics which are also topics.
meaning the subtopics may also have subtopics.
for example
example image
I tried to create a topic schema and add a subtopic field which will ref to topic schema too.
cant seem to get this working my schema code:
const mongoose = require('mongoose');
const TopicSchema = new mongoose.Schema({
name: {type:String,unique:true},
sub_topic:[{type:mongoose.Schema.Types.ObjectId, ref : 'Topic'}]
});
const Topic =mongoose.model('Topic', TopicSchema);
module.exports = Topic;
Also what the data im sending to the server should look like to make a new instance?
and on the server how do i save it?
i try to save like this now :
const topic = new Topic();
topic.name = req.body.name;
topic.sub_topic.name=req.body.sub_topic
and the data im sending is :(json)
{
"name":"TestMain",
"sub_topic":[{"name":"TestSub"}]
}
UPDATE : got this done using a recursive function.
function subtopicHandler(topic, sub_topic) {
Topic.find({
"name": topic.name
}, function (err, res) {
if (err) throw err
return;
})
if (sub_topic == undefined) {
let ntopic = new Topic();
ntopic.name = topic.name;
ntopic.sub_topic == undefined;
ntopic.save(function (err, result) {
if (err) console.log('saving err', err)
});
return ntopic._id;
}
let mainTopic = new Topic();
mainTopic.name = topic.name;
sub_topic.forEach(function (sub) {
mainTopic.sub_topic.push(subtopicHandler(sub, sub.sub_topic));
})
var retME;
mainTopic.save(function (err, result) {
if (err) {
console.log('saving err', err)
throw err;
}
});
return mainTopic._id;
}
Using this schema :
const TopicSchema = new mongoose.Schema({
name: {type:String,unique:true},
sub_topic:[{type:mongoose.Schema.Types.ObjectId, ref : 'Topic'}]
});
and data sent as :
{
"name":"A",
"sub_topic":[
{"name":"C","sub_topic":
[
{"name":"C1"}
,
{"name":"C2"}
]
}
,
{"name":"B"}
,
{"name":"D","sub_topic":
[
{"name":"D1"}
,
{"name":"D2"}
,
{"name":"D3"}
]
}
]
}
to the API endpoint
handled this way:
let mainTopic = new Topic();
mainTopic.name = req.body.name;
subtopicHandler(mainTopic, req.body.sub_topic);
})
If you are sending following json
const obj = {
"name":"TestMain",
"sub_topic":[{"name":"TestSub"}]
}
Then,
let mainTopic = new Topic();
let subTopic = new Topic();
// assuming for now you have only one sub-topic in array
subTopic.name = obj[0].name;
subTopinc.save(function(err,result)=>{
if(!err){
mainTopic.name = obj.name;
mainTopic.sub_topic = [result._id]
mainTopic.save(function(err,result){
console.log(result);
})
}
});
From you schema definition and the given json you can follow the above step to get the results.
Hope this will help you.
You can do this with sub docs check out the documentation.
https://mongoosejs.com/docs/subdocs.html

TypeError: Cannot read property 'json' of undefined google assistant

I'm working on a Bus Stop google assistant script in node.js
I based it on the weather API example by Google. Given the right API key, the weather function will work and return the weather for a place on a date.
The Bus Stop API will return the correct output in the console.log, but the output does not get passed on to the else if statement where the function is called.
I get 2 errors:
"Unhandled rejection" Which can be alleviated by commenting out the reject code in the callBusApi.
"TypeError: Cannot read property 'json' of undefined
at callBusApi.then.catch (/user_code/index.js:45:9)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)" This is where it breaks. I think because it doesn't get the output from the function.
My script looks as follows:
'use strict';
const http = require('http');
const host = 'api.worldweatheronline.com';
const wwoApiKey = 'enter a working key';
exports.weatherWebhook = (req, res, re) => {
if(req.body.queryResult.intent['displayName'] == 'weather'){
// Get the city and date from the request
let city = req.body.queryResult.parameters['geo-city']; // city is a required param
// Get the date for the weather forecast (if present)
let date = '';
if (req.body.queryResult.parameters['date']) {
date = req.body.queryResult.parameters['date'];
console.log('Date: ' + date);
}
// Call the weather API
callWeatherApi(city, date).then((output) => {
res.json({ 'fulfillmentText': output }); // Return the results of the weather API to Dialogflow
}).catch(() => {
res.json({ 'fulfillmentText': `I don't know the weather but I hope it's good!` });
});
}
else if (req.body.queryResult.intent['displayName'] == 'mytestintent'){
callBusApi().then((output) => {
re.json({ 'fulfillmentText': output }); // Return the results of the bus stop API to Dialogflow
}).catch(() => {
re.json({ 'fulfillmentText': `I do not know when the bus goes.` });
});
}
};
function callBusApi () {
return new Promise((resolve, reject) => {
http.get({host: 'v0.ovapi.nl', path: '/stopareacode/beunav/departures/'}, (re) => {
let boy = '';
re.on('data', (d) => {boy+=d});
re.on('end',() => {
let response = JSON.parse(boy)
var firstKey = Object.keys(response['beunav']['61120250']['Passes'])[0];
var timeKey = Object.keys(response['beunav']['61120250']['Passes'][firstKey])[19];
var destKey = Object.keys(response['beunav']['61120250']['Passes'][firstKey])[1];
let destination = response['beunav']['61120250']['Passes'][firstKey][destKey];
let datetime = response['beunav']['61120250']['Passes'][firstKey][timeKey];
let fields = datetime.split('T');
let time = fields[1];
let output = `Next bus to ${destination} departs at ${time} .`;
console.log(output)
resolve(output);
});
re.on('error', (error) => {
console.log(`Error talking to the busstop: ${error}`)
reject();
});
});
});
};
function callWeatherApi (city, date) {
return new Promise((resolve, reject) => {
let path = '/premium/v1/weather.ashx?format=json&num_of_days=1' +
'&q=' + encodeURIComponent(city) + '&key=' + wwoApiKey + '&date=' + date;
console.log('API Request: ' + host + path);
http.get({host: host, path: path}, (res) => {
let body = '';
res.on('data', (d) => { body += d; });
res.on('end', () => {
let response = JSON.parse(body);
let forecast = response['data']['weather'][0];
let location = response['data']['request'][0];
let conditions = response['data']['current_condition'][0];
let currentConditions = conditions['weatherDesc'][0]['value'];
let output = `Current conditions in the ${location['type']}
${location['query']} are ${currentConditions} with a projected high of
${forecast['maxtempC']}°C or ${forecast['maxtempF']}°F and a low of
${forecast['mintempC']}°C or ${forecast['mintempF']}°F on
${forecast['date']}.`;
console.log(output);
resolve(output);
});
res.on('error', (error) => {
console.log(`Error calling the weather API: ${error}`)
reject();
});
});
});
}
It appears that your method has a parameter too much
exports.weatherWebhook = (req, res, re) => {
should be:
exports.weatherWebhook = (req, res) => {
And as well on the variable 're' used in the handling of the 'mytestintent' inside the webhook.
This explains the 'not defined' error when trying to set a json value on it.
Regarding your 2 question: It usually comes when the value of the variable is not defined.
First check wheather you have defined the JSON variable in your .js file.
Or its in some other format.

CSV import in neo4j using node.js

I'm trying to import csv file into neo4j using node.js. I have to insert data into multiple collection/table, so I have to insert data using node.js script. But my problem is, I can't prevent data duplication when inserting csv data.
Sample CSV data:
name
-------------
Afghanistan
Afghanistan
Aland
Albania
Albania
Bangladesh
Bangladesh
index.js
cp = require('child_process');
child = cp.fork(__dirname + "/background-import-csv-file.js");
child.on('message', function(msg) {
console.log("background-insert-process said : ", msg);
});
file = path.resolve(__dirname, `./file/simplemaps.csv`);
child.send(file);
In background-import-csv-file.js, I have write code in two different way.
First Promise based (background-import-csv-file.js) :
cp = require('child_process');
csv = require('fast-csv');
Q = require('q');
DB = require("./common/driver");
Country = require('./collection/country');
process.on("message", (file) => {
stream = fs.createReadStream(file);
csv
.fromStream(stream, { headers: true })
.on("data", function(data) {
let countryData = { "name": data.name };
neo = new DB();
country = new Country(neo);
country.insert(countryData)
.then(resp => process.send(resp.msg) )
.catch(err => process.send(err) )
})
.on("end", () => process.send("file read complete") );
});
./collection/country.js:
Q = require('q');
Country = function Country(neo) {
this.country = "Country"; this.neo = neo;
};
Country.prototype.find = function find(filters) {
query = `MATCH (a:Country { name: '${filters.name}' } ) RETURN {country:properties(a)}`;
return this.neo.run(query, filters).then(resp => resp);
}
Country.prototype.create = function create(data) {
query = `CREATE (ax:Country { name: '${data.name}' } ) RETURN ax `;
return this.neo.run(query, {}).then(resp => resp[0].properties).catch(err => err)
}
Country.prototype.insert = function insert(country) {
filter = { name: country.name };
return Q(this.find(filter))
.then(resp => resp.length > 0 ? Q.resolve({ msg: `country: [${country.name}] is already exist` }) : Q.resolve(this.create(country)) )
.then(resp => resp)
.catch(e => Q.reject(e));
}
module.exports = Country;
./common/driver.js
neo4j = require('neo4j-driver').v1;
function DB() {
this.driver = neo4j.driver(); this.session = this.driver.session();
}
DB.prototype.run = function run(query, data) {
return this.session.run(query, data)
.then(response => response.records.map(
record => record._fields[0] ?
record._fields.length ? record._fields[0] : {} : {}
) ).catch(err => new Error(err) );
}
module.exports = DB;
When I run index.js in terminal, In database, I have 2 Afghanistan, 1 Aland, 2 Albania and 2 Bangladesh. But I need 1 Afghanistan, 1 Aland, 1 Albania and 1 Bangladesh in my database. When I analyze code, than found that before inserting data, I'm checking data ( Country.prototype.find = function find(filters)) if it is already exist or not, but it always return empty result. That why it insert multiple data. If I run index.js again, then no new data is inserted into database. To solve this problem, I've tried following CQL :
MERGE (c:Country { name: '${data.name}' } ) RETURN c
It is inserted unique data, but It kill so much time. Then I have written the following code:
Event-driven (background-import-csv-file.js) :
process.on("message", (file) => {
stream = fs.createReadStream(file);
csv
.fromStream(stream, { headers: true })
.on("data", function(data) {
countryData = { "name": data.name };
neo = new DB();
country = new Country(neo);
country.find(countryData);
country.on('find', resp => resp.length > 0 ? Q.resolve({ msg: `country: [${country.name}] is already exist` }) : Q.resolve(country.create(countryData)) );
country.on('create', resp => console.log(resp) );
})
.on("end", () => process.send("file read complete") );
});
./collection/country.js:
EventEmitter = require('events').EventEmitter;
util = require('util');
Country = function Country(neo) {
this.neo = neo; EventEmitter.call(this);
};
util.inherits(Country, EventEmitter);
Country.prototype.find = function find(filters) {
query = `MATCH (a:Country { name: '${filters.name}' } ) RETURN {country:properties(a)}`;
return this.neo.run(query, {}).then(resp => this.emit('find', resp));
}
Country.prototype.create = function create(data) {
query = `CREATE (ax:Country { name: '${data.name}' } ) RETURN ax `;
return this.neo.run(query, {}).then(resp => this.emit('create', resp[0].properties)).catch(err => err)
}
And this time, it shows same result. What am I missing? Any suggestion will be very helpfull.
NB: I'm using fast-csv for csv parsing and Q for promise.
Actually I can imagine the following solutions:
Modify the CSV file itself with programming language(like node.js) to remove the duplicate lines with the same name.
Add the neo4j unique constrains
CREATE CONSTRAINT ON (c:Country) ASSERT c.name IS UNIQUE
Involve the middleware, like a queue which to prevent the duplicate items, for this, you need to define you own message structure and duplicate arithmetic.
above.
My problem was, in csv file parsing, it was so fast (event-driven) it wasn't wait to finish insert data into database. So I have to pause file parsing and then resume it.
I solve my problem using the following code:
Promise based (background-import-csv-file.js) :
cp = require('child_process');
csv = require('fast-csv');
Q = require('q');
DB = require("./common/driver");
Country = require('./collection/country');
process.on("message", (file) => {
stream = fs.createReadStream(file);
csvstream = csv
.fromStream(stream, { headers: true })
.on("data", function(data) {
csvstream.pause(); // pause the csv file parsing
countryData = { "name": data.name };
neo = new DB();
country = new Country(neo);
country.insert(countryData)
.then(resp => {
process.send(resp.msg);
neo.close();
return csvstream.resume(); // after completing db process, resume
})
.catch(err => {
process.send(err);
return csvstream.resume(); // if failed, then resume
});
})
.on("end", () => process.send("file read complete") );
});

Categories

Resources