Reputation: 4481
I have a document that looks like this. Where I need to update a specific object inside a nested array. I need to edit the text object with the name which is having a locale of en.
[
{
"_id": ObjectId("5e049ebc8e935c407f78c190"),
"source": "homepage",
"url": [
{
"type": "admindsg",
"text": [
{
"locale": "en",
"name": "Admin DSG"
},
{
"locale": "nb",
"name": "Admin DSG"
},
{
"locale": "li",
"name": "Admin DSG"
},
{
"locale": "fi",
"name": "Admin DSG"
}
],
"value": [
{
"locale": "en",
"link": "https://www.facebook.cloud"
},
{
"locale": "nb",
"link": "https://www.facebook.cloud"
},
{
"locale": "li",
"link": "https://www.facebook.cloud"
},
{
"locale": "fi",
"link": "https://www.facebook.cloud"
}
],
"datetime": "2020-02-08 13:36:37"
},
{
"type": "dataauth",
"text": [
{
"locale": "en",
"name": "Data Authorities"
},
{
"locale": "nb",
"name": "Data Authorities"
},
{
"locale": "li",
"name": "Data Authorities"
},
{
"locale": "fi",
"name": "Data Authorities"
}
],
"value": [
{
"locale": "en",
"link": "https://www.facebook.cloud"
},
{
"locale": "nb",
"link": "https://www.facebook.cloud"
},
{
"locale": "li",
"link": "https://www.facebook.cloud"
},
{
"locale": "fi",
"link": "https://www.facebook.cloud"
}
],
"datetime": "2020-02-08 13:36:38"
},
{
"type": "blog",
"text": [
{
"locale": "en",
"name": "facebook blog"
},
{
"locale": "nb",
"name": "facebook blog"
},
{
"locale": "li",
"name": "facebook blog"
},
{
"locale": "fi",
"name": "facebook blog"
}
],
"value": [
{
"locale": "en",
"link": "https://www.facebook.no"
},
{
"locale": "nb",
"link": "https://www.facebook.no"
},
{
"locale": "li",
"link": "https://www.facebook.no"
},
{
"locale": "fi",
"link": "https://www.facebook.no"
}
],
"datetime": "2020-02-08 13:36:39"
},
{
"type": "guide",
"text": [
{
"locale": "en",
"name": "Guidelines for you"
},
{
"locale": "nb",
"name": "Guidelines for you"
},
{
"locale": "li",
"name": "Guidelines for you"
},
{
"locale": "fi",
"name": "Guidelines for you"
}
],
"value": [
{
"locale": "en",
"link": "https://my.instagram.as/"
},
{
"locale": "nb",
"link": "https://my.instagram.as/"
},
{
"locale": "li",
"link": "https://my.instagram.as/"
},
{
"locale": "fi",
"link": "https://my.instagram.as/"
}
],
"datetime": "2020-02-08 13:36:41"
}
]
}
]
What should be the best approach to get this done?
This the query I have tried:
db.getCollection('general').update({
"source": "homepage",
"url.type": "admindsg"
}, {
"$set": {
"url.text.$[elem].name": "YOYO"
}
}, {
"arrayFilters": [{
"elem.locale": {
"$eq": "en"
}
}],
"multi": true
})
This throws an error saying:
No array filter found for identifier 'elem' in path 'url.text.$[elem].name'
Upvotes: 1
Views: 102
Reputation: 59456
Problem is you have 2 array filters
url.type = "admindsg"
url.text.local = "en"
You can use an aggregation pipeline. This would be one solution:
db.getCollection('general').aggregate([
{ $match: { source: "homepage" } },
{ $unwind: "$url" },
{
$set: {
"url.text": {
$cond: {
if: { $eq: ["$url.type", "admindsg"] },
then: {
$reduce: {
input: "$url.text",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[{
$cond: {
if: { $eq: ["$$this.locale", "fi"] },
then: {
$mergeObjects: [
{ locale: "$$this.locale" },
{ name: "Järjestelmänvalvoja DSG" }
]
},
else: "$$this"
}
}]
]
}
}
},
else: "$url.text"
}
}
}
},
{
$group: {
_id: { _id: "$_id", source: "$source" },
url: { $push: "$$ROOT.url" }
}
},
{ $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$_id"] } } }
]).forEach(function (doc) {
db.getCollection('general').updateOne(
{ _id: doc._id },
{ $set: { url: doc.url } }
);
})
Instead of $reduce
you can also use $map
:
{
$set: {
"url.text": {
$cond: {
if: { $eq: ["$url.type", "admindsg"] },
then: {
$map: {
input: "$url.text",
in: {
$cond: {
if: { $eq: [ "$$this.locale", "fi" ] },
then: {
$mergeObjects: [
{ locale: "$$this.locale" },
{ name: "Järjestelmänvalvoja DSG" }
]
},
else: "$$this"
}
}
}
},
else: "$url.text"
}
}
}
},
Upvotes: 0
Reputation: 17858
url
field is an array, so you need to use this syntax:
db.getCollection('general').update({
"source": "homepage"
}, {
"$set": {
"url.$[urlId].text.$[textId].name": "YOYO"
}
}, {
"arrayFilters": [
{ "urlId.type": "admindsg" },
{ "textId.locale": "en" },
],
"multi": true
})
filtered positional operator $
Upvotes: 2