Mongoose populate not populating array of ref objects - node.js

I have two schemas - User and Site. User has an array of references to Site. I am trying to return the array of Sites but for some reason its returning the array containing the ObjectIds only.
UserSchema
const UserSchema = new mongoose.Schema({
sites: [{ site: { type: mongoose.Schema.Types.ObjectId, ref: 'Site' },
siteRole: { type: Number } }
]})
module.exports = mongoose.model('User', UserSchema)
SiteSchema
const SiteSchema = new mongoose.Schema({
name: { type: String },
url: { type: String }
})
module.exports = mongoose.model('Site', SiteSchema)
I have tried two ways but both returns the same output i.e. returns the array of site ObjectIds and not the populated version
// 1st try
const user = await User.findById(id)
.populate({
path: 'sites',
model: 'Site',
populate: { path: 'site', model: 'Site' }
}).exec()
// 2nd try
const user = await User.findById(id)
.populate('sites.site')
.exec(function (err, site) {
if (err) return next(err)
if (!site) return res.json(401)
return res.json(site)
})
Both ways returns (array of site ids instead of sites):
[
{
"_id": "60476bf103016cef60b63319",
"siteRole": 11
}
]

Related

How to make mongoDB return the item from inside the array through the id? [duplicate]

This question already has answers here:
Mongoose populate does not populate array
(3 answers)
Closed 3 months ago.
I'm trying to create an order controller where I would like to store in an array other "cart" models by reference, as in "list":
const mongoose = require('mongoose');
const OrderSchema = new mongoose.Schema(
{
list: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Cart',
}],
totalAmount: {
type: Number,
required: true,
},
payment: {
type: String,
required: true,
},
address: {
type: String,
required: true,
},
addressNote: {
type: String,
required: false,
},
createdAt: {
type: Date,
default: Date.now,
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Order", OrderSchema);
I can store the cart ids in the list and ok, but the problem is that when I do a get from order, I would like the list to return what is in the cart and not the ids that I sent
show all order controller:
const Order = require('../../models/Order');
class ShowAllProduct {
async show(req, res) {
try {
const order = await Order.find({}).populate('list').exec();
return res.status(200).json(order);
} catch (err) {
return res.status(500).json(err);
}
}
}
module.exports = new ShowAllProduct();
I tried to do this through the populate method, but without success.
try this way to populate
const order = await Order.find({}).populate({
path:'list',
model:'Cart'
}).lean();
update : if you want to populate Product model from Cart model ,
const order = await Order.find({}).populate({
path:'list',
model:'Cart',
populate:{
path:'product', // field name of product in Cart model
model:'Product'
}
}).lean();
use select:'name image_url' inside populate if you want to select only specific fields from model .
const order = await Order.find({}).populate({
path:'list',
model:'Cart',
populate:{
path:'product', // field name of product in Cart model
model:'Product',
select:'name image_url'
}
}).lean();

Return list of saved items using populate() in Mongoose

I am trying to create a page displaying saved items per user, so far my view is showing the JSON string, I want to render the original document (image, title, date,..)
Trying to understand populate() better.
Thanks
In DB
{
"_id":{"$oid":"6089d131efbe7e3e0c5c51f3"},
"joinDate":{"$date":"2021-04-28T21:17:06.928Z"},
"name":"...",
"email":"...",
"password":"...",
"createdAt":{"$date":"2021-04-28T21:18:41.832Z"},
"updatedAt":{"$date":"2021-04-28T21:18:41.832Z"},
"__v":0,
"avatar":"18756.png",
"savedArticles":[
"5eb91189c2de0f24fc1517db", // >> ObjectId of original
"585ad7294a91aa0de83d8ac4"
]}
My function:
router.get('/savedItems/:id/', checkAuthenticated, (req, res) => {
const id = req.params.id;
mongoose.model('Users').find({
_id: id
})
.populate('savedArticles', 'title')
.exec(function(err, results) {
if (err) {
console.log(err)
return;
}
res.render('savedItems.ejs', { savedArticles: results, user: req.user })
console.log( JSON.stringify(results))
});
})
Models:
var Schema = mongoose.Schema;
var userSchema = new Schema({
// ...
savedArticles:[{type: Schema.Types.ObjectId, ref: 'Article' }]
},{ autoCreate: true})
var Schema = mongoose.Schema;
var articleSchema = new Schema({
title: { type: String, required: true },
//...
})
const Users = mongoose.model('Users', userSchema)
const Article = mongoose.model('Article', articleSchema)
In view:
<% for(i=0; i < savedArticles.length; i++) {%>
<a href='#'><%= savedArticles[i].savedArticles %></a>
<% } %>
Output:
{ _id: 5eb91189c2de0f24fc1517db, title: 'Gatsby' },
{ _id: 585ad7294a91aa0de83d8ac4, title: 'Blade runner' }
Output Update with objects
{
_id: ...,
name: '',
email: '',
password: '....',
savedArticles: [
{
_id: ...,
title: '...',
director: '...',
year: '...'
},
{
_id: ...,
title: '...',
director: '...',
year: '...'
}
],
}
When you do populate, then mongoose will based on the _ids that are stored in savedArticles array go and fetch from the Articles collection documents with those _ids and it will change _ids in savedArticles array with actual documents. So, when your frontend got the result, savedArticles array will be array of objects (documents from Articles collection) and not array of _ids.
Now you can iterate over that array and access specific field of each article, like: savedArticles[0].title.

How to populate an object inside an object in an array?

I am currently working on a project with mongoose, and I have been stuck in a problem with the mongoose populate() method.
The problem is that I cannot populate an object inside an object in an array.
// * I simplified the code below.
// Profile Document
Profile = model('Profile', new Schema({
_user: {
type: Schema.Types.ObjectId,
ref: 'User',
},
posts: [
{
type: Schema.Types.ObjectId,
ref: 'Post',
// Post = {
// _id: Number,
// _poster: {
// type: Schema.Types.ObjectId,
// ref: 'User',
// }
// };
},
],
}));
// app.js
const profile = await Profile.findOne({ _user: userId })
.populate('_user') // it works
.populate('posts') // it works too
.populate('posts._poster'); // it doesn't work
Is there any way to populate a nested object in an array?
It'd be great if you could answer my question. Thank you in advance.
I tried accessing the notation properly to populate *_poster*, however, it didn't still work. The code is below.
const profile = await Profile.findOne({ _user: userId })
.populate('_user')
.populate('posts');
await profile.toObject();
profile.posts.forEach((post) => {
profile.populate('posts.post._poster');
})
I have tested your schema this works great, could you test and let me know
const profile = await Profile.findOne({
_id: '5f108ed7ecc4881a6c35e27b',
})
.populate('_user')
.populate({
path: 'posts',
populate: {
path: '_poster',
model: 'User',
},
})
.exec();
console.log(profile);

Adding object into Array of of Objects, Nodejs, Mongoose

I'm working on a small app where you can add products into a users cart.
I have a route set up for adding a specified product into a cart and that works fine. However when I add it in, all that shows up the array is the _id of the product, when what I'm looking for is an object containing some information (i.e, product name, product price, etc.). So that I can access it later in my React-Redux App. I've found a few suggestions online, but they either seem to not work for me at all or give me the same thing I've been getting.
Heres the route:
Cart.findOne({ user: req.user.id })
.then(cart => {
Product.findById(req.params.product_id).then(product => {
const newItem = {}
newItem._id = product._id;
newItem.name = product.name;
newItem.price = product.price;
const total =
parseFloat(cart.total) + parseFloat(product.price);
cart.total = Math.round(total*100)/100;
cart.items.push(newItem);
cart.save().then(cart=> res.json(cart));
});
})
.catch(err => res.status(404).json(err));
})
.catch(err => res.status(404).json(err));
Here's the corresponding Schema:
const CartSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
total: {
type: String,
default: 0
},
items: [
{
product: {
type: Schema.Types.ObjectId,
ref: "product"
}
}
]
});
Any help would be much appreciated.
Thank you
Try passing the product as is. Maybe because of the schema a Product is expected by mongoose, not a generic object such as, in your case, newItem
Cart.findOne({ user: req.user.id }).then(cart => {
return Product.findById(req.params.product_id);
}).then(product => {
const total = parseFloat(cart.total) + parseFloat(product.price);
cart.total = Math.round(total*100)/100;
cart.items.push(product);
return cart.save();
}).then(cart => {
return res.json(cart)
}).catch(err => res.status(404).json(err));
NOTE: i also fixed your promise chain structure a bit. This way you avoid callback hell, and only need one catch statement for the whole chain of promises.
How about
const CartSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
total: {
type: String,
default: 0
},
items: [
{
type: Schema.Types.ObjectId,
ref: "product"
}
]
});
And item pushed to array of items should be instance of Product, not just plain object.

Adding field with migration on mongoDB

So I tried to migrate a new field to the mongoDB collections.
New field is a array that is filled with objects.
The migration runs and is successful, it even shows the new field when
looking the collections.
Problem comes when I try to add data to this field - it shows that the
field is undefined.
What should be done to overcome this problem?
Migration code:
exports.up = async function(db) {
await db
.collection('useractions')
.update({}, {
$set: {
history: []
}
}, {multi: true, upsert: false});
};
Code to populate the new field:
const bookId = req.body.bookId;
const timestamp = req.body.timestamp;
const userId = req.body.userId;
const container = {bookId, timestamp};
UserAction.update(
{ userId },
{$set: { history: container}},
(err, cb) => {
if(err)next({error: err});
res.status(200).json({
cb
})
})
EDIT:
Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userActionModel = new Schema({
userId: {
type: String
},
likes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Podcast',
default: []
}],
tags: {
type: [String],
default: []
},
orderedBook: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Show',
default: []
}]
})
module.exports = mongoose.model('userAction', userActionModel);

Resources