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.
CodePudding user response:
I believe the reason the array is not getting saved is because the type of the guildScores
field is Array
. If you look under Array
in the usage notes for Mongoose schema types, it says that specifying just Array
is equivalent to Mixed
. On the same page the documentation 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 }}
)