I have a problem in populate method in mongodb it can't retrieve data from model. Can anyone help me solve that problem?
This is the code
router.get('/', auth, async (req, res) => {
try {
const user = req.user._id;
const wishlist = await Wishlist.find({ user, isLiked: true })
.populate({
path: 'Products',
select: 'title',
})
.sort('-updated');
res.status(200).json({
wishlist,
});
} catch (error) {
res.status(400).json({
error: 'Your request could not be processed. Please try again.',
});
}
});
When I navigate to http://localhost:3000/wishlist/, this is the response I get:
{
"wishlist": [
{
"product": "60cb5eb82cc7091ae2e31c88",
"user": "60cb6c46291247466fe08f92",
"isLiked": true,
"_id": "60d1a656567e08bf89571209",
"updated": "2021-06-22T10:09:25.295Z",
"created": "2021-06-22T08:59:02.434Z",
"__v": 0
}
]
}
The model of products
const mongoose = require('mongoose');
const { Schema } = mongoose;
const ProductSchema = mongoose.Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
category:{
type: Schema.Types.ObjectId,
ref: 'Categories',
default: null
},
photo: { type: String, required: true },
createdAt: {
type: Date,
default: new Date(),
},
updateAt: Date,
price: {
type: String,
required: true,
},
quantity: {
type: Number
}
,
isActive: {
type: Boolean,
default: true
},
user: {
type: Schema.Types.ObjectId,
ref: "User",
}
});
module.exports = mongoose.model('Product', ProductSchema);
The model for wishlist:
const Mongoose = require('mongoose');
const { Schema } = Mongoose;
// Wishlist Schema
const WishlistSchema = new Schema({
product: {
type: Schema.Types.ObjectId,
ref: 'Product',
default: null,
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
default: null,
},
isLiked: {
type: Boolean,
},
updated: {
type: Date,
default: Date.now,
},
created: {
type: Date,
default: Date.now,
},
});
module.exports = Mongoose.model('Wishlist', WishlistSchema);
Can anyone help me please to find the solution for that problem?
Related
I'm very new to Nodejs and MongoDB, I have 3 collections one for chapter,one for lecture and one for asset
every course have chapters, every chapter has array of lectures and every lecture have assets
so I want to get chapters by courseId which already done, and inside chapter to get its lectures and in every lecture to get its assets
Course:
const mongoose = require('mongoose');
var Double = require('#mongoosejs/double');
const courseSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
requirements: {
type: String,
},
code: {
type: String,
required: true,
},
coverPhoto: {
type: String,
required: false,
},
description: {
type: String
},
instructor:{
type:mongoose.Schema.Types.ObjectId,
ref:'User',
required:true
},
category:{
type:mongoose.Schema.Types.ObjectId,
ref:'Category',
required:true
},
learns: [{
type: String
}],
subCategory:{
type:mongoose.Schema.Types.ObjectId,
ref:'SubCategory',
required:true
},
isCoaching: {
type: Boolean,
default: false,
},
isFree: {
type: Boolean,
default: false,
},
price: {
type: Double,
default: 0,
},
rating: {
type: Double,
default: 0,
},
isPublished: {
type: Boolean,
default: false,
},
dateCreated: {
type:Date,
default:Date.now,
},
});
exports.Course = mongoose.model('Course', courseSchema);
exports.courseSchema = courseSchema;
Chapter:
const mongoose = require('mongoose');
const chapterSchema = new mongoose.Schema({
course:{
type:mongoose.Schema.Types.ObjectId,
ref:'Course',
required:true
},
title: {
type: String,
required: true,
},
sort_order: {
type: Number,
default: 1,
},
is_published: {
type: Boolean,
default: true,
},
});
exports.Chapter = mongoose.model('Chapter', chapterSchema);
exports.chapterSchema = chapterSchema;
Lecture:
const mongoose = require('mongoose');
const lectureSchema = new mongoose.Schema({
chapter:{
type:mongoose.Schema.Types.ObjectId,
ref:'Chapter',
required:true
},
title: {
type: String,
required: true,
},
sort_order: {
type: Number,
default: 1,
},
is_published: {
type: Boolean,
default: true,
},
});
exports.Lecture = mongoose.model('Lecture', lectureSchema);
exports.lectureSchema = lectureSchema;
Asset:
const mongoose = require('mongoose');
const assetSchema = new mongoose.Schema({
lecture:{
type:mongoose.Schema.Types.ObjectId,
ref:'Lecture',
required:true
},
title: {
type: String,
required:true
},
asset_type: {
type: String
},
description: {
type: String,
require:true
},
file_url: {
type: String,
require:true
},
page_number: {
type: Number,
default:1
},
time_estimation: {
type: String,
require:true
},
is_external: {
type: Boolean,
default: false,
},
is_published: {
type: Boolean,
default: true,
},
});
exports.Asset = mongoose.model('Asset', assetSchema);
exports.assetSchema = assetSchema;
Get Chapters of a course
router.get(`/`, async (req, res) => {
let course_filter = {};
if (req.query.course) {
course_filter = {course:req.query.course};
}
const chapterList = await Chapter.find(course_filter).populate('lecture').sort('sort_order');
if (!chapterList) {
res.status(500).json({ success: false });
}
res.send(chapterList);
});
using simple aggregation:
const chapterList = await Chapter.aggregate([{
$lookup: {
from: 'lectures',
localField: '_id',
foreignField: 'chapter',
as: 'lectures'
}}]);
You have to nest the populate in another populate:
router.get(`/`, async (req, res) => {
let course_filter = {};
if (req.query.course) {
course_filter = { course: req.query.course };
}
const chapterList = await Chapter.find(course_filter)
.populate({ path: 'lectures', populate: { path: 'assets' } })
.sort('sort_order');
if (!chapterList) {
res.status(500).json({ success: false });
}
res.send(chapterList);
});
You have to ensure that you have set a virtual for 'assets' prop in lectureSchema accordingly. I assume you have also done it for your 'Chapter' schema.
If not, you have do add the following:
virtual for Chapter schema:
chapterSchema.virtual('lectures', {
ref: Lecture.collection.collectionName,
localField: '_id',
foreignField: 'chapter'
});
virtual for Lecture schema:
lectureSchema.virtual('assets', {
ref: Asset.collection.collectionName,
localField: '_id',
foreignField: 'lecture'
});
I want to get the comment by ID that is an array of embedded schema object in feedback schema model, see the code below:
Feedback schema:
const FeedbackSchema = new Schema({
receiver: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
comments: [
{
text: { type: String, maxlength: 500 },
postedBy: { type: Schema.Types.ObjectId, ref: 'User' },
date: { type: Date, default: Date.now() },
},
],
createdAt: {
type: Date,
default: Date.now(),
},
});
module.exports = {
FeedbackSchema: mongoose.model('Feedback', FeedbackSchema),
};
Comment controller and route:
router.get('/comment/:feedback_id/:comment_id', userAuthorization, getComment);
exports.getComment = async (req, res) => {
const feedback = await FeedbackSchema.findById({
_id: req.params.feedback_id,
});
if (!feedback) return res.status(404).send('Feedback not found');
const comment = feedback.comments.find({ _id: req.params.comment_id }); //error is in this line
if (!comment) return res.status(404).send('Comment not found');
return res.json(comment);
}
How do I get the comment based on feedback_id and comment_id?
Try to use findOne:
exports.getComment = async (req, res) => {
const feedback = await FeedbackSchema.findOne({
_id: req.params.feedback_id,
comments._id: req.params.comment_id
});
if (!feedback) return res.status(404).send('Feedback not found');
return res.json(feedback.comments);
};
Also, make sure that you declare the Comment schema separately:
const CommentSchema = new Schema({
text: { type: String, maxlength: 500 },
postedBy: { type: Schema.Types.ObjectId, ref: 'User' },
date: { type: Date, default: Date.now() },
})
const FeedbackSchema = new Schema({
receiver: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
comments: [CommentSchema],
createdAt: {
type: Date,
default: Date.now(),
},
});
here's the booking schema
const bookingSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
routeId:{
type: mongoose.Schema.Types.ObjectId,
ref: "Route",
required: true,
}
})
In this table the routeId(route schema) contains the Bus table(referenced).
const routeSchema = new mongoose.Schema({
location:{
type: mongoose.Schema.Types.ObjectId,
ref: 'Location',
required: true
},
duration: {
type: Number,
required: true
},
Bus:{
type: mongoose.Schema.Types.ObjectId,
ref:"Bus",
required: true
},
date: {
type:String,
required: true
},
},
{
timestamps: true,
});
and then finally the bus model
const busSchema = new mongoose.Schema(
{
busNumber: {
type: String,
unique: true,
required: true,
},
seats: {
type: Number,
},
},
{
timestamps: true,
}
);
Now I want to show only bus number and not whole bus data. this is the response I am getting. https://i.stack.imgur.com/S1qyZ.png
this is the query that I applied.
router.get("/bus/myBooking/one/:id", auth, async (req, res) => {
const _id = req.params.id;
const myBooking = await Booking.findById({ _id })
.populate({path: 'routeId', populate: 'Bus'})
.select(["-_id","-userId","-createdAt","-updatedAt","-__v","-passengers._id","-departureDetails._id","-arrivalDetails._id"]);
try {
return !myBooking
? res.status(404).send("No booking found")
: res.status(200).send(myBooking);
} catch (e) {
res.status(500).send();
}
});
This method works on some versions of mongoose, So you can try this:
await Booking.findById({ _id })
.populate({
path: 'routeId',
populate: {
path: 'Bus',
model: Bus,
select:"-_id busNumber"
}
})
I have some issues
Cant populate CartProduct, just show the ObjectId.
There is way to make every time that CartProduct create, add automatically to? cart.
is this the right way of schemas structure?
Cart
const CartSchema = new Schema({
active: { type: Boolean, required: true, default: true },
createAt: { type: Date, default: Date.now },
client: { type: Schema.Types.ObjectId, ref: "User", required: true },
products: [{ type: Schema.Types.ObjectId, ref: "CartProduct" }],
});
const Cart = model("Cart", CartSchema);
Cart Product
const CartProductSchema = new Schema({
item: { type: Schema.Types.ObjectId, ref: "Product", required: true },
cart: { type: Schema.Types.ObjectId, ref: "Cart", required: true },
quantity: { type: Number, required: true },
totalPrice: { type: Number, required: true },
});
const CartProduct = model("CartProduct", CartProductSchema);
Product
const ProductSchema = new Schema({
name: { type: String, required: true },
price: { type: Number, required: true },
image: { type: String, required: true },
category: { type: Schema.Types.ObjectId, ref: "Category", require: true },
});
const Product = model("Product", ProductSchema);
Cart Controller
router.post("/", async (req, res) => {
try {
const { userId } = req.body;
const cart = await Cart.findOne({ client: userId
}).populate("CartProduct");
if (cart === null) {
const newCart = new Cart({
client: userId,
});
await newCart.save();
return res.status(201).send({ cart: newCart });
}
res.status(200).send({ cart });
} catch (error) {
res.status(500).send(error);
}
});
Add Product to Cart
router.post("/addProductToCart", async (req, res) => {
try {
const { item, cart, quantity, price } = req.body;
const newProduct = new CartProduct({
item,
cart,
quantity,
totalPrice: price * quantity,
});
await newProduct.save();
await Cart.findOneAndUpdate(
{ _id: cart },
{ $push: { products: newProduct } },
{
new: true,
}
);
res.status(201).send({ message: "New Product Added To Cart" });
} catch (error) {
res.status(500).send(error);
}
});
adding product to cart does working,
but populate not working
adding the output
{
"cart": {
"active": true,
"products": [
"602bc081daf867167c2eb5da"
],
"_id": "602aab802f625d1654805ef0",
"client": "601c50211c94cf5d642c67fb",
"createAt": "2021-02-15T17:12:32.997Z",
"__v": 0
}
}
your missing { in cartSchema
const CartSchema = new Schema({
active: { type: Boolean, required: true, default: true },
createAt: { type: Date, default: Date.now },
client: { type: Schema.Types.ObjectId, ref: "User", required: true },
products: [{ type: Schema.Types.ObjectId, ref: "CartProduct" }],
});
export the the models like this
module.exports = Cart
There is way to make every time that CartProduct create, add automatically to? cart
there is not a automatic way for adding new _id of CartProduct to collection, so you should use findOneAndUpdate() or find() and push in to products array and save()
is this the right way of schemas structure
yes, It is.
so for populate you can try:
let resultCarts = await Cart.find(filter).populate("products")
let resultProducts = await Product.find(filter).populate("category")
so change the CartProduct to products because you should pass name of field as a argument not name of schema
await Cart.findOne({ client: userId
}).populate("products");
I have a schema for post like below, but am having problem fetching post from my followers. i have also tried using but all to no avail. please help
I have a schema for post like below, but am having problem fetching post from my followers. i have also tried using but all to no avail. please help
I have a schema for post like below, but am having problem fetching post from my followers. i have also tried using but all to no avail. please help
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema =new Schema({
user: {
type: Schema.Types.ObjectId,
ref:'users'
},
text:{
type:String,
required: true
},
name:{
type:String
},
avatar:{
type:String
},
likes:[
{
user:{
type: Schema.Types.ObjectId,
ref: 'users'
}
}
],
comments:[
{
user:{
type: Schema.Types.ObjectId,
ref: 'users'
},
text:{
type:String,
required: true
},
name: {
type: String
},
avatar: {
type: String
},
date:{
type:Date,
default: Date.now
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}
],
}
],
reposter: [
{
user: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
date: {
type: Date,
default: Date.now
}
}
],
numberOfRepost: { type: Number, default: 0 },
date: {
type: Date,
default: Date.now
}
});
module.exports = Post = mongoose.model('post', PostSchema);
First of all, you should rethink about mongo-collection design, Here is some tip which it is better to consider.
Use Upper Camel case for declaring mongoose model objects. ( Post, User, ... )
Always put _ before any reference variable. ( _user in Post models )
Separate your collections and avoid redundant properties as much as possible.
Always use Plural of a name for collections. ( posts vs post )
Do not forget to add created and updated property to each collection. ( this hack helps you for logging and investigating your models )
Now, let's look at our new design:
name and avatar are redundant data in the Post model. you can populate them later.
Separate Like, Comment, RePoster from Post model.
Here is the refined Post model object.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema(
{
_user: { type: Schema.Types.ObjectId, ref: 'User' },
text:{ type:String, required: true },
rePostsNum: { type: Number, default: 0 },
// any other meta data which you need
creaetd: Date,
updated: Date
},
{
collection: 'posts',
strict: true,
autoIndex: true
}
);
PostSchema.pre('save', function (next) {
if( this.isNew )
this.created = new Date();
this.updated = new Date();
next();
});
module.exports = Post = mongoose.model('Post', PostSchema);
You can also put _comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }] into the Post model, but think about it! If it is possible to store thousands of comments reference key in _comments array, it is not recommended, it's like technical debt.
Other models:
Comment:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CommentSchema = new Schema(
{
_user: { type: Schema.Types.ObjectId, ref: 'User' },
_post: { type: Schema.Types.ObjectId, ref: 'Post' },
text:{ type:String, required: true },
likesNum: { type: Number, default: 0 },
creaetd: Date,
updated: Date
},
{
collection: 'posts',
strict: true,
autoIndex: true
}
);
CommentSchema.pre('save', function (next) {
if( this.isNew )
this.created = new Date();
this.updated = new Date();
next();
});
module.exports = Comment = mongoose.model('Comment', CommentSchema);
LikePost
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LikePostSchema = new Schema(
{
_user: { type: Schema.Types.ObjectId, ref: 'User' },
_post: { type: Schema.Types.ObjectId, ref: 'Post' },
creaetd: Date,
updated: Date
},
{
collection: 'likePosts',
strict: true,
autoIndex: true
}
);
LikePostSchema.pre('save', function (next) {
if( this.isNew )
this.created = new Date();
this.updated = new Date();
next();
});
module.exports = LikePost = mongoose.model('LikePost', LikePostSchema);
LikeComment
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LikeCommentSchema = new Schema(
{
_user: { type: Schema.Types.ObjectId, ref: 'User' },
_comment: { type: Schema.Types.ObjectId, ref: 'Comment' },
creaetd: Date,
updated: Date
},
{
collection: 'likeComments',
strict: true,
autoIndex: true
}
);
LikeCommentSchema.pre('save', function (next) {
if( this.isNew )
this.created = new Date();
this.updated = new Date();
next();
});
module.exports = LikeComment = mongoose.model('LikeComment', LikeCommentSchema);
User
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema(
{
name:{ type:String, required: true },
avatar:{ type:String, required: true },
// any other meta data which you need
creaetd: Date,
updated: Date
},
{
collection: 'users',
strict: true,
autoIndex: true
}
);
UserSchema.pre('save', function (next) {
if( this.isNew )
this.created = new Date();
this.updated = new Date();
next();
});
module.exports = User = mongoose.model('User', UserSchema);
RePost
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const RePostSchema = new Schema(
{
_user: { type: Schema.Types.ObjectId, ref: 'User' },
_post: { type: Schema.Types.ObjectId, ref: 'Post' },
creaetd: Date,
updated: Date
},
{
collection: 'rePosts',
strict: true,
autoIndex: true
}
);
RePostSchema.pre('save', function (next) {
if( this.isNew )
this.created = new Date();
this.updated = new Date();
next();
});
module.exports = RePost = mongoose.model('RePost', RePostSchema);
Welcome back!
Now our new design is fully scalable and guides you to clean and robust code.
Finally, we can query and populate data, Here is two cool sample code:
Load specific user's posts
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Post = mongoose.model('Post');
var Comment = mongoose.model('Comment');
var LikePost = mongoose.model('LikePost');
var LikeComment = mongoose.model('LikeComment');
var RePost = mongoose.model('RePost');
Post
.find({ _user: userId })
.select('_id _user text ...')
.populate({
path: '_user',
select: '_id name avatar ...'
})
.exec(function (err, poats) {
Load specific post's comments
Comment
.find({ _post: postId })
.select('_id _post _user text ...')
.populate({
path: '_user',
select: '_id name avatar ...'
})
.exec(function (err, comments) {
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
phonenumber: {
type: String,
required: true
},
email: {
type: String
},
resetPasswordToken: {
type: String
},
lastlogin: {
type: Date, default: Date.now
},
passwordUpdated: {
type: Date, default: Date.now
},
resetPasswordExpires: {
type: Date
},
dateofbirth: {
type: Date
},
dateJoined: {
type: Date,
default: Date.now
},
sentRequest: [{
username: { type: String, default: '' },
date: {
type: Date,
default: Date.now
}
}],
request: [{
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
username: { type: String, default: '' },
name: { type: String, default: '' },
phone: { type: String, default: '' },
date: {
type: Date,
default: Date.now
}
}],
friendsList: [{
friendId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
friendName: { type: String, default: '' },
friendUsername: { type: String, default: '' },
friendPhone: { type: String, default: '' },
date: {
type: Date,
default: Date.now
}
}],
followers: [{
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
date: {
type: Date,
default: Date.now
}
}],
blockedContacts: [
{
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
date: {
type: Date,
default: Date.now
}
}],
totalRequest: { type: Number, default: 0 },
repost: [
{
postId: { type: mongoose.Schema.Types.ObjectId, ref: "Post" },
date: {
type: Date,
default: Date.now
}
}
],
accolades: [
{
user: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'
},
remarks: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
followers: [{
user: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'
},
date: {
type: Date,
default: Date.now
}
}],
following: [{
user: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'
},
date: {
type: Date,
default: Date.now
}
}],
})
UserSchema.pre('save', function (next) {
var user = this;
//check if password is modified, else no need to do anything
if (!user.isModified('pass')) {
return next()
}
user.pass = bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
next()
})
module.exports = User = mongoose.model('Users', UserSchema);
This is the user schema