Search code examples
node.jsmongoose

Mongoose projection on nested field


I'm trying to do a projection upon a nested field but it's retrieving the whole field instead of the nested type attribute:

Nested field in User document:

enter image description here

const type=await User.findOne({email: enteredEmail}).select({_id:0, 'accountType.type':1})
console.log(type)

Output:

{ accountType: { type: 'social_account' } }

How to modify query to retrieve: 'social_account' instead of the whole AccountType attribute ?


Solution

  • Use aggregation $project operator to project field from embed document to top-level document.

    import mongoose from 'mongoose';
    import { config } from '../../config';
    
    mongoose.set('debug', true);
    
    const userSchema = new mongoose.Schema({
        email: String,
        accountType: {
            type: {
                type: String,
            },
        },
    });
    const User = mongoose.model('user', userSchema);
    
    (async function main() {
        try {
            await mongoose.connect(config.MONGODB_URI);
            await User.collection.drop();
            // seed
            await User.create({ email: '[email protected]', accountType: { type: 'social_account' } });
    
            //test
            const r = await User.aggregate()
                .match({ email: '[email protected]' })
                .project({ type: '$accountType.type', email: 1, _id: 0 })
                .exec();
            console.log(r);
        } catch (error) {
            console.error(error);
        } finally {
            await mongoose.connection.close();
        }
    })();
    

    Debug logs:

    Mongoose: users.drop()
    Mongoose: users.insertOne({ email: '[email protected]', accountType: { type: 'social_account' }, _id: ObjectId("649c2b69fdb39ffcc3c31854"), __v: 0}, {})
    Mongoose: users.aggregate([ { '$match': { email: '[email protected]' } }, { '$project': { type: '$accountType.type', email: 1, _id: 0 } }], {})
    [ { email: '[email protected]', type: 'social_account' } ]