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 }
Related
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.
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.
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
}
}
I have a document that looks like this, I am using mongo $map to project fields inside the table and rename the key. I can't use $unwind due to some internal complexity.
{
"Table":[
{"Lookup":{
"CreatedBy":{
"id": "User001",
"Name":"UserName"
}
}
}]
}
The output I am expecting looks something like this
{
"Table":[
{"Lookup":{
"CreatedBy":"UserName"
}
}]
}
I am trying to achieve it with mongo $map but it is not supported
db.getCollection('TableDoc').aggregate([
{
"$project": {
"Table": {
"$map": {
"input": "$Table",
"in": {
"Lookup.CreatedAt": "$$this.Lookup.CreatedAt.Name",
}
}
}
}
}
])
Is there any other way to achieve this without using $unwind
This is supported with $map, but just not using "dotted field paths". Instead you use "absolute" object structures:
collection.aggregate([
{ "$addFields": {
"Table": {
"$map": {
"input": "$Table",
"in": {
"Lookup": {
"CreatedBy": "$$this.Lookup.CreatedBy.Name"
}
}
}
}
}}
])
Alternately if you have lots of fields in the objects you can use $mergeObjects where supported:
collection.aggregate([
{ "$addFields": {
"Table": {
"$map": {
"input": "$Table",
"in": {
"$mergeObjects": [
"$$this",
{
"Lookup": {
"CreatedBy": "$$this.Lookup.CreatedBy.Name"
}
}
]
}
}
}
}}
])
That makes more sense when an example shows more fields than your sample in the question does.
Consider the collection
{ "name": "Student1", "marks": [
{
"subject": "Subject1",
"marks": 12
},
{
"subject": "Subject2",
"marks": 15
},
{
"subject": "Subject3",
"marks": 20
} ] }
I am trying to perform $subtract on marks. I tried the aggregation as below.
db.mycoll.aggregate(
[
{
"$addFields": {
diff: {$subtract:["$marks.0.marks","$marks.1.marks","$marks.2.marks"]}
}
}
]
)
The approach doesnt seems to be working. I am getting an error
"Couldn't execute query: cant $subtract a array from a array"
You should be able to leverage the use of $reduce operator to achieve the desired output as follows:
db.mycoll.aggregate([
{
"$addFields": {
"diff": {
"$reduce": { // expression returns difference
"input": "$marks",
"initialValue": 0,
"in": { "$subtract": ["$$this.marks", "$$value"] }
}
}
}
}
])