Skip to content

Commit 12c8bcd

Browse files
authored
fix(populate): set populated docs in correct order when populating virtual underneath doc array with justOne (#14105)
Fix #14018
1 parent d929f29 commit 12c8bcd

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

lib/helpers/populate/assignRawDocsToIdStructure.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@ const kHasArray = Symbol('mongoose#assignRawDocsToIdStructure#hasArray');
3232
*/
3333

3434
function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) {
35-
// honor user specified sort order
35+
// honor user specified sort order, unless we're populating a single
36+
// virtual underneath an array (e.g. populating `employees.mostRecentShift` where
37+
// `mostRecentShift` is a virtual with `justOne`)
3638
const newOrder = [];
37-
const sorting = options.sort && rawIds.length > 1;
39+
const sorting = options.isVirtual && options.justOne && rawIds.length > 1
40+
? false :
41+
options.sort && rawIds.length > 1;
3842
const nullIfNotFound = options.$nullIfNotFound;
3943
let doc;
4044
let sid;

lib/helpers/populate/assignVals.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ module.exports = function assignVals(o) {
1919
// `o.options` contains options explicitly listed in `populateOptions`, like
2020
// `match` and `limit`.
2121
const populateOptions = Object.assign({}, o.options, userOptions, {
22-
justOne: o.justOne
22+
justOne: o.justOne,
23+
isVirtual: o.isVirtual
2324
});
2425
populateOptions.$nullIfNotFound = o.isVirtual;
2526
const populatedModel = o.populatedModel;

test/model.populate.test.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10694,4 +10694,86 @@ describe('model: populate:', function() {
1069410694
[company._id.toString(), company2._id.toString()]
1069510695
);
1069610696
});
10697+
10698+
it('sets populated docs in correct order when populating virtual underneath document array with justOne (gh-14018)', async function() {
10699+
const shiftSchema = new mongoose.Schema({
10700+
employeeId: mongoose.Types.ObjectId,
10701+
startedAt: Date,
10702+
endedAt: Date
10703+
});
10704+
const Shift = db.model('Shift', shiftSchema);
10705+
10706+
const employeeSchema = new mongoose.Schema({
10707+
name: String
10708+
});
10709+
employeeSchema.virtual('mostRecentShift', {
10710+
ref: Shift,
10711+
localField: '_id',
10712+
foreignField: 'employeeId',
10713+
options: {
10714+
sort: { startedAt: -1 }
10715+
},
10716+
justOne: true
10717+
});
10718+
const storeSchema = new mongoose.Schema({
10719+
location: String,
10720+
employees: [employeeSchema]
10721+
});
10722+
const Store = db.model('Store', storeSchema);
10723+
10724+
const store = await Store.create({
10725+
location: 'Tashbaan',
10726+
employees: [
10727+
{ _id: '0'.repeat(24), name: 'Aravis' },
10728+
{ _id: '1'.repeat(24), name: 'Shasta' }
10729+
]
10730+
});
10731+
10732+
const employeeAravis = store.employees
10733+
.find(({ name }) => name === 'Aravis');
10734+
const employeeShasta = store.employees
10735+
.find(({ name }) => name === 'Shasta');
10736+
10737+
await Shift.insertMany([
10738+
{ employeeId: employeeAravis._id, startedAt: new Date('2011-06-01'), endedAt: new Date('2011-06-02') },
10739+
{ employeeId: employeeAravis._id, startedAt: new Date('2013-06-01'), endedAt: new Date('2013-06-02') },
10740+
{ employeeId: employeeShasta._id, startedAt: new Date('2015-06-01'), endedAt: new Date('2015-06-02') }
10741+
]);
10742+
10743+
const storeWithMostRecentShifts = await Store.findOne({ location: 'Tashbaan' })
10744+
.populate('employees.mostRecentShift')
10745+
.select('-__v')
10746+
.exec();
10747+
assert.equal(
10748+
storeWithMostRecentShifts.employees[0].mostRecentShift.employeeId.toHexString(),
10749+
'0'.repeat(24)
10750+
);
10751+
assert.equal(
10752+
storeWithMostRecentShifts.employees[1].mostRecentShift.employeeId.toHexString(),
10753+
'1'.repeat(24)
10754+
);
10755+
10756+
await Shift.findOne({ employeeId: employeeAravis._id }).sort({ startedAt: 1 }).then((s) => s.deleteOne());
10757+
10758+
const storeWithMostRecentShiftsNew = await Store.findOne({ location: 'Tashbaan' })
10759+
.populate('employees.mostRecentShift')
10760+
.select('-__v')
10761+
.exec();
10762+
assert.equal(
10763+
storeWithMostRecentShiftsNew.employees[0].mostRecentShift.employeeId.toHexString(),
10764+
'0'.repeat(24)
10765+
);
10766+
assert.equal(
10767+
storeWithMostRecentShiftsNew.employees[0].mostRecentShift.startedAt.toString(),
10768+
new Date('2013-06-01').toString()
10769+
);
10770+
assert.equal(
10771+
storeWithMostRecentShiftsNew.employees[1].mostRecentShift.employeeId.toHexString(),
10772+
'1'.repeat(24)
10773+
);
10774+
assert.equal(
10775+
storeWithMostRecentShiftsNew.employees[1].mostRecentShift.startedAt.toString(),
10776+
new Date('2015-06-01').toString()
10777+
);
10778+
});
1069710779
});

0 commit comments

Comments
 (0)