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 virtual count field returns the sum of all referencing documents for all populated documents #7573

Closed
1 of 2 tasks
useo6 opened this issue Mar 1, 2019 · 3 comments
Labels
has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue
Milestone

Comments

@useo6
Copy link

useo6 commented Mar 1, 2019

Do you want to request a feature or report a bug?

  • Bug
  • Feature

What is the current behavior?

I've a model with a virtual property (let's call it "numS3") that counts the number documents in another model (https://mongoosejs.com/docs/populate.html#count). When I populate this virtual property in a chain, "numS3" is not the sum of each document, it's the sum of all "numS3" of all documents found.

If the current behavior is a bug, please provide the steps to reproduce.

let s1 = new mongoose.Schema({})

s1.virtual('s2', {
  ref: 'schema2',
  localField: '_id',
  foreignField: 's1'
})

let s2 = new mongoose.Schema({
  s1: {type: mongoose.Schema.Types.ObjectId, ref: 'schema1'}
})

s2.virtual('numS3', {
  ref: 'schema3',
  localField: '_id',
  foreignField: 's2',
  count: true
})

let s3 = new mongoose.Schema({
  s2: {type: mongoose.Schema.Types.ObjectId, ref: 'schema2'}
})

const schema1 = mongoose.model('schema1', s1)
const schema2 = mongoose.model('schema2', s2)
const schema3 = mongoose.model('schema3', s3)

schema1.findOne({})
  .populate({
    path: 's2',
    populate: {
      path: 'numS3'
    }
  })
  .exec((err, r) => {
    // Let's say we've the following structure:
    //                Schema1
    //               /       \
    //              /         \
    //          Schema2      Schema2
    //         /       \       |
    //     Schema3   Schema3 Schema3
    // 
    // This means:
    //  Schema1: 1 Entry
    //  Schema2: 2 Entries (both referencing the Schema1 entry)
    //  Schema3: 3 Entries (one referencing one Schema2 entry and 2 the other Schema2 entry)
    //  
    //  Now... numS3 of each s2 becomes 3 instead of either 1 or 2
    console.log(r.s2[0].numS3) // numS3 = 3 -> expecting: should be either 1 or 2
    console.log(r.s2[1].numS3) // numS3 = 3 -> expecting: should be either 1 or 2
  })

// Edit: When I limit the number of s2 populated to 1, it returns the 1 or 2 (depending on the instance of s2 found).`

What is the expected behavior?
Populating the virtual "count-property" numS3 should return the number of documents of Schema3 and not the sum of all Schema3 that referencing Schema2.

Please mention your node.js, mongoose and MongoDB version.

node.js: 8.11.4
mongoose: 5.4.16
MongoDB: 3.6

@vkarpov15 vkarpov15 added this to the 5.4.20 milestone Mar 3, 2019
@vkarpov15 vkarpov15 added the has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue label Mar 3, 2019
vkarpov15 added a commit that referenced this issue Mar 22, 2019
@vkarpov15 vkarpov15 added confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Mar 22, 2019
@izubia
Copy link

izubia commented Jul 30, 2019

Can you confirm if this is the same issue? A "developers" list in a "team" collection. The developers have two virtual fields related with "tickets":

  • "tickets" (with the full array of its tickets)
  • "ticketsCount" (with the count of its tickets).

Code ready to run

const mongoose = require('mongoose');
mongoose.connect('mongodb://@127.0.0.1:27017/test-virtual-count', { useNewUrlParser: true });

const DeveloperSchema = new mongoose.Schema({
    name: String
}, { toJSON: { virtuals: true }, toObject: { virtuals: true } });

const TeamSchema = new mongoose.Schema({
    name: String,
    developers: [DeveloperSchema]
});

const TicketSchema = new mongoose.Schema({
    assigned: String,
    description: String
});

DeveloperSchema.virtual('tickets', {
    ref: 'Ticket',
    localField: 'name',
    foreignField: 'assigned',
  });

DeveloperSchema.virtual('ticketsCount', {
    ref: 'Ticket',
    localField: 'name',
    foreignField: 'assigned',
    count: true
});

const Ticket = mongoose.model('Ticket', TicketSchema);
const Team = mongoose.model('Team', TeamSchema);

const run = async () => {

    await new Team({
        name: 'Rocket',
        developers: [
            {
                name: 'John'
            },
            {
                name: 'Mike'
            },
            {
                name: 'Ignacio'
            }
        ]
    }).save();

    await Ticket.insertMany([
        {
            assigned: 'Mike',
            description: 'Build a Demo'
        },
        {
            assigned: 'Mike',
            description: 'Rollback'
        },
        {
            assigned: 'John',
            description: 'Refactor'
        },
        {
            assigned: 'Mike',
            description: 'Tests'
        },
        {
            assigned: 'Mike',
            description: 'HotFix'
        }
    ]);

    const team = await Team.findOne({}, {_id: 0,__v:0}).populate('developers.tickets', {_id: 0,__v:0}).populate('developers.ticketsCount');
    console.log(JSON.stringify(team, null, 2));
};

run();

Output

The tickets array is almost perfect (except for the developer who doesn't have tickets...) but the tickesCount doesn't match with de tickets.length, it's equal to the number of developers.

{
    "name": "Rocket",
    "developers": [
      {
        "_id": "5d3f146ffd04097b0cd10532",
        "name": "John",
        "id": "5d3f146ffd04097b0cd10532",
        "tickets": [
          {
            "assigned": "John",
            "description": "Refactor"
          }
        ],
        "ticketsCount": 3
      },
      {
        "_id": "5d3f146ffd04097b0cd10531",
        "name": "Mike",
        "id": "5d3f146ffd04097b0cd10531",
        "tickets": [
          {
            "assigned": "Mike",
            "description": "Build a Demo"
          },
          {
            "assigned": "Mike",
            "description": "Rollback"
          },
          {
            "assigned": "Mike",
            "description": "Tests"
          },
          {
            "assigned": "Mike",
            "description": "HotFix"
          }
        ],
        "ticketsCount": 3
      },
      {
        "_id": "5d3f146ffd04097b0cd10530",
        "name": "Ignacio",
        "id": "5d3f146ffd04097b0cd10530",
        "tickets": [
          "Ignacio"
        ],
        "ticketsCount": 3
      }
    ]
  }

@vkarpov15 vkarpov15 reopened this Aug 3, 2019
@vkarpov15 vkarpov15 modified the milestones: 5.4.20, 5.6.9 Aug 3, 2019
@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue and removed confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. labels Aug 3, 2019
vkarpov15 added a commit that referenced this issue Aug 3, 2019
vkarpov15 added a commit that referenced this issue Aug 4, 2019
@yharaskrik
Copy link

Hi, I think this might have regressed.

I have the following example

itemSchema

itemSchema.virtual('numBids', {
    ref: 'Bids',
    localField: 'itemId',
    foreignField: 'itemId
    count: true,
});
{
  items: [     // [itemSchema]
    {id: '123'}
  ]
}

When I .populate('items.numBids') and there are no Bids that match in the ref collection, the count for each item comes back as items.length as soon as there is one or more bids for any of the items then the numbers are fine.

@vkarpov15
Copy link
Collaborator

@yharaskrik it hasn't regressed, because we have tests for this issue and they're passing. Please open a new issue and follow the issue template.

@Automattic Automattic locked as resolved and limited conversation to collaborators Jul 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
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

4 participants