Home > Net >  "Cast to Number failed for value " (type Object) - Mongoose
"Cast to Number failed for value " (type Object) - Mongoose

Time:01-28

I am working on an endpoint where I want to update the user's game stats: highestScore and totalGamesPlayed. As far as I can tell, the code should be working. However, when making a PATCH request with Postman, I receive this error:

PS. I am using findOneAndUpdate because save() wasn't working, I wasn't getting the updated version of the user.

"Cast to Number failed for value \"{ '$max': [ '$userStats.highestScore', 100 ] }\" (type Object) at path \"highestScore\"",

Here's my userModel.js:

import mongoose from "mongoose";

const userSchema = mongoose.Schema(
  {
    username: {
      type: String,
      required: [true, "Please add a username"],
    },

    email: {
      type: String,
      required: [true, "Please add your email"],
    },

    password: {
      type: String,
      required: [true, "Please add your password"],
    },
    userStats: [
      {
        totalGamesPlayed: {
          type: Number,
          default: 0,
        },
        highestScore: {
          type: Number,
          default: 0,
        },
      },
    ],
  },
  { timestamps: true }
);

const User = mongoose.model("User", userSchema);

export default User;

My update controller:

export const updateUserStats = asyncHandler(async (req, res) => {
  // Extract token from headers
  const token = req.headers.authorization.split(" ")[1];

  // Find the user by token

  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  // Find user by id

  const user = await User.findById(decoded.id);

  if (!user) {
    return res.status(404).send("User not found");
  }

  // Update the user's stats

  // Save the updated user
  const updatedUser = await User.findOneAndUpdate(
    { _id: decoded.id },
    {
      $set: {
        "userStats.totalGamesPlayed": { $inc: 1 },
        "userStats.highestScore": {
          $max: ["$userStats.highestScore", req.body.highestScore],
        },
      },
    },
    { new: true }
  );

  if (updatedUser) {
    res.json(updatedUser);
  } else {
    res.status(400);
    throw new Error("Something went wrong");
  }
});

CodePudding user response:

It seems like a mix in syntax between update $max and $max (aggregation) (also $inc )

tl;dr

Your call to findOneAndUpdate should probably look like this:

 const updatedUser = await User.findOneAndUpdate(
    { _id: decoded.id },
    {
      $inc: {
        "userStats.totalGamesPlayed": 1
      },
      $max: {
        "userStats.highestScore": req.body.highestScore
      },
    },
    { new: true }
  );

explanation

The syntax you used seems like it belongs to the Aggregation Framework (see Introduction to the MongoDB Aggregation Framework), where in your case findOneAndUpdate is an operation with slightly different syntax.

In the operation findOneAndUpdate the $set operator interprets the syntax you used as if you are trying to assign the Object { '$max': [ '$userStats.highestScore', 100 ] } as if it was a number to the field $userStats.highestScore. The $max operator does not work inside $set (but it would work in the Aggregation Framework).

Mongodb's more "simple" operations like insert, find, findOneAndUpdate, etc.. only have one stage. The Aggregation Framework allows more sophisticated multi-stage operations. These operations work where more context or data is available. That also means some operators like $max need to be written slightly differently to make use of additional data or sometimes to avoid ambiguity (and probably for other reasons as well).

In the simple operation of findOneAndUpdate, there's no "direct" access to the current value of the a field. But an operator like $max can still do a comparison and work on it. The syntax you used, which would work in an Aggregation Framework stage, opens the door to using values from other fields to calculate the max value. But it is unavailable for findOneAndUpdate, so the $max syntax is simpler and more limited.

"regular" operator: $max

Aggregation Framework operator: $max (aggregation)

  • Related