mongodb-js / mongoose-autopopulate

Always populate() certain fields in your mongoose schemas
Apache License 2.0
221 stars 36 forks source link

Last defined discriminator of nested array discriminators is not populated #99

Closed 0xddff closed 2 years ago

0xddff commented 2 years ago

When using mongoose-autopopulate only the last defined discriminator (see comment in code) is populated. In my case "internal-resource" populates to "null", but if you switch the discriminator initialization "internal-resource" popultes, but "external-resource" not.

Am I missing something or this is a bug?

I've also double checked with the bug-fix test "autopopulates embedded discriminator (gh-82)", but can't find any obvious difference.

const mongoose = require( 'mongoose' );
const assert = require( 'assert' );
const autopopulate = require( 'mongoose-autopopulate' );
const { exit } = require( 'process' );

mongoose.set( 'debug', true );

async function run() {
        await mongoose.connect( 'mongodb://localhost:27017' );
        await mongoose.connection.dropDatabase();

    const gradeSchema = new mongoose.Schema( { name : String } );
    const Grade = mongoose.model( 'Grade', gradeSchema );

    const personSchema = new mongoose.Schema( { firstName : String, lastName : String } );
    const Person = mongoose.model( 'Person', personSchema );

    const resourcePlanSchema = new mongoose.Schema( {
        resources : [ new mongoose.Schema( { hours : Number }, { discriminatorKey: 'kind', _id: false } ) ]
    } );

    const contentPath = resourcePlanSchema.path( 'resources' );

    const internalResourceSchema = new mongoose.Schema( {
        resource : {
            type : mongoose.Schema.Types.ObjectId, ref : 'Person', autopopulate : true
        }
    } );

    const externalResourceSchema = new mongoose.Schema( {
        resource : {
            type : mongoose.Schema.Types.ObjectId, ref : 'Grade', autopopulate : true
        }
    } );

    const InternalResource = contentPath.discriminator( 'internal-resource', internalResourceSchema );
    const ExternalResource = contentPath.discriminator( 'external-resource', externalResourceSchema );

    resourcePlanSchema.plugin( autopopulate );
    const ResourcePlan = mongoose.model( 'ResourcePlan', resourcePlanSchema );

    const gradeDoc = await Grade.create( { name : 'Associate' } );
    const gradeDoc2 = await Grade.create( { name : 'Senior' } );

    const personDoc = await Person.create( { firstName : 'John', lastName : 'Doe' } );

    await ResourcePlan.create( {
        resources : [
            new InternalResource( {
                resource : personDoc._id,
                hours : 1
            } ),
            new ExternalResource( {
                resource : gradeDoc._id,
                hours : 2
            } ),
            ,
            new ExternalResource( {
                resource : gradeDoc2._id,
                hours : 3
            } )
        ]
    } );

    const resourcePlanDoc = await ResourcePlan.find();
    console.log( resourcePlanDoc[ 0 ].resources );

    assert.ok( resourcePlanDoc[ 0 ].resources[ 0 ].resource.firstName );
    assert.ok( resourcePlanDoc[ 0 ].resources[ 1 ].resource.name );
    exit( 0 );
}

run();

Node.js version 16.13.2

MongoDB Server Version 4.4

Mongoose / Mongoose-autopopulate 6.5.4 / 0.16.1

Expected Behavior Resource[ 0 ] should auto populate to Person Resource[ 1 ] should auto populate to Grade

0xddff commented 2 years ago

Took me hours to figure this out. Discriminators can not have the same attribute name "resource", the identifier must be unique. Why is that? This should be definitely mentioned in the documentation.

If I rename "internalResourceSchema" attribute "resource" to "person" and rename "resource" from externalResourceSchema to "grade" everything is populated.

Hope this helps someone in the future.