I have a subdocument that is nested as an array. Inside that subdocument I have references to other models. Using the .Find
and .Populate
methods I can receive the entire objects for single models referenced in the subdocument (check out Stop below) but not an array of model instances, Facts/Recommendations. For Facts/Recommendations I receive an array of object _ids
. I can probably just take those _ids
and make another query but this seems messy.
Is there a way to populate the array? Do I need to use Aggregate + $lookup? Is there a more Mongo way to restructure the Schema to simplify this?
Thank you for all and any help!
My subdocument called TourStop:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const TourStopSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref: 'Stop',
required:[true, 'must have a Stop'],
},
mandatory: {
type:Boolean,
default: false
},
check_in_time: {
type: Number,
default: 0
},
order: {
type:Number,
required: [true, 'Must have an order']
},
facts: [{
type: Schema.Types.ObjectId,
ref: 'Fact'
}],
recommendations: [{
type: Schema.Types.ObjectId,
ref: 'Recommendation'
}]
});
module.exports = TourStopSchema;
TourStops lives inside of Tour:
const mongoose = require('mongoose');
const TourStopSchema = require('../subdocs/tour_stop');
const Schema = mongoose.Schema;
const tourSchema = new Schema({
name: {
type:String,
required:[true,'Name is required!'],
unique: true
},
city: {
type: String,
required: [true, 'City is required!']
},
tourStops:[TourStopSchema]
});
const Tour = mongoose.model('Tour', tourSchema);
module.exports = Tour;
Stop
Schema which is populated just fine.
const mongoose = require('mongoose');
const LocationSchema = require('../subdocs/location');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const stopSchema = new Schema({
name:{
type: String,
required:[true,'Must have a name!']
},
location:LocationSchema,
categories: {
type: [String],
default:[]
},
images:{
type:[ImageSchema],
default:[]
}
});
const Stop = mongoose.model('Stop', stopSchema);
module.exports = Stop;
And Fact Schema which is not populated correctly, instead returns an array of strings with the _ids
Fact:
const mongoose = require('mongoose');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const factSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref:'Stop',
required:[true, 'A Fact must have a Stop!'],
},
description: {
type: String,
required: [true,'A Fact must have a description!']
},
images: {
type:[ImageSchema],
default:[]
}
});
const Fact = mongoose.model('Fact', factSchema);
module.exports = Fact;
And I'm running a test to check that the schema is properly hooked up to retrieve all the attributes of a TourStop:
it('saves a full relation graph', (done) => {
User.findOne({ first_name: 'Dean' })
.populate({
// in that user find the tours property and load up all tours
path: 'tours',
// inside of all those tours, find the tourstops property and load all associated stops
populate: {
path: 'tour_stops.facts',
model: 'Fact'
},
populate: {
path: 'tour_stops.stop',
model: 'Stop'
}
})
// .populate('tours.tour_stops[0].facts')
.then((user) => {
// console.log(user.tours[0].tour_stops[0].stop);
console.log(user.tours[0].tour_stops[0]);
// console.log(Array.isArray(user.tours[0].tour_stops[0].facts))
assert(user.first_name === 'Dean' );
assert(user.tours.length === 1);
assert(user.tours[0].name === "Awesome New Tour");
assert(user.tours[0].tour_stops[0].stop.name === 'Jaffa Clock Tower');
// assert(user.tours[0])
// assert(user.blogPosts[0].title === 'JS is Great');
// assert(user.blogPosts[0].comments[0].content === 'Congrats on great post!' );
// assert(user.blogPosts[0].comments[0].user.name === 'Joe' )
done();
})
})