mongodb-js / mongoose-autopopulate

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

How to hide the selected field which is necessary for the virtual field in the result data #103

Closed secreter closed 1 year ago

secreter commented 1 year ago

In my case, I have a User schema and a Post schema, I want to populate a virtual field 'name' in User to the Post, when the 'nickname' is empty, use '**' concat the part of 'phoneNumber'。And never expose the 'phoneNumber' in post data。 But I find it difficult to achieve。Without selecting the 'phoneNumber' field, can't get the correct 'name'。 I need some help,Thanks.

// this is the user schema
const UserSchema = new mongoose.Schema(
    {
      nickname: {
        type: String
      },
      phoneNumber: {
        type: String,
      }
    },
    {
      toJSON: { virtuals: true },
      toObject: { virtuals: true }
    }
  );
// user may not set nickname, use '*' and part of phoneNumber
UserSchema.virtual('name').get(function() {
  return this.nickname||'**'+this.phoneNumber?.slice(-4);
});

In the Post schema , I want to auto populate the 'user' field,I expect return the name field and hide the phoneNumber field,but it seems can't achieve。

// only select 'name  ', hide 'phoneNumber'
{
    autopopulate: {
      select: 'nickname',
    }
}

In this case,the post.user.name return '**undefined'。 when I add the 'phoneNumber' field it works, but how can I hide the 'phoneNumber' field?

{
    autopopulate: {
      select: 'nickname phoneNumber',
    }
}

In this case,the post.user.name return 'Bob'。

vkarpov15 commented 1 year ago

'name' is a virtual, not a field. You can't select: 'name'. You should do select: 'nickname' instead.

secreter commented 1 year ago

Sorry, it's a typo, I finally want the name virtual in Post data,and never expose the 'phoneNumber' in post data。

// user may not set nickname, use '*' and part of phoneNumber
UserSchema.virtual('name').get(function() {
  return this.nickname||'**'+this.phoneNumber?.slice(-4);
});
vkarpov15 commented 1 year ago

Your best bet would be to add a getter that obfuscates the phone number. Something like:

const UserSchema = new mongoose.Schema(
    {
      nickname: {
        type: String
      },
      phoneNumber: {
        type: String,
        get(v) { return v == null ? v : v.slice(-4); }
      }
    },
    {
      toJSON: { virtuals: true },
      toObject: { virtuals: true }
    }
  );

More info on getters and setters in Mongoose: https://mongoosejs.com/docs/tutorials/getters-setters.html