MongoDb SUM GROUP BY WHERE - arrays

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)
}

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)

MongoDB Aggregate subfields with individual date ranges

I have a schema which is:
{
"_id" : "12345678",
"action1" : [
{
"date" : "2021-01-15",
"value" : 20
},
{
"date" : "2021-01-14",
"value" : 16
}
],
"action2" : [
{
"date" : "2021-01-15",
"value" : 30
},
{
"date" : "2021-01-14",
"value" : 10
}
],
"action3" : [
{
"date" : "2021-01-15",
"value" : 40
},
{
"date" : "2021-01-14",
"value" : 20
}
],
"action4" : [
{
"date" : "2021-01-15",
"value" : 60
},
{
"date" : "2021-01-14",
"value" : 40
}
]
}
Now I want to write an aggregate query to filter out counts within a date range (for last 7 days, or 30 days or 90 days)
so the final sum should look something like the following:
{
_id: "12345678"
action1 : {
alltime: number,
last7Days : number,
last30Days: number,
last90Days: number
},
action2: {
alltime: number,
last7Days : number,
last30Days: number,
last90Days: number
},
action3: {
alltime: number,
last7Days : number,
last30Days: number,
last90Days: number
},
action4: {
alltime: number,
last7Days : number,
last30Days: number,
last90Days: number
},
}
I am trying to get the total number of actions using $project and $match for a particular _id
But how can I filter the past7days/30days/90days data
My query looks like the following
db.collection.aggregate([
{
$project: {
alltimeAction1: {$sum: "$action1.value"},
alltimeAction2: {$sum: "$action2.value"},
alltimeAction3: {$sum: "$action3.value"},
alltimeAction4: {$sum: "$action4.value"}
}
},
{
$match: {
_id: "12345678"
}
}
])
is mapReduce the only option available?
db.test1.aggregate([
{
"$project": {//Reshape actions, you need this as you have dynamic keys
data: {
"$objectToArray": "$$ROOT"
}
}
},
{//Denormalize
"$unwind": "$data"
},
{//Denormalize
"$unwind": "$data.v"
},
{
"$project": {//Formatting date
"_id": 1,
"key": "$data.k",
"date": {
"$dateFromString": {
"dateString": "$data.v.date",
"format":"%Y-%m-%d"
}
},
"value": "$data.v.value"
}
},
{
"$addFields": {//Getting the conditions ready
"7Days": {
$gte:["$date", new Date(new Date().getTime() - (7*24*3600*1000))]
},
"30Days": {
$gte:["$date", new Date(new Date().getTime() - (30*24*3600*1000))]
}
}
},
{
$group:{//Grouping them, you can add few more cases
"_id": "$key",
"7days":{
$sum:"$value"
},
"30days":{
$sum:"$value"
}
}
}
])
You don't need to do this much complex query if you have flattened schema where actionName can be identified by a constant field name.

How to agregate mongodb rows to columns

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

Group by project,user and month , and sume the hours

This keeps a record of each time the worker has worked on that project, and there may be several records in a single day because he works on several things.
I have this collection "WORKSDAYS":
{
"_id" : "00007cASDASDASDAS32423423",
"workDate" : ISODate("2017-01-20T00:00:00.000+00:00"),
"hours" : 6.0,
"worker" : {
"$ref" : "T_WORKERS",
"$id" : "f3f849d1-8777-4066-bdc6-64625475bbff"
},
"project" : {
"$ref" : "T_PROJECTS",
"$id" : "03b80b87-dc3b-4dc3-88c4-2b3334758459"
}
}
I need something similar to this for each project:
{
_id: { $ref: T_WORKERS, $id: id},
january: 168,
febrary: 120,
...
}
Or instead of January put 0 or 1, february = 02, etc.
I tried this that the workers gives me and the total hours of the project, but I would like the hours for each month:
aggregate([
{
$match: {
"project.$id": "03b80b87asdasdasdasd9"
}
},{
$group: {
"_id": "$worker",
"hours":{"$sum":"$hours"}
}
}
])
I had also thought, in passing him the start and end date (example: January and December and to join them)
.aggregate([
{$match: {
"project.$id": "03b80b8asdasdasdasdasd7"
}
}, {
$group: {
"_id": "$worker",
"month": {
"$sum": {
"$cond": [
{ "$gt": [
{ "$subtract": [ISODate("2018-01-01T00:00:00.000Z"), ISODate("2017-01-01T00:00:00.000Z") ] },
new Date().valueOf() - ( 1000 * 60 * 60 * 24 )
]},
"$hours",
0
]
}
}
}
}])

How can I check for duplicate nested arrays inside of documents in Mongoose?

Here is an example of a nested document that I have in my collection:
{
"title" : "front-end developer",
"age" : 25,
"name" : "John",
"city" : "London",
"skills" : [
{
"name" : "js",
"project" : "1",
"scores" : [
{
max: 76,
date: date
},
{
max: 56,
date: date
}
]
},
{
"name" : "CSS",
"project" : "5",
"scores" : [
{
max: 86,
date: date
},
{
max: 36,
date: date
},
{
max: 56,
date: date
},
]
}
]
}
Is there a simple way of determining whether other documents have an identical/duplicate structure to the skills array only? e.g. has the same keys, value and array indexes? Any help would be greatly appreciated. Thanks!
Here's how you get that:
collection.aggregate({
"$group": {
"_id": "$skills",
"docs": {
"$push": "$$ROOT"
},
"count": {
$sum: 1
}
}
}, {
$match: {
"count": {
$gt: 1
}
}
})
If you are looking for developers with the same skillset, you can use the $all operator:
var john = db.developers.findOne(...);
var devs = db.developers.find({ 'skills.name': { $all: john.skills.map(x => x.name) } });

Resources