mongoose Not able to return populated
see blow
import { Schema, model } from 'mongoose'
import { transformVirtuals } from 'src/utils/mongoose'
const GameSchema = new Schema(
{
_t: {
type: String,
alias: 'title',
required: true,
},
_r: {
type: Number,
alias: 'round',
min: 1,
max: 7,
default: 7,
},
_pr: {
type: Boolean,
default: false,
alias: 'private',
},
_o: {
alias: 'owner',
required: true,
type: Schema.Types.ObjectId,
ref: 'User',
},
_pl: {
type: [{ type: Schema.Types.ObjectId, ref: 'User' }],
alias: 'players',
validate: [
{
validator: playersLimit,
message: 'board is full',
},
{
validator: playersUnique,
message: 'player already exist',
},
],
},
_p: {
type: Number,
alias: 'playersCount',
default: 4,
min: 2,
max: 4,
},
_s: {
type: Number,
alias: 'status',
default: 0,
enum: [
0, //waiting for player
1, // waiting for select hakem
2, // playing
3, //game over
],
},
}
)
export default model('game', GameSchema)
async create(data, user) {
const players = new Array(data.playersCount || 4).fill(null)
players[0] = user.id
const newGame = new Game({
...data,
owner: user.id,
players: players,
})
const board = await newGame.save()
const pop = await board.populate({ path: '_pl', select: '_n', options: { retainNullValues: true } })
// this pop is not populated
const res = await pop.toJSON({ virtuals: true })
return {
board: res,
channel: createGameChannel(res.id),
}
}
in case using const pop = await board.populate({ path: '_pl', select: '_n', options: { retainNullValues: true } })
it return only refrence id like below
{
_r: 7,
_pr: false,
_pl: [
5e62c3ebb304b991aa92bedf,
5e66d697636392d78c267c80,
5e6bc110e5ae9b25315c03a1,
5e62dbe88af6a6986dd12d04
],
_p: 4,
_s: 1,
_id: 5e6bd5ce63a31d33985f909d,
_t: 'a',
_o: 5e62c3ebb304b991aa92bedf,
__v: 3
}
but on call back await x.populate({ path: '_pl', select: '_n' }, console.log)
working fine
{
_r: 7,
_pr: false,
_pl: [
{ _id: 5e62c3ebb304b991aa92bedf, _n: 'masoud' },
{ _id: 5e66d697636392d78c267c80, _n: 'mina' },
{ _id: 5e6bc110e5ae9b25315c03a1, _n: 'mitra' },
{ _id: 5e62dbe88af6a6986dd12d04, _n: 'soroush' }
],
_p: 4,
_s: 1,
_id: 5e6bd5ce63a31d33985f909d,
_t: 'a',
_o: 5e62c3ebb304b991aa92bedf,
__v: 3
}
Do you want to request a feature or report a bug?
Its a bug i think
What is the current behavior?
If the current behavior is a bug, please provide the steps to reproduce.
What is the expected behavior?
What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
nodejs: 10
"mongoose": "^5.9.2",
db version v4.2.3
EDIT: Look at the next comment.
~Can confirm this is an issue, I simplified the script a little bit.~
const mongoose = require('mongoose');
const { Schema } = mongoose;
const assert = require('assert');
mongoose.connect('mongodb://localhost:27017/8671', { useNewUrlParser: true, useUnifiedTopology: true });
const commentSchema = new Schema({ content: String });
const Comment = mongoose.model('Comment', commentSchema);
const postSchema = new Schema({ commentsIds: [{ type: Schema.ObjectId, ref: 'Comment' }] });
const Post = mongoose.model('Post', postSchema);
async function run () {
await Promise.all([
Post.deleteMany(),
Comment.deleteMany()
]);
const [comment1, comment2] = await Comment.create([{ content: 'first comment' }, { content: 'second comment' }]);
const post = await Post.create({ commentsIds: [comment1._id, comment2._id] });
await post.populate({ path: 'commentsIds' });
assert.equal(post.commentsIds[0].content, 'first comment');
}
run().catch(console.error);
The reason this is happening is because Document.populate()
does not return a promise, nor a thenable. We're not getting an error because await wraps the value that populate returns into a promise that immediately resolves to the same value.
If we were to use post.populate().then()
we would receive an error populate().then is not function
.
I'll be looking into it.
Oh, this seems to be by design.
For that, we'll need to use .execPopulate()
like that
await post.populate({ path: 'commentsIds' }).execPopulate();
@vkarpov15 Wouldn't it be neat if we made Document.prototype.populate(...)
thenable, and chainable? Just like Model.find().populate().select()
@AbdelrahmanHafez I already handle that like you say
await board
.populate({
path: '_pl',
select: '_n',
options: { retainNullValues: true },
})
.execPopulate()
but its gonna be extra excute and triky!
this populate not working as the documentation says so its look like a bug or maybe should update docs
populating-multiple-paths
The example in the referred-to-documentation uses Model.populate()
, which is different than Document.prototype.populate()
The use case in the first comment (Document.prototype.populate) is that we have found a document, and later we found that we needed to populate some fields for that document.
const board = await Game.findOne({ _id: someGame._id });
// here the API only supports execPopulate();
await board.populate({ path: '_p1' }).execPopulate();
Using Model.populate() however, supports chaining, and is a thenable.
// notice Story is a model, not a document
Story.
find(...).
populate('fans').
populate('author')
Please notice that mongoose queries are thenables, but are _not_ real promises. Read more here.
Also, now that I have given it some though, I don't think we can make Document.prototype.populate thenable without introducing a breaking change.
@AbdelrahmanHafez Its good to have populated as the prototype in future but for now, I prefer this line of code to docs
const board = await Game.findOne({ _id: someGame._id });
// here the API only supports execPopulate();
await board.populate({ path: '_p1' }).execPopulate();
I believe it's already present in the documentation
NOTE:
Population does not occur unless a callback is passed or you explicitly call execPopulate(). Passing the same path a second time will overwrite the previous path options. See Model.populate() for explaination of options.
We have an issue to track this change: #3834 . We'll make it so that Document#populate()
returns a thenable for 6.0.
Most helpful comment
We have an issue to track this change: #3834 . We'll make it so that
Document#populate()
returns a thenable for 6.0.