Node.js and Mongoose - map populated array inside object - javascript

I have 2 schemas - "Category" and "Tag".
Each Category has an array of tags linked to it.
What I'm trying to achieve is: get category by id with its tags populated but instead of returning the category with array of tag objects I would like to map the tags to an array of tags names:
This approach below is not working, The returned Category has still an array of tag objects instead of tag names
const getById = async (req, res, next) => {
const id = req.params.id;
let category;
let tags;
try {
category = await Category.findById(id)
.populate('tags');
if (!category) throw new HttpError("Could not find category with the provided id", 400);
tags = category.tags.map(tag => tag.name);
category.tags = tags;
} catch (err) {
return next(err);
}
const statusCode = 200;
res.status(statusCode).json({
category,
code: statusCode,
message: "Success"
});
}
const {Schema} = mongoose;
const categorySchema = new Schema({
name: {
type: String,
required: [true, 'Enter name'],
trim: true,
unique: true,
set: name => capitalizeFirstLetter(name)
},
dateCreated: {type: Date, default: new Date(), get: (dateCreated) => moment(dateCreated)},
tags: [{type: mongoose.Types.ObjectId, ref: 'Tag'}],
});
module.exports = mongoose.model("Category", categorySchema);
const {Schema} = mongoose;
const tagSchema = new Schema({
name: {
type: String,
required: [true, 'Enter name'],
trim: true,
unique: true,
set: name => capitalizeFirstLetter(name)
},
dateCreated: {type: Date, default: new Date(), get: (dateCreated) => moment(dateCreated)}
});
module.exports = mongoose.model("Tag", tagSchema);

Found the solution:
const getById = async (req, res, next) => {
const id = req.params.id;
let category;
let tags;
try {
category = await Category.findById(id)
.populate('tags').lean();
if (!category) throw new HttpError("Could not find category with the provided id", 400);
tags = category.tags.map(tag => tag.name);
category.tags = tags;
} catch (err) {
return next(err);
}
const statusCode = 200;
res.status(statusCode).json({
category,
code: statusCode,
message: "Success"
});
}

Related

Mongoose Database populate issue

This is the code
app.get("/cart", checkAuthentication, function (req, res) {
Orders.find({ user: req.user._id })
.populate('user')
.populate('order')
.exec((err, orders) => {
console.log(orders);
if (err) {
console.log("ERROR /cart :\n" + err);
res.redirect("/");
} else {
const OrderList = [];
orders.forEach((order) => {
const obj = {
order: order.order,
id: order._id
}
OrderList.push(obj);
});
var sum=0
OrderList.forEach(function(item){
sum += item.order.price
});
req.session.sum = sum;
req.session.orders = OrderList;
res.render("cart", { itemList: OrderList, login: true, name: req.user.name });
// res.render("cart", { itemList: OrderList, login: false, name: "abc" });
}
});
});
This is order model =>
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const orderSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User" },
order: { type: Schema.Types.ObjectId, ref: "SellingItem" },
date: { type: Date, default: Date.now }
});
module.exports = mongoose.model("Orders", orderSchema);
THIS IS THE ERROR
ERROR(null)
This is the github link for my repo - https://github.com/Paras0750/Bakery_Website/
I am trying to populate orders field but it is showing null.
You are importing csv file with _id value. It will become string in the database instead of ObjectId. That's why it cannot populate. Your code is correct.

Validation Error in mongoDB while making a reference to another Schema?

I have created a blog schema in mongo which makes reference to the user schema.
However, when I try to save the blog in MongoDB I get the following error: -
CUrrrent post user: new ObjectId("61d28db34c78f60375189033")
User validation failed: passwordHash: Path `passwordHash` is required., name: Path `name` is required., username: Path `username` is required.
I am sending this via JSON
{
"title": "Best Copywriting formulas!",
"author": "Copywriters Inc.",
"url": "https://buffer.com/resources/copywriting-formulas/",
"likes": 420
}
I am unable to decode why I am getting this validation error when I am adding nothing new to the User schema.
Here is my main router code: -
blogRouter.post('/', async (request, response) => {
const blog = new Blog(request.body)
if (blog.author === undefined || blog.title === undefined)
return response.status(400).json({
error: "name or title missing!"
})
//temporary get the first user from the Users db
const userDB = await User.find({});
//Get the first available user in db
const currentUser = userDB[0]._id;
console.log('CUrrrent post user: ', currentUser);
const newBlog = new User({
title: request.body.title,
author: request.body.author,
url: request.body.url,
likes: request.body.likes || 0,
user: currentUser
})
try {
const newEntry = await newBlog.save()
response.status(200).json(newEntry);
} catch (error) {
logger.error(error.message);
}
})
My Blog Schema: -
const blogSchema = new mongoose.Schema({
title: String,
author: String,
url: String,
likes: {
type: Number,
default: 0
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
})
blogSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
}
})
module.exports = mongoose.model('Blog', blogSchema)
Here is my user Schema: -
var uniqueValidator = require('mongoose-unique-validator');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minLength: 3,
unique: true
},
name: {
type: String,
required: true
},
passwordHash: {
type: String,
required: true
}
})
userSchema.plugin(uniqueValidator, {message: 'username already taken. {VALUE} not available.'});
userSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
delete returnedObject.passwordHash
}
})
const User = mongoose.model('User', userSchema);
module.exports = User
You should change your model name when creating a new Blog document:
const newBlog = new Blog({
title: request.body.title,
author: request.body.author,
url: request.body.url,
likes: request.body.likes || 0,
user: currentUser,
});
Also, a good practice would be to check if there are any users in the database before retrieving the first one.
This to avoid possible index out of bounds exceptions:
const userDB = await User.find({});
if (userDB.length > 0) {
const currentUser = userDB[0]._id;
...

Express doesn't get another user with .map()

I came to a problem, where I can create conversations with multiple people 2 and so on. However, I can't understand why it doesn't store data to seperate User models.
Here is a code that you only need to know:
router.post(
"/",
auth,
[
check("conversators", "There should be at least two conversators").isLength(
{ min: 2 }
),
],
async (req, res) => {
const { conversators } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
let conversation = new Conversation({
user: req.user.id,
conversators: conversators,
});
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
console.log('Created data', newData);
let newUser = await User.findOneAndUpdate(
{ user: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
await newUser.save();
console.log(newUser);
});
await conversation.save();
res.status(200).json(conversation);
} catch (error) {
console.error(error.message);
res.status(500).send("Server error.");
}
}
);
module.exports = router;
What I can assure is that this line: console.log('Created data', newData); prints the desired data. However, the next console: console.log(newUser); prints the same User model as the previous one.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
surname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
conversations: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "conversation",
},
],
date: {
type: Date,
default: Date.now,
},
});
module.exports = User = mongoose.model("user", UserSchema);
The reason might be the difference in search methods used to get a record for newData and newUser. You have used User.findById for newData, which will obviously return different objects for different ids. But User.findOneAndUpdate uses filter criteria that may satisfy several results, but only first will be returned. So it boldly depends on what that user field is.
Here is the part that I changed and started to see the data on MongoDB:
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
new Promise(async (resolve, reject) => {
user = await User.findOneAndUpdate(
{ id: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
return resolve;
})
return await user.save();
});
Posted on behalf of the question asker

Item.filter is not a function

Hi everyone I am making a route to get the items that are created by the logged-in user but when I use the .filter function I get an error. Not sure why I am getting this error. I have made other apps before doing the same thing and never got an error
Item.filter is not a function
The my-items route
const requireAuth = require("../middleware/requireAuth");
const express = require("express");
const mongoose = require("mongoose");
const Item = mongoose.model("Item");
router.get("/my-items", requireAuth, async (req, res) => {
try {
const items = Item.filter((item) => item.userId === req.user.userId);
res.send(items);
} catch (err) {
console.log(err);
}
});
Item Schema
const mongoose = require("mongoose");
const itemSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
phone: {
type: mongoose.Schema.Types.String,
ref: "User",
},
email: {
type: mongoose.Schema.Types.String,
ref: "User",
},
seller: {
type: mongoose.Schema.Types.String,
ref: "User",
},
title: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
detail: {
type: String,
requiredL: true,
},
condition: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
});
mongoose.model("Item", itemSchema);
const items = await Item.find(({userId:req.user.userId}).lean();
it should return exact items from db that you want you can use more query if you need.
Item is a model but not the documents in the database, you need to do a query first in order to get the items.
router.get("/my-items", requireAuth, async (req, res) => {
try {
const query = Item.find()
query.exec().then(items => {
const filteredItems = items.filter((item) => item.userId === req.user.userId);
res.send(items);
})
} catch (err) {
console.log(err);
}
});
This error can occur when you are trying to use the array methods on other data structures.
This piece of code returns an error .filter is not a function:
const myList = await getList().filter(item => item.myKey > 10);
Solution:
const data = await getList();
const myList = data.filter(item => item.myKey > 10);

Mongoose assign a collection to another

I am trying to add a post to a user collection after the user was created with empty posts. I have tried with populate with no success .. any help is much appreciated.
// Post Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UserModel = require('./user-model');
let PostSchema = new Schema({
author: {
ref: 'users',
type: Schema.Types.ObjectId
},
content: String,
description: String,
date: {
default: new Date(),
type: Date
},
title: String,
updatedAt: {
default: new Date(),
type: Date
}
});
let PostModel = mongoose.model('posts', PostSchema);
module.exports = PostModel;
// User Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PostModel = require('./post-model');
let UserSchema = new Schema({
name: {
type: String
},
email: {
lowercase: true,
type: String,
trim: true,
unique: true
},
password: {
type: String,
},
postList: [{
ref: 'posts',
type: Schema.Types.ObjectId
}],
});
const UserModel = mongoose.model('users', UserSchema);
module.exports = UserModel;
// save post controller
exports.savePost = (request, response, next) => {
let { author, description, title } = request.body;
let post = new PostModel({ author, description, title }).save();
UserModel.findById(author)
.then((user) => {
user.postList.push(post);
// Still Fails
// How can i assign the post to the user ?
});
}
Is there any way of doing this other then push or populate ?
To solve this problem I prefer to use $push of mongo
UserModel.findOneAndUpdate({
_id: author.id,
{
$push: {
postList: post
}
}
});
You need to follow this process to save successfully
Save post if success then
Update user to push postId in postlist
can try this one
exports.savePost = (request, response, next) => {
let post = new PostModel(request.body)
post.save(function(err, data) {
if(err) {
//return error
}
// check by console your author is present or not
// let author in your req.body
let author = req.body.author
UserModel.findOneAndUpdate({_id: author},{$push: {postList: post._id}},{new:true} function(error, user) {
if(error) {
// return error
}
console.log(user)
// return success
})
});
}
exports.savePost = (request, response, next) => {
let { user, description, title } = request.body;
let post = new PostModel({ user, description, title });
post.save()
.catch((error) => {
if (error)
throw new Error(error);
})
.then((post) => {
UserModel.findOneAndUpdate({ _id: user }, {$push: { postList: post._id } })
.populate('postList')
.catch((error) => {
if (error)
throw new Error(error);
})
.then((user) => {
user.postList.forEach((item, postion) => {
console.log(`${item} -at ${postion} \n`);
});
});
});
}
This is what i did and it worked after all. I don't know if this is the correct solution but this is working.

Categories