I have a NodeJS application that communicates with an Angular app. This is a simplified version, but I'll try to explain my problem:
I will get a value from the socket connection, for example:
{
Id : "1",
Checked : true,
TeamName : "Team1"
}
I want to update the mongoDb with mongoose so that every person with the TeamName : "Team1" gets the value Checked : true.
I have done something like this:
relayModel.find({TeamName : 'Team1'},
{'$set' :{Checked: true}},function(err,docs){
if(err){
console.log("Could not save value for relay runner: ", err);
}
else{
console.log("The runners updated: ",docs)
}
But I get the error:
Could not save value for relay runner:
{ [MongoError: Can't canonicalize query:
BadValue Unsupported projection option:
$set: { Checked: true }] name: 'MongoError' }
This really is well documented. Will go away for sure, but just to explain for you:
relayModel.update(
{ "TeamName": "Team1" },
{ "$set": { "Checked": true } },
{ "multi": true },
function(err,numAffected) {
if (err) throw err;
console.log( "updated n docs: %s", numAffected );
}
);
So the .update() with a "multi" option affects more than one document, and without it it just affects the first document matched. The returned values in the callback are the standard err and the "number of documents affected".
Therefore basically speaking:
You actually need and operator that "updates" in order to affect and update.
You set "multi" for more than one document and you get only the number returned as well as using the $set operator to just affect the field that you want to change.
Related
I am writing a database backend for our game. I have been wrestling for the past 3 days with this issue. No matter what I try, I cannot get the $set operator to work on one field. Just one.
I have a GlobalLeaderboard collection, with each document containing this structure:
{
"_id" : { "$oid" : "5e7d445f5010bb548850d2ee" },
"PlayerName" : "Regen_erate",
"Rating" : 38.24,
"TotalMapsPlayed" : 372,
"UserId" : "P526993347"
}
The Node.js code that I am running to edit the database is as follows:
rating = await getRating(Plays.find({"UserId": newPlayData.UserId}).sort({"Rating": -1}));
console.log(rating);
Global.findOneAndUpdate({"UserId": newPlayData.UserId},
{
$inc: {"TotalMapsPlayed": 1},
$set: {"PlayerName": newPlayData.PlayerName},
$set: {"Rating": rating.toFixed(2)},
$set: {"UserId": newPlayData.UserId}
},
{
upsert: true,
bypassDocumentValidation: true,
ignoreUndefined: true
}).catch(err => {
console.log("ERR: " + err);
res.status(500).send("Whoops! Something went horribly wrong! Here's some info: " + err);
});
Even if I stick a random number (double) into the $set operation it still won't update. This seems to be happening for no reason at all...
I am able to run $set on all other fields except the Rating field. Out of curiosity, I tried to use $inc on the field in question, and, surprisingly, I was able to get it to work. What is going on?
The second argument to findOneAndUpdate is an object. An object can only have 1 instance of a specific field name, when you specify the same field multiple times, the last one is the value that remains. So your update document:
{
$inc: {"TotalMapsPlayed": 1},
$set: {"PlayerName": newPlayData.PlayerName},
$set: {"Rating": rating.toFixed(2)},
$set: {"UserId": newPlayData.UserId}
}
Is equivalent to
{
$inc: {"TotalMapsPlayed": 1},
$set: {"UserId": newPlayData.UserId}
}
To set multiple fields in the same update, list all of the fields inside a single $set like:
{
$inc: {"TotalMapsPlayed": 1},
$set: {"PlayerName": newPlayData.PlayerName,
"Rating": rating.toFixed(2),
"UserId": newPlayData.UserId}
}
I have a problem with update method which returns this object when I run my endpoint
{ n: 1, nModified: 1, ok: 1 }
This is the code which I tried, and I tried with { new: true } but that doesnt help, i want to get updated data back.
router.put('/:username/experience/edit/:id', function(req, res) {
const { title, company, location, from, to, workingNow, description } = req.body;
User
.update({'experience._id': req.params.id},
{'$set': {
'experience.$.title': title,
'experience.$.company': company,
'experience.$.location': location,
'experience.$.from': from,
'experience.$.to': to,
'experience.$.workingNow': workingNow,
'experience.$.description': description,
}},
function(err, model) {
console.log(model);
if(err){
return res.send(err);
}
return res.json(model);
});
})
If you are on MongoDB 3.0 or newer, you need to use the .findOneAndUpdate() and use projection option to specify the subset of fields to return. You also need to set returnNewDocument to true. Of course you need to use the $elemMatch projection operator here because you cannot use a positional projection and return the new document.
As someone pointed out:
You should be using .findOneAndUpdate() because .findAndModify() is highlighed as deprecated in every official language driver. The other thing is that the syntax and options are pretty consistent across drivers for .findOneAndUpdate(). With .findAndModify(), most drivers don't use the same single object with "query/update/fields" keys. So it's a bit less confusing when someone applies to another language to be consistent. Standardized API changes for .findOneAndUpdate() actually correspond to server release 3.x rather than 3.2.x. The full distinction being that the shell methods actually lagged behind the other drivers ( for once ! ) in implementing the method. So most drivers actually had a major release bump corresponding with the 3.x release with such changes.
db.collection.findOneAndUpdate(
{
"_id": ObjectId("56d6a7292c06e85687f44541"),
"rankings._id" : ObjectId("46d6a7292c06e85687f55543")
},
{ $inc : { "rankings.$.score" : 1 } },
{
"projection": {
"rankings": {
"$elemMatch": { "_id" : ObjectId("46d6a7292c06e85687f55543") }
}
},
"returnNewDocument": true
}
)
From MongoDB 3.0 onwards, you need to use findAndModify and the fields options also you need to set new to true in other to return the new value.
db.collection.findAndModify({
query: {
"_id": ObjectId("56d6a7292c06e85687f44541"),
"rankings._id" : ObjectId("46d6a7292c06e85687f55543")
},
update: { $inc : { "rankings.$.score" : 1 } },
new: true,
fields: {
"rankings": {
"$elemMatch": { "_id" : ObjectId("46d6a7292c06e85687f55543") }
}
}
})
Both queries yield:
{
"_id" : ObjectId("56d6a7292c06e85687f44541"),
"rankings" : [
{
"_id" : ObjectId("46d6a7292c06e85687f55543"),
"name" : "Ranking 2",
"score" : 11
}
]
}
I'm working on an inline grid editor that calls an express rest api after a single value in the grid is updated. So when a user changes a single field in the grid, I am calling a PATCH request to update the field. However I can't figure out how to only update a single field. When I try it tries to update them all and if there's no value it makes the value NULL in the database. I want to only update a single field, and only the one passed into the API (it could be any of the fields). Here's my method to patch:
// Update record based on TxnID
router.patch('/editablerecords/update', function (req, res) {
let qb_TxnID = req.body.txnid
let type = req.body.type;
let margin = req.body.margin;
if (!qb_TxnID) {
return res.status(400).send({ error:true, message: 'Please provide TxnID' });
}
connection.query("UPDATE pxeQuoteToClose SET ? WHERE qb_TxnID = '" + qb_TxnID + "'", { type, margin }, function (error, results, fields) {
if(error){
res.send(JSON.stringify({"status": 500, "error": error, "response": null }));
//If there is error, we send the error in the error section with 500 status
} else {
res.send(JSON.stringify({ error: false, data: results, message: 'Record updated.' }));
//If there is no error, all is good and response is 200OK.
}
});
});
I will only be updating 1 field at a time, either type or margin, but not both (in this case) at the same time. If I only send one of the fields, the other field becomes null. I've tried to read up on the connection.query() method but can find no information and I don't understand how it builds the query, except that every req.body.value that is passed to it gets used to build the query.
I'm new to building this REST API and feel like I'm missing something simple.
EDIT: I'd like to add, I MAY want to update both fields, but I'd also like to update a single field at a time. Thanks
Per the RFC, the body of a PATCH call should not be the updated representation, but rather a set of instructions to apply to the resource.
The PATCH method requests that a set of changes described in the
request entity be applied to the resource identified by the Request-
URI. The set of changes is represented in a format called a "patch
document" identified by a media type.
One good proposed standard for using PATCH with JSON can be found at https://www.rfc-editor.org/rfc/rfc6902. An example patch document using that standard would be:
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
I've got a rather specific case: Using mongoose/mongo and user objects
I want to find and update user in one call.
DB.collection('users').findOneAndUpdate({localId: id} ,{ "$set": { "name": "lla", "usnme": "As"} } ,callback);
Note that 'username' is spelled wrong. Yet mongo updated the first field(name) and does not give any error about the second.
How can I validate the keys I pass in $set without making more than one query?
What MongoDB suggests here is called schema validation:
In your specific case you could run the following command to make sure that no additional ("incorrect") fields can be added by anyone:
db.runCommand({ "collMod": "users", "validator": {
$jsonSchema: {
additionalProperties: false,
properties: {
"_id": {
bsonType: "objectId"
},
"name": {
bsonType: "string"
},
"username": {
bsonType: "string"
}
}
}
}})
Beyond that I cannot really think of any solution since MongoDB is a document database which by default is schemaless and hence won't stop you from creating the fields you tell it to create...
I have this existing document in a collection Article in MongoDB database:
[ { site: 'www.atlantico.fr',
date: '2014-05-27T11:10:19.000Z',
link: 'http://www.atlantico.fr/example.html',
_id: 538473817eb00f082f4803fc,
__v: 0} ]
I want to add a new field day with value 'example' to this document, using Mongoose in Node.js. So I do:
Article.update(
{ link: 'http://www.atlantico.fr/example.html'},
{ $set : {day : 'example'} },
function(err){
});
But it does not work because when I query the document after that, no new field day appears...
I must have made a mistake when using update or $set in Mongoose, but I cannot find exactly my mistake.
What am I missing? Thanks!
try
Article.update(
{link: 'http://www.atlantico.fr/example.html'},
{day : 'example' },
{multi:true},
function(err, numberAffected){
});
and don't forget to add day to schema.
await Users.updateOne( {link: 'http://www.atlantico.fr/example.html'},{ $set: { day : 'example'} }, { multi: true });
update is deprecated
use await for db operation
if you want to add new filed in collection ,first check it is added in Model or not
(if you don't wan't use that filed as mandatory make is as "required: false")
Article.findByIdAndUpdate(id, { $set: { day: 'example' }}, { new: true }, function (err, article) {
if (err) return handleError(err);
res.send(article);
});
I prefer this way because it's contains a callback function.
reference and more info: http://mongoosejs.com/docs/documents.html