Problem:
Trying to populate the User object products: with all matching Products Object, but returns empty array
Before posting this question I did the following:
- Read & implemented mongoose docs (with correct version) https://mongoosejs.com/docs/populate.html#populate_an_existing_mongoose_document
- Consulted stack overflow, listed all other similar questions I tried to implement their respective answers.
I have read the mongoose Docs, tried implementing as stated - to no avail
- mongodb populate method not working
- MongoDB Combining Collections - Not Populating
- Mongoose populate() returns empty array with no errors
- Mongoose populate returns empty array or list of ObjectIds
- Mongoose populate not populating an array and always returns an empty array
- Mongoose returning empty array after populate() method and all 7 links he tried aswell
The following in all possible combinations : -async -exec() -then() -populate() with/without specifying path, model, select
here is my repo incase more details are needed: https://github.com/FlyingVespa/Capstone-BE/tree/main/src
User Schema:
const userSchema = new Schema(
{
role: notReqString,
email: {
type: String,
lowercase: true,
required: [true, "An email is required."],
unique: [true, "An email is already registered."],
match: [/. \@. \.. /, "Not a valid email"],
// validate: [isEmail, "Please enter valid email"],
},
password: reqString,
url: notReqString,
businessname: reqString,
category: notReqString,
username: notReqString,
address: {
lat: notReqString,
lng: notReqString,
street_number: notReqString,
street_name: notReqString,
city: notReqString,
state: notReqString,
country: notReqString,
},
companydetails: {
bio: notReqString,
mobile: notReqString,
public_email: notReqString,
store_services: { type: Array },
shipping: notReqString,
},
tradingtimes: [
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
{
day: notReqString,
trading: reqBoolean,
open: notReqString,
closed: notReqString,
},
],
img_logo: {
...notReqString,
default: () => {
return `https://eu.ui-avatars.com/api/?name=Test`;
},
},
products: [{ type: Schema.Types.ObjectId, ref: "Product" }], /*REFRENCING HERE*/
},
{ timestamps: true }
);
userSchema.pre("save", async function (next) {
const newUser = this;
const plainPW = newUser.password;
if (newUser.isModified("password")) {
newUser.password = await bcrypt.hash(plainPW, 10);
}
next();
});
userSchema.methods.toJSON = function () {
const userDocument = this;
const userObject = userDocument.toObject();
delete userObject.password;
delete userObject.__v;
delete userObject.refreshToken;
return userObject;
};
export default model("User", userSchema);
Product Schema: import mongoose from "mongoose";
const { Schema, model } = mongoose;
const reqString = { type: String, required: false };
const notReqString = { type: String, required: false };
const reqNumber = { type: Number, required: true };
const productSchema = new Schema(
{
businessId: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
name: notReqString,
price: reqNumber,
units: notReqString,
status: notReqString,
sku: notReqString,
brand: notReqString,
description: notReqString,
image: {
...notReqString,
default: () => {
return `https://eu.ui-avatars.com/api/?name=product`;
},
},
},
{ timestamps: true }
);
productSchema.methods.toJSON = function () {
const productDocument = this;
const productObject = productDocument.toObject();
delete productObject.__v;
return productObject;
};
export default model("Product", productSchema);
Add new product:
export const addNewProduct = async (req, res, next) => {
try {
const userId = req.params.userId;
console.log(req.params);
const user = await User.findById(userId);
if (!user) {
return next(createError(404, `User with id ${userId} not found`));
}
const newProductData = { ...req.body, businessId: userId };
const newProduct = new Product(newProductData);
const createdProduct = await newProduct.save();
res.status(201).send(createdProduct);
} catch (error) {
if (error.name === "ValidationError") {
next(createError(400, error));
} else {
next(createError(500, error));
}
}
};
Fetch Single User:
export const getSingleUser = async (req, res, next) => {
try {
const userId = req.params.userId;
User.findById(userId)
.populate({ path: "products", model: "Product" })
.exec((err, result) => {
if (err) {
console.log("err", err);
return res.send({ error: err });
}
console.log("result", result);
res.send({ result: result });
});
} catch (error) {
next(error);
}
};
current log output:
{
"result": {
"address": {
"lat": "38.2305534",
"lng": "15.5532993",
"street_number": "1256",
"street_name": "Autumn Leaf",
"city": "Messina",
"state": "Sicilia",
"country": "Italy"
},
"companydetails": {
"bio": "i vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at",
"mobile": "139-432-2309",
"public_email": "[email protected]",
"store_services": [
"Overhead Doors",
"HVAC",
"Temp Fencing, Decorative Fencing and Gates"
],
"shipping": "false"
},
"_id": "62569c28dff1ca6175d48299",
"role": "user",
"email": "[email protected]",
"url": "Orca",
"businessname": "Kaymbo",
"category": "Capital Goods",
"username": "llefort0",
"tradingtimes": [
{
"day": "0",
"trading": true,
"_id": "62569c28dff1ca6175d4829a"
},
{
"day": "1",
"trading": false,
"_id": "62569c28dff1ca6175d4829b"
},
{
"day": "2",
"trading": true,
"_id": "62569c28dff1ca6175d4829c"
},
{
"day": "3",
"trading": true,
"_id": "62569c28dff1ca6175d4829d"
},
{
"day": "4",
"trading": false,
"_id": "62569c28dff1ca6175d4829e"
},
{
"day": "5",
"trading": false,
"_id": "62569c28dff1ca6175d4829f"
},
{
"day": "6",
"trading": false,
"_id": "62569c28dff1ca6175d482a0"
}
],
"products": [],
"img_logo": "https://eu.ui-avatars.com/api/?name=Test",
"createdAt": "2022-04-13T09:47:20.541Z",
"updatedAt": "2022-04-13T09:47:20.541Z"
}
}
modules versions:
"node": v14.17.5
"express":"^4.17.1",
"mongodb": "^4.1.3",
"mongoose": "^6.2.10"
CodePudding user response:
When you save the new product, you have to add the product's _id (which is returned) to the user's product's array.
Something like this
const createdProduct = await newProduct.save();
// Since you have access to user you can do
user.products.push(createdProduct._id);
await user.save();
Check Saving refs in mongoose