Linus on slack posed an interesting question,
Is there any way I can choose how populate should be returned? If I for example want to populate('user', 'name contact.address'), the return object will be { name: 'foo', contact.address: 'bar' }
can I in any way get { name: 'foo', address 'bar' } as returnobject from populate instead?
I think it would be cool to have a transform property on the populate object that would be applied to each doc returned from the population before it gets assigned back to the document's path.
#!/usr/bin/env node
'use strict';
const assert = require('assert');
const mongoose = require('mongoose');
const { Schema, connection} = mongoose;
const DB = 'linus';
const URI = `mongodb://localhost:27017/${DB}`;
const OPTS = { family: 4, useNewUrlParser: true };
const userSchema = new Schema({
name: String,
contact: {
address: String,
phone: String
},
friends: [{
type: Schema.Types.ObjectId,
ref: 'user'
}]
});
const User = mongoose.model('user', userSchema);
const ted = new User({
name: 'Ted',
contact: {
address: '123 Wild Stallions blvd, Long Beach, CA 90808',
phone: '555-123-4567'
}
});
const bill = new User({
name: 'Bill',
contact: {
address: '456 Woah blvd, Long Beach, CA 90808',
phone: '555-123-4568'
}
});
bill.friends.push(ted._id);
ted.friends.push(bill._id);
async function run() {
await mongoose.connect(URI, OPTS);
await connection.dropDatabase();
const docs = await User.create([bill, ted]);
const popObj = {
path: 'friends',
select: 'name contact.address',
transform: function(friend) {
friend.address = friend.contact.address;
delete friend.contact;
}
};
let popped = await User.findOne({ _id: ted._id }).populate(popObj);
assert.strictEqual(popped.friends[0].contact, undefined, 'transform not yet implemented.');
assert.strictEqual(popped.friends[0].address, '456 Woah blvd, Long Beach, CA 90808');
console.log('All Assertions Pass.');
await connection.close();
}
run().catch(error);
function error(e) {
console.error(e);
return connection.close();
}
slack: ./linus.js
{ AssertionError [ERR_ASSERTION]: transform not yet implemented.
at run (/Users/lineus/dev/node/mongoose/slack/linus.js:57:10)
at process._tickCallback (internal/process/next_tick.js:68:7)
generatedMessage: false,
name: 'AssertionError [ERR_ASSERTION]',
code: 'ERR_ASSERTION',
actual: { address: '456 Woah blvd, Long Beach, CA 90808' },
expected: undefined,
operator: 'strictEqual' }
slack:
slack: ./linus.js
All Assertions Pass.
slack:
That's an interesting idea. But can't you already do this with virtual populate and a getter?
@vkarpov15 yeah, a virtual getter solves this already ( I can't believe this never even crossed my mind )
adding a virtual with getter:
userSchema.virtual('$friends').get(function() {
return this.friends.map(friend => {
return {
address: friend.contact.address,
name: friend.name
};
});
});
and adding the $
to the assertions:
assert.strictEqual(popped.$friends[0].contact, undefined);
assert.strictEqual(popped.$friends[0].address, '456 Woah blvd, Long Beach, CA 90808');
I'll close this for now. Re-open if this is still an issue.
Most helpful comment
@vkarpov15 yeah, a virtual getter solves this already ( I can't believe this never even crossed my mind )
This works ( full gist here ):
adding a virtual with getter:
and adding the
$
to the assertions: