I'm trying populate function in mongoose docs
i've searched a lot for this, i couldn't find anything.
and i have one more question. is this good approach to do this since i would need books whenever i need author in my front end code
is there any other ideas to get this behavior?
I've a simple example.
here is my models
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Author = new Schema({
name: {
type: String,
required: true,
},
books: [{ type: Schema.Types.ObjectId, ref: "Book" }],
});
const Book = new Schema({
title: {
type: String,
required: true,
},
author: { type: Schema.Types.ObjectId, ref: "Author" },
});
module.exports = {
Author: mongoose.model("author", Author),
Book: mongoose.model("book", Book),
};
then I created some routes
const express = require("express");
const { Book, Author } = require("../models");
const router = express.Router();
router.get("/books", async (req, res) => {
let books = await Book.find();
res.send(books);
});
router.get("/authors", async (req, res) => {
let authors = await Author.find();
res.send(authors);
});
router.post("/book/:author", async (req, res) => {
let { title } = req.body;
let { author } = req.params;
let book = new Book({
title,
author,
});
book.save().then((data) => {
res.send(data);
});
});
router.post("/author", (req, res) => {
let { name } = req.body;
let author = new Author({
name,
});
author.save().then((data) => {
res.send(data);
});
});
module.exports = router;
then I created a new Author and new Book calling these 2 end points I created using postman
here is result from GET /authors
[
{
"books": [],
"_id": "6085a6fb098c0003944b1dcd",
"name": "john",
"__v": 0
}
]
why books are empty array?
I was expecting it to be something like this
[
{
"books": [
{
"_id": "6085a702098c0003944b1dce",
"title": "hello",
}
],
"_id": "6085a6fb098c0003944b1dcd",
"name": "joe",
"__v": 0
}
]
if your other routes have no problems when saving and retrieving data (check the result using mongo shell or Mongo compass or anything else, or better, write tests to automate things for you before coding, anyhow), you should populate books field
router.get("/authors", async (req, res) => {
let authors = await Author.find().populate('books').lean();
res.send(authors);
});
this is your code with out considering your http handler
try it with and without populate to understand the difference
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/testX', { useNewUrlParser: true, useUnifiedTopology: true });
const Author = mongoose.model('Author', {
name: {
type: String,
required: true,
},
books: [{ type: mongoose.ObjectId, ref: 'Book' }],
});
const Book = mongoose.model('Book', {
title: {
type: String,
required: true,
},
author: { type: mongoose.ObjectId, ref: 'Author' },
});
(async () => {
try {
const book1 = new Book({ title: 'titleHere' });
await book1.save();
const author1 = new Author({ name: 'nameHere' });
author1.books.push(book1);
await author1.save();
const authors = await Author.find().populate('books').lean();
console.log(authors);
} catch (error) {
console.error(error);
}
})();
Related
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.
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
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);
I'm attempting to get a better grasp on Express.js, and am trying to create a simple blogging site.
My user model is simple: Username, Display Name, and an array of posts.
const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3
},
displayname: {
type: String,
required: true,
minlength: 1
},
posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }]
}, {
timestamps: true,
});
The same goes for my Post model: content and author.
const postSchema = new Schema({
body: {
type: String,
required: true,
minlength: 1,
maxlength: 140
},
author: { type: Schema.Types.ObjectId, ref: 'User', required: true }
});
I've successfully created a new user and post:
[
{
"posts": [],
"_id": "5d32c9474e28f66c08119198",
"username": "Prince",
"displayname": "The Artist",
"createdAt": "2019-07-20T07:56:55.405Z",
"updatedAt": "2019-07-20T07:56:55.405Z",
"__v": 0
}
]
[
{
"_id": "5d34af1ecae7e41a40b46b5a",
"body": "This is my first post.",
"author": "5d32c9474e28f66c08119198",
"__v": 0
}
]
Here's my routes for creating a user and post
//Create a user
router.route('/create').post((req, res) => {
const username = req.body.username;
const displayname = req.body.displayname;
const newUser = new User({
username,
displayname
});
newUser.save()
.then(() => res.json('New user created!'))
.catch(err => res.status(400).json(`Error: ${err}`));
});
//Create A Post
router.route('/create').post((req, res) => {
const body = req.body.body;
const author = req.body.author;
const newPost = new Post({
body,
author
});
newPost.save()
.then(() => res.json('Created new post!'))
.catch(err => res.status(400).json(`Error: ${err}`));
});
And my method to get all posts by a user:
//Get all posts by user
router.route('/posts/:id').get((req, res) => {
User.findById(req.params.id)
.populate('posts')
.exec((err, user) => {
if(err) {
res.status(400).json(`Error: ${err}`);
} else {
res.json(user.posts);
}
});
});
Whenever I look at the response from /users/posts/:id, the array is empty, but I would expect it to be filled with the post made by the user with the ID I supply? Am I misunderstanding how populate works?
You need to add the posts id to the users post list in order for mongoose's populate to work properly.
router.post('/create' async (req, res) => {
const body = req.body.body;
const author = req.body.author;
const newPost = new Post({
body,
author
});
try {
const user = await User.findById(author);
const post = await newPost.save()
user.posts = user.posts.concat(post._id)
await user.save()
return res.json(post.toJSON())
} catch (e) {
return res.status(400).json(`Error: ${e}`));
}
});
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.