My mongoose populate() function isnt working, I dont know why - node.js

I'm trying to populate the content key of my posts model with the contents of my content collection but it isnt working. The content collection is getting populated but the data isnt getting passed to posts collection's content key.
Schemas:
const postsSchema = new mongoose.Schema({
_id: {
type: String,
required: true
},
title: {
type: String,
required: true
},
content: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Content' }]
})
const contentSchema = new mongoose.Schema({
_id: {
type: String,
required: true
},
subHeading: String,
subHeadingContent: String,
image: {
data: Buffer,
contentTyle: String
}
})
const content = mongoose.model("Content", contentSchema)
const posts = mongoose.model("Posts", postsSchema)
Post request to populate the collections:
// Write a new post
app.post("/create", function (req, res) {
let Posts = {
title: req.body.newTitle,
subheading: req.body.newSubHeading,
subheadingcontent: req.body.newSubHeadingContent
}
postsArray = [];
postsArray.push(Posts)
console.log(postsArray);
postsArray.forEach((post) => {
content.create({
_id: uuidv4(),
subHeading: post.subheading,
subHeadingContent: post.subheadingcontent
})
posts.create({
_id: uuidv4(),
title: post.title,
})
posts.find().populate('content').exec((err, posts) => console.log("populated: ", posts))
})
res.redirect("/overview");
})

You do not need to have a postsArray & a for loop since your only trying to create a post from a single object.
Anyways, the reason it's not showing up is because you need to push the content object to your posts array.
First, you give a variable to the content.create,
then you push the variable to the array when you're creating the post
app.post("/create", function (req, res) {
let post = {
title: req.body.newTitle,
subheading: req.body.newSubHeading,
subheadingcontent: req.body.newSubHeadingContent
}
const postContent = content.create({
_id: uuidv4(),
subHeading: post.subheading,
subHeadingContent: post.subheadingcontent
})
posts.create({
_id: uuidv4(),
title: post.title,
{ $push: { content: postContent } }
})
posts.find().populate('content').exec((err, posts) => console.log("populated: ", posts))
res.redirect("/overview");
})

Related

Moongoose populate method returning an empty array

I am trying to fetch the blogs that a user has posted using mongoose populate method but not able to do so but when I try to look for the user who posted a blog I am able to do so. Tell me what I am doing wrong.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
blogPosts: [{ type: mongoose.Types.ObjectId, ref: "Blogs", required: true }],
});
const Users = mongoose.model("Users", userSchema);
module.exports = Users;
This is my user model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const blogSchema = new Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
user: {
type: mongoose.Types.ObjectId,
ref: "Users",
required: true,
},
});
const blogs = mongoose.model("Blogs", blogSchema);
module.exports = { blogs, blogSchema };
this is my blogs model
router.get("/users/:id", async (req, res) => {
let userBlogs;
try {
userBlogs = await users.find({ _id: req.params.id }).populate("blogPosts");
if (!userBlogs) {
return res.status(404).json({ msg: "No user found" });
}
return res.status(200).json(userBlogs);
} catch (err) {
return res.status(500).json({ err });
}
});
this is the route that I use for fetching all the blogs that a user has posted.This is just sending me back an empty posts array.
router.post("/newBlog/:id", async (req, res) => {
let blogAuthor;
try {
blogAuthor = await users.findOne({ _id: req.params.id });
} catch (err) {
return res.status(404).json({ message: "user not found" });
}
const newBlog = blogs({
title: req.body.title,
description: req.body.description,
image: req.body.imageUrl,
user: blogAuthor._id,
});
try {
await blogAuthor.blogPosts.push(newBlog);
await newBlog.save();
return res.status(200).json(blogAuthor.blogPosts);
} catch (err) {
return res.status(500).json(err);
}
});
the above is the route that I use to add new blog to the db.
router.get("/allBlogs", async (req, res) => {
let allBlogs;
try {
allBlogs = await blogs.find({}).populate("user");
} catch (err) {
return res.status(404).json(err);
}
res.status(200).json(allBlogs);
});
this is the route that sends all the blogs posted by all the users.
Interesting thing is that when I try to populate the user of a blogpost that is working exactly as expected it is populating the user of the blog from the users model however when I try to do the reverse that's not working. It is not populating all the blogs that a user has posted.

Is there a way i could keep track of the Time and the entity that was changed in a model

Basically I'm trying to get the time and the entity changed in a particular model when ever the update method is called.
This is my model I want to keep track of:
const mongoose = require("mongoose");
const modelSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
name: {
type: String,
required: true,
},
note1: String,
note2: String,
note3: String,
images: {
type: Array,
required: true
},
status: {
enum: ['draft', 'pending_quote', 'pendong_payment', 'in_production', 'in_repair', 'pemding_my_review', 'fulfilled'],
type: String,
default: "draft"
},
price: {
type: mongoose.Schema.Types.ObjectId,
ref: "Price",
}
}, {
timestamps: true,
})
module.exports = mongoose.model("Model", modelSchema)
And this is the method I call to update the status:
exports.updateModel = async (req, res) => {
try {
let id = req.params.id;
let response = await Model.findByIdAndUpdate(id, req.body, {
new: true
})
res.status(200).json({
status: "Success",
data: response
})
} catch (err) {
res.status(500).json({
error: err,
msg: "Something Went Wrong"
})
}
}
you can add a new field in your schema like:
logs:[{
entity: String,
timeStamp: Date
}]
Then updating it basing on your current code:
let id = req.params.id;
// I don't know whats in the req.body but assuming that it
// has the correct structure when passed from the front end
let response = await Model.findByIdAndUpdate(id,
{
$set:req.body,
$push:{logs:{entity:'your entity name here',timeStamp:new Date()}}
}, {
new: true
})

How do I query a specific Post by slug property value in mongoose instead of id?

I know how to query a specific post by id. However I want to use the slug property of the post instead of its id to to query it. How do I do so ?
//Instead of req.params.id, we have req.params.slug instead
//How do get the post in this case if the Post database model has a slug property.
//We have the req.params.slug
//This is what needs to be changed
const post = await Post.findById(req.params.id, (error, post) => {
console.log(error, post)
}).populate('author')
Here is the Post model:
const mongoose = require('mongoose')
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
subtitle: {
type: String,
required: true,
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
content: {
type: String,
required: true
},
image: String,
category: String,
subCategory: String,
createdAt: {
type: Date,
default: new Date()
}
})
module.exports = mongoose.model('Post', PostSchema)
you can use find() if you have many documents share the same slug or findOne() if this slug is unique for each document
Post.find({ slug: req.params.slug }, (error, post) => {
console.log(error, post)
});
or
Post.findOne({ slug: req.params.slug }, (error, post) => {
console.log(error, post)
});

how to fetch user details from populated table in nodejs?

I have two tables in my MongoDB cluster i.e Post and User. Actually I want to retrieve the username and userID along with a post from the post table when a user posts something.
my post controller
router.getPostsList = async (req, res) => {
try {
const posts = await Post.list();
return res.status(200).json(posts);
} catch (err) {
return res.status(400).json(err);
}
};
MongoDB Post collection Image
PostSchema
const PostSchema = new mongoose.Schema(
{
text: {
type: String,
trim: true
},
slug: {
type: String,
trim: true,
lowercase: true
},
user: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
]
},
{ timestamps: true }
);

How can I query an Array for a subdocument with mongoose?

I am looking to query a subdocument that contains an array.
You can think of this as an application similar to medium.com or tumblr.com:
I have a user schema, and User's have many Posts. My post schema has a 'tags' key which is an array I am trying to create a route which will display all posts that have a certain tag.
So for instance,
GET /user/posts/:tag
GET /user/posts/dogs
might return back the data for all posts (by any user) that have the tag 'dogs'
{
description: 'dogs are cool',
title: 'huskies',
tags: ['dogs','animals','outdoors'],
original_poster: '34255352426'
},{
description: 'My puppies are nice',
title: 'Puppies',
tags: ['pugs','dogs','small'],
original_poster: '44388342426'
},{
description: 'I like cats more than dogs',
title: 'Cats',
tags: ['kittens','cats','dogs'],
original_poster: '15708213454'
}
Here is my User Schema:
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
PostSchema = require('./post-model.js');
let UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: false, //only required for local users
unique: false,
default: null
},
following: {
type: [Number],
required: false,
unique: false
},
followers: {
type: [Number],
required: false,
unique: false
},
posts: [PostSchema],
})
module.exports = mongoose.model('User',UserSchema);
Here is my Post Schema:
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
let PostSchema = new Schema({
description: {
type: String,
required: false,
unique: false
},
title: {
type: String,
required: false,
unique: false
},
tags: {
type: [String],
required: true
},
original_poster: {
type: String,
required: true
}
});
module.exports = PostSchema;
EDIT:
to clarify my question, imagine that this was a regular js object, here is what a function to return all data relevant to a specific tag:
let users = [];
let user1 = {
username: 'bob',
posts: [
{
title: 'dogs r cool',
tags: ['cats','dogs']
},
{
title: 'twd is cool',
tags: ['amc','twd']
}]
};
let user2 = {
username: 'joe',
posts: [{
title: 'mongodb is a database system that can be used with node.js',
tags: ['programming','code']
},
{
title: 'twd is a nice show',
tags: ['zombies','horror-tv','amc']
},
{
title: 'huskies are my favorite dogs',
tags: ['huskies','dogs']
}]
}
users.push(user1);
users.push(user2);
function returnPostByTag(tag) {
let returnedPosts = [];
users.forEach((user,i) => {
user.posts.forEach((post) => {
if(post.tags.includes(tag)) {
let includespost = {}
includespost.title = post.title;
includespost.tags = post.tags;
includespost.original_poster = user.username;
returnedPosts.push(includespost);
}
})
})
return returnedPosts;
}
If you want to see a full repl where I use a plain js example here it is: https://repl.it/repls/LavenderHugeFireant
You can do the following using Mongoose to return all users where the tags array in any of the subdocuments in the posts array includes tag:
User.find({ 'posts.tags': tag }, function(err, users) {
if (err) {
// Handle error
}
let filteredPosts = users
.map(user => {
user.posts.forEach(post => post.original_poster = user.username)
return user
})
.map(user => user.posts)
.reduce((acc, cur) => acc.concat(cur), [])
.filter(post => post.tags.includes(tag))
// Do something with filteredPosts
// ...such as sending in your route response
})
...where User is your Mongoose model and tag contains the tag string you want to query (perhaps from your route request).
If you are using promises then you could do:
User.find({ 'posts.tags': tag })
.then(users => {
let filteredPosts = users
.map(user => {
user.posts.forEach(post => post.original_poster = user.username)
return user
})
.map(user => user.posts)
.reduce((acc, cur) => acc.concat(cur), [])
.filter(post => post.tags.includes(tag))
// Do something with filteredPosts
// ...such as sending in your route response
})
.catch(err => {
// Handle error
})

Resources