Updating Value of Array Element in MongoDB - mongodb

I'd like to know how to update the "value" field of one of the elements identified by the "name" field in the array "array_of_stuff". For example, I want to update the value for "name_of_thing_1" to "new_value_of_thing_1". How can I do this ONLY using the second parameter (i.e. the update parameter) to the update command. I am re-using a class library written in-house I don't have control over the first argument to the update command (i.e. the query parameter). Is this possible?
{
"array_of_stuff": [
{
"name": "name_of_thing_1",
"value": "value_of_thing_1",
},
{
"name": "name_of_thing_2",
"value": "value_of_thing_2",
}
]
}
Thanks for your help!

You can update the value of a single item in an array (if you know its index) like this:
db.stuff.update(/* query ... */, {$set:{"arrayname.<index>":new_value}})
If your array contains documents, you can update a particular field of a document at that index like this:
db.stuff.update(/* query ... */, {$set:{"array_of_stuff.0.value":"new_value_of_thing_1"}})
// If you could use the query parameter and knew something
// about the value in the array you wanted to change:
db.stuff.update({"array_of_stuff.value":"value_of_thing_1"}, {$set:{"array_of_stuff.$.value":"new_value_of_thing_1"}})

See if this example help you:
db.bruno.insert({"array": [{"name": "Hello", "value": "World"}, {"name": "Joker", "value": "Batman"}]})
db.bruno.update({"array.name": "Hello"}, {$set: {"array.$.value": "Change"}})
db.bruno.find().pretty()
output:
db.bruno.find().pretty()
{
"_id" : ObjectId("52389faaafd72821e7b25a73"),
"array" : [
{
"name" : "Hello",
"value" : "Change"
},
{
"name" : "Joker",
"value" : "Batman"
}
]
}

I don't think it is possible. In order to update field of one of the elements in array, you should use positional $ operator, e.g.:
update({'array_of_stuff.name':'name_of_thing_1'},
{ $set: {'array_of_stuff.$.value':'new_value_of_thing_1'}})
But according to documentation: positional $ operator acts as a placeholder for the first element that matches query document, and the array field must appear as part of the query document.

Related

Find result within Array of Objects and match email address field

I'm trying to match the emailAddress field and the page_slug. Currently I'm using the following which matches just the about page in the modularSequence:
db.getCollection('users').find({"modularSequence.page_slug": "about"}, {"modularSequence.$": 1 })
This works and returns:
{
"_id" : ObjectId("5740c631742da6e83389abb4"),
"modularSequence" : [
{
"page_id" : "1",
"sequence" : "m_1",
"category" : "headers",
"page_slug" : "about"
}
]
}
Which it half what I want. I'm looking to return the emailAddress field as well. I've tried using this but it returns everything and multiple modular elements:
db.getCollection('users').find({$and:[{"emailAddress": 'paul#example.com'}, {"modularSequence.page_slug": "about"}, {"modularSequence": {$elemMatch: {page_slug:'about'}}}]})
[
{
"emailAddress": "paul#example.com",
"modularSequence": [
{
"page_slug": "about",
"category": "headers",
"sequence": "m_1",
"page_id": "1"
},
{
"page_slug": "contact",
"category": "content",
"sequence": "m_4",
"page_id": "2"
}
]
}
]
How do I match both the emailAddress field and the modularSequence.page_slug - only return a result if both the email address matches and the page_slug?
Your $and array is including your field selection parameter as well. But you don't need to use $and here anyway as multiple query terms are implicitly ANDed by default, so you can simplify your query to:
db.users.find({"emailAddress": 'paul#example.com', "modularSequence.page_slug": "about"},
{"emailAddress": 1, "modularSequence.$": 1})
Which is your first query, but with an emailAddress field added to both the query and field selection parameters.
The first parameter of find is the query (which docs), and the second is the projection (which fields within those docs), so that's why those fields are there twice. The $ in the projection represents the modularSequence array element matched in the query.

Concurrent update of array elements which are embedded documents in MongoDB

I have documents like this one at collection x at MongoDB:
{
"_id" : ...
"attrs" : [
{
"key": "A1",
"type" : "T1",
"value" : "13"
},
{
"key": "A2",
"type" : "T2",
"value" : "14"
}
]
}
The A1 and A2 elements above are just examples: the attrs field may hold any number of array elements.
I'd need to access concurrently to the attrs array from several independent clients accessing to MongoDB. For example, considers two clients, one wanting to change the value of the element identified by key equal to "A1" to "80" and other wanting to change the value of the element identified by key equal to "A2" to "20". Is there any compact way of doing it using MongoDB operations?
It is important to note that:
Clients doesn't know the position of each element in the attr array, only the key of the element which value has to be modified.
Reading the whole attrs array in client space, searching the element to modify at client space, then updating attrs with the new array (in which the element to modify has been changed) would involve race conditions.
Clients also may add and remove elements in the array. Thus, doing a first search at MongoDB to locate the position of the element to modify, then update it using that particular position doesn't work in general, as elements could have been added/removed thus altering of the position previously found.
The process here is really quite simple, it only varies in where you want to "find or create" the elements in the array.
First, assuming the elements for each key are in place already, then the simple case is to query for the element and update with the index returned via the positional $ operator:
db.collection.update(
{
"_id": docId,
"attrs": { "$elemMatch": { "key": "A1", "type": "T1" } }
}
{ "$set": { "attrs.$.value": "20" }
)
That will only modify the element that is matched without affecting others.
In the second case where "find or create" is required and the particular key may not exist, then you use "two" update statements. But the Bulk Operations API allows you to do this in a single request to the server with a single response:
var bulk = db.collection.initializeOrderedBulkOp();
// Try to update where exists
bulk.find({
"_id": docId,
"attrs": { "$elemMatch": { "key": "A1", "type": "T2" } }
}).updateOne({
"$set": { "attrs.$.value": "30" }
});
// Try to add where does noes not exist
bulk.find({
"_id": docId,
"attrs": { "$not": { "$elemMatch": { "key": "A1", "type": "T2" } } }
}).updateOne({
"$push": { "attrs": { "key": "A1", "type": "T2", "value": "30" } }
});
bulk.execute();
The basic logic being that first the update attempt is made to match an element with the required values just as done before. The other condition tests for where the element is not found at all by reversing the match logic with $not.
In the case where the array element was not found then a new one is valid for addition via $push.
I should really add that since we are specifically looking for negative matches here it is always a good idea to match the "document" that you intend to update by some unique identifier such as the _id key. While possible with "multi" updates, you need to be careful about what you are doing.
So in the case of running the "find or create" process then element that was not matched is added to the array correctly, without interferring with other elements, also the previous update for an expected match is applied in the same way:
{
"_id" : ObjectId("55b570f339db998cde23369d"),
"attrs" : [
{
"key" : "A1",
"type" : "T1",
"value" : "20"
},
{
"key" : "A2",
"type" : "T2",
"value" : "14"
},
{
"key" : "A1",
"type" : "T2",
"value" : "30"
}
]
}
This is a simple pattern to follow, and of course the Bulk Operations here remove any overhead involved by sending and receiving multiple requests to and from the server. All of this hapily works without interferring with other elements that may or may not exist.
Aside from that, there are the extra benefits of keeping the data in an array for easy query and analysis as supported by the standard operators without the need to revert to JavaScript server processing in order to traverse the elements.

Mongo : Trouble updating a Mongo DB row

I am trying to update a row in Mongo DB .
I have a collection named users
db.users.find()
{ "_id" : ObjectId("50e2efe968caee13be412413"), "username" : "sss", "age" : 32 }
I am trying to update the row with the username as Erinamodobo and modify the age to 55
I have tried the below way , but its not working .
db.users.update({ "_id": "50e2efe968caee13be412413" }, { $set: { "username": "Erinamodobo" } });
Please let me know where i am making the mistake ??
Pass in the _id as an ObjectId if you're using the mongo shell, otherwise, it won't find the existing user.
db.users.update({"_id": ObjectId("50e2efe968caee13be412413")},
{ "$set" :
{ "username": "Erinamodobo", "age" : "55" }})
With this query you are updating the name itself.
Notice that the syntax of an update is the following:
db.COLLECTION.update( {query}, {update}, {options} )
where query selects the record to update and update specify the value of the field to update.
So, in your case the correct command is:
db.users.update({ "name": "Erinamodobo" }, { $set: { "age": 55 } });
However, I suggets you to read the mongodb documentation, is very well written (http://docs.mongodb.org/manual/applications/update/)
As an extension to #WiredPrairie, even though he states the right answer he doesn't really explain.
The OjbectId is not a string, it is actually a Object so to search by that Object you must supply an Ojbect of the same type, i.e. here an ObjectId:
db.users.update({ "_id": ObjectId("50e2efe968caee13be412413") }, { $set: { "username": "Erinamodobo" } });
The same goes for any specific BSON type you use from Date to NumberLong you will need to wrap the parameters in Objects of the type.

How do I update Array Elements matching criteria in a MongoDB document?

I have a document with an array field, similar to this:
{
"_id" : "....",
"Statuses" : [
{ "Type" : 1, "Timestamp" : ISODate(...) },
{ "Type" : 2, "Timestamp" : ISODate(...) },
//Etc. etc.
]
}
How can I update a specific Status item's Timestamp, by specifying its Type value?
From mongodb shell you can do this by
db.your_collection.update(
{ _id: ObjectId("your_objectid"), "Statuses.Type": 1 },
{ $set: { "Statuses.$.Timestamp": "new timestamp" } }
)
so the c# equivalent
var query = Query.And(
Query.EQ("_id", "your_doc_id"),
Query.EQ("Statuses.Type", 1)
);
var result = your_collection.Update(
query,
Update.Set("Statuses.$.Timestamp", "new timestamp", UpdateFlags.Multi,SafeMode.True)
);
This will update the specific document, you can remove _id filter if you wanted to update the whole collection
Starting with MongoDB 3.6, the $[<identifier>] positional operator may be used. Unlike the $ positional operator — which updates at most one array element per document — the $[<identifier>] operator will update every matching array element. This is useful for scenarios where a given document may have multiple matching array elements that need to be updated.
db.yourCollection.update(
{ _id: "...." },
{ $set: {"Statuses.$[element].Timestamp": ISODate("2021-06-23T03:47:18.548Z")} },
{ arrayFilters: [{"element.Type": 1}] }
);
The arrayFilters option matches the array elements to update, and the $[element] is used within the $set update operator to indicate that only array elements that matched the arrayFilter should be updated.

Updating array of objects using mongoose and date [duplicate]

I have a document with an array field, similar to this:
{
"_id" : "....",
"Statuses" : [
{ "Type" : 1, "Timestamp" : ISODate(...) },
{ "Type" : 2, "Timestamp" : ISODate(...) },
//Etc. etc.
]
}
How can I update a specific Status item's Timestamp, by specifying its Type value?
From mongodb shell you can do this by
db.your_collection.update(
{ _id: ObjectId("your_objectid"), "Statuses.Type": 1 },
{ $set: { "Statuses.$.Timestamp": "new timestamp" } }
)
so the c# equivalent
var query = Query.And(
Query.EQ("_id", "your_doc_id"),
Query.EQ("Statuses.Type", 1)
);
var result = your_collection.Update(
query,
Update.Set("Statuses.$.Timestamp", "new timestamp", UpdateFlags.Multi,SafeMode.True)
);
This will update the specific document, you can remove _id filter if you wanted to update the whole collection
Starting with MongoDB 3.6, the $[<identifier>] positional operator may be used. Unlike the $ positional operator — which updates at most one array element per document — the $[<identifier>] operator will update every matching array element. This is useful for scenarios where a given document may have multiple matching array elements that need to be updated.
db.yourCollection.update(
{ _id: "...." },
{ $set: {"Statuses.$[element].Timestamp": ISODate("2021-06-23T03:47:18.548Z")} },
{ arrayFilters: [{"element.Type": 1}] }
);
The arrayFilters option matches the array elements to update, and the $[element] is used within the $set update operator to indicate that only array elements that matched the arrayFilter should be updated.