This is more or less my current schema:
{ "_id" : 1
"user_id": 1,
"message" : "Yes"
"translations" : [
{
"destination" : "fr",
"text": "Oui",
"rating" : [
{ "user_id" : 1,
"rating" : 1
},
{ "user_id" : 2,
"rating" : 1
}
]
}
]
}
The problem:
At the moment (3.2) there’s no way to update a nested array using the positional operator. This is not new. It’s been more then 4-5 years … So how can i update a rating for a specific user & destination ?
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single
There’s an issue on Jira related to this for a long time and there’s not solution for now and it’s in the backlog (not assigned to anyone). The desired version is 3.3 so we may have a solution, but it was supposed to be on 1.9 or 2.1 version and after 2.4 or 2.6, so don’t expect anything until it’s done. You can also find an exemple of the problems here on gist.
db.getCollection('message').update({
'_id': 1,
'translations.destination': 'fr',
'translations.rating.user_id':'1'
},
{
'$set': {
"translations.$.rating.$.rating": 5
}
}
Too many positional (i.e. ‘$’) elements found in path ‘translation.$.rating.$.rating’
The possible solutions:
As you expect, there’s no easy solution for now. That’s why it’s important to design your DB properly at the beginning.
You can always use the position in the array if you know which one you want to modify. So in this case, this will update the rating of this user_id on the first translation. It means, 2 queries, the first to know which translation you want to update and after update the rating using this.
“translations.0.rating.$.rating”: 5
In my case since there’s a limited number of translations possible (language), i decided to restructure my collection and using translations as a dict/obj.
{ "_id" : 1
"user_id": 1,
"message" : "Yes"
"translations" : {
"fr": {
"destination" : "fr",
"text": "Oui",
"rating" : [
{ "user_id" : 1,
"rating" : 1
},
{ "user_id" : 2,
"rating" : 1
}
}
},
"en": {...}
]
}
So you can do this directly:
“translations.fr.rating.$.rating”: 5
You can find more information on SO here , here and here