Steve N
Steve N

Reputation: 2737

Mongoose update dependent field from another field's setter?

I have a scenario in node/express/mongoose where I have users who accumulate points, and when that point total crosses a certain threshold, they "level up" (think games with point-based levels).

I have created a custom setter on the points field that checks if the value has changed, and if so tries to update the level field. Levels are defined in another collection, but are saved as simple JSON objects when associated with user docs (hence the .lean() in the query). I did it this way vs a virtual field or population for efficiency.

Problem: this doesn't actually seem to update the user 'level' field when it should. What am I doing wrong?

// Define our user schema
var UserSchema = new mongoose.Schema({
    ...
    points:       {type: Number, default: 0, set: pointsChangeHandler},
    level:        {name: String, minPoints: Number, maxPoints: Number},
    ...
});

And the setter looks like so:

function goodPointsChangeHandler(newValue) {
    var self = this;
    if (newValue != self.goodPoints) {
        console.log('Point change detected.');
        // Find level that corresponds with new point total
        Level.findOne({
            'minPoints': {$lte : self.goodPoints},
            'maxPoints': {$gt : self.goodPoints}}, '-_id').lean().exec(function(err, level) {
            if (self.goodLevel == undefined || self.goodLevel.rank != level.rank) {
                console.log('Level changed.');
                self.goodLevel = level;
            }
            return newValue;
        });
    }
    return newValue;
}

Upvotes: 2

Views: 2939

Answers (1)

Steve N
Steve N

Reputation: 2737

Based on @laggingreflex's comment, I tried modifying this within the scope of the model method (i.e. not in the Level.findOne() callback, and changes made that way were persisted without an explicit save() call.

Also, I had a pretty silly error where I was returning newValue from thefindOne` callback.. not sure what I was thinking there...

Long story short, and this may be obvious to node/express/mongoose experts, but you can modify fields other than the one whose setter method you're currently in, but the moment you find yourself in the callback of another async method, you'll have to do an explicit save() or your modifications to this will not be persisted.

So:

function myFieldSetterMethod(newValue) {
    this.myField = "a";
    this.myOtherField = "b";
    return newValue;
    // no save() necessary, this will update both fields
}

function myFieldSetterMethod(newValue) {
    this.myField = "a";

    SomeModel.findOne(..., function(err, doc) {
        this.myOtherField = doc.somethingIWantFromThisDoc;
        // now you'll need an explicit save
        // this.save(...)
    });

    return newValue;      
}

Upvotes: 5

Related Questions