First of all, it is important to note that what you are attempting to achieve cannot be done using the populate
method. This is because the ref
property in the schema declaration is meant for referencing a model, not a path. It informs mongoose which model should be used for the $lookup
, and mongoose expects this model to correspond with a collection in your database.
Additionally, since you are utilizing subdocuments within your productSchema
for the featureHeaders
and features
arrays, the use of populate
will not work in this scenario.
However, there is a way to rectify this issue and make populate
functional for your needs. Despite the confusion surrounding your naming convention and usage, the following explanation should clarify the process:
- The entities
FeatureHeader
and Feature
must have their own collections rather than being subdocuments, necessitating the creation of models for them.
import mongoose from "mongoose";
const featureHeaderSchema = new mongoose.Schema({
header: {
type: String,
required: true,
}
});
// Create the FeatureHeader model
const FeatureHeader = mongoose.model("FeatureHeader", featureHeaderSchema);
const featureSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
parentHeader: {
type: mongoose.Schema.Types.ObjectId,
ref: "FeatureHeader" // Reference the FeatureHeader model
},
parentFeature: {
type: mongoose.Schema.Types.ObjectId,
ref: "Feature" // Reference the same model (self)
}
});
// Create the Feature model
const Feature = mongoose.model("Feature", featureSchema);
const productSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
thumbnail: String,
description: String,
manufacture: {
type: mongoose.Schema.Types.ObjectId,
ref: "User" // Capitalize the model name
},
featureHeaders: [{
type: mongoose.Schema.Types.ObjectId,
ref: "FeatureHeader" // Reference the FeatureHeader model
}],
features: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Feature" // Reference the Feature model
}],
},
{ timestamps: true });
// Create the Product model
const Product = mongoose.model("Product", productSchema);
FeatureHeader
and Feature
need to be stored separately in their own collection in order to be referenced individually.
const newFeatureOne = await Feature.create({
title: 'Marvel',
});
const newFeatureHeader = await FeatureHeader.create({
header: 'Superhero'
});
const newFeatureTwo = await Feature.create({
title: 'Repulsor Beams',
parentHeader: newFeatureHeader._id,
parentFeature: newFeatureOne._id
});
const newProduct = await Product.create({
title: 'Repulsor Beams',
//...
//...
featureHeaders: [newFeatureHeader._id],
features: [newFeatureTwo._id],
});
- You can now utilize
populate
to replace the ObjectId
references with their corresponding documents:
const id = req.body.id; // Could be req.params.id or another way of obtaining the id
const product = await Product.findById(id)
.populate("featureHeaders")
.populate("features");
Note: Depending on the structure of your design and how the models are scoped, you may need to specify the path
and/or model
properties within the populate
options object like this:
const id = req.body.id; // Could be req.params.id or another way of obtaining the id
const product = await Product.findById(id)
.populate({path: "featureHeaders", model: "FeatureHeader"})
.populate({path: "features", model: "Feature"});
If you have any questions or require further clarification, feel free to ask in the comments section.