Calculated Fields with nestTables Option for MySQL Query in NodeJS - json

I'm using the 'nestTables' option in the code below to separate the data returned from an SQL query into their respective tables. I am also including a calculated field in my query that I would like to include in the primary table.
As an example, I am executing the following route:
router.route('/person/:personId').get(function(req,res){
var person_id = req.params.personId;
db.getConnection(function(err, connection) {
if (err) {
return res.status(503).send({ result: false, error: 'CONNECTION error: ' + err.code});
} else {
var sqlString='SELECT *, someField - 1 as calculated FROM person LEFT JOIN person_status ON person.id = person_status.person_id WHERE person.id = ' + person_id;
var options={sql:sqlString,nestTables:true};
connection.query(options, function(error, rows, fields) {
connection.release();
var nestingOptions = [
{tableName: 'person', pkey:'id'},
{tableName: 'person_status', pkey:'id', fkeys:[{table:'person',col:'person_id'}]}
];
if (error) {
return res.status(500).send({ result: false, error: 'QUERY ERROR: ' + error.code});
} else {
return res.status(200).send(rows);
}
});
}
});
});
and I am receiving the following JSON Response:
[
{
"person": {
"id": 1,
other person data . . .
"person_status": [
{
"id": 3,
other data . . .
}
]
},
"person_status": {
"id": 3,
other data . . .
},
"": {
"calculated": 0
}
}
]
Ideally, I would like to include the calculated field into the person subgroup as shown below:
[
{
"person": {
"id": 1,
"calculated": 0
other person data . . .
"person_status": [
{
"id": 3,
other data . . .
}
]
},
"person_status": {
"id": 3,
other data . . .
}
}
]
Is there any way that I can include the calculated field into the person table, or is there a better solution that fits this problem?
Thanks in advance!

I see two options to solve this, one dirty way using SQL:
var sqlString = 'SELECT *, someField - 1 as calculated FROM person WHERE person.id=?';
sqlString = 'SELECT * FROM (' + sqlString + ') as person';
sqlString += ' LEFT JOIN person_status ON person.id = person_status.person_id';
var options={sql:sqlString, nestTables:true};
connection.query(options, [person_id], function(error, rows, fields) {
and the obvious solution using js:
var sqlString = 'SELECT *, someField - 1 as calculated FROM person LEFT JOIN person_status ON person.id = person_status.person_id WHERE person.id=?';
var options = {sql:sqlString, nestTables:true};
connection.query(options, [person_id], function(error, rows, fields) {
rows.forEach(function(row) {
row.person.calculated = row[''].calculated;
delete row[''];
});
I don't think there is a more pleasant solution for this. I even checked if there is a possibility to trick the FieldPacket or RowDataPacket parser but didn't see a way to fake a table name (without monkey patching the mysql driver).

Related

Sequelize raw query to return data as array

my current output is:
{
"students": [
{
"email": "studenthon#gmail.com"
},
{
"email": "studentjon#gmail.com"
}
]
}
What i am trying to achieve is:
{
"students" :
[
"studenthon#gmail.com",
"studentjon#gmail.com"
]
}
How do i get rid of the "email" and put them all into a single array?
My sequelize raw query is as follows:
const studentsFound = await db.sequelize.query(
"SELECT email FROM TeacherStudents INNER JOIN Students on TeacherStudents.studentId = Students.id WHERE teacherId IN (:teachersIds) GROUP BY studentId HAVING COUNT(DISTINCT teacherId) = :arrayLength ",
{
replacements: {
teachersIds: ArrayOfIds,
arrayLength: ArrayOfIds.length
},
type: db.sequelize.QueryTypes.SELECT
}
);
My current solution is
let commonStudentEmail = studentsFound.map(student => {
return student.email;
});
But it seems a little redundant as i feel there should be some sort of configuration.
Sequelize have to distinct one field from another. That's why all fields in a query are named so you know what values in what fields you get exactly.

How to get data from database in array format using node js and MySql

I am using node.js as server language and Mysql as database so I am running query and getting data from database but is is showing in format like this
[ BinaryRow { name: 'Dheeraj', amount: '77.0000' },
BinaryRow { name: 'Raju', amount: '255.0000' } ]
What I want is
['Dheeraj', 77.0000],
['Raju', 66255.000030],
This what I am doing in my backend (node.js):
My model:
static getChartData(phoneNo, userType) {
let sql = 'select businessname as name,sum(billamt) amount from cashbackdispdets where consphoneno =' + phoneNo + ' group by businessid order by tstime desc limit 10'
return db.execute(sql, [phoneNo]);
My controller:
exports.getColumnChart = function(req, res) {
const phoneNo = req.body.userId
const userType = req.body.userType
console.log(phoneNo)
dashboardModule.getChartData(phoneNo, userType)
.then(([rows]) => {
if (rows.length > 0) {
console.log(rows)
return res.json(rows)
} else {
console.log("error")
return res.status(404).json({ error: 'Phone No. already taken' })
}
})
.catch((error) => {
console.log(error)
return res.status(404).json({ error: 'Something went wrong !!' })
})
}
I am sending this data to Ui and when I am receiving it on UI it is in the form of object inside array which is not the required data type I want
axios().post('/api/v1/Dashboard/DashboardColumnChart',this.form)
.then(res=>{
console.log(res.data)
debugger
this.chartData= res.data
})
The above code consoles on browser like
I am not getting any idea how o do it should I do it with backend or with front end and how
Nodejs will send you a JSON response if you want to change it. It is better to change or maniuplate it in a Front end framework. But if you want to change it in backend as you have asked Make sure that the rows is in the format that you want to recive.
let data = [
{ "name": "Dheeraj", "amount": "77.0000" },
{ "name": "Raju", "amount": "255.0000" }
]
// empty array to store the data
let testData = [];
data.forEach(element => {
testData.push(element.name)
});
You can format it using array.map and Object.values. map functions loops over each element and returns a modified element according to the callback provided. Object.values simply returns all the values of an object in an array.
const data = [ { "name": "Dheeraj", "amount": "77.0000" }, { "name": "Raju", "amount": "255.0000" } ];
const formattedData = data.map(obj => Object.values(obj));
console.log("Initial Data: ", data);
console.log("Formatted Data: ", formattedData);
// Map function example
const a = [1,2,3]
const mappedA = a.map(e => e * 2)
console.log(a, " mapped to: ", mappedA);
// Object.values example
const b = { firstName: 'John', lastName: 'Doe', number: '120120' }
console.log(Object.values(b));

Include ressource link in Sequelize result

I'm building a rest api that uses Sequelize to interact with the database. A query looks like this:
function read_category(req, res) {
Category.findById(req.params.categoryId, {rejectOnEmpty: true}).then(category => {
res.json(category);
}).catch(Sequelize.EmptyResultError, function () {
res.status(404).json({message: 'No category found'});
}
).catch(function (err) {
res.send(err);
}
);
}
Now I want the category object that is returned from Sequelize and then returned to the user to include the linkto the ressource. I could do:
category.dataValues.link = config.base_url + 'categories/' + category.dataValues.id;
Which would result in:
{
"id": 1,
"name": "TestCategory 1",
"position": 1,
"createdAt": "2018-08-19T11:42:09.000Z",
"updatedAt": "2018-08-19T11:42:09.000Z",
"link": "http://localhost:3000/categories/1"
}
Since I have more routes than this one I'm wondering if there's a dynamic way to add the link property to every category. I don't want to save it in the database because the base-url might differ.
Thanks!
Better way to do it is , create a getter method :
const Category = sequelize.define( 'category' , {
....
your_fields
....
},
{
getterMethods:{
link() {
return config.base_url + 'categories/' + this.id;
}
}
});
module.exports = Category;
Then
Category.findAll(...).then(categories => {
// Now there is no need to append data manually , it will added each time when you query
console.log(categories); // <-- Check the output
})

Modify nested object with multiple keys without replacing existing keys using Mongoose/NodeJS

Schema and model:
var schema = new mongoose.Schema({
timestamp_hour: Date,
deviceID: Number,
minutes: {
'0': {temperature: Number},
'1': {temperature: Number},
.
.
.
'59': {temperature: Number}
}
},{
collection: 'devices'
});
var model = mongoose.model('deviceData', schema);
Now in a POST request, I receive some data from an external source containing a timestamp, deviceID and temperature value.
My primary key is timestamp_hour and deviceID, so if there is an existing document in the database, I need to store the temperature value in minutes: {[minute_value]: temperature}. I currently derive minute_value from the timestamp, and I can query the database, all well and good. Now I need to update the minutes object in the document by adding the new key-value pair.
So after deriving the required values, I try running this:
var query = {timestamp_hour: timestamp, deviceID: deviceID};
var update = {minutes: {[minute]: {temperature: tempData}}};
deviceData.findOneAndUpdate(query, update, {upsert: true}, function(err, doc){
if(err) return res.send(500, {error: err});
return res.send("successfully saved");
});
Now the issue is, it replaces the entire minutes object inside document with the new single value.
Example:
Original document:
{
"deviceID" : 1,
"timestamp_hour" : ISODate("2016-10-29T08:00:00Z"),
"minutes" : { "38" : { "temperature" : 39.5 } },
}
Document after update:
{
"deviceID" : 1,
"timestamp_hour" : ISODate("2016-10-29T08:00:00Z"),
"minutes" : { "39" : { "temperature" : 38.0 } },
}
What I need:
{
"deviceID" : 1,
"timestamp_hour" : ISODate("2016-10-29T08:00:00Z"),
"minutes" : { "38" : { "temperature" : 39.5 }
"39" " { "temperature" : 38.0 } },
}
I'm new to MEAN, but I can see why my approach doesn't work since the update call just modifies the nested object.
I'd appreciate any help regarding the correct approach to use for achieving this functionality.
You can do this within a single update using a combination of the dot and bracket notations to construct the update object as follows:
var query = { "timestamp_hour": timestamp, "deviceID": deviceID },
update = { "$set": { } },
options = { "upsert": true };
update["$set"]["minutes."+ minute] = { "temperature": tempData };
deviceData.findOneAndUpdate(query, update, options, function(err, doc){
if(err) return res.send(500, {error: err});
return res.send("successfully saved");
});
Okay, so this works:
deviceData.findOne(query, function(err, doc) {
if(err) return done(err);
if(!doc){
data.save(function(err){
if(err) throw err;
res.json({"Result":"Success"});
});
} else {
doc.minutes[minute] = {temperature: tempData}
doc.save(function(err) {});
res.json(doc);
}
});

How to query in mongodb programmatically?

I wannt to create code like this:
var result = Attributes.find({
attribute_name : {
$exist : true,
$in : [1]
}
});
but programmatically, so i ceate code like this:
var genQuery = '{ "' + by + '" : { "$exists" : true, "$in" : [' + data + ']} }';
var result = Attributes.find(genQuery);
but I get error maximum call stack
because result of JSON.parse(genQuery)
{ _id: { '$exists': true, '$in': [ 1 ] } }
How to query in mongodb programmatically?
Your genQuery variable you declare is a String, but you cannot pass strings as selectors or modifiers in find() functions.
You should create an Object to make it works:
var genQuery = {};
//use this notation to declare a new object key depending on a variable
genQuery[by] = {
$exists: true,
$in: [1]
};
var result = Attributes.find(genQuery);