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 Embedded Discriminator Not Working When Virtual Field Is Nested Within Embedded Discriminator #6488

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

Comments

@jimmytsao
Copy link

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

What is the current behavior?
Virtual population of field on an embedded discriminator works when the virtual field is at the root of the embedded object schema. However, if the virtual field is nested within the object it will not work.

May be related to: #6487 and #6411

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

Virtual Population WORKING When Virtual Field Is At Root of Embedded Object 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: { 
      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_$', {    // <=== Virtual at root of embedded discriminator schema
    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 Virtual Field Is Nested Within Embedded Object 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: { 
      events: [eventSchema]
    }
  });

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

  // *** Adding Nested Layer and adding virtual to schema of nestedLayer
  const nestedLayerSchema = new Schema({
    users: [ Number ]
  },
  {
    toJSON: { virtuals: true},
    toObject: { virtuals: true}
  })

  nestedLayerSchema.virtual('users_$', {
    ref: 'Users',
    localField: 'users',
    foreignField: 'employeeId'
  })
  
  // First embedded discriminator schema
  const clickedSchema = new Schema(
    {
      element: { type: String },
      nestedLayer: nestedLayerSchema
    },
    {
      toJSON: { virtuals: true},
      toObject: { virtuals: true}
    }
  );

  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', nestedLayer: { users: [1] } }, // <--- Added nestedLayer
        { 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.nestedLayer.users_$')    // <--- Populating users_$ on nestedLayer
      .lean()
      .then(results => {
        console.log(JSON.stringify(results, null, 2))
      })
  })
})

Expected output with "nestedLayer.users_$" populated but received it unpopulated

[
  {
    "_id": "5b01ea6679c45893dadb79dd",
    "nested": {
      "events": [
        {
          "_id": "5b01ea6679c45893dadb79df",
          "kind": "Clicked",
          "element": "#hero",
          "message": "hello",
          "nestedLayer": {
            "users": [
              1
            ],
            "_id": "5b01ea6679c45893dadb79e0"
          }
        },
        {
          "_id": "5b01ea6679c45893dadb79de",
          "kind": "Purchased",
          "product": "action-figure-1",
          "message": "world"
        }
      ]
    },
    "__v": 0
  }
]

What is the expected behavior?
Expected "nestedLayer.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

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