MongoDB setOnInsert and push if already existent - mongodb

I would like to add a document if it does not exist and else add an element to one of it's sub-documents.
db.test.update(
{
name : 'Peter'
},
$setOnInsert : {
name : 'Peter',
visits: { 'en' : ['today'], 'us' : [] }
},
$push : {
visits.en : 'today'
},
{ upsert : true }
)
If Peter exists, add an element to its visists.en or visists.us arrays. Else, create a document for Peter. This document should have the format for visits which should contain the current element ('today').
My issue is that I have "have conflicting mods in update".
I.e. (afaik), I cannot write to two things in one query. Yet how can I solve this dilemma?

You could implement it without $setOnInsert operator.
db.test.update(
{
name : 'Peter'
},
{
$push : {
"visits.en" : 'today'
}
},
{ upsert : true }
)
If Peter exists, element 'today' will be added to its visits.en array. Else, will be created a document for Peter, with visits object, that will be contain array en with 'today' element.
And I think, that error occured because of you using same property (visits) in two operations ($setOnInsert and $push).

You can still use $setOnInsert but when $setOnInsert and $push doesn't updates in the same fields as mentioned before.
N.b: We use $addToSet if you don't want a duplicated values in your Array
db.test.update(
{
name : 'Peter'
},
{
$setOnInsert: {name : 'Peter'},
$addToSet: {"visits.en": 'today'} // or $push
},
{upsert: true})

Related

Mongodb- Query to check if a field in the json array exists or not

I have a JSON in MongoDB and I am trying to check if at least one of the items in the JSON doesn't contain a specific field.
{
"_id" : 12345,
"orderItems" : [
{
"itemId" : 45678,
"isAvailable" : true,
"isEligible" " false
},
{
"itemId" : 87653,
"isAvailable" : true
}
]
}
So in the above JSON, since the 2nd one under order items doesn't contain iseligible field, I need to get this _id.
I tried the below query so far, which didnt work:
db.getCollection('orders').find({"orderItems.iseligible":{$exists:false})
You can use $elemMatch to evaluate the presence of the nested key. Once that's accomplished, project out the _id value.
db.orders.find({
orderItems: {
$elemMatch: {
"isEligible": {
$exists: false
}
}
}
},
{
_id: 1
})
Here is a Mongo playground with the finished code, and a similar SO answer.

Does MongoDB's $elemMatch projection guarantee the returned element is the same one that was matched in the query?

Let's say I have a collection with this single document:
{
"_id" : ObjectId("…"),
"cartId" : "61",
"items" : [
{
"prodType" : "hardware",
"prod" : "screwdriver",
"checked": false
},
{
"prodType" : "hardware",
"prod" : "hammer",
"checked": false
},
{
"prodType" : "decor",
"prod" : "vase",
"checked": false
}
]
}
And I want to do findAndModify to find any hardware product and modify its checked field. Then it will look like this:
db.col.findAndModify({
query: {
items: {
$elemMatch: {
prodType: "hardware"
}
}
},
update: {
$set: {
"items.$.checked": true
}
}
})
Okay, but this isn't the whole story. findAndModify will return the whole matched document, and I want to project specifically the array item that was matched (and also modified), so I'll add a fields section to my query:
db.col.findAndModify({
query: {
items: {
$elemMatch: {
prodType: "hardware"
}
}
},
update: {
$set: {
"items.$.checked": true
}
},
fields: {
items: {
$elemMatch: {
prodType: "hardware"
}
}
}
})
And now to the question: does MongoDB guarantee that the returned array item from my query is the exact same one that was matched (and modified) in the update section even though we have two items matching the criteria?
YES. It will return only the first sub-document that matched your criteria and was modified in the update section as shown here
According to the official docs, then yes - the projected array element is the exact one that was modified using the same one modified by the positional operator.
$ (update) states:
the positional $ operator acts as a placeholder for the first element that matches the query document
and $elemMatch (projection) states:
The $elemMatch operator limits the contents of an <array> field from the query results to contain only the first element matching the $elemMatch condition
They both apply to the first array element so it directly implies that the modified array element is the one that is projected

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.

In Mongodb, how do I push a value into an array?

Let's say my document is this:
{ names: ['jacob','jessie','andrew'] }
I want to push "michael" to all the documents. How do I do that?
db.mycol.update( {}, { $push : { names : "michael" } }, false, true );
Check out the official docs on Updating for more info, the general syntax is:
db.collection.update( criteria, objNew, upsert, multi )
Have you checked this document?
db.your_collection.update({your_documents} { $push : { name : "michel" }})

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.