How to rename mongodb field using regex expression - mongodb

I'm trying to update the name of a field in the mongodb document using the regex expression on the name of that field but I can't, can someone help me to do it?
I have a person document that contains a field with the email of the person, the name of that field is like that "firstname_lastname#gmail.com" and I want to replace "_" character in all email fields by "_dot_".
Here is what I did
:
db.getCollection("person").updateMany(
{
{'email.'+'.*_.*': { $exists: true }
},
{
$rename:{'email.'+'.*_.*': 'email.'+'.*_dot_.*'
});
and here the structure of my document:
person {
name: { .... }
email:{
firstname_lastname1#gmail.com : {
.... other fields
},
firstname_lastname2#gmail.com : {
.... other fields
}
}
}
thanks in advance

As #Wernfried Domscheit suggested in the comment, you should change your schema to avoid using dynamic email address as field name.
Nevertheless, you can use $objectToArray to convert the emails into an array of k-v tuples. Then, $split them by _ and rejoin by _dot_. Finally do a $arrayToObject to convert back to original structure.
db.collection.aggregate([
{
"$addFields": {
"email": {
"$objectToArray": "$email"
}
}
},
{
"$addFields": {
"email": {
"$map": {
"input": "$email",
"as": "e",
"in": {
k: {
"$ltrim": {
"input": {
"$reduce": {
"input": {
"$split": [
"$$e.k",
"_"
]
},
"initialValue": "",
"in": {
"$concat": [
"$$value",
"_dot_",
"$$this"
]
}
}
},
"chars": "_dot_"
}
},
v: "$$e.v"
}
}
}
}
},
{
"$addFields": {
"email": {
"$arrayToObject": "$email"
}
}
}
])
Here is the Mongo Playground for your reference.

Related

MongoDB - update property of subdocument where the string starts with certain text

I need to update many subdocument array objects where property 'name' starts with 'Test:'
I need the new 'name' to be the current 'name' but updated with the 'Test:' part removed from the string.
I have already achieved it but not in a subdocument, this is how I got it to work in a document:
db.getCollection('test').updateMany(
{ name: { $regex: '^Test :' } },
[{ $set: { name: { $replaceOne: { input: "$name", find: 'Test : ', replacement: "" } } } }]
)
Thanks for any help.
You can use $map to iterate through the array elements and and use $mergeObjects to update the field name
db.collection.update({
"arr.name": {
$regex: "^Test :"
}
},
[
{
"$set": {
"arr": {
"$map": {
"input": "$arr",
"as": "a",
"in": {
"$mergeObjects": [
"$$a",
{
name: {
"$replaceOne": {
"input": "$$a.name",
"find": "Test : ",
"replacement": ""
}
}
}
]
}
}
}
}
}
])
MongoPlayground

How to find all the phone number matches written in different formats using MongoDB Compass?

I need to find phones by the field in which they are stored in different formats:
1-234-567-89-10
1 (234) 567-89-10
1 234 567 89 10
12345678910
I tried to solve it this way, but it didn't work.
{ $replaceOne: { input: phone_field, find: "[^0-9]+", replacement: "" } : {$regex : "12345678910"}}
It is best if you keep your phone number field in one format , for example you can remove any other characters and keep only the number in the entire collection as follow:
db.collection.update({},
[
{
"$set": {
"phone": {
"$reduce": {
"input": {
"$regexFindAll": {
"input": "$phone",
"regex": "\\d+"
}
},
"initialValue": "",
"in": {
"$concat": [
"$$value",
"$$this.match"
]
}
}
}
}
}
],
{
multi: true
})
Playground
And later you can search via $regex as follow:
db.collection.find({
"$expr": {
"$regexMatch": {
"input": "$phone",
"regex": "123"
}
}
})
Playground2

Update name of a key in nested mongo document

I did check on multiple platforms for the solution, however did not get the answer. Here is my problem:
I have a nested Mongo Document of the format:
{
_id: "xxxx",
"Type": <value>,
"Data" : {
"1": { <<< this key might differ for diff documents
"00": { <<< this key might differ for diff documents
"cont": "India"
},
"05": {
"cont": "India"
},
....
}
"32": {
"41": {
"cont": "India"
},
"44": {
"cont": "India"
},
....
}
}
}
I want to rename the key cont to country in all the documents in the collection, however as mentioned above the key 1, 00, 32 might differ for different documents hence I could not reach to a solution for this.
Is there something like:
db.collection.updateMany({Type: "abc"}, { $rename: { "Data.*.*.cont": "Data.*.*.country" }})
because the key cont resides at the third level after the key Data
Can someone please help me out here?
Thanks!
Using dynamic value as field name is generally considered as an anti-pattern. Nevertheless, you can still use $objectToArray to convert the nested objects into k-v tuples and manipulate them. Then converted it back using $arrayToObject
db.collection.update({},
[
{
"$addFields": {
"arr": {
// first conversion
"$objectToArray": "$Data"
}
}
},
{
"$addFields": {
"arr": {
// second conversion
"$map": {
"input": "$arr",
"as": "a",
"in": {
k: "$$a.k",
v: {
"$objectToArray": "$$a.v"
}
}
}
}
}
},
{
"$addFields": {
"arr": {
"$map": {
"input": "$arr",
"as": "a",
"in": {
k: "$$a.k",
v: {
"$map": {
"input": "$$a.v",
"as": "a2",
"in": {
k: "$$a2.k",
// rename the field `country`
v: {
country: "$$a2.v.cont"
}
}
}
}
}
}
}
}
},
{
"$addFields": {
"arr": {
"$map": {
"input": "$arr",
"as": "a",
"in": {
k: "$$a.k",
v: {
// 1st backward conversion
"$arrayToObject": "$$a.v"
}
}
}
}
}
},
{
"$project": {
_id: 1,
Type: 1,
"Data": {
// 2nd backward conversion
"$arrayToObject": "$arr"
}
}
}
])
Here is the Mongo playground for your reference.

How to check if a key exists in a mongodb object where the key is a value of some another field in the document while doing aggregation?

First of all I know we can check if a key exists using the dot operator but in my case it is not working and I dont know why.
So far in the aggregation pipeline I have the following records.
{
"my_key":"1234"
"data":{
1234:"abc"
4567:"xyz"
}
}
{
"my_key":"6666"
"data":{
1234:"abc"
4567:"xyz"
}
}
I want to return the document where the my_key value does not exists in the data object. So according to the above example it should return the 2nd document.
I was trying using the $match operator as following but it does not seem to work.
$match :
{
"data.$my_key":{$exists:false}
}
This does not work and I dont get why :(
Is it because the my_key value is a string and the keys in the data object are not strings?
playground
db.collection.aggregate([
{
"$project": {//Reshape the data
"data": {
"$objectToArray": "$data"
},
"my_key": 1
}
},
{
"$unwind": "$data"
},
{
"$match": {//matching
"$expr": {
"$eq": [
"$data.k",
"$my_key"
]
}
}
}
])
Another way
Wihtout unwind
db.collection.aggregate([
{
"$project": {
"data": {
"$objectToArray": "$data"
},
"my_key": 1
}
},
{
$project: {
"output": {
"$map": {
"input": "$data",
"as": "data",
"in": {
"$eq": [
"$$data.k",
"$my_key"
]
}
}
},
"data": 1,
"my_key": 1
}
},
{
$match: {
output: true
}
}
])
If you need original format of data, you can add the below as last stage
{
$project: {
"data": {
"$arrayToObject": "$data"
},
"my_key": 1
}
}

How can I concat an array of integer in MongoDB aggregation method?

I have a field in mongodb document which is an array of integer as like:
import_ids=[5200, 4710, 100]
I want this array as double ## separated string, So that expected result would be
import_hashed="5200##4710##100"
I have tried with following code in $project pipeline of aggregation method.
{
$projct:{
import_hashed:{
$reduce:{
input:"$import_ids",
initialValue:"",
in:{$concat:["$$value", "##", "$$this"]}
}
}
}
}
But no result found and no erros too!
You can try below aggregation
You can use $toLower aggregation to convert integer to string or $toString if you are using mongodb 4.0
db.collection.aggregate([
{ "$project": {
"import_hashed": {
"$let": {
"vars": {
"key": {
"$reduce": {
"input": "$import_ids",
"initialValue": "",
"in": { "$concat": ["$$value", "##", { "$toLower": "$$this" }] }
}
}
},
"in": { "$substrCP": ["$$key", 2, { "$strLenCP": "$$key" }] }
}
}
}}
])
Output
{ "import_hashed": "5200##4710##100 }