Skip to content

Commit

Permalink
fix(populate): set populated docs in correct order when populating vi…
Browse files Browse the repository at this point in the history
…rtual underneath doc array with justOne (#14105)

Fix #14018
  • Loading branch information
vkarpov15 committed Nov 24, 2023
1 parent a848de0 commit 6def405
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 3 deletions.
8 changes: 6 additions & 2 deletions lib/helpers/populate/assignRawDocsToIdStructure.js
Expand Up @@ -32,9 +32,13 @@ const kHasArray = Symbol('assignRawDocsToIdStructure.hasArray');
*/

function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) {
// honor user specified sort order
// honor user specified sort order, unless we're populating a single
// virtual underneath an array (e.g. populating `employees.mostRecentShift` where
// `mostRecentShift` is a virtual with `justOne`)
const newOrder = [];
const sorting = options.sort && rawIds.length > 1;
const sorting = options.isVirtual && options.justOne && rawIds.length > 1
? false :
options.sort && rawIds.length > 1;
const nullIfNotFound = options.$nullIfNotFound;
let doc;
let sid;
Expand Down
3 changes: 2 additions & 1 deletion lib/helpers/populate/assignVals.js
Expand Up @@ -19,7 +19,8 @@ module.exports = function assignVals(o) {
// `o.options` contains options explicitly listed in `populateOptions`, like
// `match` and `limit`.
const populateOptions = Object.assign({}, o.options, userOptions, {
justOne: o.justOne
justOne: o.justOne,
isVirtual: o.isVirtual
});
populateOptions.$nullIfNotFound = o.isVirtual;
const populatedModel = o.populatedModel;
Expand Down
82 changes: 82 additions & 0 deletions test/model.populate.test.js
Expand Up @@ -10694,4 +10694,86 @@ describe('model: populate:', function() {
[company._id.toString(), company2._id.toString()]
);
});

it('sets populated docs in correct order when populating virtual underneath document array with justOne (gh-14018)', async function() {
const shiftSchema = new mongoose.Schema({
employeeId: mongoose.Types.ObjectId,
startedAt: Date,
endedAt: Date
});
const Shift = db.model('Shift', shiftSchema);

const employeeSchema = new mongoose.Schema({
name: String
});
employeeSchema.virtual('mostRecentShift', {
ref: Shift,
localField: '_id',
foreignField: 'employeeId',
options: {
sort: { startedAt: -1 }
},
justOne: true
});
const storeSchema = new mongoose.Schema({
location: String,
employees: [employeeSchema]
});
const Store = db.model('Store', storeSchema);

const store = await Store.create({
location: 'Tashbaan',
employees: [
{ _id: '0'.repeat(24), name: 'Aravis' },
{ _id: '1'.repeat(24), name: 'Shasta' }
]
});

const employeeAravis = store.employees
.find(({ name }) => name === 'Aravis');
const employeeShasta = store.employees
.find(({ name }) => name === 'Shasta');

await Shift.insertMany([
{ employeeId: employeeAravis._id, startedAt: new Date('2011-06-01'), endedAt: new Date('2011-06-02') },
{ employeeId: employeeAravis._id, startedAt: new Date('2013-06-01'), endedAt: new Date('2013-06-02') },
{ employeeId: employeeShasta._id, startedAt: new Date('2015-06-01'), endedAt: new Date('2015-06-02') }
]);

const storeWithMostRecentShifts = await Store.findOne({ location: 'Tashbaan' })
.populate('employees.mostRecentShift')
.select('-__v')
.exec();
assert.equal(
storeWithMostRecentShifts.employees[0].mostRecentShift.employeeId.toHexString(),
'0'.repeat(24)
);
assert.equal(
storeWithMostRecentShifts.employees[1].mostRecentShift.employeeId.toHexString(),
'1'.repeat(24)
);

await Shift.findOne({ employeeId: employeeAravis._id }).sort({ startedAt: 1 }).then((s) => s.deleteOne());

const storeWithMostRecentShiftsNew = await Store.findOne({ location: 'Tashbaan' })
.populate('employees.mostRecentShift')
.select('-__v')
.exec();
assert.equal(
storeWithMostRecentShiftsNew.employees[0].mostRecentShift.employeeId.toHexString(),
'0'.repeat(24)
);
assert.equal(
storeWithMostRecentShiftsNew.employees[0].mostRecentShift.startedAt.toString(),
new Date('2013-06-01').toString()
);
assert.equal(
storeWithMostRecentShiftsNew.employees[1].mostRecentShift.employeeId.toHexString(),
'1'.repeat(24)
);
assert.equal(
storeWithMostRecentShiftsNew.employees[1].mostRecentShift.startedAt.toString(),
new Date('2015-06-01').toString()
);
});
});

0 comments on commit 6def405

Please sign in to comment.