mongodb inner object to parent level - mongodb

I got result after aggregate like
{
data: [
{
"_id": "61922aed85c74b2d1ef671bb",
"employee": {
"firstName": "fname",
"lastName": "lname",
"middleName": "mname"
},
"days": [
{
"_id": "61922aed85c74b2d1ef671be",
"day": "2021-09-01T21:00:00.000Z",
"data": {
"hours": 0,
"type": "OT",
"_id": "61922aed85c74b2d1ef671bf",
"updateDate": "2021-11-15T09:39:57.624Z"
}
},
{
"_id": "61922aed85c74b2d1ef671c0",
"day": "2021-09-02T21:00:00.000Z",
"data": {
"hours": 0,
"type": "OT",
"_id": "61922aed85c74b2d1ef671c1",
"updateDate": "2021-11-15T09:39:57.625Z"
}
}
]
}
]
}
Is it possible extract inner object of data of days array to the parent level.
From this
{
"_id": "61922aed85c74b2d1ef671be",
"day": "2021-09-01T21:00:00.000Z",
"data": {
"hours": 0,
"type": "OT",
"_id": "61922aed85c74b2d1ef671bf",
"updateDate": "2021-11-15T09:39:57.624Z"
}
}
to this
{
"_id": "61922aed85c74b2d1ef671be",
"day": "2021-09-01T21:00:00.000Z",
"hours": 0,
"type": "OT",
"updateDate": "2021-11-15T09:39:57.624Z"
}

Simply use $project like this:
db.collection.aggregate([
{
"$project": {
"day": 1,
"hours": "$data.hours",
"type": "$data.type",
"updateDate": "$data.updateDate"
}
}
])
Example here
Or to update values inside the array:
db.collection.aggregate([
{
"$project": {
"_id": 1,
"employee": 1,
"days": {
"$map": {
"input": "$days",
"as": "d",
"in": {
"_id": "$$d._id",
"day": "$$d.day",
"hours": "$$d.data.hours",
"type": "$$d.data.type",
"updateData": "$$d.data.updateDate"
}
}
}
}
}
])
Example here

Related

Aggregate occurrences of events in nested array

Given the following input:
[
{
"statuses": [
{
"status": "allowed",
"count": 3,
"events_count": [
"2001",
"1001",
"1001"
]
}
],
"date": "2022-09-10 15:00",
"_id": "2022-09-10 15:00"
}
]
I need count the number of occurrences of stauses.events_count, so the output would be:
[
{
"statuses": [
{
"status": "allowed",
"count": 3,
"events_count": [
{"type": "2001", "count": 1},
{"type": "1001", "count": 2},
]
}
],
"date": "2022-09-10 15:00",
"_id": "2022-09-10 15:00"
}
]
What I've tried
This is what I got so far:
db.collection.aggregate([
{
"$unwind": "$statuses"
},
{
"$unwind": "$statuses.events_count"
},
{
"$group": {
"_id": {
"event_count": "$statuses.events_count",
"status": "$statuses.status",
"date": "$date",
"count": "$statuses.count"
},
"occurences": {
"$sum": 1
}
}
}
])
Which produces:
[
{
"_id": {
"count": 3,
"date": "2022-09-10 15:00",
"event_count": "2001",
"status": "allowed"
},
"occurences": 1
},
{
"_id": {
"count": 3,
"date": "2022-09-10 15:00",
"event_count": "1001",
"status": "allowed"
},
"occurences": 2
}
]
I'm having difficulties grouping everything back together. I tried grouping by date and pushing back to a 'statuses' array, but it produces two items in the array (with status==allowed), rather than 1 item with status==allowed
You did 2 $unwinds, so it should be 2 $groups in reverse order:
{
"$group": {
"_id": {
"status": "$_id.status",
"count": "$_id.count",
"date": "$_id.date"
},
"event_count": {
"$push": {
"type": "$_id.event_count",
"count": "$occurences"
}
}
}
},
{
"$group": {
"_id": "$_id.date",
"date": {"$last": "$_id.date"},
"statuses": {
"$push": {
"status": "$_id.status",
"count": "$_id.count",
"event_count": "$event_count"
}
}
}
}

Compare duplicates by Score By Not Max Field or Earlier Date

I need to compare duplicated documents and get the duplicated ones with the Lowest Score.
If the Score between two duplicates is Equal, then get the one with earlier date.
{
"_id": UUID("c77c72de-edd8-4576-a72c-983cf93a0f31"),
"DocumentId": "05240423067",
"Name": "John Doe",
"CreationDate": ISODate("0001-01-01T00:00:00.000+00:00"),
"Score": 5,
},
{
"_id": UUID("b5a7d404-a341-45dd-b875-864cd1e6bda2"),
"DocumentId": "05240423067",
"Name": "John Doe",
"CreationDate": ISODate("2021-07-17T00:00:00.000+00:00"),
"Score": 2
},
{
"_id": UUID("9efddd23-4b6b-4e96-ab43-b24a080107db"),
"DocumentId": "05240423067",
"Name": "John Doe",
"CreationDate": ISODate("2021-07-10T00:00:00.000+00:00"),
"Score": 2
},
{
"_id": UUID("f1a063a5-f9dd-4998-b6aa-df2071dd8677"),
"DocumentId": "88313825863",
"Name": "Marcus Joseph",
"CreationDate": ISODate("2021-07-17T00:00:00.000+00:00"),
"Score": 2
},
{
"_id": UUID("e3262f8e-bd6a-49e8-abe5-c3c1a4e49900"),
"DocumentId": "88313825863",
"Name": "Marcus Joseph",
"CreationDate": ISODate("0001-01-01T00:00:00.000+00:00"),
"Score": 1
}
Later, the resulting documents will be deleted.
Expected Result:
{
"_id": UUID("b5a7d404-a341-45dd-b875-864cd1e6bda2"),
"DocumentId": "05240423067",
"Name": "John Doe",
"CreationDate": ISODate("2021-07-17T00:00:00.000+00:00"),
"Score": 2 // Return Documents with the **Lowest Score**
},
{
"_id": UUID("9efddd23-4b6b-4e96-ab43-b24a080107db"),
"DocumentId": "05240423067",
"Name": "John Doe",
"CreationDate": ISODate("2021-07-10T00:00:00.000+00:00"),
"Score": 2 // Return Documents with the **Lowest Score**
},
{
"_id": UUID("e3262f8e-bd6a-49e8-abe5-c3c1a4e49900"),
"DocumentId": "88313825863",
"Name": "Marcus Joseph",
"CreationDate": ISODate("0001-01-01T00:00:00.000+00:00"),
"Score": 2 // If both Scores Equal, Compare CreationDate earlier
}
Mongo Version 4.2.21
This would be easier with some of the newer "$group" accumulators introduced in more recent versions of MongoDB, but here's one way you could do it.
db.collection.aggregate([
{
"$group": {
"_id": "$DocumentId",
"count": {"$sum": 1},
"docs": {"$push": "$$ROOT"}
}
},
{ // if only 1, keep it
"$match": {
"$expr": {"$gt": ["$count", 1]}
}
},
{ // find the doc to keep
"$set": {
"keepDoc": {
"$reduce": {
"input": "$docs",
"initialValue": {
"Score": {"$minKey": 1}
},
"in": {
"$switch": {
"branches": [
{
"case": {"$gt": ["$$this.Score", "$$value.Score"]},
"then": "$$this"
},
{
"case": {"$eq": ["$$this.Score", "$$value.Score"]},
"then": {
"$cond": [
{"$gt": ["$$this.CreationDate", "$$value.CreationDate"]},
"$$this",
"$$value"
]
}
}
],
"default": "$$value"
}
}
}
}
}
},
{ // get docs other than keepDoc
"$project": {
"_id": 0,
"expiredDocs": {
"$filter": {
"input": "$docs",
"cond": {"$ne": ["$$this", "$keepDoc"]}
}
}
}
},
{"$unwind": "$expiredDocs"},
{"$replaceWith": "$expiredDocs"}
])
Try it on mongoplayground.net.
N.B.: On mongoplayground.net, there's no easy way that I know of to enter binary UUID values in the BSON configuration, so I just used strings. It should be inconsequential to the pipeline.

MongoDB lookup in nested array

I am trying to make lookup in MongoDB nested array. My Data is looks like.
[
{
"_id": "621eedae92979fd8f0e9451d",
"name": "Pallab Koley",
"shifts": {
"_id": "62636b9fcbda6d2b17f5cae0",
"month": "2022-05",
"shift": [
{
"date": "2022-05-01",
"shiftId": "622bb0f4b88dc92e3c2cac56"
},
{
"date": "2022-05-02",
"shiftId": "622b55f8f59dcdd1ab9b36b1"
},
]
}
},
{
"_id": "62626a7446ba9a911a623b37",
"name": "Pinki Das",
"shifts": {
"_id": "62636ba4cbda6d2b17f5cae1",
"month": "2022-05",
"shift": [
{
"date": "2022-05-01",
"shiftId": "622bb0f4b88dc92e3c2cac56"
}
]
}
}
]
I was trying with the lookup.
{
"$lookup": {
"from": "shifts",
"localField": "shifts.shift.shiftId",
"foreignField": "_id",
"as": "shifts.shift.shiftId"
}
}
And getting the result.
[
{
"_id": "621eedae92979fd8f0e9451d",
"name": "Pallab Koley",
"shifts": {
"_id": "62636b9fcbda6d2b17f5cae0",
"month": "2022-05",
"shift": {
"date": "2022-05-01",
"shiftId": [
{
"_id": "622bb0f4b88dc92e3c2cac56",
"date": "2022-05-01",
"name": "Day"
}
]
}
}
},
{
"_id": "621eedae92979fd8f0e9451d",
"name": "Pallab Koley",
"shifts": {
"_id": "62636b9fcbda6d2b17f5cae0",
"month": "2022-05",
"shift": {
"date": "2022-05-02",
"shiftId": [
{
"_id": "622b55f8f59dcdd1ab9b36b1",
"date": "2022-05-02",
"name": "Morning"
}
]
}
}
},
{
"_id": "62626a7446ba9a911a623b37",
"name": "Pinki Das",
"shifts": {
"_id": "62636ba4cbda6d2b17f5cae1",
"month": "2022-05",
"shift": {
"date": "2022-05-01",
"shiftId": [
{
"_id": "622bb0f4b88dc92e3c2cac56",
"date": "2022-05-01",
"name": "Day"
}
]
}
}
}
]
But my require data should looks like as bellow. shiftId should nested under shift array along with shifts data.
{
"_id": "621eedae92979fd8f0e9451d",
"name": "Pallab Koley",
"shifts": {
"_id": "62636b9fcbda6d2b17f5cae0",
"month": "2022-05",
"shift": [
{
"date": "2022-05-01",
"shiftId": [
{
"_id": "622bb0f4b88dc92e3c2cac56",
"date": "2022-05-01",
"name": "Day"
}
]
},
{
"date": "2022-05-02",
"shiftId": [
{
"_id": "622b55f8f59dcdd1ab9b36b1",
"date": "2022-05-02",
"name": "Morning"
}
]
}
]
}
},
{
"_id": "62626a7446ba9a911a623b37",
"name": "Pinki Das",
"shifts": {
"_id": "62636ba4cbda6d2b17f5cae1",
"month": "2022-05",
"shift": {
"date": "2022-05-01",
"shiftId": [
{
"_id": "622bb0f4b88dc92e3c2cac56",
"date": "2022-05-01",
"name": "Day"
}
]
}
}
}
]
Here is date field under shift is missing. And also need to group the shift array. Please help me out. PlayGround
Use $set after $lookup
db.employees.aggregate([
{
$lookup: {
from: "shifts",
localField: "shifts.shift.shiftId",
foreignField: "_id",
as: "shifts.shift2"
}
},
{
$set: {
"shifts.shift": {
$map: {
input: "$shifts.shift",
as: "s",
in: {
$mergeObjects: [
"$$s",
{
shiftId: {
$filter: {
input: "$shifts.shift2",
as: "s2",
cond: { $eq: [ "$$s2._id", "$$s.shiftId" ] }
}
}
}
]
}
}
}
}
},
{
$unset: [ "shifts.shift2" ]
}
])
mongoplayground

data stored in map, need all values of all object in an array

We have data like
[{
"parameterId": "5f914ca2679bae721d38410b",
"average": 574998.153846154,
"count": 26.0,
"date": "2020-09-08T18:30:00.000Z",
"dataPerHour": {
"0": {
"min": 92570.0,
"max": 995170.0,
"avg": 578268.826086957,
"count": 23,
"date": "2020-09-04T19:07:41.000Z",
"values": [{
"paramValue": "100414",
"time": "2020-09-04T19:07:41.000Z"
},
{
"paramValue": "705811",
"time": "2020-09-04T19:08:41.000Z"
}
]
},
"1": {
"min": 92570.0,
"max": 995170.0,
"avg": 678268.826086957,
"count": 23,
"date": "2020-09-03T19:07:41.000Z",
"values": [{
"paramValue": "100414",
"time": "2020-09-03T19:07:41.000Z"
},
{
"paramValue": "705811",
"time": "2020-09-03T19:08:41.000Z"
}
]
}
}
}, {
"parameterId": "5f914ca2679bae721d38410b",
"average": 574998.153846154,
"count": 26.0,
"date": "2020-09-08T18:30:00.000Z",
"dataPerHour": {
"0": {
"min": 92570.0,
"max": 995170.0,
"avg": 778268.826086957,
"count": 23,
"date": "2020-09-04T19:07:41.000Z",
"values": [{
"paramValue": "100414",
"time": "2020-09-04T19:07:41.000Z"
},
{
"paramValue": "705811",
"time": "2020-09-04T19:08:41.000Z"
}
]
}
}
}]
We need output:
[
"2020-09-08T18:30:00" : "578268.826086957",
"2020-09-03T19:07:41" : "678268.826086957",
"2020-09-08T18:30:00" : "778268.826086957"
]
I need mongo query for this. I need data like key = date and value = avg of each data in dataPerHour.
Does this help?
Playground
out
[
{
"data": [
{
"2020-09-03T19:07:41": 678268.826086957,
"2020-09-04T19:07:41": 578268.826086957
},
{
"2020-09-04T19:07:41": 778268.826086957
}
]
}
]
pipe
db.collection.aggregate([
{
$project: {
data: {
"$arrayToObject": {
$map: {
input: {
"$objectToArray": "$dataPerHour"
},
as: "d",
in: {
$cond: [
"$$d",
[
{
"$dateToString": {
"date": {
$toDate: "$$d.v.date"
},
"format": "%Y-%m-%dT%H:%M:%S"
}
},
{
"$toDouble": "$$d.v.avg"
}
],
"$$d"
]
}
}
}
}
}
},
{
$group: {
_id: null,
data: {
$push: "$$ROOT.data"
}
}
},
{
$unset: "_id"
}
])

MongoDB projection. Operator $add field|expression array awareness or after $slice

I've got collection that looks like:
[{
"org": "A",
"type": "simple",
"payFor": 3,
"price": 100
},
{
"org": "A",
"type": "custom",
"payFor": 2,
"price": 115
},
{
"org": "B",
"type": "simple",
"payFor": 1,
"price": 110
},
{
"org": "B",
"type": "custom",
"payFor": 2,
"price": 200
},
{
"org": "B",
"type": "custom",
"payFor": 4,
"price": 220
}]
And need to produce result with query to perform group by "org" where payments appears for only first "payFor" prices in "type".
I'm trying to use expression result by $slice operator in $add but this is not works.
pipeline:
[{
"$group": {
"_id": {
"org": "$org",
"type": "$type"
},
"payFor": {
"$max": "$payFor"
},
"count": {
"$sum": 1
},
"prices": {
"$push": "$price"
}
}
},
{
"$group": {
"_id": "$_id.org",
"payments": {
"$push": {
"type": "$_id.type",
"forFirst": "$payFor",
"sum": {
"$cond": [
{
"$gte": [
"$payFor",
"$count"
]
},
{
"$add": {
"$prices": {
"$slice": "$count"
}
}
},
{
"$add": "$prices"
}
]
}
}
}
}
}]
I know that it is possible to traverse unwinded prices and pick only "payFor" count of them. but result collections are more rich than in example above and this operation will produce some unecessary overheads.
Need some advice from community. Please. Thanks.