mongodb findAndModify update element in array - mongodb

There is an bson document:
{
"_id" : ObjectId("5718441f5116a60b08000b8c"),
"mails" : [
{
"id" : 2,
"a" : [
{
"a" : 1
},
{
"a" : 2
}
]
},
{
"id" : 1,
"a" : [
{
"a" : 1
},
{
"a" : 2
}
]
}
]
}
I need to return and clear the array "a" which belong to "mails.id == x" for given document. So I use findAndModify like:
db.mail.findAndModify({query: {"_id":ObjectId("5718441f5116a60b08000b8c")}, update: {$set:{"mails.$.a":[]}}, new: false, fields:{"mails":{$elemMatch:{"id":1}}}})
However this don't work. The problem is the $set should apply on one document in array rather than the whole document. So I need a projection to project it out.
If I left update to blank, it will return the desired part:
{
"_id" : ObjectId("5718441f5116a60b08000b8c"),
"mails" : [
{
"id" : 1,
"a" : [
{
"a" : 1
},
{
"a" : 2
}
]
}
]
}
But I don't know how to clear the array 'a' in 'mails'

You have to specify array element match in the query:
db.mail.findAndModify({query: {"_id":ObjectId("5718441f5116a60b08000b8c"), "mails":{$elemMatch:{"id":1}}}, update: {$set:{"mails.$.a":[]}}, new: false, fields:{"mails":{$elemMatch:{"id":1}}}})
mails.$ in you update matches the first matched element in the doc, so you have to match it in the query. Also, this query will update the doc, but it will return the old version, since you use new: false, if you want to get the updated version set it to true.

Related

Insert new fields to document at given array index in MongoDB

I have the following document structure in a MongoDB collection :
{
"A" : [ {
"B" : [ { ... } ]
} ]
}
I'd like to update this to :
{
"A" : [ {
"B" : [ { ... } ],
"x" : [],
"y" : { ... }
} ]
}
In other words, I want the "x" and "y" fields to be added to the first element of the "A" array without loosing "B".
Ok as there is only one object in A array you could simply do as below :
Sample Collection Data :
{
"_id" : ObjectId("5e7c3cadc16b5679b4aeec26"),
A:[
{
B: [{ abc: 1 }]
}
]
}
Query :
/** Insert new fields into 'A' array's first object by index 0 */
db.collection.updateOne(
{ "_id" : ObjectId("5e7c3f77c16b5679b4af4caf") },
{ $set: { "A.0.x": [] , "A.0.y" : {abcInY :1 }} }
)
Output :
{
"_id" : ObjectId("5e7c3cadc16b5679b4aeec26"),
"A" : [
{
"B" : [
{
"abc" : 1
}
],
"x" : [],
"y" : {
"abcInY" : 1.0
}
}
]
}
Or Using positional operator $ :
db.collection.updateOne(
{ _id: ObjectId("5e7c3cadc16b5679b4aeec26") , 'A': {$exists : true}},
{ $set: { "A.$.x": [] , "A.$.y" : {abcInY :1 }} }
)
Note : Result will be the same, but functionally when positional operator is used fields x & y are inserted to first object of A array only when A field exists in that documents, if not this positional query would not insert anything (Optionally you can check A is an array condition as well if needed). But when you do updates using index 0 as like in first query if A doesn't exist in document then update would create an A field which is an object & insert fields inside it (Which might cause data inconsistency across docs with two types of A field) - Check below result of 1st query when A doesn't exists.
{
"_id" : ObjectId("5e7c3f77c16b5679b4af4caf"),
"noA" : 1,
"A" : {
"0" : {
"x" : [],
"y" : {
"abcInY" : 1.0
}
}
}
}
However, I think I was able to get anothe#whoami Thanks for the suggestion, I think your first solution should work. However, I think I was able to get another solution to this though I'm not sure if its better or worse (performance wise?) than what you have here. My solution is:
db.coll.update( { "_id" : ObjectId("5e7c4eb3a74cce7fd94a3fe7") }, [ { "$addFields" : { "A" : { "x" : [ 1, 2, 3 ], "y" : { "abc" } } } } ] )
The issue with this is that if "A" has more than one array entry then this will update all elements under "A" which is not something I want. Just out of curiosity is there a way of limiting this solution to only the first entry in "A"?

How to remove an element in double nested array in MongoDB

I'm working on mongo, in this time in need to remove a specific element from array nested in another one, the structure is like the following:
{
"_id" : ObjectId("5e616314946b6d3ac4ed8252"),
"uid" : "5db069478556622b4a0adca5",
"areas" : [
{
"name" : "mexico",
"elements" : [
"23452345",
"24454675"
],
},
{
"name" : "usa",
"elemets" : [
"123123123",
"1234334"
],
}
]
}
So I need a query that lets me remove any element in "elements" property.
I tried with this query but I couldn't reach it.
db.areas.update(
{ },
{ $pull:
{ areas: {
$elemMatch: {
elements: { $eq : "123123123" }
}
}
}
},
{ multi:false }
);
This just said
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Here is the update for removing the specific array element in a sub-array (elements).
Note that to remove (pull) the specified element (elements value "24454675"), you need to specify the outer-array's element matching condition (note that there are two elements in the outer-array field areas).
db.areas.update(
{ "areas.name": "mexico" },
{ $pull: { "areas.$.elements": "24454675" } }
)
NOTE: If you don't specify the { "areas.name": "mexico" } condition, the whole sub-document of the areas array will be removed.

MongoDB: How to update a nested array value after checking if greater than zero?

I'm able to decrease a value in a nested array, but I want to check that the value is greater than zero (so never go to the negative numbers). For example, I have this:
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots" : [
{
"id" : "V2qlAEk7Wp0tWwlyWSfX7KRZ",
"number" : 5.0
},
.......
.......
{
"id" : "VfB4f8G1KcgRA8qx0aby5nI0",
"number" : 0.0
}]
}
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839ag"),
"slots" : [
{
"id" : "V2qlAEEESbrEB4bwberbResbd",
"number" : 10.0
},
.......
.......
{
"id" : "DFwseEb5enRbfsbre54rtFfds",
"number" : 1.0
}]
}
If I want to decrease the number value of the first document [id=5a3b0bd69c0000c2a1d839af] of the slots.id = V2qlAEk7Wp0tWwlyWSfX7KRZ, I use:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots.id" :"V2qlAEk7Wp0tWwlyWSfX7KRZ"
},
{
"$inc":{"slots.$.number":-1}
})
But I don't know how to check if the number is greater than 0 before decrease the value. I tried to use The filtered positional operator $[<identifier>], but I have an error:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots.id" :"V2qlAEk7Wp0tWwlyWSfX7KRZ"
},
{
"$inc":{"slots.$[elm].number":-1}
},
{
arrayFilters: [ { "elm.number": {"$gt" : 0}} ]
})
The error is:
cannot use the part (slots of slots.$[elm].number) to traverse the element....
Also, apply the condition in the first part of the query doesn't fix the problem, cause mongo traverses all the array, so also if the number is zero it simply move to another element in the array until it finds number > 0 and when it finds it, it starts to decrease the number value of another element completely ignoring the two id conditions [because now the $ then point to another element of the array]:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots.id" :"V2qlAEk7Wp0tWwlyWSfX7KRZ",
"slots.number":{"$gt" : 0}
},
{
"$inc":{"slots.$.number":-1}
})
I think the correct way to obtain what I want is to use the arrayFilters but I don't understand where is the problem.
Fixed using:
db.getCollection('schedulers').update(
{
"_id" : ObjectId("5a3b0bd69c0000c2a1d839af"),
"slots" :
{
"$elemMatch":
{
"id":"V2qlAEk7Wp0tWwlyWSfX7KRZ",
"number":
{
"$gt" : 0
}
}
}
},
{
"$inc":
{
"slots.$.number":-1
}
})
Thanks

Positional operator and field limitation

In a find query projection, fields I specify after the positional operator are ignored and the whole document is always returned.
'myArray.$.myField' : 1 behave exactly like 'myArray.$' : 1
the positional operator selects the right document. But this document is quite big. I would like to project only 1 field from it.
Exemple:
db.getCollection('match').find({"participantsData.id" : 0001}, { 'participantsData.$.id': 1, })
here the response I have
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001,
"plenty" : "of",
"other" : "fields",
"and" : "subdocuments..."
}
]
}
This is what I want
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001
}
]
}
Is it possible with mongo?
Yes it can be done in mongo
Please try the below query
db.getCollection('match').find(
{"participantsData.id" : 0001},
{"participantsData.id": 1, "matchCreation": 1 })
This will give you the below result
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 1
}
]
}

$type for Array field returns false

I have a collection having following data:
{"_id" : ObjectId("5220222f8188d30ce85d61cc"),
"testfields" : [{
"test_id" : 1,
"test_name" : "xxxx"
}]
}
when I query :
db.testarray.find({ "testfields" : { "$type" : 4 } })
it returns no data,but same returns data when I do:
db.testarray.find({ "$where" : "Array.isArray(this.testfields)" })
It returns data, do the type:4 identifies some other kind of list?
Because testfields is an Array, $type : 4 will perform the "is array" check against every element in testfields as opposed to testfields itself. Since your testfields contains just one Object, it does not get returned.
If on the other hand you inserted the following into your collection,
db.testarray.insert( { "testfields" : [ { "test_id" : 1, "test_name" : "xxxx" },
[ "a", "b" ] ] } );
it would get returned because now one of the elements of testfields is an Array.
More info explaining this in the docs.