How to agregate mongodb rows to columns - arrays

is there a way to aggregate rows in to columns in Mongodb
here is the schema
[{
"_id" : "2020-04-17",
"wares" : [{"ware" : "NYC","total" : 5},{"ware" : "SFO","total" : 10}]
},
{
"_id" : "2020-04-18",
"wares" : [{"ware" : "NYC","total" : 6},{"ware" : "SFO","total" : 12},{"ware" : "CHI","total" : 13}]
}]
Final result should be like this
[
{
date: '2020-04-17', NYC: 5, SFO: 10
},
{
date: '2020-04-18', NYC: 6, SFO: 12, CHI:13
}

You can use below aggregation
db.collection.aggregate([
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
{ "$arrayToObject": {
"$map": {
"input": "$wares",
"in": {
"k": "$$this.ware",
"v": "$$this.total"
}
}
}},
{ "_id": "$_id" }
]
}
}}
])
MongoPlayground

Related

get the sum after the $unwind and $lookup returns 0

Player collection:
{ "_id" : 1, "Name" : "John Aims", "Gender" : "M", "DoB" : ISODate("1990-01-01T00:00:00Z"), "Nationality" : "USA", "Hand" : "R", "YearTurnedPro" : 2010, "Tournament" : [ { "tournamentID" : 1, "TournamentYear" : 2016 }, { "tournamentID" : 2, "TournamentYear" : 2019 }, { "tournamentID" : 3, "TournamentYear" : 2021 } ] }
{ "_id" : 2, "Name" : "George Brown", "Gender" : "M", "DoB" : ISODate("1997-03-04T00:00:00Z"), "Nationality" : "GB", "Hand" : "L", "YearTurnedPro" : 2013, "Tournament" : [ { "tournamentID" : 2, "TournamentYear" : 2016 }, { "tournamentID" : 5, "TournamentYear" : 2019 } ] }
Tournament collection:
{ "_id" : ObjectId("626c18a3d880647a888888ff"), "TournamentID" : 1, "TournamentCode" : "GS1", "Position" : 8, "PrizeMoney" : 125000, "RankingPoints" : 250 }
{ "_id" : ObjectId("626c18c2d880647a888888ff"), "TournamentID" : 2, "TournamentCode" : "GS1", "Position" : 4, "PrizeMoney" : 250000, "RankingPoints" : 500 }
{ "_id" : ObjectId("626c18ddd880647a888888ff"), "TournamentID" : 3, "TournamentCode" : "GS1", "Position" : 1, "PrizeMoney" : 1000000, "RankingPoints" : 2000 }
1st Question:
Hello, I want to get the sum of ranking points of each player.
I have tried:
db.Player.aggregate([
{"$unwind" : "$Tournament"},
{"$lookup":
{"from":"Tournament",
"localField":"Tournament.tournamentID",
"foreignField":"TournamentID",
"as":"Tennis-player"}},
{ "$group": {
"_id": { Name:"$Name" },
"total_qty": { "$sum": "$Tennis-player.PrizeMoney" }
}}
])
But I get for every played the sum is 0.
I can show it on playground as it is using more than 1 collection.
2nd question:
Would it be better to create only 1 collections with all the data?
$unwind
$lookup
$set - As from stage 2 Tennis-player returns an array with guarantee only 1 document in array. Use $first to get the first document in Tennis-player array field to become a document field.
$group
db.Player.aggregate([
{
"$unwind": "$Tournament"
},
{
"$lookup": {
"from": "Tournament",
"localField": "Tournament.tournamentID",
"foreignField": "TournamentID",
"as": "Tennis-player"
}
},
{
$set: {
"Tennis-player": {
"$first": "$Tennis-player"
}
}
},
{
"$group": {
"_id": {
Name: "$Name"
},
"total_qty": {
"$sum": "$Tennis-player.PrizeMoney"
}
}
}
])
Sample Mongo Playground
Alternative:
$lookup - Work $lookup with an Array
$project - Decorate output documents. Create total_qty field and use $reduce to perform sum operation of Tennic-player.PrizeMoney.
db.Player.aggregate([
{
"$lookup": {
"from": "Tournament",
"localField": "Tournament.tournamentID",
"foreignField": "TournamentID",
"as": "Tennis-player"
}
},
{
"$project": {
"_id": {
Name: "$Name"
},
"total_qty": {
"$reduce": {
"input": "$Tennis-player",
"initialValue": 0,
"in": {
$sum: [
"$$value",
"$$this.PrizeMoney"
]
}
}
}
}
}
])
Sample Mongo Playground (Alternative)

How to query on embedded documents

{
"_id" : ObjectId("5fa919a49bbe481d117506c9"),
"isDeleted" : 0,
"productId" : 31,
"references" : [
{
"_id" : ObjectId("5fa919a49bbe481d117506ca"),
"languageCode" : "en",
"languageId" : 1,
"productId" : ObjectId("5fa919a49bbe481d117506ba")
},
{
"_id" : ObjectId("5fa91cc7d7d52f1e389dee1f"),
"languageCode" : "ar",
"languageId" : 2,
"productId" : ObjectId("5fa91cc7d7d52f1e389dee1e")
}
],
"createdAt" : ISODate("2020-11-09T10:27:48.859Z"),
"updatedAt" : ISODate("2020-11-09T10:27:48.859Z"),
"__v" : 0
},
{
"_id" : ObjectId("5f9aab1d8e475489270ebe3a"),
"isDeleted" : 0,
"productId" : 21,
"references" : [
{
"_id" : ObjectId("5f9aab1d8e475489270ebe3b"),
"languageCode" : "en",
"languageId" : 1,
"productId" : ObjectId("5f9aab1c8e475489270ebe2d")
}
],
"createdAt" : ISODate("2020-10-29T11:44:29.852Z"),
"updatedAt" : ISODate("2020-10-29T11:44:29.852Z"),
"__v" : 0
}
This is my mongoDB collection in which i store the multilingual references to product collection. In productId are the references to product Collection. Now If we have ar in our request, then we will only have the productId of ar languageCode. If that languageCode does not exist then we will have en langCode productId.
For Example if the user pass ar then the query should return
"productId" : ObjectId("5fa91cc7d7d52f1e389dee1e")
"productId" : ObjectId("5f9aab1c8e475489270ebe2d")
I have tried using $or with $elemMatch but I am not able to get the desired result. Also i am thinking of using $cond. can anyone help me construct the query.
We can acheive
$facet helps to categorized the incoming documents
In the arArray, we get all documents which has"references.languageCode": "ar" (This document may or may not have en), then de-structure the references array, then selecting the "references.languageCode": "ar" only using $match. $group helps to get all productIds which belong to "references.languageCode": "ar"
In the enArray, we only get documents which have only "references.languageCode": "en". Others are same like arArray.
$concatArrays helps to concept both arArray,enArray arrays
$unwind helps to de-structure the array.
$replaceRoot helps to make the Object goes to root
Here is the mongo script.
db.collection.aggregate([
{
$facet: {
arAarray: [
{
$match: {
"references.languageCode": "ar"
}
},
{
$unwind: "$references"
},
{
$match: {
"references.languageCode": "ar"
}
},
{
$group: {
_id: "$_id",
productId: {
$addToSet: "$references.productId"
}
}
}
],
enArray: [
{
$match: {
$and: [
{
"references.languageCode": "en"
},
{
"references.languageCode": {
$ne: "ar"
}
}
]
}
},
{
$unwind: "$references"
},
{
$group: {
_id: "$_id",
productId: {
$addToSet: "$references.productId"
}
}
}
]
}
},
{
$project: {
combined: {
"$concatArrays": [
"$arAarray",
"$enArray"
]
}
}
},
{
$unwind: "$combined"
},
{
"$replaceRoot": {
"newRoot": "$combined"
}
}
])
Working Mongo playground
You can test this solution to see if it is useful for you question:
db.collection.aggregate([
{
$addFields: {
foundResults:
{
$cond: {
if: { $in: ["ar", "$references.languageCode"] }, then:
{
$filter: {
input: "$references",
as: "item",
cond: {
$and: [{ $eq: ["$$item.languageCode", 'ar'] },
]
}
}
}
, else:
{
$filter: {
input: "$references",
as: "item",
cond: {
$and: [{ $eq: ["$$item.languageCode", 'en'] },
]
}
}
}
}
}
}
},
{ $unwind: "$foundResults" },
{ $replaceRoot: { newRoot: { $mergeObjects: ["$foundResults"] } } },
{ $project: { _id: 0, "productId": 1 } }
])

How to query by a field in an array of sub-documents with greater than condition?

data: [
{
"_id" : ObjectId("5ebda923a52984db48ab45f6"),
"detectorid" : 1371,
"loopdata" : [
{
"starttime" : "9/15/2011 0:00:00",
"volume" : 2,
"speed" : 65,
"occupancy" : 2,
"status" : 2,
"dqflags" : 0
},
{
"starttime" : "9/15/2011 0:00:20",
"volume" : 2,
"speed" : 53,
"occupancy" : 2,
"status" : 2,
"dqflags" : 0
},
{
"starttime" : "9/15/2011 0:00:40",
"volume" : 0,
"speed" : "",
"occupancy" : 0,
"status" : 0,
"dqflags" : 0
}
]
Hey guys, this is the data that I have in my collection. I want to return back the speed is over 53. I have tried and
db.collection.find({"data.speed":{$gt:53}})
it returned the wrong results (basically returned everything) and I have no idea what I wrong. Any hints guys? Thanks
I made to you two solutions:
If you just want to keep a speeds field and the _id document, this solve the problem:
Query:
db.collection.aggregate([
// $filter will apply the condition to every element of the array
// in this case the array is [65, 53 ""]
{
$project: {
"speeds": {
$filter: {
"input": "$loopdata.speed",
"as": "speed",
"cond": {
$and: [
{
$eq: [
{
$type: "$$speed" // check if the type is numeric
},
"double"
]
},
{
$gt: [
"$$speed", // check if it's greater than 53
53
]
}
]
}
}
}
}
}
])
Result:
[
{
"_id": ObjectId("5ebda923a52984db48ab45f6"),
"speeds": [
65
]
}
]
Now if you want to keep all the fields, and filter just the array loopdata, so this solves the problem:
Query 2:
db.collection.aggregate([
{
$addFields: {
"loopdata": {
$filter: {
"input": "$loopdata",
"as": "data",
"cond": {
$and: [
{
$eq: [
{
$type: "$$data.speed"
},
"double"
]
},
{
$gt: [
"$$data.speed",
53
]
}
]
}
}
}
}
}
])
Result:
[
{
"_id": ObjectId("5ebda923a52984db48ab45f6"),
"detectorid": 1371,
"loopdata": [
{
"dqflags": 0,
"occupancy": 2,
"speed": 65,
"starttime": "9/15/2011 0:00:00",
"status": 2,
"volume": 2
}
]
}
]

MongoDB query subarray in sub array

i want fetch a unitHouse from my document which is an sub array of sub array
Here is the data
{
"_id" : ObjectId("5a17d305c438324308bffb19"),
"floorRow" : [
{
"floorRowNo" : "F1",
"floorRowInfo" : "Best Floor Ever that i have ever seen",
"_id" : ObjectId("5a17d333c438324308bffb1a"),
"unitHouse" : [
]
},
{
"floorRowNo" : "F2",
"floorRowInfo" : "view",
"_id" : ObjectId("5a1bdfbb4d63841c3cb6fc89"),
"unitHouse" : [
{
"unitHouseNo" : "Unit001",
"unitHouseType" : "OFFICE",
"unitHouseStatus" : "SELL",
"_id" : ObjectId("5a1d212bed3a552f0421fd6b"),
},
{
"unitHouseNo" : "Unit002",
"unitHouseType" : "CAT003",
"unitHouseStatus" : "SELL",
"_id" : ObjectId("5a1e3691af12544ff05690e3"),
}
]
}
],
}
Here is what I have queried so far, which i can get floor F2 that i wanted, but it came with both unit. I want only unitHouse with id : 5a1e3691af12544ff05690e3.
propertyDevModel.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId("5a17d305c438324308bffb19"),
}
},
{
$project: {
floorRow: {
$filter: {
input: '$floorRow',
as: 'floorRow',
cond: {
$eq: ['$$floorRow._id', mongoose.Types.ObjectId("5a1bdfbb4d63841c3cb6fc89")],
}
}
}
}
},
])
I have answered this q by myself, but i will keep this post for others who have the same problem.
db.aggregate([
{
$match: {
_id: projectId,
'floorRow._id': floorRowId
}
},
{$unwind: '$floorRow'},
{
$match: {
'floorRow._id': floorRowId
}
},
{$unwind: '$floorRow.unitHouse'},
{
$match: {
'floorRow.unitHouse._id': unitId
}
},
{
$project:{
'floorRow.unitHouse': 1
}
}
])

MongoDb SUM GROUP BY WHERE

I am very new to MongoDb. I have a collection Order which has multiple documents as follows.
{
"vendor": "amazon",
"date": ISODate("2016-12-05T21:10:39.100Z"),
"products" : [
{
"id": NumberLong(590573),
"totalSold": NumberLong(59),
"totalCost": NumberLong(7350),
"variations": [
{
"varId": NumberLong(1),
"totalSoldV": NumberLong(30),
"totalCostV": NumberLong(3000)
},
{
"varId": NumberLong(2),
"totalSoldV": NumberLong(29),
"totalCostV": NumberLong(4350)
},
]
}
]
}
So what I am trying to achieve is for a particular product.id I want to calculate sum(totalSold) and sum(totalCost) group by date. I have been playing around with aggregate but haven't been able to do so.
db.collection.aggregate([
{$unwind: "$products"},
{$match: {"products.id":NumberLong(590573) }},
{
$group: {
_id: {
year : { "$year" : "$date" },
month : { "$month" : "$date" },
day : { "$dayOfMonth" : "$date" },
hour : { "$hour" : "$date" },
minute : { "$minute" : "$date" },
},
sumTotalSold: {$sum: "$products.totalSold"}, sumTotalCost: {$sum: "$products.totalCost"}
}
}
]).pretty();
Result:
{
"_id" : {
"year" : 2016,
"month" : 12,
"day" : 5,
"hour" : 21,
"minute" : 10
},
"sumTotalSold" : NumberLong(79),
"sumTotalCost" : NumberLong(9850)
}

Resources