Is there an opposite to mongoose populate() method? (Mongoose, node.js, express) - node.js

I have two mongoose schemes one for users and one for posts.
One user can like many posts.
At the end I would like to present in the client side all the posts the user liked in one section which I can do using the populate() method, and in another section the posts the user didn’t like without creating duplicates of the liked posts.
Is there unpopulate() method I can use to get only the unliked posts? If not, what is the best way to approach this?
userScheme =
{
// some other fields…
post: {
type: Schema.Types.ObjectId,
ref: 'Post'
}
}
postScheme =
{
// some other fields…
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
likes: [
{
type: Schema.Types.ObjectId,
ref: 'User'
}
],
}

You can query for all the posts that doesn't have the particular user in their likes array i.e. is not liked the user yet. Reference
Post.find({ likes: { $nin: [user._id] }})

Related

Mongoose references: Should documents cross reference each other

I need some input on schema design with mongodb and mongoose.
I have the following scenario data entities:
Posts
Comments
Users
Requirements:
A user can make a comment on a post.
Comments can be served for a post.
A list of all comments of a user can be retrieved.
I'm thinking to make all 3 of them a seperate schema and connect them by using ref.
I see two approaches here and need input on what might be smarter. Should every entity link to its relations or is it enough if only the comments are a "link" to the other data objects?
export const commentSchema = new Schema<CommentDocument>({
content: { type: String, required: true },
userId: { required: true, type: Schema.Types.ObjectId, ref: "user" },
postId: { type: Schema.Types.ObjectId, ref: "post" },
});
And then for both user and post, should they also link to the comment again or is it enough if the relationship is stored once in comment?
export const userSchema = new Schema<UserDocument>({
// ... all my user data
comments: [{ type: Schema.Types.ObjectId, ref: "comment" }] // <--- Is the referencing on the other documents useful?
});
export const postSchema = new Schema<PostDocument>({
// ... all my post data
comments: [{ type: Schema.Types.ObjectId, ref: "comment" }] // <--- Is the referencing on the other documents useful?
});
Is there any rule of thumb for declaring the references between the documents? Is this generally a good schema design approach?
your commentSchema is good
But no need to store commentsId in userSchema& postSchema.
Because, it will be good for fews comment but for large no of comments it is not a good approach.

Using "deleteMany" post middleware in mongoose

In my Nodejs and Express app, I have a mongoose User schema, a Post schema and a Comment schema as follows:
const UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
},
password: String,
posts : [
{
type : mongoose.Schema.Types.ObjectId,
ref : 'Post'
}
]
});
const PostSchema = new Schema({
author : {
type : mongoose.Schema.Types.ObjectId,
ref : 'User'
},
createdAt: { type: Date, default: Date.now },
text: String,
comments : [
{
type : mongoose.Schema.Types.ObjectId,
ref : 'Comment'
}
],
});
const CommentSchema = new Schema({
author : {
type : mongoose.Schema.Types.ObjectId,
ref : 'User'
},
createdAt: { type: Date, default: Date.now },
text: String
});
I have coded the general CRUD operations for my User. When deleting my user, I can easily delete all posts associated with that user using deleteMany:
Post.deleteMany ({ _id: {$in : user.posts}});
To delete all the comments for all the deleted posts, I can probably loop through posts and delete all the comments, but I looked at mongoose documentation here and it seems that deleteMany function triggers the deleteMany middleware. So In my Post schema, I went ahead and added the following after defining schema and before exporting the model.
PostSchema.post('deleteMany', async (doc) => {
if (doc) {
await Comment.deleteMany({
_id: {
$in: doc.comments
}
})
}
})
When deleting user, this middleware is triggered, but the comments don't get deleted. I got the value of doc using console.log(doc) and I don't think it includes what I need for what I intend to do. Can someone tell me how to use the deleteMany middleware properly or if this is not the correct path, what is the most efficient way for me to delete all the associated comments when I delete the user and their posts?
deleteMany will not give you access to the affected document because it's a query middleware rather than a document middleware (see https://mongoosejs.com/docs/middleware.html#types-of-middleware). Instead it returns the "Receipt-like" object where it tells it successfully deleted n objects and such.
In order for your hook to work as expected, you'll need to use something other than deleteMany, such as getting all of the documents (or their IDs), and loop through each one, using deleteOne.

How to display all the posts referenced to logged in user?

I am trying to understand Mongoose's populate function and am so far stumped by available resources and the docs.
Essentially I have a schema arrangement as below, and I am trying to display all the posts referenced to a user on one page rendered with ejs.
var UserSchema = new Schema ({
username: String,
password: String,
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}
]
});
var PostSchema = new Schema ({
Title: String,
Content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}
});
I have got passport/sessions setup, so I am able to get the id of the active user. Using this id I would like to display all of the user's posts. I have been stuck on this for hours and tried following various tutorials and different approaches, please someone put me out of my misery! How should I approach this?
Try to match the users first with the ids list (activeUserIds) then populate the posts and author fields in fetched records.
await User.find({'_id': {$in: [activeUserIds]}})
.populate({
path: 'posts',
model: 'Post',
populate: ({
path: 'author',
model: 'User'
})
});

Mongoose middleware when adding multiple elements to a mongo array

Basically I have two mongodb collections, users and projects.
I want to keep track on which projects a user is working on,
and also which users that are working on a given problem.
const User = mongoose.model('User', new Schema({
name: {
type: String,
},
_projects: [{
type: Schema.ObjectId,
ref: 'Project'
}]
})
and
const Project = mongoose.model('Project', new Schema({
title: {
type: String,
},
_usersWorkingOn: [{
type: Schema.ObjectId,
ref: 'User',
}]
})
Now, let's say i want to add multiple existing user-id to an existing project.
What would best practice be?
Right now I'm using findOneAndUpdate to get the project document to push the user-id to the array. And then a mongoose middleware were I take the last added entry and get the user-id from there:
Schema.post('findOneAndUpdate', addProjectToUser)
function addProjectToUser(updatedProject, next) {
User.findByIdAndUpdate(
updatedProject._usersWorkingOn[updatedProject._usersWorkingOn.length - 1],
{ $push: { '_projects': updatedProject._id}},
{ 'new': true }
).then(() => {
next()
})
}
This works when I add one user to a project. But if a want to add multiple users in one save, addProjectToUser have no way of knowing how many entries were added to the array.
Am I missing something here, or thinking about it all wrong?

Using Mongoose to findbyid and update sibling

I'm trying to locate a reference to another schema and update a sibling field. Specifically, I'm trying to manipulate the hasResponded field below based on a particular 'survey' ObjectId.
My schema looks like this:
var userSchema = new mongoose.Schema({
// some other stuff
surveys: [{
survey: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Survey'
},
hasResponded: {
type: Boolean,
default: false
}
}]
});
If you have a survey id, simply search for all the users that have this particular id in the array.
Something like that:
Users.find({surveys: { "$elemMatch": { type: <ID> } } });
Then, iterate through the users and their corresponding survey array to find the ones that match the id you gave.
Got to say I would structure this db a little different if this query takes place often.
Make a new Schema - UserSurveys that holds the id of the user and the survey + hasResponded. Like this:
var UserSurveySchema = new mongoose.Schema({
user_id: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
survey_id: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'}
hasResponded: {type:Boolean, 'default':false}
...
});
You might also want to keep an index on the user and survey ids.
Then, it will be much easier to update the field, requests will take much shorter times.
Hope this helps.

Resources