mongoose, populate V.S another query - mongodb

my case is:
I have postSchema and commentSchema, like this:
const postSchema = new Schema({
comments: [
{
type: Schema.Types.ObjectId,
ref: 'Comment'
}
]
}
const commentSchema = new Schema({
post: {
type: Schema.Types.ObjectId,
ref: 'Post'
}
}
If I know the post._id, I want to query the post documents and comment documents.
I think there are two way:
popluate: Post.findById(post._id).populate('Comment').exec()
start another query:
Post.findById(post._id).exec()
Comment.find({post: new Types.ObjectId(post._id)}).exec()
So, which is better? Or, when to use populate and when to start another query?

Related

How to use populate, within select, in MongoDB?

Suppose I have two schemas on MongoDB:
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
email: String,
things: [{ type: Schema.Types.ObjectId, ref: 'Thing' }]
});
const thingSchema = Schema({
_id: Schema.Types.ObjectId,
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
Every time a user logs in, I would like to show the things that they have posted, as well as the fans that are following each of the things. I am able to use populate and select to get to that:
const user = await personModel
.findOne({ _id: req.user._id })
.populate({
path: "things",
select: ["title", "fans"]
}),
However, I am only getting the id of each fan, and not the fan's name and email. I can't quite figure out how to use populate to reference the person collection again.
The outcome I am trying to achieve is that:
the user object would have an array of things
the thing object would have an array of fans
the fan object would have two values - name and email of the fan
You can do nested population with:
const user = await personModel
.findOne({ _id: req.user._id })
.populate({
path: 'things',
select: ['title', 'fans'],
populate: { path: 'fans' },
})
.exec();

The most efficient way to handle one to many relationship in MongoDB

Let's say I have a one-to-many relationship - A user has many todos. I want to be able to perform the following:
Give me a specific user without the todos
Give me the todos belongs to a specific user
Give me a single todo by id
Add, update, and delete todo
I can tackle it in three ways:
Embedding the todos inside the user document
import { Schema, model } from 'mongoose';
const userSchema = model('User', new Schema({
name: String,
todos: [{ title: String, id: String }],
}));
Use Todo Ref
import { Schema, model } from 'mongoose';
const userSchema = model('User', new Schema({
name: String,
todos: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Todo"
}],
}));
const todoSchema = model('Todo', new Schema({
name: String
}));
Use Todo Id
import { Schema, model, ObjectId } from 'mongoose';
const userSchema = model('User', new Schema({
name: String
}));
const todoSchema = model('Todo', new Schema({
name: String,
userId: ObjectId
}));
What will be the most efficient way to handle this scenario in MongoDB? Adding an index on the userId property in the last solution will make the query faster?

Is there anyway to remove one Document and then update ref model's ObjectId array automatically?

Here my models.
const UserSchema = mongoose.Schema({
name: String,
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
});
const CommentSchema = new mongoose.Schema({
comment: String,
creatorId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
User could have many comments.
So User and Comment is one-to-many relationship.
When User create a new comment, A new Comment is created and it's ObjectId is pushed to User's comments array.
What I want to do is,
delete Comment's ObjectId automatically when delete one Comment.
I know I can do this with two queries.
Is there anyway to combine these two to one?
await user.comments.remove(comment.id);
await comment.remove();
You can't run remove on two collections / schemas however you can take advantage of Post Middleware and define such middleware on CommentsSchema:
CommentsSchema.post('remove', function(comment) {
await UserSchema.update({ _id: comment.creatorId }, { $pull: { comments: comment._id } })
});

Additional field in array of _id's referencing another collection

this is what I have and it works:
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component"
}]
})
This is what I want to achieve:
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component",
amount: {type: Integer}
}]
})
Is it possible in MongoDB, if not what is the best workaround?
Thank you :-)
This schema work because of an element or filed name is provided
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component"
}]
})
Now you made a single mistak you want to create schema name without name in object with two different filed
Right way to create schema like this is to make other variable inside of array which contain type of filed
var comboSchema = new Schema({
components: [{
id: {
type: Schema.Types.ObjectId,
ref: "Component"
},
amount: { //If you want to make every component amount
type: Number
}
}]
})
Or
var comboSchema = new Schema({
amount: { type: Number },
//If you want to make only single amount on multiple components
components: [{
componentId: {
type: Schema.Types.ObjectId,
ref: "Component"
}
}]
})
But in both case you can't populate directly. You need to use aggregation for that to get data for embedded documents.

Using Mongoose Populate Virtuals on Arrays

Here is my Follow model
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let FollowSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
followers: [{
type: Schema.Types.ObjectId,
ref: 'Card'
}],
following: [{
type: Schema.Types.ObjectId,
ref: 'Card'
}]
});
module.exports = mongoose.model('Follow', FollowSchema);
Here's my Card model
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let CardSchema = new Schema({
title: String,
content: String,
likes: Number,
createdById: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
module.exports = mongoose.model('Card', CardSchema);
A typical Follow Model in DB will have something like this:
{
"_id" : ObjectId("59f0eef155ee5a5897e1a66d"),
"user" : ObjectId("59e3e617149f0a3f1281e849"),
"following" : [
ObjectId("59e21942ca5652efc4ca30ab"),
ObjectId("59e13b2dca5652efc4ca2cf5")
]
}
Here's what I'm trying to do:
To get all post from those you follow, find user in Follow model, and for each ObjectId in the following array, pull all documents from Card model, matching createdById field.
Using Mongoose Populate Virtuals, how do I do the schema relationship and populate query eventually?
Or, how would I go about creating and querying such a relationship with or without virtuals?