Get data from main table by querying sub table and main table - node.js

I've got table product and sub table series in one too many relation
const Product = sequelize.define('product', {
id: {type: Sequelize.INTEGER, autoIncrement: true, allowNull: false, primaryKey: true},
order: Sequelize.STRING
})
const Series = sequelize.define('series', {
id: {type: Sequelize.INTEGER, autoIncrement: true, allowNull: false, primaryKey: true},
name: Sequelize.STRING,
})
Relation
Product.belongsTo(Series);
Series.hasMany(Product);
I want to get all products where series name is some text and product order name is some text.
exports.searchProduct = async (req, res, next) => {
const name = req.body.name || ''
const order = req.body.order || ''
try {
const series = await Series.findAll({
where: {
name: {[Op.like]: '%' + name + '%'}}
});
const products = await series.getProducts //<= get stuck here
res.status(200)
.json({result: products})
}
catch (err) {
if (!err.statusCode) {
err.statusCode = 500
}
next(err);
}
}
I managed to query series but get stuck to query product result with order name. So how can I query product result with order name? I tried to add where clause in getProducts but that come up with error getProducts is not a function

How about this
cosnt products = await Products.findAll({
where: {
order: {[Op.like]: '%' + order + '%'}
},
include: [
{
model: Series,
where: {
name: {[Op.like]: '%' + name + '%'}
},
required: true
}
]
})

Related

How to create a likes and comment system in PostgreSQL?

Context: I'm in an apprenticeship at Openclassrooms.com (Web dev path), and I'm doing the last project: I have to create a social network website from scratch, for the front-end I must use a framework of my choice (Vue.js, React, Angular, Svelte...) and for the back-end I must use an SQL database (like MySQL, PostgreSQL, Oracle, SQLite...).
For the Front-end, I'm using Angular.
For the Back-end, I'm using Node.js + Express + Sequelize
For the Database, I chose PostgreSQL
I did almost all the routes for the signup, login, view all posts and do CRUD operations with a single post.
But I'm stuck with the post controller for the likes and comments.
In the previous project of my apprenticeship, I had to create the Back-end where I had to do almost the same thing the posts were without comments), at the exception that I used Mongoose to make CRUD operations in MongoDB
The user had this model:
const mongoose = require("mongoose");
const sauceSchema = mongoose.Schema({
userId: { type: String, required: true },
name: { type: String, required: true },
manufacturer: { type: String, required: true },
description: { type: String, required: true },
mainPepper: { type: String, required: true },
imageUrl: { type: String, required: true },
heat: { type: Number, required: true, minimum: 1, maximum: 10 },
likes: { type: Number, required: true },
dislikes: { type: Number, required: true },
usersLiked: { type: Array, required: true },
usersDisliked: { type: Array, required: true },
});
module.exports = mongoose.model("Sauce", sauceSchema);
So here's how the likes for the previous project with MongoDB + Mongoose worked:
When the user liked a post, the front-end would send the userId (string) of the person who liked + like (integer between -1 and 1) + post_id (string) to the back-end
We search for the post in the database, and if it's present in the database, then we'll manipulate the likes/dislikes
Switch case 1: the like = 1/-1 → is the like/dislike present in the array of usersLiked/usersDisliked?
If it is → we remove the userId from the usersLiked/usersDisliked
If not → we add the userId in the usersLiked/usersDisliked array
And the amount of likes/dislikes is the length of the array
case 2: the like = 0 (meaning the user removes the like/dislike of the post) → Is the userId present in the usersLiked array?
If it is → we remove the userId from the usersLiked
If not → we remove the userId from the usersDisiked
Here's the code btw:
const findIndexInArray = require("../utils/likeSauce-function");
exports.likeSauce = (req, res, next) => {
Sauce.findOne({
_id: req.params.id,
})
.then((sauce) => {
let userId = req.body.userId;
let numberOfLikes = sauce.likes;
let numberOfDislikes = sauce.dislikes;
let usersLikedArray = sauce.usersLiked;
let usersDislikedArray = sauce.usersDisliked;
let userLikedOrDisliked = req.body.like;
console.log("Valeur de likes dans B2D: ", userLikedOrDisliked);
switch (userLikedOrDisliked) {
case 1:
{
//Où on like
let indexFound = findIndexInArray(usersLikedArray, userId);
if (indexFound > -1) {
console.log(
"User ID: " +
userId +
" found in the usersLikedArray → Like cancelled"
);
usersLikedArray.splice(indexFound, 1);
} else {
console.log(
"User ID: " +
userId +
" has NOT been found in the array of userIDs → Like added"
);
usersLikedArray.push(userId);
}
numberOfLikes = usersLikedArray.length;
let sauceObject = {
...JSON.parse(JSON.stringify(sauce)),
usersLiked: usersLikedArray,
likes: numberOfLikes,
};
Sauce.updateOne({ _id: req.params.id }, {...sauceObject, _id: req.params.id })
.then(() =>
res
.status(200)
.json({ message: "Sauce Object SUCCESSFULLY modified !" })
)
.catch((error) => res.status(400).json({ error }));
break;
}
//
case -1:
{
//Où on like
let indexFound = findIndexInArray(usersDislikedArray, userId);
if (indexFound > -1) {
console.log(
"User ID: " +
userId +
" found in the usersLikedArray → Like cancelled"
);
usersDislikedArray.splice(indexFound, 1);
} else {
console.log(
"User ID: " +
userId +
" has NOT been found in the array of userIDs → Like added"
);
usersDislikedArray.push(userId);
}
numberOfDislikes = usersDislikedArray.length;
let sauceObject = {
...JSON.parse(JSON.stringify(sauce)),
usersDisliked: usersDislikedArray,
dislikes: numberOfDislikes,
};
Sauce.updateOne({ _id: req.params.id }, {...sauceObject, _id: req.params.id })
.then(() =>
res
.status(200)
.json({ message: "Sauce Object SUCCESSFULLY modified !" })
)
.catch((error) => res.status(400).json({ error }));
break;
}
case 0:
{
let index = findIndexInArray(usersLikedArray, userId);
if (index > -1) {
usersLikedArray.splice(index, 1);
numberOfLikes = usersLikedArray.length;
let sauceObject = {
...JSON.parse(JSON.stringify(sauce)),
usersLiked: usersLikedArray,
likes: numberOfLikes,
};
Sauce.updateOne({ _id: req.params.id }, {...sauceObject, _id: req.params.id })
.then(() =>
res.status(200).json({
message: "Sauce Object SUCCESSFULLY liked !",
})
)
.catch((error) => res.status(400).json({ error }));
} else {
index = findIndexInArray(usersDislikedArray, userId);
if (index > -1) {
usersDislikedArray.splice(index, 1);
numberOfDislikes = usersDislikedArray.length;
let sauceObject = {
...JSON.parse(JSON.stringify(sauce)),
usersDisliked: usersDislikedArray,
dislikes: numberOfDislikes,
};
Sauce.updateOne({ _id: req.params.id }, {...sauceObject, _id: req.params.id })
.then(() =>
res.status(200).json({
message: "Sauce Object SUCCESSFULLY disliked !",
})
)
.catch((error) => res.status(400).json({ error }));
}
}
break;
}
default: //Pas de likes/dislikes AJOUTÉS par défaut
break;
}
})
.catch((error) => {
console.log("Error while attempting to find the sauce: " + error);
res.status(404).json({ error });
});
};
So that's the algorithm with the likes/dislikes of a post with MongoDB + Mongoose,
It's a rather simple implementation...
But how do I implement the like/dislike feature with PostgreSQL + Sequelize?
At first, I thought that I had to add a likes attribute to the table post, but that's wrong, I actually had to create a table for the likes and comments
So for the likes table:
-1 like_id attribute as the primary key
-2 other attributes as foreign keys (user_id and post_id)
For the comments table:
-1 comment_id attribute as the primary key
-3 other attributes as foreign keys (user_id, post_id and like_id)
Here also are the Sequelize models for each feature:
module.exports = (sequelize, Sequelize) => {
const Like = sequelize.define("like", {
like_id: {
type: Sequelize.INTEGER,
allowNull: false,
unique: true,
autoIncrement: true,
primaryKey: true,
},
user_id: {
type: Sequelize.STRING,
allowNull: false,
// references: "user", //Nom de notre table
// referencesKey: "user_id", //L'attribut référencé de la PK ce cette table
},
post_id: {
type: Sequelize.STRING,
allowNull: false,
// references: "post", //Nom de notre table
// referencesKey: "post_id", //L'attribut référencé de la PK ce cette table
},
});
return Like;
};
module.exports = (sequelize, Sequelize) => {
const Comment = sequelize.define("comment", {
comment_id: {
type: Sequelize.INTEGER,
allowNull: false,
unique: true,
autoIncrement: true,
primaryKey: true,
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: "user",
referencesKey: "user_id",
},
post_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: "post",
referencesKey: "post_id",
},
likes_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: "likes", //Nom de notre table
referencesKey: "likes_id",
},
});
return Comment;
};
For the controller, I just have no idea as to how to do it, I looked at some posts but nothing very satisfying, if anyone could enlighten me, even just by giving me a hint, I'd be very grateful.
Also, here's an Entity Relationship Diagram to show all the tables I created:
I try this with mongoose but i hope with sequelize you can adjust to fit your request. I hope this can help you if there's any problem you can fix this comment and let me know.
export const commentPost = async (req, res) => {
try {
const { id } = req.params;
const { comment } = req.body;
if (!mongoose.Types.ObjectId.isValid(id)) throw new Error(`No post with id: ${id}`);
const post = await PostMessage.findById(id);
if (!post) throw new Error(`No post found with id: ${id}`);
post.comments.push({ text: comment });
const updatedPost = await post.save();
res.json(updatedPost);
} catch (err) {
res.status(404).send(err.message);
}
}

Sequelize model query with [or] not retrieving data

I have a project that I am working on. I build the database models with sequelize. Not I am running the controller. In one of the methods, I want to pass in a userId into the url as a parameter. With that userId, I want to check and see if any of the records has the passed in userId in the friendedId or frienderId fields within the databse.
I am using the [or] in the filtering to make that query return all records and it currenty returns none.
this is the controller method:
getByUser: (req, res) => {
let user_id = req.params.userId;
Friend.findAll({ where: {[or]: [{ frienderId: user_id }, { friendedId: user_id }]}})
.then((response) => {
console.log(response)
res.status(200).send(response.dataValues)
})
.catch()
},
this is the model:
const seq = require('sequelize');
const { database } = require('../index');
const { User } = require('./User');
const Friend = database.define(
"friend",
{
id: {type: seq.INTEGER, primaryKey: true, autoIncrement: true},
status: {type: seq.STRING, allowNull: false,
validate: {isIn:[['Pending', 'Accepted']], isAlpha: true}
},
favorite: {type: seq.BOOLEAN, allowNull: false, defaultValues: false,
validate: {isIn: [['true', 'false']]}
},
},
{
createdAt: seq.DATE,
updatedAt: seq.DATE,
}
)
Friend.belongsTo(User, {as: 'friender'})
Friend.belongsTo(User, {as: 'friended'})
database.sync()
.then(() => {
console.log('Connected the Friend model to database')
})
.catch((err) => {
console.log('Issue connected the Friend model to the database: ' + JSON.stringify(err))
})
module.exports.Friend = Friend;

Lifecycle hooks with sequelize on a different model

I've been looking through this but I couldn't find what I was looking for.
Basically I have two models.
"X" and "Y"
On lifecycle events of Y(which defines a belongsTo to X) I'm supposed to be updating(incrementing) the values of X. I'll illustrate with code in a while but this is the gist of it.
So whenever I create an entry of Y a value inside X must increment by 1.
Using a require to the sequelize generated Index.js function didn't seem to do anything and I don't want to add even more complexity to my main routes.js file.
This is the "X" model code(named User):
const bcrypt = require("bcrypt");
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true,
required: true
},
username: {
type: DataTypes.STRING,
unique: true,
required: true,
validate: {
len: [4, 32]
}
},
password: {
type: DataTypes.STRING,
required: true,
protect: true,
validate: {
len: [4, 32]
}
},
likedByCount: {
type: DataTypes.INTEGER,
defaultValue: 0
},
user_likes:{
type: DataTypes.INTEGER,
defaultValue:0
}
});
user.associate = function (models) {
models.user.hasMany(models.like, {
onDelete: 'cascade'
});
};
user.beforeCreate((user) => {
return bcrypt.hash(user.password, 10).then(hashedPw => {
console.log(user.password + " hash " + hashedPw);
user.password = hashedPw;
});
});
user.beforeUpdate((user) => {
return bcrypt.hash(user.password, 10).then(hashedPw => {
console.log(user.password + " hash " + hashedPw);
user.password = hashedPw;
});
});
return user;
};
And this is the "Y" model definition:
const moduleFile = require("./index.js");
module.exports = (sequelize, DataTypes) => {
sequelize
.sync()
.then(function (err) {
console.log('Sync successful');
}, function (err) {
console.log('An error occurred while creating the table:', err);
});
const like = sequelize.define('like', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true,
required: true,
},
target_username: {
type: DataTypes.STRING
},
source_username: {
type: DataTypes.STRING
},
target: {
type: DataTypes.INTEGER
}
});
//HasOne and BelongsTo insert the association key in different models from each other.
//HasOne inserts the association key in target model whereas BelongsTo inserts the association key in the source model.
like.associate = function (models) {
models.like.belongsTo(models.user, {
foreignKey: {
allowNull: false
}
});
};
""
//Update the field likedByCount of target user by +1, update the field likes of source user by 1
like.afterCreate((like) => {
const target_id = like.target_id;
const source_id = like.source_id;
moduleFile.user.findById(target_id).then(user => {
user.increment('likedByCount', {
by: 1
})
}).catch((err)=>console.log("Decrement error" + err.message));
});
return like;
};
My code obviously produces an error because "moduleFiles" isn't found correctly. But I do it the same way in router.js:
var models = require("../models/index.js");
Folder structure checks out:
/ :index.js(this just launches the server)
/models/: index.js,user.js,like.js
/routes/: routes.js(this is where the above code(var models=) is from

SailsJS Perform sorting on populate data

Here is my current event model:
module.exports = {
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
client: {
model: 'client',
required: true
},
location: {
model: 'location',
required: true
}
}
};
The client Model:
module.exports = {
attributes: {
name: {
type: 'string',
required: true,
unique: true
},
address: {
type: 'string'
},
clientContact: {
model: 'user',
required: true
}
}
};
So how do I implement sorting based on the client name and also have the skip and limit property(for pagination) to work along with it.
I tried using the following query:
Event.find({ id: ['583eb530902db3d926345215', '583eb6dd91b972ee26dd97b1'] },
{ select: ['name', 'client'] })
.populate('client', { sort: 'name DESC' })
.exec((err, resp) => resp.map(r => console.log(r.name, r.client)));
But this does not seem to do it.
Waterline doesn't support sorting a result by child records like this. If client was a collection of child records attached to an event, then your code would sort each of the returned Event record's populated client array by name, but I'm assuming in this case client is a singular association (i.e. model: 'client').
If what you want is an array of Event records sorted by the name of their client, you can do it relatively easily using Lodash after you retrieve the records:
var _ = require('lodash');
Event.find(queryCriteria).populate('client').exec((err, resp) => {
if (err) { /* handle error */ }
var sortedRecords = _.sortBy(eventRecords, (record) => { return record.client.name; });
});

How to set/create records in associations in Sequelize? (many to many relation)

I'm trying to use the parent.addChild() method Sequelize provides, but for some reason it can't recongise that function and throws and error on me.
I read a bunch of other questons here and they all mention that these methods are added to the model. I read the whole docs, re-read the part containing the information I need, but just I can't get this thing to run... I can't see what I'm doing wrong here, can you guys give me a hand?
// Models
const Person = Conn.define('person', {
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
}
});
const Post = Conn.define('post', {
title: {
type: Sequelize.STRING,
allowNull: false
},
content: {
type: Sequelize.STRING,
allowNull: false
}
});
// Relationships
Person.belongsToMany(Post, {
as: 'person',
through: 'people_posts',
foreignKey: 'person_id'
});
Post.belongsToMany(Person, {
as: 'post',
through: 'people_posts',
foreignKey: 'post_id'
});
Conn.sync({ force: true }).then(() => {
const firstName = Faker.name.firstName();
const lastName = Faker.name.lastName();
return Person.create({
firstName: firstName,
lastName: lastName
})
.then(() => {
return Post.create({
title: `Title by ${firstName}`,
content: 'Content'
});
}).then(() => {
const person = Person.findOne({ where: { firstName: firstName }});
const post = Post.findOne({ where: { content: "Content" }})
return post.addPerson(person);
// this gives me: Unhandled rejection TypeError: post.addPerson is not a function
});
});

Resources