Consider the code below:
require("./connection");
// //----------------------------------------------------
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
band: String,
father: String
});
const ManagerSchema = new Schema({
name: String,
country: String
});
const BandSchema = new Schema({
name: String
});
BandSchema.virtual("members", {
ref: "Person", // The model to use
localField: "name", // Find people where `localField`
foreignField: "band", // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false,
options: { sort: { name: -1 }, limit: 5 }
});
BandSchema.virtual("managers", {
ref: "Manager", // The model to use
localField: "name", // Find people where `localField`
foreignField: "country", // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false,
options: { sort: { name: 1 }, limit: 5 }
});
//BandSchema.set("toObject", { virtuals: true });
BandSchema.set("toJSON", { virtuals: true });
const Person = mongoose.model("Person", PersonSchema);
const Manager = mongoose.model("Manager", ManagerSchema);
const Band = mongoose.model("Band", BandSchema);
/**
* Suppose you have 2 bands: "Guns N' Roses" and "Motley Crue"
* And 4 people: "Axl Rose" and "Slash" with "Guns N' Roses", and
* "Vince Neil" and "Nikki Sixx" with "Motley Crue"
*/
// Person.create([
// {
// name: "Axl Rose",
// band: "Guns N' Roses"
// },
// {
// name: "Slash",
// band: "Guns N' Roses"
// },
// {
// name: "Vince Neil",
// band: "Motley Crue"
// },
// {
// name: "Nikki Sixx",
// band: "Motley Crue"
// }
// ]);
// Manager.create([
// {
// name: "Bibi",
// country: "South Africa"
// },
// {
// name: "Storm",
// country: "Italy"
// },
// {
// name: "Wolverine",
// country: "Canada"
// },
// {
// name: "Jorge Pires",
// country: "Brazil"
// }
// ]);
// Band.create([{ name: "Motley Crue" }, { name: "Guns N' Roses" }]);
/////////////////////////////////////////////////////////////////////////
const app = require("express")();
app.use("/", (req, res) => {
Band.find({})
.populate("members")
.populate("managers")
.exec(function(error, bands) {
/* `bands.members` is now an array of instances of `Person` */
console.log(bands);
res.json(bands);
});
});
app.listen(3000, () => {
console.log("We are on port 3000");
});
/**
*https://stackoverflow.com/questions/43882577/mongoosejs-virtual-populate
https://stackoverflow.com/questions/60875380/populate-virtuals-does-not-seem-to-work-could-anyone-show-me-the-error
*/
Consider the related questions:
My question is: how do you define foreignField
?
Members
populate properly, but manager does not.
I know the problem is foreignField
because if I repeat all the information from members, it will populate properly, but now we have members and manage with the same data source.
After some studies, trying to answer another question here on Stack Overflow (mongoose: populate in mongoose which doesn't have any ObjectId ), I realized how it works!
Consider part of the code presented:
BandSchema.virtual("members", {
ref: "Person", // The model to use
localField: "name", // Find people where `localField`
foreignField: "band", // this field here has to match the ref path we want to populate!
justOne: false,
});
So, the trick is to make sure foreignField
match the field at the ref
model, and localField
is the name of the field that you can find at the populated model: we must have a match between foreignField
and localField
, more precisely: the value of localField
has to match the one of foreignField
in real situation, in the database, not in the schema naming process. That is how mongoose can find and populate!
What now I realise I was having difficult is that Virtual
operates somehow in the opposite direction of populate
. You can picture populate by working as a tree: it just populate from the id
to the document, whereas virtual
will populate the document that does not contain the key, the one that contain the key is the one to be added in the populate process: it is somehow backward, that is it was so hard to me to grasp! The document to be populated does not have the field just populated! it is mind-blowing!
Conclusion and final remarks
Nonetheless, I still find it limited compared to populate
. You still must keep local key tracks, which does not solve for instance the story memory issue, and it is more limited according to my studies. The only advantage I see is that you do not need to use the _id
, you can use any key you please. I was hoping to use to solve my problem here How to save an JSON file using GridFs, but since we still have to store local keys, I fall into de same trap!
Correction
I am glad to say that I was wrong! Virtuals is awsome! and solves my probem on How to save an JSON file using GridFs, I am going to update there!