How to update field name in array in document(Mongo)? - mongodb

In MongoDB I have collection of users. There I have such document:
{
_id: ObjectId("619365c54a7d53716438933e"),
name: 'Chris',
hobbies: [
{ title: 'Sports', frequency: 5 },
{ title: 'Cooking', fequency: 5 },
{ title: 'Hiking', frequency: 1 }
],
isSporty: true,
cookMaster: true,
age: 35 }
So, it has array value on hobbies and there during insert I made a mistake. When I inserted hobby Cooking, I added key fequeny instead of frequency. Now I want to fix this. In Mong exists such operator as $rename. I tried:
db.users.updateMany({name:"Chris"}, {$rename: {"hobbies.fequency": "frequency"}})
But got error. How can I rich and change this key properly?

Simply do an update with a $set to correct the field name with $map.
db.collection.update({},
[
{
"$set": {
"hobbies": {
"$map": {
"input": "$hobbies",
"as": "h",
"in": {
title: "$$h.title",
frequency: {
"$ifNull": [
"$$h.frequency",
"$$h.fequency"
]
}
}
}
}
}
}
])
Here is the Mongo playground for your reference.

$set - $map hobbies array field, with $mergeObject to add new field frequency.
$unset - Remove field for fequency in hobbies array field.
db.collection.update({
name: "Chris"
},
[
{
$set: {
"hobbies": {
$map: {
input: "$hobbies",
in: {
$mergeObjects: [
"$$this",
{
frequency: "$$this.fequency"
}
]
}
}
}
}
},
{
$unset: "hobbies.fequency"
}
])
Sample Mongo Playground

Related

How to update a subfield element from another subfield element in mongodb?

Image this data structure :
{
_id: 1
...
sections: [
{
Title: 'hello world',
Title2: '',
},
{
Title: 'goodbye',
Title2: ''
}
]
}
I need to update all Title2 from Title.
I tried things like :
db...update(
{ },
[{ $set:
{
"sections.Title2": "sections.Title",
}
}])
but without success. Also tried with updateMany and some variants like sections.$.Title2.
Thank you for any help
You can do via update + aggregation pipleine(mongod 4.2+) & $map as follow:
db.collection.update({
sections: {
$exists: true
}
},
[
{
$addFields: {
"sections": {
$map: {
input: "$sections",
as: "s",
in: {
"Title": "$$s.Title",
"Title2": "$$s.Title"
}
}
}
}
}
],
{
multi: true
})
Explained:
Find and replace the existing sections with mapped the necessary array values for Title2 to Title.
Add the option {multi:true} to update all documents in collection
playground
Improved version2:
db.collection.update({
sections: {
$exists: true
}
},
[
{
$addFields: {
"sections": {
$map: {
input: "$sections",
as: "s",
in: {
$mergeObjects: [
"$$s",
{
"Title2": "$$s.Title"
}
]
}
}
}
}
}
],
{
multi: true
})
Explained:
In this version you merge the changed value with the subdocument so you dont need to add every of the other fields that are nto going to be changed.
playground2

MongoDB Query: How can I aggregate an array of objects as a string

I have an array of objects where I want to make a string concatenating all of the same attributes from the array. Example:
{
_id: 123,
example_document: true,
people: [
{
name: "John",
age: 18
}, {
name: "Clint",
age: 20
}
]
}
And I wanna make a query where my result would be:
{
_id: 123,
example_document: true,
people: [
{
name: "John",
age: 18
}, {
name: "Clint",
age: 20
}
],
concat_names: "John, Clint"
}
I think aggregate is the path I should take, but I'm not being able to find a way of getting a string out of this, only concat array elements into another array. Anyone could help?
You can use $concat combined with $reduce to achieve this, like so:
db.collection.aggregate([
{
$addFields: {
concat_names: {
$reduce: {
input: "$people",
initialValue: "",
in: {
$concat: [
"$$value",
{
$cond: [
{
$eq: [
{
"$strLenCP": "$$value"
},
0
]
},
"",
", "
]
},
"$$this.name"
]
}
}
}
}
}
])
Mongo Playground
You can use aggregation operators $project, and $map:
db.collection.aggregate([
{
$project:
{
concat_names:
{
$map:
{
input: "$people",
as: "i",
in: "$$i.name"
}
}
}
}])

MongoDB Aggregation project field to be conditional value

I try to project my latest aggregation artifact, into a new one, with an additional field, whose value should be conditional.
My latest artifact of the aggregation pipeline is:
[
server: {
_id: 6108d87b9255993b26796ca9,
version: '2.0.366',
name: 'aaaaaa',
variables: [
{
key: 'aKey',
value: 'aVal',
},
{
key: 'bKey',
value: 'bVal',
},
{
key: 'cKey',
value: 'cVal',
}
],
__v: 0
}
]
So basically I want to project the additional artifact with an additional field whose name is bucketName.
I want to check in the variables array, whether there is an item, whose key is pre-known of value aKey. If so, the result of the projected bucketName would be the value field: aVal. If could not find, value would be NOT_FOUND.
So the result of the sample above would be:
[
server: {
_id: 6108d87b9255993b26796ca9,
version: '2.0.366',
name: 'aaaaaa',
variables: [
{
key: 'aKey',
value: 'aVal',
},
{
key: 'bKey',
value: 'bVal',
},
{
key: 'cKey',
value: 'cVal',
}
],
bucketName: 'aVal',
__v: 0
}
]
My try was:
{
$project: {
bucketName: {
$cond: {
if: {
$eq : ['$server.variables.key', 'aKey'],
},
then: '$server.variables.value',
else: 'NOT_FOUND',
},
},
},
},
But that just didn't do the query correctly.
You should use $in operator because this variables key is array:
Then, use also $filter within the then to extract that value.
db.collection.aggregate([
{
"$set": {
"bucketName": {
"$cond": {
"if": {
"$in": [
"aKey",
"$server.variables.key"
]
},
"then": {
"$filter": {
"input": "$server.variables",
"as": "variable",
"cond": {
"$eq": [
"$$variable.key",
"aKey"
]
}
},
},
"else": "NOT_FOUND"
}
}
},
},
{
"$unwind": "$bucketName",
},
{
"$project": {
"bucketName": "$bucketName.value",
"server": 1
}
}
])
you can check $in condition because server.variables.key will return an array of string
$indexOfArray to get index of matching key element from array server.variables.key
$arrayElemAt to select value from server.variables.value by about returned index
db.collection.aggregate([
{
$project: {
bucketName: {
$cond: {
if: { $in: ["aKey", "$server.variables.key"] },
then: {
$arrayElemAt: [
"$server.variables.value",
{ $indexOfArray: ["$server.variables.key", "aKey"] }
]
},
else: "NOT_FOUND"
}
}
}
}
])
Playground
Your $server.variables.key is an array, is that why $eq fails, you are comparing an string with an array. You need to use $in. Check this example.
But also there is a problem, as $server.variables.key is an array with all keys values, the bucketName variable will be an array too.
You have to use the string aKey into then stage like this
db.collection.aggregate({
"$set": {
"bucketName": {
"$cond": {
"if": {
"$in": [
"aKey",
"$server.variables.key"
]
},
"then": "aKey",
"else": "NOT_FOUND"
}
}
}
})

How to use $addFields in mongo to add elements to just existing documents?

I am trying to add a new field to an existing document by using a combination of both $ifnull and $cond but an empty document is always added at the end.
Configuration:
[
{
line: "car",
number: "1",
category: {
FERRARI: {
color: "blue"
},
LAMBORGHINI: {
color: "red"
}
}
},
{
line: "car",
number: "2",
category: {
FERRARI: {
color: "blue"
}
}
}
]
Query approach:
db.collection.aggregate([
{
$match: {
$and: [
{ line: "car" },
{ number: { $in: ["1", "2"] } }
]
}
},
{
"$addFields": {
"category.LAMBORGHINI.number": {
$cond: [
{ "$ifNull": ["$category.LAMBORGHINI", false] },
"$number",
"$$REMOVE"
]
}
}
},
{
$group: {
_id: null,
CATEGORIES: {
$addToSet: "$category.LAMBORGHINI"
}
}
}
])
Here is the link to the mongo play ground:
https://mongoplayground.net/p/RUnu5BNdnrR
I tried the mentioned query but I still get that ugly empty set added at the end.
$$REMOVE will remove last field/key, from your field category.LAMBORGHINI.number the last field is number that is why it is removing number from the end, you can try another approach,
specify just category.LAMBORGHINI, if condition match then it will return object of current category.LAMBORGHINI and number object after merging using $mergeObjects
{
"$addFields": {
"category.LAMBORGHINI": {
$cond: [
{ "$ifNull": ["$category.LAMBORGHINI", false] },
{
$mergeObjects: [
"$category.LAMBORGHINI",
{ number: "$number" }
]
},
"$$REMOVE"
]
}
}
}
Playground

How to update a value from all mongo documents from string to array?

My mongo documents look like this
{
"_id": "5e816eab7ed25c3d99c10749",
"category": [
"bookmarks",
"too read"
],
....
},
I would like to update the category array to be an Object array, like so:
"category": [
{
"pin": false,
"value": "bookmarks"
},
{
"pin": false,
"value": "to read"
}
],
is there a way to do it?
You can use update with aggregation pipeline starting from MongoDB 4.2,
$map to iterate loop of category array
db.collection.updateMany(
{},
[{
$set: {
category: {
$map: {
input: "$category",
in: {
pin: false,
value: "$$this"
}
}
}
}
}]
)
Playground
For the lower version you can use $out to clone result and updated documents in another blank collection,
db.collection.aggregate([
{
$set: {
category: {
$map: {
input: "$category",
in: {
pin: false,
value: "$$this"
}
}
}
}
},
{ $out: "give your collection name" }
])