I have a collection 'users' with an array property called compass
, which is a list of document objects. Below is one of the users in the collection:
{
"_id": {
"$oid": "64c4ef6615fc23530f9f2a29"
},
"username": "jay",
"compass": [
{
"point": "64ce3416f695e4ddd587e982",
"disposition": true,
"_id": {
"$oid": "64ceaa650b583901b5744d6b"
}
},
{
"point": "64ce343bf695e4ddd587e983",
"disposition": true,
"_id": {
"$oid": "64cf622faba9c3c1dd607994"
}
}
]
}
I'm writing a function to search the compass
array looking for a match in the point
property. If it does not exist, it creates a new array element, and if it does exist it updates the disposition
property to a new value. I'm developing in the context of a MERN stack and using the mongoose library. In my current function, below, UserModel.js
is imported and contains my UserModel
schema:
export const setPoint = async(req, res) => {
try {
const user = await UserModel.findById(req.body.userId);
const disposition = req.body.disposition;
const pointId = req.body.pointId;
var index = undefined;
for (var i = 0; i < user._doc.compass.length; i++) {
if (user._doc.compass[i].point == pointId) {
index = i;
}
}
if (index !== undefined) {
await user.updateOne(
{"compass.point": pointId },
{ $set: {"compass.$.disposition": disposition } },
false,
false );
} else {
const newObj = {point: pointId, disposition: disposition }
await user.updateOne( {$push: { compass: newObj } } );
}
res.status(200).json("Point set.");
} catch (error) {
res.status(500).json(error);
}
}
When I test using a pointId
that doesn't exist in the compass
array, it successfully pushes the new element onto the array, and so I know the request is reaching the server and being parsed correctly. However, when I test using a point
that exists in the array and attempt to change the disposition
property of an existing object, it fails with status 500, with a server response of:
{
"index": 0,
"code": 28
}
From the documentation it's clear that I want to use the $ positional operator, which I have seen successfully implemented here and here. As far as I can tell, I'm using it just as in the docs with the exception that I'm trying to update a document using a schema.
Troubleshooting:
When I use the updateOne()
method in mongosh, the disposition is successfully changed:
db.users.updateOne(
{ "compass.point": "64ce343bf695e4ddd587e983" },
{ $set: { "compass.$.disposition" : false } }
)
which leads me to believe that it's a problem with either my query document or my update document.
I've confirmed that the pointId
is the same as the point
value I'm targeting:
console.log(pointId); // 64ce343bf695e4ddd587e983
console.log(user._doc.compass[1].point) // 64ce343bf695e4ddd587e983
console.log(user._doc.compass[1].point === pointId); // true
When I remove the query document and the "upsert" and "multi" parameters, and specify the index in the update document, the correct element is targeted and updated:
await user.updateOne( { $set: {"compass.1.disposition": disposition } } );
My questions:
What is the server response telling me? I have not been able to find
"code": 28
in the docs, or anywhere else.What part of my
user.updateOne
syntax is incorrect?(curiosities:)
If I went with the clunkier approach of using the value of the
index
variable generated in thefor
loop to specify thecompass
element directly, what syntax would I use (instead of'compass.$.disposition'
)?Pushing a new element onto the array in the
else
statement generates a new document, but both my schema and the object that I'm inserting only specify an object withpoint
anddisposition
properties. Why is MongoDB creating a new document with an_id
field?