Reputation: 1
I want to populate a model which has an ObjectId of a document which is present as sub schema on another model
Sub Schema
var addressSchema = new Schema({
address: String,
phoneNumber: Number,
name: String,
pincode: Number,
augmontAddressId: String,
});
Main Schema - address is an array that can have multiples addresses and each object has its own objecId which i want to refer to
var userSchema = new Schema(
{
name: String,
address: [addressSchema],
},
{
timestamps: true,
}
);
Ledger Schema referencing to Main Schema
var ledgerSchema = new Schema(
{
txnId: String,
paymentId: String,
addressId:{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
},
{
timestamps: true,
}
);
I tried
LedgerSchema.findById().populate("addressId").then(res=>{
console.log(res);
})
But its not populating the addressId instead sending null value
{
_id: new ObjectId("650c49a20483f1ec8eb7aecd"),
addressId: null,
createdAt: 2023-09-21T13:48:18.667Z,
updatedAt: 2023-09-21T13:48:18.667Z,
__v: 0
}
Also i tried setting ref in ledgerSchema as 'User.address' But it gives an error
MissingSchemaError: Schema hasn't been registered for model "User.Address".
Upvotes: 0
Views: 81
Reputation: 8082
Oh how nice it would be if you could just do User.address
to reference things. Unfortunately, this can't be done with populate
due to the way you have set up your subdocuments. In your userSchema
each address
is essentially a subdocument that is embedded in the top-level document. In contrast, within in your ledgerSchema
, each addressId
is a referenced document and are separate top-level documents.
My advice would be to change your userSchema
so that each address
is a referenced document but that will create another collection in your database (addresses
). You need that to do references.
If for whatever reason you cant modify your schema and need to keep each address
as an embedded subdocument then you can achieve the desired result with aggregate. It is not a trivial task and it is not pretty but it does work:
const ledger = await ledgerModel.aggregate([
{ $match: { _id: new mongoose.Types.ObjectId(id) } }, //< Your ledger _id
{
$lookup: //< Do a lookup to match addressId with userSchema.address._id
{
from: "users",
localField: "addressId",
foreignField: "address._id",
as: "address_details" //< This creates a new array
}
},
{ $unwind: '$address_details'}, //< Now you need to unwind the new array
{ $unwind: '$address_details.address'}, //< And unwind each address
{
$project: //< This stage only outputs the original ledger properties
{
txnId: 1,
paymentId: 1,
address_details: 1,
compare_address_ids: { $cmp: [ "$addressId", "$address_details.address._id" ] },
//^ Compare addressId against ones done via lookup to find a match
}
},
{
$match: {
compare_address_ids: 0 //< 0 means they matched
}
},
{
$project: //< Now get rid of these two made up properties from the output
{
compare_address_ids: 0,
'address_details.name': 0,
}
}
]);
Upvotes: 0