问题
I have the following record:
r = {
"_children" : {
"addressesR" : [
{
"street" : "Bitton",
"city" : "Perth",
"id" : ObjectId("5317c149f45dfdb114deec41")
},
{
"id" : ObjectId("5317c149f45dfdb114deec42"),
"street" : "Ivermey",
"city" : "Perth",
],
},
"_searchData" : {
"addressesR" : [
{
"street" : "BITTON",
"city" : "PERTH",
"id" : ObjectId("5317c149f45dfdb114deec41")
},
{
"id" : ObjectId("5317c149f45dfdb114deec42"),
"street" : "IVERMEY",
"city" : "PERTH",
}
],
},
}
Just save it:
db.p.save( r );
The record has two sub-documents: one has the list of children, and the other one has the identical list of children but in uppercase (for searching purposes).
When I update, I want to be able to update both. Now, this works:
db.p.update( { '$and': [
{ '_searchData.addressesR.street': 'BITTON' } ] },
{ '$set': {
'_searchData.addressesR.$.street':'BITTON CHANGED' ,
'_children.addressesR.$.street': 'Bitton CHANGED'
}
})
The $
operator is a positional operator. So, if the positions match, it will work fine. However, if the positions for any reasons mismatch, I suspect I am headed for disasters.
Now, considering that I always $push to those two arrays at the same time, and also that I $pull from them at the same time, within the same mongoDb query, should I be OK with this?
Also, is this going to work (see: positions will always always be the same) in an environment where the db has replication going on etc.?
(Note: this is the end result of an automatic mechanism that gets the children records within one record.)
回答1:
Considering you comment you seem to be on the right track. As you are aware, the positional $ operator is just a "value" container, that has the index of the first array element matched in your query.
You "could" use that if you were absolutely sure that your two arrays always contained the same number of elements and that these corresponding entries were in the same position at all times.
So yes, in order to be safe, use $elemMatch with two update operations like so
db.p.update( { '$and': [
{ '_searchData.addressesR': {$elemMatch: { 'street': 'BITTON' } } ] },
{ '$set': {
'_searchData.addressesR.$.street':'BITTON CHANGED' ,
}
})
db.p.update( { '$and': [
{ '_children.addressesR': {$elemMatch: { 'street': 'Bitton' } } ] },
{ '$set': {
'_children.addressesR.$.street': 'Bitton CHANGED'
}
})
The only real consideration here is because of the two updates it is possible that a read of the document could occur in between those updates and your _searchData
and _children
documents would not have the same corresponding entries in the addressesR
arrays.
Same thing applies with replication, as both operations have to be replayed by the secordary nodes.
What would be a nice feature, would be to be able to use $elemMatch in the update part of your query. In that way you would be querying for the position of the element on that side. But this doesn't exist yet. But from 2.6 upwards you can do something like this:
db.runCommand({
"update": "p",
"updates": [
{
"q": { '_children.addressesR': {$elemMatch: { 'street': 'Bitton' } },
"u": {
"$set": {
"_children.addressesR.$.street": "Bitton CHANGED"
}
}
},
{
"q": { '_searchData.addressesR': {$elemMatch: { 'street': 'BITTON' } },
"u": {
"$set": {
"_searchData.addressesR.$.street": "BITTON CHANGED"
}
}
}
]
})
That is covered in the manual page under Bulk Update.
来源:https://stackoverflow.com/questions/22212834/positional-working-on-different-parts-of-the-same-document