Closed brianmtully closed 7 years ago
Same problem here!
The below seems to work fine for me:
'use strict';
var assert = require('assert');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/gh4680');
mongoose.set('debug', true);
var personSchema = new Schema({
name: String,
age: Number,
instrument: String,
band: String
});
var bandSchema = new Schema({
name: String
});
bandSchema.virtual('members', {
ref: 'Person', // The model to use
localField: 'name', // Find people where localField
foreignField: 'band' // is equal to foreignField
});
var Person = mongoose.model('Person', personSchema);
var Band = mongoose.model('Band', bandSchema);
Band.remove({}).
then(() => Person.remove({})).
then(() => Band.create({ name: 'Guns N Roses' })).
then(() => Person.create({ name: 'Axl Rose', age: 28, band: 'Guns N Roses' })).
then(() => Band.findOne({}).populate({ path: 'members', options: { select: { name: 1, band: 1 } } }).exec()).
then(doc => {
console.log(doc.members);
});
Output:
$ node gh-4680.js
(node:12044) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
Mongoose: bands.remove({}, {})
Mongoose: people.remove({}, {})
Mongoose: bands.insert({ name: 'Guns N Roses', _id: ObjectId("5827bfb59f14982f0c027c39"), __v: 0 })
Mongoose: people.insert({ name: 'Axl Rose', age: 28, band: 'Guns N Roses', _id: ObjectId("5827bfb59f14982f0c027c3a"), __v: 0 })
Mongoose: bands.findOne({}, { fields: undefined })
Mongoose: people.find({ band: { '$in': [ 'Guns N Roses' ] } }, { fields: { name: 1, band: 1 } })
[ { _id: 5827bfb59f14982f0c027c3a,
name: 'Axl Rose',
band: 'Guns N Roses' } ]
^C
$
Please modify the above script to reproduce your issue.
I found the mistake...
In the example of @brianmtully, he doesn't selected the field that serves as reference. Try selecting 'band instruments', than it will works..
It will be nice if mongoose would add automatically the foreignField in the "select" option object...
Thanks..
@LucGranato Thanks that was it. I needed to add the foreignField in my select!
Ah ok I misread the question, thanks for clarifying. For both virtual populate and regular populate you need to make sure the foreign field is selected, otherwise mongoose doesn't have a good way to map which document goes where since you can populate multiple docs.
I can't quite understand the solution offered here. I notice when I add select option to the populate function it will just return an empty array in place of the related collection - without the select it works fine.
I am trying to limit my selected fields in my example below.
A room has several actions. With a room schema and virtual like so:
const roomSchema = Schema({
id: {type: String, required: true, index: true, default: shortid.generate},
name: {type: String, required: true, default: names},
owner: {type: String},
createdOn: {type: Date, required: true, default: Date.now},
}, {
minimize: false
});
//virtuals
roomSchema.virtual('actions', {
ref: 'Action', //the model to use
localField: 'id', //find actions where `localField`
foreignField: 'roomId' // is equal to `foreignField`
//i.e. room.id is connected to action.roomId
});
And action schema like:
const actionSchema = mongoose.Schema({
roomId: {
type: String,
required: true
},
actionObject: {}
});
The populate is like this (the select is commented out to make this work):
Room.find({})
.populate({
path: 'actions',
// select: 'actionObject', //<- including this will return an empty array for actions
options: {sort: { 'actionObject.meta.timestamp': 1 }}
})
.exec((err, rooms) => {
...
});
Any ideas what the problem is?
As stated in the previous responses when using the select you need to include the foreign field. So you can't just select 'actionObject' you need to select the foreign field 'roomId' as well so it can map the document in the populate. so select : 'actionObject roomId' should work.
Ah yes, I see now. Thanks!
Wow, spent hours on this same issue. The solution makes sense, but none of the examples I've seen mention the need to select the foreignField.
"It will be nice if mongoose would add automatically the foreignField in the "select" option object..."
Agree 100%. If it's a necessary field for mapping, then it should be auto-selected.
@vkarpov15 first of all, thanks for add this feature in the 4.8.3 version! But now I have a problem with this.
Currently I have my "Employee" document
const employeeSchema = new Schema({
profile : {
name : {type: String, required: true, set: formatName},
registration : {type: String, trim: true}
}
});
And the configuration to a virtual population
employeeSchema.virtual('__shifts', {
ref : 'shift',
localField : '_id',
foreignField : 'main._employee'
});
And this is my "Shift" document
const shiftSchema = new Schema({
tolerance : {
minutes : {type: Number, required: true}
},
info : {
_employee : {type: Schema.ObjectId, ref: 'employee'},
_contract : {type: Schema.ObjectId, ref: 'contract'},
_enter : {type: Schema.ObjectId, ref: 'activity'},
_leave : {type: Schema.ObjectId, ref: 'activity'},
_late : {type: Schema.ObjectId, ref: 'activity'},
_absence : {type: Schema.ObjectId, ref: 'activity'}
}
});
The problem is, when I query employees and populate the virtual '__shifts' and select the 'info' , it returns only the 'info._employee' selected, probably because you are forcing the selection of 'info._employee', not returning the rest of the subdocument.
Thanks!
Thanks for reporting @LucGranato , please track #5037 for updates on this issue
The below seems to work fine for me:
'use strict'; var assert = require('assert'); var mongoose = require('mongoose'); var Schema = mongoose.Schema; mongoose.connect('mongodb://localhost/gh4680'); mongoose.set('debug', true); var personSchema = new Schema({ name: String, age: Number, instrument: String, band: String }); var bandSchema = new Schema({ name: String }); bandSchema.virtual('members', { ref: 'Person', // The model to use localField: 'name', // Find people where localField foreignField: 'band' // is equal to foreignField }); var Person = mongoose.model('Person', personSchema); var Band = mongoose.model('Band', bandSchema); Band.remove({}). then(() => Person.remove({})). then(() => Band.create({ name: 'Guns N Roses' })). then(() => Person.create({ name: 'Axl Rose', age: 28, band: 'Guns N Roses' })). then(() => Band.findOne({}).populate({ path: 'members', options: { select: { name: 1, band: 1 } } }).exec()). then(doc => { console.log(doc.members); });
Output:
$ node gh-4680.js (node:12044) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html Mongoose: bands.remove({}, {}) Mongoose: people.remove({}, {}) Mongoose: bands.insert({ name: 'Guns N Roses', _id: ObjectId("5827bfb59f14982f0c027c39"), __v: 0 }) Mongoose: people.insert({ name: 'Axl Rose', age: 28, band: 'Guns N Roses', _id: ObjectId("5827bfb59f14982f0c027c3a"), __v: 0 }) Mongoose: bands.findOne({}, { fields: undefined }) [ { _id: 5827bfb59f14982f0c027c3a, name: 'Axl Rose', band: 'Guns N Roses' } ] ^C $
Please modify the above script to reproduce your issue.
Is there a way to select specific fields when populating virtuals?
Using the following example, can I populate the virtual 'members' but only select the instrument field? I tried the normal select functionality for populate and it is not working. Thanks.
` var PersonSchema = new Schema({ name: String, age: Number, instrument: String, band: String });
var BandSchema = new Schema({ name: String }); BandSchema.virtual('members', { ref: 'Person', // The model to use localField: 'name', // Find people where
localField
foreignField: 'band' // is equal toforeignField
});var Person = mongoose.model('Person', personSchema); var Band = mongoose.model('Band', bandSchema);
Band.find({}).populate('members').exec(function(error, bands) { /
bands.members
is now an array of instances ofPerson
/ }); `