Retrieve the field from the array - mongodb

I have a document like this
[
{
"empno": "×325007",
"vehicle": [
{
"valdate": "2020-08-02T13:17z",
"Inspectvalue": {
"price": "2000"
}
}
]
}
]
But I want document like this
{
"empno":"x325007",
"valdate":"2020-08-02T13:17z",
"price":"2000"
}

keeping them in the array, and including the keys to each element.
Example
db.collection.aggregate([
{
$addFields: {
newData: {
"$map": {
"input": "$vehicle",
"as": "v",
"in": {
valdate: "$$v.valdate",
price: "$$v.Inspectvalue.price",
empno: "$empno"
}
}
}
}
},
{
$unset: [
"vehicle",
"empno"
],
}
])
(you can't have repeated top level fields in JSON).
Or if you want them as top level keys, unwind the elements:
db.collection.aggregate([
{
$unwind: "$vehicle"
},
{
$addFields: {
valdate: "vehicle.valdate",
price: "vehicle.Inspectvalue.price"
}
},
{
$unset: "vehicle"
}
])
Example

Related

MongoDB get $sum of fields created via $addFields

I'm trying to get sum of fields that were created with $addFields operator.
I'd like to get sum of fields for the first month among all documents.
Please see link to the MongoDB sandbox.
Data:
[
{
"key": 1,
"account": "a",
"cases_total_date": {
"20220101": 1,
"20220102": 2,
"20220103": 3,
"20220501": 4,
"20221201": 5,
"20221202": 6,
}
},
{
"key": 2,
"account": "b",
"cases_total_date": {
"20220101": 11,
"20220102": 12,
"20220103": 13,
"20220501": 14,
"20221201": 15,
"20221202": 16,
}
}
]
Query I've tried:
db.collection.aggregate([
{
"$match": {
"account": {
"$in": [
"a",
"b"
]
}
}
},
{
"$addFields": {
"cases_total_months|202201": {
"$sum": [
"$cases_total_date.20220101",
"$cases_total_date.20220102",
"$cases_total_date.20220103"
]
}
}
},
{
"$group": {
"_id": "",
"cases_total_months|202201_all": {
"$sum": "$cases_total_months|20220101"
}
}
}
])
The response I've got vs expected:
[
{
"_id": "",
"cases_total_months|202201_all": 0 # EXPECTED sum of fields from 2 docs 6+36=42
}
]
Would appreciate any feedback. Thank you!
Using dynamic values as field names is considered an anti-pattern and introduces unnecessary complexity to the queries. With a proper schema, you can do something simple as this:
db.collection.aggregate([
{
"$set": {
"cases_total_months|202201_all": {
"$filter": {
"input": "$cases_total_date",
"as": "ctd",
"cond": {
$and: [
{
$eq: [
2022,
{
$year: "$$ctd.date"
}
]
},
{
$eq: [
1,
{
$month: "$$ctd.date"
}
]
}
]
}
}
}
}
},
{
$group: {
_id: null,
"cases_total_months|202201_all": {
$sum: {
$sum: "$cases_total_months|202201_all.value"
}
}
}
}
])
Mongo Playground
For your current schema, you can still rely on $objectToArray and iterate through the resulting k-v tuples to get what you need.
db.collection.aggregate([
{
$set: {
cases_total_date: {
"$objectToArray": "$cases_total_date"
}
}
},
{
$set: {
"cases_total_months|202201_all": {
"$filter": {
"input": "$cases_total_date",
"as": "ctd",
"cond": {
$eq: [
0,
{
"$indexOfCP": [
"$$ctd.k",
"202201"
]
}
]
}
}
}
}
},
{
$set: {
"cases_total_months|202201_all": {
$sum: "$cases_total_months|202201_all.v"
}
}
},
{
$group: {
_id: null,
"cases_total_months|202201_all": {
$sum: "$cases_total_months|202201_all"
}
}
}
])
Mongo Playground

Variables from values in MongoDB Arrays with variable length

This is my first question ever here so super excited to learn and apologies if the syntax is not up to mark, I will improve with time.
"item" is an Array ( this doc has only one element)
"adjudication" is a nested Array with a variable number of elements(same structure)
I want to create keys out of "adjudication.category.coding.code" without hardcoding as the values will be different with each document but will have the same string length
I tried using "$map" to apply the same logic to each array element but failed
"item": [
{
"adjudication": [
{
"amount": {"code": "USD", "system": "4217", "value": 22.51},
"category": {
"coding": [
{
"code": "bb.org/paid_amt",
"system": "bb.org/adjudication"
}
]
},
"reason": {
"coding": [
{
"code": "C",
"system": "bb.org/cvrg_status"
}
]
}
},
{
"amount": {"code": "USD", "system": "4217", "value": 0},
"category": {
"coding": [
{
"code": "bb.org/discount_amt",
"system": "bb.org/adjudication"
}
]
}
}
]
}
]
Output desired
adjudication: {paid_amt: 22.51, discount_amt: 0}
Welcome to SO. I hope the following code will solve your exception. Sometime you may modify based on you wish.
$unwind to deconstruct the array
$map to loop / modify through the array, and $arrayElementAt to. get the first object from the array
$let to find the last potion by $spliting for "paid_amt" and "discount_amt". So this will be an array of object. But you need objects. So the next part I make it as k:v pair.
$arrayToObject to make array to object by using above k:v pair. ("k" and "v" names are must. Can't replace by any other names)
$group to reconstruct the array that we did in 1st step
Here is the code,
db.collection.aggregate([
{ "$unwind": "$item" },
{
$project: {
"item.adjudication": {
"$map": {
"input": "$item.adjudication",
"in": {
amount: "$$this.amount.value",
code: {
"$arrayElemAt": [ "$$this.category.coding", 0 ]
}
}
}
}
}
},
{
$project: {
"item.adjudication": {
"$map": {
"input": "$item.adjudication",
"in": {
v: "$$this.amount",
k: {
"$let": {
"vars": {
"code": {
"$split": [ "$$this.code.code", "/" ]
}
},
"in": { "$arrayElemAt": [ "$$code", -1 ] }
}
}
}
}
}
}
},
{
$project: {
"item.adjudication": {
"$arrayToObject": "$item.adjudication"
}
}
},
{
"$group": {
"_id": "$_id",
"item": { "$push": "$item" }
}
}
])
Working Mongo playground
try this playground
db.collection1.aggregate(
[{
$unwind: {
path: '$item'
}
}, {
$unwind: {
path: '$item.adjudication'
}
}, {
$unwind: {
path: '$item.adjudication.category'
}
}, {
$unwind: {
path: '$item.adjudication.category.coding'
}
}, {
$group: {
_id: null,
data: {
$push: {
k: '$item.adjudication.category.coding.code',
v: '$item.adjudication.amount.value'
}
}
}
}, {
$project: {
_id:0,
adjudication: {$arrayToObject: '$data'}
}
}]
)

Regroup all elements of nested array of nested array into a single array

I have this collection :
{
"_id": ObjectId("6045eec8f113547ddd3472d9"),
"execution": {
"_id": ObjectId("6045eec8f113547ddd3472d8"),
"steps": [
{
"_id": ObjectId("6045eec8f113547ddd3472d7"),
"actions": [
{
"_id": ObjectId("6045eec8f113547ddd3472d6"),
"title": "action title"
}
]
}
]
}
}
and i want to achieve this result:
{
"_id": ObjectId("6045eec8f113547ddd3472d9"),
"allActions": [
{
"_id": ObjectId("6045eec8f113547ddd3472d6"),
"title": "action title"
}
]
}
to clarify more, i want to add an array that groups together all "actions" under the "steps" array
db.getCollection("missions").aggregate([
{
$match: {
_id: ObjectId("6045eec8f113547ddd3472d9")
}
},
{
$addFields: {
"allActions": "$execution.steps"
}
}
])
Could someone help me complete the part of the code that will allow me to achieve the desired output?
You may just use $reduce and $concatArrays to group all the entries in the actions array
db.missions.aggregate([
{
$match: {
_id: ObjectId("6045eec8f113547ddd3472d9")
}
},
{
$addFields: {
"allActions": {
"$reduce": {
"input": "$execution.steps.actions",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
"$$this"
]
}
}
}
}
}
])
Here is the Mongo playground for your reference.

Aggregate mongodb change objects to array

I am working with a mongodb aggregation and I would need to replace the part of my object array to an array concatenation. I understand that the part that I have to modify would be push but it is added as objects and I am not very clear how to concatenate that part
This is my current aggregation:
Datagreenhouse.aggregate([
{ "$match": { "id_sensor_station_absolute": { "$in": arr }, "recvTime": { $gte: fechainicio, $lte: fechafin } } }, //, "id_station": { "$in": id_station }
{
"$lookup": {
"from": "station_types",
"localField": "id_station", // local field in measurements collection
"foreignField": "id_station", //foreign field from sensors collection
"as": "sensor"
}
},
{ "$unwind": "$sensor" },
{
"$addFields": {
"sensor.attrValue": "$attrValue", // Add attrValue to the sensors
"sensor.recvTime": "$recvTime", // Add attrName to the sensors
"sensor.marca": "$sensor.marca",
"sensor.sensor_type": {
$filter: {
input: '$sensor.sensor_type',
as: 'shape',
cond: { $eq: ['$$shape.name', '$attrName'] },
}
}
}
},
{
"$group": {
"_id": {
"name_comun": "$sensor.sensor_type.name_comun",
"name_comun2": "$sensor.marca"
}, // Group by time
"data": {
"$push": {
"name": "$sensor.recvTime",
"value": "$sensor.attrValue",
},
},
}
},
{
"$project": {
"name": {
$reduce: {
input: [
["$_id.name_comun2"]
],
initialValue: "$_id.name_comun",
in: { $concatArrays: ["$$value", "$$this"] }
}
},
"_id": 0,
"data": 1
}
}
]
The current output of this aggregation is:
[
{
"data":[
{
"name":"2020-06-04T14:30:50.00Z",
"value":69.4
},
{
"name":"2020-06-04T14:13:31.00Z",
"value":68.9
}
],
"name":[
"Hum. Relativa",
"Hortisys"
]
},
{
"data":[
{
"name":"2020-06-04T14:30:50.00Z",
"value":25.5
},
{
"name":"2020-06-04T14:13:31.00Z",
"value":26.6
}
],
"name":[
"Temp. Ambiente",
"Hortisys"
]
}
]
I need to change the format of data for data: [[], [], []]
Example:
[
{
"data":[
["2020-06-04T14:30:50.00Z",69.4],
["2020-06-04T14:13:31.00Z",68.9],
"name":[
"Hum. Relativa",
"Hortisys"
]
},
{
"data":[
["2020-06-04T14:30:50.00Z",69.4],
["2020-06-04T14:13:31.00Z",68.9],
"name":[
"Temp. Ambiente",
"Hortisys"
]
}
]
The $push operator won't accept an array directly, but you can give it another operator that returns an array, like
data:{ $push:{ $concatArrays:[[ "$sensor.recvTime", "$sensor.attrValue" ]]}},

Mongoose Summing Up subdocument array elements having same alias

I have a document like this:
_id:'someId',
sales:
[
{
_id:'111',
alias:'xxx',
amount:500,
name: Apple, //items with same alias always have same name and quantity
quantity:2
},
{
_id:'222',
alias:'abc',
amount:100,
name: Orange,
quantity:14
},
{
_id:'333',
alias:'xxx',
amount:300,
name: Apple, //items with same alias always have same name and quantity
quantity:2
}
]
The alias field is here to 'group' items/documents whenever they appear to have same alias i.e to be 'embeded' as one with the amount summed up.
I need to display some sort of a report in such a way that those elements which have same alias they should be displayed as ONE and the others which doesn't share same alias to remain as they are.
Example, For the sample document above, I need an output like this
[
{
alias:'xxx',
amount:800
},
{
alias:'abc',
amount:100
}
]
WHAT I HAVE TRIED
MyShop.aggregate([
{$group:{
_id: "$_id",
sales:{$last :"$sales"}
},
{$project:{
"sales.amount":1
}}
}
])
This just displays as a 'list' regardless of the alias. How do I achieve summing up amount based on the alias?
You can achieve this using $group
db.collection.aggregate([
{
$unwind: "$sales"
},
{
$group: {
_id: {
_id: "$_id",
alias: "$sales.alias"
},
sales: {
$first: "$sales"
},
_idsInvolved: {
$push: "$sales._id"
},
amount: {
$sum: "$sales.amount"
}
}
},
{
$group: {
_id: "$_id._id",
sales: {
$push: {
$mergeObjects: [
"$sales",
{
alias: "$_id.alias",
amount: "$amount",
_idsInvolved: "$_idsInvolved"
}
]
}
}
}
}
])
Mongo Playground
You can use below aggregation
db.collection.aggregate([
{
"$addFields": {
"sales": {
"$map": {
"input": {
"$setUnion": [
"$sales.alias"
]
},
"as": "m",
"in": {
"$let": {
"vars": {
"a": {
"$filter": {
"input": "$sales",
"as": "d",
"cond": {
"$eq": [
"$$d.alias",
"$$m"
]
}
}
}
},
"in": {
"amount": {
"$sum": "$$a.amount"
},
"alias": "$$m",
"_idsInvolved": "$$a._id"
}
}
}
}
}
}
}
])
MongoPlayground