ExpressJs - Mongoose: Delete documents with Many To Many relationship - express

I've two Models, Post and Tag with Many To Many relationships.
Post Schema:
const postSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: [true, 'A post must belong to a user.'],
},
title: {
type: String,
unique: [true, 'A Post already exists with this title.'],
required: [true, 'A Post must have a title.'],
},
slug: { type: String, unique: true },
body: { type: String, required: [true, 'A Post must have a body.'] },
coverImage: String,
images: Array,
isDraft: { type: Boolean, default: false },
isPublished: { type: Boolean, default: false },
tags: [{ type: Schema.Types.ObjectId, ref: 'Tag' }],
},
{
timestamps: { currentTime: () => Math.floor(Date.now() / 1000) },
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
)
Tag Schema:
const tagSchema = new Schema(
{
title: { type: String, required: true },
slug: { type: String },
posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }],
},
{
timestamps: { currentTime: () => Math.floor(Date.now() / 1000) },
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
)
Now I want to remove all the references of the Posts from the Tag documents when a Post is deleted.
I'm trying to the following remove middleware in the Post model but it is not working. The post get deleted but the reference still there on the Tag documents.
postSchema.pre('remove', function (next) {
var post = this
post
.model('Tag')
.update(
{ posts: { $in: post.tags } },
{ $pull: { posts: post._id } },
{ multi: true },
next
)
})

After trying many times I finally fired out what wrong I was doing. Following the fix I made to make it work:
In Post Controller I was previously doing this:
const post = await Post.findByIdAndDelete(req.params.id)
Which I changed to:
const post = await Post.findById(req.params.id)
await post.remove()
And in Post Model:
postSchema.pre('remove', async function (next) {
await this.model('Tag').updateMany(
{ posts: this._id },
{ $pull: { posts: this._id } },
{ multi: true },
next
)
})

Related

I need ordering the user's likes with start current user like in prisma

I am using prisma.
I need to get likes of posts with last 3 liked user .
So I want to get these user that starts with current user like .
Becouse I can check is liked this post in frontend part easly.
How can I do this issue with prisma.
Thanks for your help :)
let posts = await prisma.posts.findMany({
where: {
active: true,
},
orderBy: {
id: 'desc'
},
select: {
id: true,
userId: true,
text: true,
files: {
select: {
files: true
}
},
createdAt: true,
active: true,
user: {
select: {
id: true,
fullName: true,
profileImg: {
select: {
url: true
}
}
},
},
_count: {
select: {
likes: true,
comments: true
}
},
likes: {
orderBy: {
id: 'desc'
},
select: {
postId: true,
userId: true,
user: {
select: {
id: true,
fullName: true,
profileImg: {
select: {
url: true
}
}
},
}
},
take: 3
}
},
})

like/dislike button with api call not working using vue an mongoDB

I am learning vuejs and i am working on my first project which is a social network, and i want to implement a like button that call the api to add a like or remove it if the user has already liked it. It does work in my backend but i can't make it work in the front.
I need to send the userId and add or remove the like when i click on the button
This is the data
data() {
return {
post: {
file: "",
content: "",
likes: 0,
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
the last method i tried
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then(() => {
console.log("response", response);
this.user._id = response.data._id;
if(post.usersLiked == user._id) {
this.post.likes += 0
} else if (post.usersLiked != user._id) {
this.post.likes += 1
};
})
.catch((error) => console.log(error));
}
and this is the model
const postSchema = mongoose.Schema({
userId: { type: String, required: true, ref: "User" },
content: { type: String, required: true, trim: true },
imageUrl: { type: String, trim: true },
likes: { type: Number, default: 0 },
usersLiked: [{ type: String, ref: "User" }],
firstname: {type: String, required: true, trim: true },
lastname: {type: String, required: true, trim: true },
created_at: { type: Date},
updated_at: { type: Date }
});
Any idea what is wrong ? Thank you !
.then(() => { // you missed value response from Promise here
this.user._id = response.data._id;
if(post.usersLiked == user._id)
})
Do you mean this.post.usersLiked === user._id I suppose, so post within your data options should be
post: {
file: "",
content: "",
likes: 0,
usersLiked: false,
// something else reflect to your post schema
},
i want to implement a like button that call the api to add a like or remove it if the user has already liked it
By saying that you just need a simple boolean value to do this
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
// Just need to toggle state
this.$set(this.post, 'usersLiked', this.post.usersLiked !== response?.data?._id)
})
.catch((error) => console.log(error));
}
Found the answer, i changed the axios method to this
likePost(id) {
let userId = localStorage.getItem('userId');
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, { userId }, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
console.log(response.data);
this.getAllPost();
})
.catch((error) => console.log(error));
}
i also made a few changes to the data
data() {
return {
posts: [],
post: {
file: "",
content: "",
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
and i also made some changes on the controller
exports.ratePost = (req, res, next) => {
console.log(req.body.userId)
//using findOne function to find the post
Post.findOne({ _id: req.params.id }).then(post => {
if (!post.usersLiked.includes(req.body.userId)) {
// making a object with $inc and $push methods to add a like and to add the user's id
let toChange = {
$inc: { likes: +1 },
$push: { usersLiked: req.body.userId },
};
// we update the result for the like
Post.updateOne({ _id: req.params.id }, toChange)
// then we send the result and the message
.then(post =>
res
.status(200)
.json(
{ message: "Liked !", data: post }
)
)
.catch(error => res.status(400).json({ error }));
} else if (post.usersLiked.includes(req.body.userId)) {
// using the updateOne function to update the result
Post.updateOne(
{ _id: req.params.id },
// we use a pull method to take off a like
{ $pull: { usersLiked: req.body.userId }, $inc: { likes: -1 } }
)
.then(post => {
// then we send the result and the message
res
.status(200)
.json(
{ message: "Post unliked", data: post }
);
})
.catch(error => res.status(400).json({ error }));
}
});
};

mongodb aggregation query to include a specific field

I have a mongodb schema which looks like
{
post_id: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Post"
},
comment_by: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User"
},
comment: {
type: String,
required: true
},
parent_comment_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
},
is_deleted: {
type: Boolean,
default: false
}
}
I want to group the comments such that
they have common "parent_comment_id" but I also want to include the "comment" field of the
document whose "id" is "parent_comment_id".
This is query I have written
var comments = await Comment.aggregate([
{
"$match": {
post_id: mongoose.Types.ObjectId(post_id)
}
},
{
"$group": {
_id: `$parent_comment_id`,
replies: { "$count": {} }
}
}
])
and the result is
{ _id: new ObjectId("6278e11fa7887263e6e6fada"), replies: 3 },
{ _id: new ObjectId("6278fb9f6a8d30c46eb53a84"), replies: 5 },
{ _id: null, replies: 2 }
Result I want
{ _id: new ObjectId("6278e11fa7887263e6e6fada"), replies: 3 , comment : <comment>},
{ _id: new ObjectId("6278fb9f6a8d30c46eb53a84"), replies: 5 , comment : <comment>},
Welcome heeya joshi!.
You can do something like this:
db.collection.aggregate([
{
$match: {post_id: mongoose.Types.ObjectId(post_id)}
},
{
$addFields: {
parent_comment_id: {$ifNull: ["$parent_comment_id", "$_id"]}
}
},
{
$group: {
_id: "$parent_comment_id",
"comment": {
$push: {
$cond: [
{$eq: ["$parent_comment_id", "$_id"]},
"$comment",
"$$REMOVE"
]
}
},
replies: {"$count": {}}
}
},
{
$project: {
comment: {$arrayElemAt: ["$comment", 0]},
replies: 1
}
}
])
As you can see here.
After your $match, the addFields adds the parent_comment_id to the parents themselves. Then the $group keeps the comment only for the parents.

Sequelize migration converting Sequelize.UUID primary key field to integer autoincrement in MYSQL

I am using sequelize CLI to generate and run db migrations. The issue I am having is the id field set to data type Sequelize.UUID appearing as an autoincrement integer in mysql. Here is my user model and migration:
User Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
UserName: {type:DataTypes.STRING,unique:true,allowNull:false},
FirstName:{type: DataTypes.STRING,allowNull:true},
LastName: {type:DataTypes.STRING,allowNull:true},
Email: {type:DataTypes.STRING,allowNull:false,unique:true,validate: { isEmail: {msg: "Invalid Email"} }},
Password: {type:DataTypes.STRING,allowNull:false},
Avatar: {type:DataTypes.STRING,allowNull:true},
}, {});
User.associate = function(models) {
User.hasMany(models.RoleUser,
{
foreignKey:'UserId',
as:'userroles',
sourceKey:'id'
}),
User.belongsTo(models.Country,
{
foreignKey:'CountryId',
targetKey:'id'
}),
User.belongsToMany(models.Role,
{
through: 'RoleUser',
foreignkey:'UserId'
})
};
return User;
};
**User Migration file:**
'use strict';
const uuidv4 = require('uuid/v4');
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
UserName: {
type: Sequelize.STRING
},
FirstName: {
type: Sequelize.STRING
},
LastName: {
type: Sequelize.STRING
},
Email: {
type: Sequelize.STRING
},
Password: {
type: Sequelize.STRING
},
Avatar: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
AFTER MIGRATION, THE HIS FIELD IS CONVERTED TO INT AUTOINCREMENT IN MYSQL:
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
Any pointer as to why this is happening? Please assist. Even the associations seem not to be formed at all as foreign keys are of type Sequelize.UUID
I fixed the problem by adding the id field on model with primary key property set to true.
id: {
type:DataTypes.UUID,
allowNull:false,
unique:true,
primaryKey:true
},
Its like sequelize will automatically generate id field of type INTEGER AUTOINCREAMENT if the model does not have a field with primary key set to true.

Apollo-Client | No result from query when using certain fields

I'm trying to use apollo-client in my react-native app but for some reason I can only get results from queries when I use certain fields.
Here's my first query :
`query RootQueryType($page: Int!) {
events(page: $page) {
title
}
}`
Working perfectly in RN and GraphiQL but as soon as I add or use an other field than title I don't get any result from the query in RN. It's working perfectly in GraphiQL and there's no error at all.
For example :
`query RootQueryType($page: Int!) {
events(page: $page) {
description
}
}`
Here's my event type :
const EventType = new GraphQLObjectType({
name: 'EventType',
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
category: { type: GraphQLString },
description: { type: GraphQLString },
terminated: { type: GraphQLBoolean },
coverUrl: { type: GraphQLString },
startDate: { type: GraphQLString },
endDate: { type: GraphQLString },
price: { type: GraphQLFloat },
website: { type: GraphQLString },
ticketsUrl: { type: GraphQLString },
geometry: { type: GraphQLString },
participantsCount: { type: GraphQLInt },
participants: {
type: new GraphQLList(UserType),
resolve(parentValue) {
return Event.findParticipants(parentValue.id);
}
}
})
});