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

Virtual Population of Field on a Nested Embedded Discriminator Not Working When Being Used in Models With Nested Schemas #6487

Closed
jimmytsao opened this issue May 20, 2018 · 1 comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@jimmytsao
Copy link

jimmytsao commented May 20, 2018

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

What is the current behavior?
When I try to populate an embedded discriminator from a model that has a nested schema defined by an Object literal everything works. However, if I do the same but use a nested schema instantiated from mongoose.Schema, it no longer works . See working and non-working example in repro script section.

This may be related to: #6411 and #6488

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

Here's are some examples:

Virtual Population WORKING When Nested Schema is Defined By An Object Literal

const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/mongoosetest')
mongoose.connection.once('open', () => {

  const Schema = mongoose.Schema

  // Generate Users Model
  const userSchema = new Schema({ employeeId: Number, name: String })
  const UserModel = mongoose.model('Users', userSchema, 'users')

  // Generate Embedded Discriminators
  const eventSchema = new Schema(
    { message: String },
    { discriminatorKey: 'kind'}
  );

  const batchSchema = new Schema({
    nested: { 
      events: [eventSchema]
    }
  });

  const docArray = batchSchema.path('nested.events'); 

  // First embedded discriminator schema
  const clickedSchema = new Schema(
    {
      element: { type: String },
      users: [ Number ]
    },
    {
      toJSON: { virtuals: true},
      toObject: { virtuals: true}
    }
  );

  // Add virtual to first embedded discriminator schema for virtual population
  clickedSchema.virtual('users_$', {
    ref: 'Users',
    localField: 'users',
    foreignField: 'employeeId'
  })
  
  const Clicked = docArray.discriminator('Clicked', clickedSchema);

  // Second embedded discriminator
  const Purchased = docArray.discriminator('Purchased', new Schema({
    product: { type: String }
  }));

  const Batch = mongoose.model('EventBatch', batchSchema);

  // Generate Items
  const user = { employeeId: 1, name: 'Test name' }
  const batch = {
    nested: {   
      events: [
        { kind: 'Clicked', element: '#hero', message: 'hello', users: [1] },
        { kind: 'Purchased', product: 'action-figure-1', message: 'world' }
      ]
    }
  };

  // Clearing out the collections before creating items
  Promise.all([ UserModel.remove({}), Batch.remove({}) ])
  .then(() => Promise.all([UserModel.create(user), Batch.create(batch)]))
  .then(function(){
    Batch.find({})
      // Populate virtual field of embedded discriminator
      .populate('nested.events.users_$') 
      .lean()
      .then(results => {
        console.log(JSON.stringify(results, null, 2))
      })
  })
})

Received correct output with "users_$" populated

[
  {
    "_id": "5b01e27d2956fa8bcbd4a5f9",
    "nested": {
      "events": [
        {
          "users": [
            1
          ],
          "_id": "5b01e27d2956fa8bcbd4a5fb",
          "kind": "Clicked",
          "element": "#hero",
          "message": "hello",
          "users_$": [
            {
              "_id": "5b01e27d2956fa8bcbd4a5f8",
              "employeeId": 1,
              "name": "Test name",
              "__v": 0
            }
          ]
        },
        {
          "_id": "5b01e27d2956fa8bcbd4a5fa",
          "kind": "Purchased",
          "product": "action-figure-1",
          "message": "world",
          "users_$": []
        }
      ]
    },
    "__v": 0
  }
]

Virtual Population NOT WORKING When Nested Schema is by Schema instantiated from mongoose.Schema

const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/mongoosetest')
mongoose.connection.once('open', () => {

  const Schema = mongoose.Schema

  // Generate Users Model
  const userSchema = new Schema({ employeeId: Number, name: String })
  const UserModel = mongoose.model('Users', userSchema, 'users')

  // Generate Embedded Discriminators
  const eventSchema = new Schema(
    { message: String },
    { discriminatorKey: 'kind'}
  );

  const batchSchema = new Schema({
    nested: new Schema({   //<== Schema from mongoose.Schema instead of object literal
      events: [eventSchema]
    })
  });

  const docArray = batchSchema.path('nested.events'); 

  // First embedded discriminator schema
  const clickedSchema = new Schema(
    {
      element: { type: String },
      users: [ Number ]
    },
    {
      toJSON: { virtuals: true},
      toObject: { virtuals: true}
    }
  );

  // Add virtual to first embedded discriminator schema for virtual population
  clickedSchema.virtual('users_$', {
    ref: 'Users',
    localField: 'users',
    foreignField: 'employeeId'
  })
  
  const Clicked = docArray.discriminator('Clicked', clickedSchema);

  // Second embedded discriminator
  const Purchased = docArray.discriminator('Purchased', new Schema({
    product: { type: String }
  }));

  const Batch = mongoose.model('EventBatch', batchSchema);

  // Generate Items
  const user = { employeeId: 1, name: 'Test name' }
  const batch = {
    nested: {   
      events: [
        { kind: 'Clicked', element: '#hero', message: 'hello', users: [1] },
        { kind: 'Purchased', product: 'action-figure-1', message: 'world' }
      ]
    }
  };

  // Clearing out the collections before creating items
  Promise.all([ UserModel.remove({}), Batch.remove({}) ])
  .then(() => Promise.all([UserModel.create(user), Batch.create(batch)]))
  .then(function(){
    Batch.find({})
      // Populate virtual field of embedded discriminator
      .populate('nested.events.users_$') 
      .lean()
      .then(results => {
        console.log(JSON.stringify(results, null, 2))
      })
  })
})

Received incorrect output with "users_$" not populated

[
  {
    "_id": "5b01e42b623c3a8cf6bf256e",
    "nested": {
      "_id": "5b01e42b623c3a8cf6bf256f",
      "events": [
        {
          "users": [
            1
          ],
          "_id": "5b01e42b623c3a8cf6bf2571",
          "kind": "Clicked",
          "element": "#hero",
          "message": "hello"
        },
        {
          "_id": "5b01e42b623c3a8cf6bf2570",
          "kind": "Purchased",
          "product": "action-figure-1",
          "message": "world"
        }
      ]
    },
    "__v": 0
  }
]

What is the expected behavior?
Expected "users_$" to be populated

Please mention your node.js, mongoose and MongoDB version.
node: v8.11.1
mongoose: 5.1.1
mongo: 3.6.4

@vkarpov15
Copy link
Collaborator

Thanks for your continued detailed bug reports, I appreciate your help. Will fix this ASAP 👍

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.
Projects
None yet
Development

No branches or pull requests

2 participants