Mogodb aggregation create output as {"key": "value"} from 2 arrays - mongodb

I have a question about how to create output from 2 arrays one array with translation key and another with translation i would output as
"translation_key":"value"
Current output:
{
"_id" : ObjectId("5bfc0b2b30c4683f585078fb"),
"translation" : [
"hour",
"day"
],
"translation_key" : [
"HOUR_TEXT",
"DAY_TEXT"
],
"locale_id" : "EN_en"
}
OUTPUT should be :
{
"EN_en" :{
"HOUR_TEXT" :"hour",
"DAY_TEXT" :"day",
}
}

You can use below aggregation
You can use $range to get index from both the arrays and can create key (k) and value (v) pair for the resultant array and then finally $arrayToObject to convert resultant array into single document.
db.collection.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": [[
{
"k": "$locale_id",
"v": {
"$arrayToObject": {
"$map": {
"input": { "$range": [0, { "$size": "$translation_key" }] },
"in": {
"k": { "$arrayElemAt": ["$translation_key", "$$this"] },
"v": { "$arrayElemAt": ["$translation", "$$this"] }
}
}
}
}
}
]]
}
}}
])
Which returns
[
{
"EN_en": {
"DAY_TEXT": "day",
"HOUR_TEXT": "hour"
}
}
]

You can try below aggregation:
db.col.aggregate([
{
$project: {
array: [
{
k: "$locale_id",
v: {
$arrayToObject: {
$map: {
input: { $range: [0, { $size: "$translation" }] },
as: "index",
in: {
k: { $arrayElemAt: [ "$translation", "$$index" ] },
v: { $arrayElemAt: [ "$translation_key", "$$index" ] }
}
}
}
}
}
]
}
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$array" }
}
}
])
Basically you need to use $arrayToObject operator to manipulate your key names and in your case it should be used twice. This operator expects an array of objects with two properties k and v and therefore you should use $map to generate that values based on translation, $range is used to generate indexes to traverse translation and translation_key arrays. Then you can use $replaceRoot to promote dynamically generated key into root level of your document.
Output:
{ "EN_en" : { "hour" : "HOUR_TEXT", "day" : "DAY_TEXT" } }

Related

Mongo DB aggregation using Compass - use variable as key name

I'm trying to map an array of objects to a new array of objects. An example of the object in the array:
{
k:"Zip code"
v:{
questionId:"596080353"
question:"In which ZIP code do you currently reside?"
answer:"97213"
}
}
I want the final object to be:
{
"Zip code": "97213"
}
I'm having trouble setting k as the key name in the new object. Does anyone know how to use variables as the key name in a mongo aggregation?
Use $arrayToObject
Converts an array into a single document; the array must be either:
Shape your data in the below format
[ { "k": "Zip code", "v": "97213"}, { "k": "Zip code", "v": 97212 } ]
Example 1 - https://mongoplayground.net/p/AXKHsZf-Qzy
db.collection.aggregate([
{ $set: { doc: [ { k: "$k", v: "$v.answer" } ] } },
{ $set: { doc: { "$arrayToObject": "$doc" } } }
])
Example 2 - https://mongoplayground.net/p/Vm1DwHVb9KY
db.collection.aggregate([
{ $unwind: "$zip" },
{ $addFields: { doc: { $arrayToObject: [ [ { k: "$zip.k", v: "$zip.v" } ] ] } } },
{ $group: { _id: "$_id", zips: { $push: "$doc" } } }
])

how to label the field in MongoDB and make a sum with respect to date

Need to format the date and make a sum per day but sometimes a or b values are not available.
in the end, get one document with respect to date and sum. I'm using MongoDB 4.2.
Data Structure:
{
"data": {
"11-10-2001": {
"a": 17.281150000000001,
"b": 11.864060000000006
},
"13-10-2020": {
"b": 2.7616699999999994
},
"12-10-2001": {
"b": 4.0809599999999997
},
"09-10-2001": {
"b": 4.1286300000000005
},
"17-10-2001": {
"a": 15.140560000000123,
"b": 5.017139999999998
},
"18-10-2001": {
"b": 1.975189999999997,
"a": 7.093789999999976
}
}
}
Expected Output one document that contains the day and sum:
{
{
day: 11-10-2001,
sum : 29.145
},
{
day: 13-10-201,
sum : 2.7616699
},
{
day: 12-10-2001,
sum : 4.0809599999999997
},
{
day: 17-10-2001,
sum : 20.114
},
{
day: 18-10-2001,
sum : 9.145
}
}
You can try,
$map to iterate loop of data object after converting to array using $objectToArray
add key day, and sum, $reduce to loop of number object after converting to array using $objectToArray, $add to sum the value of number
$unwind deconstruct data array
$replaceRoot to replace data object to root
db.collection.aggregate([
{
$addFields: {
data: {
$map: {
input: { $objectToArray: "$data" },
in: {
day: "$$this.k",
sum: {
$reduce: {
input: { $objectToArray: "$$this.v" },
initialValue: 0,
in: { $add: ["$$this.v", "$$value"] }
}
}
}
}
}
}
},
{ $unwind: "$data" },
{ $replaceRoot: { newRoot: "$data" } }
])
Playground
You can do like following
db.collection.aggregate([
{
$project: { data: { "$objectToArray": "$data" } }
},
{
$unwind: "$data"
},
{
"$replaceRoot": { "newRoot": "$data" }
},
{
$addFields: { v: { "$objectToArray": "$v" } }
},
{
$addFields: {
v: {
$reduce: {
input: "$v",
initialValue: 0,
in: {
$add: [ "$$this.v", "$$value" ]
}
}
}
}
},
{
$group: {
_id: null,
data: {
$push: {
day: "$k",
sum: "$v"
}
}
}
}
])
Working Mongo playground

conditional addFields to embedded objects in MongoDb

I am trying to add in values to a list of embedded objects based on another value within the object. A sample document looks like:
{
"array" : [{
"val1" : "a"
}, {
"val1" : "b"
}]
}
What I am trying to achieve is
{
"array" : [{
"val1" : "a",
"isVal1A": true
}, {
"val1" : "b",
"isVal1A": false
}]
}
How do I go about doing this using an aggregate pipeline? Thanks!
Try
Live version
db.collection.aggregate({
$addFields: {
array: {
$map: {
input: "$array",
as: "a",
in: {
$cond: [
{
$eq: [
"$$a.val1",
"a"
]
},
{
"$mergeObjects": [
"$$a",
{
"Isval1A": true
}
]
},
{
"$mergeObjects": [
"$$a",
{
"Isval1A": false
}
]
}
]
}
}
}
}
})
You can try,
$map to iterate loop of array, check condition if val1 is 'a' then returnb true otherwise false, $mergeObjects to merge current object and status field
db.collection.aggregate([
{
$addFields: {
array: {
$map: {
input: "$array",
in: {
$mergeObjects: [
"$$this",
{
isVal1A: {
$cond: [{ $eq: ["$$this.val1", "a"] }, true, false]
}
}
]
}
}
}
}
}
])
Playground

Dynamic key in MongoDB

Im trying to create a dynamic group by (with sum agg) in MongoDB. But don't know how to right syntax that.
Lets imaging 2 documents:
{
"_id": {"$oid":"5f69f6a360c8479d0908a649"},
"key":"key1",
"data":{
"key1":"value1",
"key2":"value2",
"key3":"value3",
"key4":"value4"
},
"count":10
}
{
"_id": {"$oid":"5f69f6a360c8479d0908a649"},
"key":"key2",
"data":{
"key1":"value5",
"key2":"value6",
"key3":"value7",
"key4":"value8"
},
"count":15
}
With the key attribute, I want to control, which is the groupby attribute.
A pseudo query could look like:
[{
$group: {
_id: {
'$key': data[$key]
},
sum: {
'$sum': '$count'
}
}
}]
Output should look like:
value1 : 10
value6 : 15
Somebody knows how to do that?
I don't understand the purpose of $sum and $group, there are no arrays in your documents.
This aggregation pipeline give desired result:
db.collection.aggregate([
{ $set: { data: { $objectToArray: "$data" } } },
{ $set: { data: { $filter: { input: "$data", cond: { $eq: ["$$this.k", "$key"] } } } } },
{ $set: { data: { k: { $arrayElemAt: ["$data.v", 0] }, v: "$count" } } },
{ $set: { data: { $arrayToObject: "$data" } } },
{ $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$data"] } } },
{ $unset: ["key", "count", "data"] }
])
You can try,
$reduce input data as array using $objectToArray, check condition if key matches with data key then return key as value and value as count field
convert that returned key and value object array to exact object using $arrayToObject
replace field using $replaceWith
db.collection.aggregate([
{
$replaceWith: {
$arrayToObject: [
[
{
$reduce: {
input: { $objectToArray: "$data" },
initialValue: {},
in: {
$cond: [
{ $eq: ["$$this.k", "$key"] },
{
k: "$$this.v",
v: "$count"
},
"$$value"
]
}
}
}
]
]
}
}
])
Playground

MongoDB: aggregating by property names

I have a collection within which each document looks a bit like this:
{
"_id" : ObjectId("5b9fe6010c969210d442a377"),
"statuses" : {
"SEARCHING" : 3
},
"regions" : {
"eu-central-1" : 1,
"us-west-2": 2
}
}
I want to group and transform this into a result set like this:
eu-central-1|us-west-2
1|2
Is this possible in one step/query?
This is probably what you want:
db.collection.aggregate({
$project: {
"regions": { $objectToArray: "$regions" } // convert sub-document into an array of key-value-pairs in order to get hold of the field names
}
}, {
$unwind: "$regions" // flatten the "regions" array
}, {
$group: {
"_id": "$regions.k",
"count": { $sum: "$regions.v" } //
}
})
Alternatively, if you really want to get a pipe-delimited output here is what you'd do:
db.collection.aggregate({
$project: {
"result": {
$reduce: {
"input": { $objectToArray: "$regions" },
"initialValue": { k: "", v: "" }, // start with empty strings for both key and value
"in": {
k: { $concat: [ "$$value.k", "|", "$$this.k" ] }, // concatenate existing value with "|" followed by currently looked at value for both key and value
v: { $concat: [ "$$value.v", "|", { $substr: [ "$$this.v", 0, 1000 ] } ] } // substr is needed to convert an integer field into a string
//v: { $concat: [ "$$value.v", "|", { $toString: "$$this.v" } ] } // this works from MongoDB v4.0 onwards and looks a bit cleaner
}
}
}
}
}, {
$project: { // remove the leading "|"
"result.k": { $substr: [ "$result.k", 1, -1 ] },
"result.v": { $substr: [ "$result.v", 1, -1 ] }
}
})