Incorrect response after iteration inside for-loop for findById() - mysql

I had created 2 tables named: groupusermaps and groups. From groupusermaps i am fetching all the groupId and these groupId I am passing in findById() method to fetch all the details related to that groupId inside the for loop.
here is my method in service:
getAllGroupsByUserId(userId, callback) {
var arr = [];
return sequelize.transaction().then(function(t) {
return groupUserMapModel.GroupUserMap.findAll({
where: {
userId: userId
},
transaction: t
}).then((allGroupsByUserId) => {//from findAll i am getting 2, 1, 4
groupId
for (var p in allGroupsByUserId) {
return
groupModel.Group.findById(allGroupsByUserId[p].groupId, { transaction: t
}).then((group) => {
arr.push(
JSON.stringify(group)
);
});
}
}).then(() => {
callback(arr);
});
});
}
my controller code:
router.get('/controllers/getGroups/user/:userId/groups', (req, res) => {
groupService.getAllGroupsByUserId((req.params.userId), (result) => {
log.info('Group list: ' + JSON.stringify(result));
});
res.send('Fetched all group list');
});
But I am getting an empty array as response from the controller on the console. Is there any way to fix this issue?

groupUserMapModel.GroupUserMap.findAll({
where: {
userId: userId
},
include:[{
model:groupModel
}]
})
.then((results) => {
console.log(results);
})
.catch(err=>{
console.log(err);
})
// provided that groupUserMapModel associated to groupModel
groupUserMapModel.associate = models => {
groupUserMapModel.belongsTo(models.groupModel, {foreignKey: groupId})
}
for more info read about sequelize association

Related

Delete an image of a post stored in a backend folder when i delete a user of a social network application

I have a problem on my application, it is a social network. The user can create a post with a message and an image, stored in a backend images folder thanks to Multer. I use sequelize and MySql. When I delete a post, the image is indeed deleted in the images folder since I use multer in my post deletion function so everything goes well but when I delete the author, since I go through a relationship between tables so that when I delete a user, their posts are deleted. This works but in this case the images are not deleted from the folder they are stored in, since Multer is not in the loop. How do I get the images to be deleted from the images folder too in this specific case? Thank you for your help !
`
// Template for the Post table
const User = require("../models/User");
const Sequelize = require("sequelize");
const database = require("../config/database");
const Post = database.define("post", {
content: { type: Sequelize.STRING, allowNull: false },
image: { type: Sequelize.STRING, allowNull: true },
likes: { type: Sequelize.INTEGER, allowNull: false, default: 0 },
});
module.exports = Post;
// Relationship with the User table
User.hasMany(Post, { onDelete: "CASCADE", foreignKey: "userId" });
Post.belongsTo(User, { onDelete: "CASCADE" });
`
`
// deletePost function
exports.deleteOnePost = (req, res, next) => {
Post.findOne({ where: { id: req.params.id } })
.then((post) => {
if (!post) {
return res.status(404).json({
error: new Error("Post non trouvé !"),
});
}
if (post.userId === req.auth.userId || req.auth.userAdmin) {
if (post.image) {
const filename = post.image.split("/images/")[1];
fs.unlink(`images/${filename}`, () => {});
}
Post.destroy({ where: { id: req.params.id } })
.then(() => res.status(200).json({ message: "Post sans supprimé" }))
.catch((error) => res.status(400).json({ error }));
} else {
return res.status(403).json({
error: new Error("Requête non autorisée !"),
});
}
})
.catch((error) => res.status(500).json({ error }));
};
`
#Anatoly Thank you very much for your help, I'm sorry, I'm a beginner, I tried to adapt what you sent me to the method I use. I don't use the async/await method much and don't know much about it. Do you think I'm getting closer to the solution with what i made ? thanks again !
`
exports.deleteUser = (req, res, next) => {
const userId = req.params.id;
User.findOne({ where: { id: userId } }).then((user) => {
if (!user) {
return res.status(404).json({
error: new Error("User not found!"),
});
}
});
const userPosts = User.getAllPosts();
const postImages = posts.map((x) => x.image).filter((x) => x);
User.destroy({ where: { id: userId } })
.then((post) => {
Post.findOne({ where: { userId } })
.then((post) => {
Post.destroy({ where: { userId } }).then((res) =>
res.status(200).json({
message: "User is deleted",
})
);
for (const image of postImages) {
const filename = image.split("/images/")[1];
fs.unlink(`images/${filename}`, () => {});
}
})
.catch((error) =>
res.status(400).json({
error,
})
);
})
.catch((error) => res.status(500).json({ error }));
};
`
I don't see how Multer is related to a file deletion. It only helps you to store them. Any way you just need to get all posts of a certain user and delete them and a user in a transaction and then delete their images in a cycle:
// I did not use try/catch for simplicity
exports.deleteUser = async (req, res, next) => {
// get the user id somehow (req.params or the request context, for instance)
const userId = ...
const user = await User.findById(userId);
if (!user) {
return res.status(404).json({
error: new Error("User not found!"),
});
}
const userPosts = await user.getPosts();
const postImages = poists.map(x => x.image).filter(x => x);
// here 'sequelize' is the Sequelize instance, you used to register models
await sequelize,transaction(async transaction => {
await Post.destroy({ where: { userId } })
await User.destroy({ where: { id: userId } })
});
for (const image of postImages) {
const filename = image.split("/images/")[1];
fs.unlink(`images/${filename}`, () => {});
}
res.status(200).json({ message: "User is deleted" }))
}
I come back to put the fonction that works with my method, i often use ".then()" ".catch()", many thanks to Anatoly for helping me to find the solution, here is the result of my work :
exports.deleteUser = (req, res, next) => {
User.findOne({ where: { id: req.params.id } })
.then((user) => {
if (!user) {
return res.status(404).json({
error: new Error("user not found !"),
});
}
// I get all the posts of the author
Post.findAll({ where: { userId: req.params.id } })
.then((posts) => {
// I start a loop in the posts of the author to find the posts with an image
posts.forEach((post) => {
if (post.image) {
// I erase the files in the images backend directory
const filename = post.image.split("/images/")[1];
fs.unlink(`images/${filename}`, () => {});
}
// Now i can erase the author
User.destroy({ where: { id: req.params.id } })
.then(() =>
res.status(200).json({
message: "User erased !",
})
)
.catch((error) =>
res.status(400).json({
error,
})
);
});
})
.catch((error) =>
res.status(400).json({
error,
})
);
})
.catch((error) => res.status(500).json({ error }));
};

My response from api is undefined on frontend

I got list of items from my database mySql and also button 'edit'.
When I clicked edit (by id) I want to see all fields filled by data.
But I only have in my console: undefined
If I tested my api by postman it works fine.
There is how I am getting list.
{
const id = this.actRoute.snapshot.paramMap.get('id');
this.studentApi.GetStudent(id).subscribe((res: any) => {
console.log(res.data);
this.subjectArray = res.data;
console.log(this.subjectArray);
this.studentForm = this.fb.group({
id: [res.id, [Validators.required]],
domain_id: [res.domain_id, [Validators.required]],
source: [res.source, [Validators.required]],
destination: [res.destination]
});
});
}
There is my api.service.ts
GetStudent(id): Observable<any> {
const API_URL = `${this.endpoint}/read-student/${id}`;
return this.http.get(API_URL, { headers: this.headers })
.pipe(
map((res: Response) => {
return res || {};
}),
catchError(this.errorMgmt)
);
}
And there is my route
studentRoute.get('/read-student/:id', (request, response) => {
const id = request.params.id;
con.query('SELECT * FROM students WHERE id = ?', id, (error, result) => {
if (error) throw error;
response.send(result);
});
});
There is response from 'postman'
[
{
"id": 5,
"domain_id": 2,
"source": "tester0700#test.pl",
"destination": "testw#test.pl"
}
]
It seems like the response is an array, containing an object.
In that case, there is no need to use res.data, as that would imply the returned observable, res has a property named data, and that you are trying to access the value within that property. You can simply assign res to the subjectArray property. I am pretty sure res would be defined.
this.studentApi.GetStudent(id).subscribe((res: any) => {
console.log(res);
this.subjectArray = res;
// handle the rest here.
});

How to get return values from Async/await function when fetching the data from mySQL in Nodejs

I am fetching some exchange data from DB, then extracting the name of distinct exchanges and passing again into MYSQL query to fetch data from a different table.
The problem I am facing is that async await does not return the value rather just return Promise { }.
Below is the code that I am trying, wondering where I am going wrong.
//Function that fetches the exchanges from DB
const getExchange = () => {
return new Promise((resolve, reject) => {
db.connection.query(`
SELECT *
FROM,
(
SELECT
exchange,
COUNT(pair) as noOfMarkets
FROM ticker_data
) as t
`, (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
})
})
}
// push unique exchanges to an array.
const getExchangesData = async () => {
const allExchanges = await getExchanges();
let exchanges = []
allExchanges.forEach(item => {
let exchange = {
exchange: item.exchange
}
exchanges.push(exchange)
})
return await exchanges
}
// mapping through an array of exchanges and passing to DB query to get data from the DB.
const getSingleExchange = async () => {
const exchanges = await getExchangesData()
await Promise.all(exchanges.map(async (item) => {
db.connection.query(`
SELECT
exchange_rank,
name
volume24hUSD
(
SELECT
volume24hUSD as tradingVolYesterday
FROM exchanges
WHERE name = '${item.exchange}'
AND createdAt >= now() -interval 1 day
AND createdAt < now() -interval 1 day + interval 120 second
LIMIT 1
) volumeDay1
FROM exchanges
WHERE name = '${item.exchange}'
`, (err, resp) => {
if (!err) {
console.log(resp) // getting all the values
let volData = {
name: resp[0].name,
exchange_rank: resp[0].exchange_rank,
icon: resp[0].icon
}
return volData
}
})
}))
}
const data = getSingleExchange()
console.log(data) // returning Promise { <pending> }
Edit
After making changes suggested in an answer, I still have an issue:
//Function that fetches the exchanges from DB
const getExchange = () => {
return new Promise((resolve, reject) => {
db.connection.query(`
SELECT *
FROM,
(
SELECT
exchange,
COUNT(pair) as noOfMarkets
FROM ticker_data
) as t
`, (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
})
})
}
// push unique exchanges to an array.
const getExchangesData = async () => {
const allExchanges = await getExchanges();
let exchanges = []
allExchanges.forEach(item => {
let exchange = {
exchange: item.exchange
}
exchanges.push(exchange)
})
return await exchanges
}
// mapping through an array of exchanges and passing to DB query to get data from the DB.
const getSingleExchange = async () => {
const exchanges = await getExchangesData()
await Promise.all(exchanges.map((item) => {
return new Promise((resolve, reject) => {
db.connection.query(`...`, (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
}).then(resp => {
console.log(resp)
let volData = {
name: resp[0].name,
exchange_rank: resp[0].exchange_rank,
icon: resp[0].icon
}
return volData
})
})
}))
}
getSingleExchange().then(data => {
console.log(data)
});
I now get this error:
(node:30583) UnhandledPromiseRejectionWarning: TypeError: db.connection.query(...).then is not a function
at Promise (/getExchanges.js:217:16)
at new Promise ()
at Promise.all.exchanges.map (/getExchanges.js:145:16)
at Array.map ()
at getSingleExchange (/getExchanges.js:144:33)
The main issue is in this part:
await Promise.all(exchanges.map(async (item) => {
That map callback is not returning anything, and it has no await, so using async makes no sense.
Instead remove async:
await Promise.all(exchanges.map((item) => {
... and return a promise in the callback function, much like you had done in the first function:
return new Promise((resolve, reject) => {
db.connection.query(`...`), (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
})
}).then(resp => {
console.log(resp)
let volData = {
name: resp[0].name,
exchange_rank: resp[0].exchange_rank,
icon: resp[0].icon
}
return volData
});
You would benefit from writing one generic function that promisifies query, so that you don't have to do that new Promise-thing for every single query you need.
Finally, you cannot expect to get an asynchronous result synchronously: async functions do not return the asynchronous result synchronously, but return a promise for it. So your last lines (main code) should still await. So either do this:
(async () => {
const data = await getSingleExchange()
console.log(data)
})(); // immediately executing (async) function expression
Or:
getSingleExchange().then(data => {
console.log(data)
});
NB: doing return await exchanges in the second function makes no sense (exchanges is not a promise), so you can just do return exchanges.

Convert MySQL update query into sequelize query

UPDATE `users` SET tempToken=tempToken-"5" WHERE `id`="1"
How can I write this query into sequelize query.
For an async function, this is how you would do it, assuming you've set up your User model:
myFunction: async (req, res) => {
var tempToken = req.body.tempToken // Put whatever your data source is here for tempToken
var newValue = tempToken - 5
try {
await User.update({
tempToken: newValue
},
{
where: [{
id: 1
}]
})
res.status(200).send();
}
catch (error) {
res.status(500).send(error);
}
}
or
myFunction: async (req, res) => {
try {
const user = User.findOne({
where: {
id: 1
}
})
await user.update({
tempToken: user.tempToken - 5
})
res.status(200).send();
}
catch (error) {
res.status(500).send(error);
}
}
Also, don't forget to 'require' the user model in the .js file that you use this function in.

Why is my Mongoose query within a loop only returning the first result?

I have been struggling with this for days now. I am trying to return the data that is referenced by a list of IDs.
Example JSON of one team:
{
"Name":"Team 3",
"CaptainID":"57611e3431c360f822000003",
"CaptainName":"Name",
"DateCreated":"2016-06-20T10:14:36.873Z",
"Members":[
"57611e3431c360f822000003", //Same as CaptainID
"57611e3431c360f822000004" //Other members
]
}
Here is the route:
router.route('/teams/:user_id')
.get(function (req, res) {
TeamProfile.find({
Members : {
$in : [req.params.user_id]
}
}).exec(function (err, teamProfiles) {
teamProfiles.forEach(function (teamProfile) {
UserProfile.find({
UserID : {
$in : teamProfile.Members.map(function (id) {
return id;
})
}
}, function (err, userProfiles) {
teamProfile.Members = userProfiles;
console.log(teamProfile); //will console log the remaining 2
})
.exec(function (err) {
res.json(teamProfile) //returns the first one only
})
})
});
})
The idea is for the route to return the profiles just by using the IDs to fetch the up-to-date data.
However, it is working to a point. It gets the user information and all but it doesn't return all the Teams + all the users as commented in the code. There are 3 teams in total. Only the first one is returned. If I remove res.json(teamProfile) it console logs all 3 teams. I want to return all 3 teams.
This is because your response is being called before completing all db operations. So instead of for each use async.forEach function. Install async module
var async = require('async');
router.route('/teams/:user_id').get(function (req, res) {
TeamProfile.find({
Members : {
$in : [req.params.user_id]
}
}).exec(function (err, teamProfiles) {
async.forEach(teamProfiles,function (teamProfile,cb) {
UserProfile.find({
UserID : {
$in : teamProfile.Members.map(function (id) {
return id;
})
}
}, function (err, userProfiles) {
teamProfile.Members = userProfiles;
cb() // Callback
})
},function(){
res.json(teamProfiles)
})
});
})