How to always return the executed value of a getter?

I have a mongoose schema with a getter function for one of the properties, for example:

const User = new mongoose.Schema(
    {
        active: Boolean,
        email:  {type: String, get: toLowerCase}
    }
)

and toLowerCase is defined as:

function toLowerCase(str) {
    return str.toLowerCase()
}

When I perform User.findOne(), an example object returned looks like

// user
{
    active: true,
    email: "LowerCase@gmail.com" // note: not lower-cased
}

BUT if access the email property with same object with user.email it returns lowercase@gmail.com

user.email // lowercase@gmail.com

Is there a way to have mongo always directly return the executed getter value?

1 Like

Welcome to the MongoDB Community @lee_from_the_web !

The difference you are observing is how the data is actually stored in MongoDB versus additional processing that Mongoose is doing on retrieval in a context where the Getter function is fired.

if you always want to return the email address as lowercase, you should use a Setter instead of a Getter so the expected value is persisted to MongoDB. Storing lowercase email addresses happens to be the example used in Mongoose’s Getters/Setters documentation.

There are two approaches you might want to consider:

  • lowercase the email value before saving

  • add an emailLower field if you want to preserve the original email value and have a lower case version

You may also want to look at Mongoose’s Middleware for additional function contexts. For best performance and predictable outcomes, I would try to rely more on how the data is actually stored in MongoDB than client-side read transformations which may not execute as expected in all contexts. For example, Mongoose does not apply model schema to aggregation queries by default.

Regards,
Stennie

@Stennie_X Thanks for the help! Maybe I can throw another example your way that involves using a setter AND getter:

Let’s say for privacy reasons instead of lower-casing the email we hash it and store its encrypted value. And when we read the value, we always want the decrypted value returned. So the User mongoose schema would have a email property like (where decrypt and encrypt are the respective encryption functions):

email: { type: String, get: decrypt, set: encrypt }

Is there a way to always return the decrypted value without first having to access the property directly or convering to JSON:

user.email
// or
res.send(user)

I would like all my DB queries on User to automatically return with the decrypted email value. Would a “find” middleware be appropriate for this? (sorry, just learning about middlewares)

schema.post('find', decryptMiddlewareFunc)

I think I have found an answer:

Adding:

toObject: { getters: true, setters: true },
toJSON: { getters: true, setters: true },
runSettersOnQuery: true,

in the options object in the Schema definition, makes the setters run on query.