I'm a mongo beginner and struggling to insert multiple objects into multiple nested array in one document.
The document looks like this:
[
{
"id": 1,
"name": "myObject",
"sections": [
{
"id": "section1",
"items": [
{
"id": 1,
"name": "item1.1",
"scores": [
{
"userId": 13,
"score": 10
}
]
},
{
"id": 2,
"name": "item1.2",
"scores": [
{
"userId": 66,
"score": 10
}
]
}
]
},
{
"id": "section2",
"items": [
{
"id": 3,
"name": "item2.1",
"scores": [
{
"userId": 13,
"score": 20
}
]
}
]
}
]
}
]
I now want to insert new scores for userId=10
for every item in every section.
The score is of course different for every item.
let's assume scores like this (all for userId=10)
[
{
"sectionId": "section1"
"itemName: "item1.1"
"score: 10,
"userId": 10
},
{
"sectionId": "section1"
"itemName: "item1.2"
"score: 15,
"userId": 10
},
{
"sectionId": "section2"
"itemName: "item2.1"
"score: 33,
"userId": 10
}
]
Added for clarification
the updated document should look like the following.
{
"id": 1,
"name": "myObject",
"sections": [
{
"id": "section1",
"items": [
{
"id": 1,
"name": "item1.1",
"scores": [
{
"userId": 13,
"score": 10
},
{ // <-- newly added score
"userId": 10,
"score": 10
}
]
},
{
"id": 2,
"name": "item1.2",
"scores": [
{
"userId": 66,
"score": 10
},
{ // <- newly added score
"userId": 10,
"score": 15
}
]
}
]
}
// the remaining document is omitted for brevity but the above should also be applied to this sections
so far I have been able to achieve what I want for one single score like this
db.collection.update({
id: 1
},
{
$push: {
"sections.$[item].items.$[score].scores": {
"userId": 10,
"score": 13
},
}
},
{
arrayFilters: [
{
"score.userId": {
$ne: 10
}
},
{
"item.name": {
$eq: "item1.1"
}
}
]
})
This inserts a score for userId=10 in itemName=item1.1 if no score for userId=10 exists.
But I'm struggling on how to insert multiple scores into multiple items.
I saw that you can merge objects together, so maybe this would be an option although it kinda fells like an overkill.
So how can I insert all my scores for the different items in one atomic operation?
EDIT: Added clarification about the desired result.
CodePudding user response:
Query
- pipeline update, requires MongoDB >= 4.2
- reduce on the data that you want to insert, with initial value the sections
- nested 3 maps that always do the same
- if its not the key-value i want, keep the old value
- else
(merge {:newkey (map ...)})
- if userId exists updates its score, else insert new userID and score
- query assumes that there is a score array even if empty, i mean it only creates new userId score, not sections items etc
*you can avoid the set/unset
and use driver variables in all the places where data
is used
db.collection.update({},
[
{
"$set": {
"data": [
{
"sectionId": "section1",
"itemName": "item1.1",
"userId": 10,
"score": 20
},
{
"sectionId": "section1",
"itemName": "item1.1",
"userId": 13,
"score": 30
},
{
"sectionId": "section2",
"itemName": "item2.1",
"score": 33,
"userId": 10
}
]
}
},
{
"$set": {
"sections": {
"$reduce": {
"input": "$data",
"initialValue": "$sections",
"in": {
"$let": {
"vars": {
"data": "$$this"
},
"in": {
"$map": {
"input": "$$value",
"in": {
"$cond": [
{
"$ne": [
"$$section.id",
"$$data.sectionId"
]
},
"$$section",
{
"$mergeObjects": [
"$$section",
{
"items": {
"$map": {
"input": "$$section.items",
"in": {
"$cond": [
{
"$ne": [
"$$item.name",
"$$data.itemName"
]
},
"$$item",
{
"$mergeObjects": [
"$$item",
{
"scores": {
"$let": {
"vars": {
"user_exist": {
"$in": [
"$$data.userId",
"$$item.scores.userId"
]
}
},
"in": {
"$cond": [
{
"$not": [
"$$user_exist"
]
},
{
"$concatArrays": [
"$$item.scores",
[
{
"userId": "$$data.userId",
"score": "$$data.score"
}
]
]
},
{
"$map": {
"input": "$$item.scores",
"in": {
"$cond": [
{
"$ne": [
"$$score.userId",
"$$data.userId"
]
},
"$$score",
{
"$mergeObjects": [
"$$score",
{
"score": "$$data.score"
}
]
}
]
},
"as": "score"
}
}
]
}
}
}
}
]
}
]
},
"as": "item"
}
}
}
]
}
]
},
"as": "section"
}
}
}
}
}
}
}
},
{
"$unset": [
"data"
]
}
])