Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Populating with perDocumentLimit multiplies common entries #9906

Closed
paulholleis opened this issue Feb 8, 2021 · 2 comments
Closed

Populating with perDocumentLimit multiplies common entries #9906

paulholleis opened this issue Feb 8, 2021 · 2 comments
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue
Milestone

Comments

@paulholleis
Copy link

Do you want to request a feature or report a bug?
I think it's a bug with perDocumentLimit.

What is the current behavior?
Have 2 objects of the same Model M with each an array M.arr; both arrays have an entry that ref the same object X.
When populating with perDocumentLimit, I get, instead of just the populated X object, an array [X, X] within M.arr

If the current behavior is a bug, please provide the steps to reproduce.
This is the 9175.js with an added commonComment that is added to both Posts.
The script runs perfectly well without perDocumentLimit.

const mongoose = require('mongoose');

const { Schema } = mongoose;
const assert = require('assert');

async function createFirstPost(Post, Comment, commonComment) {
    const post = new Post({ title: 'I have 3 comments' });

    const comment1 = new Comment({ content: 'Cool first post' });
    const comment2 = new Comment({ content: 'Very cool first post' });
    const comment3 = new Comment({ content: 'Super cool first post' });

    post.commentsIds = [commonComment, comment1, comment2, comment3].map((comment) => comment._id);
    await Promise.all([
        post.save(),
        comment1.save(),
        comment2.save(),
        comment3.save(),
    ]);
}

async function createSecondPost(Post, Comment, commonComment) {
    const post = new Post({ title: 'I have 4 comments' });

    const comment1 = new Comment({ content: 'Cool second post' });
    const comment2 = new Comment({ content: 'Very cool second post' });
    const comment3 = new Comment({ content: 'Super cool second post' });
    const comment4 = new Comment({ content: 'Absolutely cool second post' });

    post.commentsIds = [commonComment, comment1, comment2, comment3, comment4].map((comment) => comment._id);
    await Promise.all([
        post.save(),
        comment1.save(),
        comment2.save(),
        comment3.save(),
        comment4.save(),
    ]);
}

async function run() {
    await mongoose.connect('mongodb://localhost:27017/test', {
        useNewUrlParser: true,
        useUnifiedTopology: true,
    });

    await mongoose.connection.dropDatabase();

    const postSchema = new Schema({
        title: String,
        commentsIds: [{ type: Schema.ObjectId, ref: 'Comment' }],
    });
    const Post = mongoose.model('Post', postSchema);

    const commentSchema = new Schema({ content: String });
    const Comment = mongoose.model('Comment', commentSchema);

    const commonComment = new Comment({ content: 'Im used in two posts' });
    await commonComment.save();
    await createFirstPost(Post, Comment, commonComment);
    await createSecondPost(Post, Comment, commonComment);

    const posts = await Post.find().populate({ path: 'commentsIds', perDocumentLimit: 2 });

    console.log(JSON.stringify(posts[0]));

    // first comment should not be an array but is [commonComment, commonComment]
    assert.notEqual(posts[0].commentsIds[0].length, 2);

    assert.equal(posts[0].commentsIds.length, 2);
    assert.equal(posts[1].commentsIds.length, 2);

    console.log('All assertions passed.');
}

run().catch(console.error);

This produces

{
  "commentsIds":
    [
        [**
           {"_id":"6021493f067381d74303df02","content":"Im used in two posts","__v":0},**
           {"_id":"6021493f067381d74303df02","content":"Im used in two posts","__v":0}**
        ]**,
        {"_id":"6021493f067381d74303df04","content":"Cool first post","__v":0}
    ],
  "_id":"6021493f067381d74303df03","title":"I have 3 comments","__v":0
}

What is the expected behavior?
It should include the commonComment only once and not an array of two commonComments.

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node: v14.15.1
Mongoose: 5.11.15
MongoDB: v4.2.5

@IslandRhythms IslandRhythms added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. labels Feb 8, 2021
@IslandRhythms
Copy link
Collaborator

@paulholleis When you say that the script works without perDocumentLimit, Do you mean it will run or that it does the expected behavior?

@IslandRhythms IslandRhythms added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Feb 8, 2021
@paulholleis
Copy link
Author

@IslandRhythms thanks for looking at its! And sorry, that was not well written from my side. I meant to say, without the perDocumentLimit, this test script fails as expected since the resulting "posts" then contain all 4 and 5 "comments" respectively (correctly as single items), e.g.:

{"commentsIds":[
    {"_id":"6022429a33fbeae353c9f777","content":"Im used in two posts","__v":0},
    {"_id":"6022429b33fbeae353c9f779","content":"Cool first post","__v":0},
    {"_id":"6022429b33fbeae353c9f77a","content":"Very cool first post","__v":0},
    {"_id":"6022429b33fbeae353c9f77b","content":"Super cool first post","__v":0}
],
"_id":"6022429b33fbeae353c9f778","title":"I have 3 comments","__v":0}

AssertionError [ERR_ASSERTION]: 4 == 2 in 9175.js:70:12

If you need a succeeding test for that, just replace the last two assertions with the expected 4 and 5 instead of 2.

    assert.equal(posts[0].commentsIds.length, 4);
    assert.equal(posts[1].commentsIds.length, 5);

@vkarpov15 vkarpov15 added this to the 5.11.17 milestone Feb 12, 2021
vkarpov15 added a commit that referenced this issue Feb 16, 2021
@vkarpov15 vkarpov15 modified the milestones: 5.11.17, 5.11.18 Feb 17, 2021
This was referenced Mar 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue
Projects
None yet
Development

No branches or pull requests

3 participants