0

I tried to update part of a document that has an array. I hear this is a big no-no in mongoose but am unaware how I can force it to update the array. It does it correctly in the code (as in, it updates the value in the locally fetched document,) but when I try to save it via await mongoUser.save(), it is not updated in mongo.

Here is my schema code

const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
  id: { type: String, required: true},
  socialCreditScore: { type: Number, required: true, default: 1000 },
  /* A+: 1050
  // A: 960 - 1050
  // B: 850 - 959
  // C: 600 - 849
  / D: 0 - 599 */
  isStaff: { type: Boolean, required: true, default: false },
  blacklisted: { type: Boolean, required: true, default: false},
  guildScores: { type: Array, required: true, strict: false } ,
  notifyChange: { type: Boolean, required: true, default: false }
}, {strict: false, timestamps: true })

module.exports = mongoose.model('User', UserSchema);

Here is an example of the locally updated document

{
  _id: new ObjectId("61c1218ae82898e9cd7af768"),
  id: '945914953495034509',
  socialCreditScore: 2100,
  isStaff: false,
  blacklisted: false,
  # Previously: guildScores: [ { id: "...", laborScore: 0 } ]
  guildScores: [ { id: '04503405340534545', laborScore: 2000 } ],
  notifyChange: false,
  createdAt: 2021-12-21T00:36:26.871Z,
  updatedAt: 2021-12-21T00:50:27.286Z,
  __v: 0
}

Code to update User

const data = await User.find({ id: message.author.id });
const mongoUser = data[0];
// ...
mongoUser.socialCreditScore += socCreditBoost;
      const guildScoreData = mongoUser.guildScores.find(guild => guild.id === message.guild.id);
      // { id, laborScore }
      guildScoreData.laborScore += salary;
      console.log(mongoUser);
      await mongoUser.save();

EDIT: I've noticed that the socialCreditScore value updates correctly every time I try to update it, however the guildScore does not.

5
  • Please add the code where you try to update the document. Commented Dec 21, 2021 at 1:29
  • @MontgomeryWattss requested acknowledged; code added Commented Dec 21, 2021 at 1:40
  • @WilliamFaircloth can you check if const guildScoreData = mongoUser.guildScores.find(guild => guild.id === message.guild.id); gives you any value? I think the problem might lie in here. You are trying to compare guild.id which is a mongo objectId with message.guild.id which might be a string. If this is not returning the data then try using guild.id.toString() === message.guild.id Commented Dec 21, 2021 at 2:10
  • @HimanshuShubham guild.id is not a mongo objectid, self explanatory explanation can be observed in the following code that console.log(guildScoreData) returns: ``` { id: '921069049289523260', laborScore: 0 } ``` Commented Dec 21, 2021 at 2:17
  • oh, ok Can you try the below code, just want to check if this works User.updateMany({ id: message.author.id, }, { $inc: { "guildScores.$[guild].laborScore": salary, "socialCreditScore": socCreditBoost } }, { arrayFilters: [ { "guild.id": message.guild.id, }, ], }) Commented Dec 21, 2021 at 2:41

1 Answer 1

2

I believe the reason the array is not getting saved is because the type of the guildScores field is Array. In the documentation of the Array schema type, it says that specifying just Array is equivalent to Mixed. The documentation for the Mixed schema type states:

Since Mixed is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect and save those changes. To tell Mongoose that the value of a Mixed type has changed, you need to call doc.markModified(path), passing the path to the Mixed type you just changed.

To avoid these side-effects, a Subdocument path may be used instead.

person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // Mongoose will save changes to `anything`.

In your case, mongoUser.markModified('guildScores') might do the trick.

Alternatively, you can also make use of Mongoose's findOneAndUpdate method to do the find and update the document in one operation (I am assuming you only have one document per id). It might look like this:

await User.findOneAndUpdate(
    { id: message.author.id, 'guildScores.id': message.guild.id },
    { $inc: { socialCreditScore: socCreditBoost, "guildScores.$.laborScore": salary }}
)
Sign up to request clarification or add additional context in comments.

2 Comments

That worked, I added the line before saving it and the code runs. Thank's for yoru support, if you have any contextual documentation you would like to provide regarding this subject, I would appreciate it.
You're welcome. I looked through the Mongoose documentation to find the info about how Mongoose treats the Array type, specifically the Schema Types page. It may be worth looking into subdocuments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.