Search code examples
node.jsmongodbmongoosemongodb-querymongoose-populate

Select field populated in Mongoose


I need to filter the fields of the result obtained as populate with the mongoose select() function. I need only the title, content, image fields of the Post object and the name and avatar of the User object.

In the User schema I have created a virtual that references the userId field of the Post schema.

// ========= MODELS =============

const mongoose = require('mongoose');

const { Schema } = mongoose;

const userSchema = new Schema({
    name: { type: String, trim: true, required: true },
    email: { type: String, trim: true, required: true },
    password: { type: String, trim: true, required: true },
    description: { type: String, trim: true, required: true },
    avatar: { type: String, trim: true, required: true },
});

userSchema.virtual('ownerPost', {
  ref: 'Post',
  localField: '_id',
  foreignField: 'userId',
});

const postSchema = new Schema(
  {
    title: { type: String, trim: true, lowercase: true, required: true },
    content: { type: String, required: true },
    summary: {type: String, required: true },
    image: { type: String, trim: true, required: true },
    userId: { type: Schema.Types.ObjectId, ref: 'User', required: true }
  });

const Post = mongoose.model('Post', postSchema);
const User = mongoose.model('User', userSchema);

// ========= CONTROLLERS =============

const getPostById = async (req, res, next) => {
  try {
    const { id } = req.params;
    const post = await Post.findById(id)
      .populate('userId')
      // it doesn't work
      // .select(['title', 'content', 'image', 'userId.name', 'userId.avatar']);
      // it doesn't work
      // .select(['title', 'content', 'image', 'name', 'avatar']);

    return res.status(200).json(post);
  } catch (error) {
    return next(error);
  }
};

// ========= RESPONSE WITHOUT SELECT IN GET POST BY ID =============

{
    "title": "Example Title",
    "content": "<h1>This is content</h1>",
    "summary": "<h4>This is summary</h4>",
    "image": "https://upload.wikimedia.org/wikipedia/commons/0/02/Great_Wave_off_Kanagawa_-_reversed.png",
    "userId": {
        "name": "peter",
        "email": "peter80@gmail.com",
        "password": "$2b$12$LaJWX1/A3ATq4c/tgNIs.uwhnpZGwsqBePFLxIFCDa9gwjitcalda",
        "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
        "avatar": "https://pngimage.net/wp-content/uploads/2018/05/avatar-profile-png-2.png",
        "id": "5e9066e5bc3e6a415134396e"
    },
    "id": "5e90677cbc3e6a4151343970"
}

// ========= RESPONSE WITH SELECT IN GET POST BY ID =============

{
    "title": "titulo de ejemplo",
    "content": "<h1>Esto es un contenido de ejemplo</h1>",
    "image": "https://upload.wikimedia.org/wikipedia/commons/0/02/Great_Wave_off_Kanagawa_-_reversed.png",
    "userId": {
        "name": "peter",
        "email": "peter80@gmail.com",
        "password": "$2b$12$LaJWX1/A3ATq4c/tgNIs.uwhnpZGwsqBePFLxIFCDa9gwjitcalda",
        "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
        "avatar": "https://pngimage.net/wp-content/uploads/2018/05/avatar-profile-png-2.png",
        "id": "5e9066e5bc3e6a415134396e"
    },
    "id": "5e90677cbc3e6a4151343970"
}


Solution

  • you can use the select method to select what you need from the current collection

    regarding the populated field, you can pass an object to the populate method to indicate the path you will populate, and selecting which items from that collection

    your query should be something like that

    Post.findById(id).select('title content image userId').populate({ path: 'userId', select: 'name avatar' })