unable to group date and field in mongodb - javascript

I am trying to group date and name field then finding the count of each day.I am not able to differentiate date and time so My approach is doing the grouping based on date as well time in ISO format.
My approach:-
db.getCollection('blog').aggregate([
{ "$group": {
"_id": {"name":"$name","date":"$date"},
"Count": {
"$sum": {
"$sum": "$Count"
}
}
}},
{ "$project": {
"name": "$_id",
"Count": "$Count",
"_id": 0
}}
]).toArray()
input:-
{ "_id" : ObjectId("1"), "Count" : 4 , "name" : Ram, "date" : ISODate("2017-02-01T00:00:00Z") }
{ "_id" : ObjectId("2"), "Count" : 4, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("3"), "Count" :2 , "name" : Ram, "date" : ISODate("2017-02-01T00:00:00Z")}
{ "_id" : ObjectId("4"), "Count" : 2, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("5"), "Count" : 6, "name" : Arjun, "date" : ISODate("2017-01-08T00:00:00Z") }
{ "_id" : ObjectId("6"), "Count" : 6, "name" : Shyam, "date" : ISODate("2017-02-09T00:00:00Z")}
{ "_id" : ObjectId("7"), "count" : 1, "name" : Shyam, "date" : ISODate("2017-02-03T00:00:00Z") }
{ "_id" : ObjectId("8"), "loginID" : 2, "name" : Arjun, "date" : ISODate("2017-02-08T00:00:00Z") }
Expected output:-
{
name:Ram,
Count:6,
date:2017-02-01
}
{
name: Arjun,
Count:12,
date:2017-02-08
}
{
name: Arjun,
Count:2,
date:2017-01-08
}
....Something like that

You are using $sum twice in your $group stage, you only need it once.
db.getCollection('blog').aggregate([
{
$group: {
_id: {
name: "$name",
date: "$date"
},
Count: {
$sum: "$Count"
}
}
},
{
$project: {
name: "$_id.name",
date: "$_id.date",
Count: 1,
_id: 0
}
}
])

Related

How to populate an array inside a document which stores uniqueID as strings and that uniqueID is from another collection rather than objectID

I have a user document as follows which holds an array of systemIDs', which is unique for another collection which holds systemID as unikey key. How to populate user with all system ID details?
User document:
{
"_id" : ObjectId("6gfg85993266db5fdgs578"),
"email" : "xyz#gmail.com",
"role" : "user",
"systemIDs" : [
"12345678",
"87654321"
],
"createdAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"updatedAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("6gfg85993266db5fdgs578"),
"email" : "abc#gmail.com",
"role" : "user",
"systemIDs" : [
"1111111",
"2135684"
],
"createdAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"updatedAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"__v" : NumberInt(0)
}
System IDs document:
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "12345678",
"err" : [
1, 5, 10
],
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "87654321",
"err" : [
3, 7
],
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("62087dsfsdfs97e1"),
"systemID" : "11111111",
"err" : [
],
"__v" : NumberInt(0)
},
I want to find details of all the systemIDs a user holds which results something like this if I query my users collection with email : xyz#gmail.com, I should get the below result or populated result like shown:
{
"_id" : ObjectId("6gfg85993266db5fdgs578"),
"email" : "xyz#gmail.com",
"role" : "user",
"systemIDs" : [
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "12345678",
"err" : [
1, 5, 10
],
"__v" : NumberInt(0)
},
{
"_id" : ObjectId("62093fdsfsdfs97e1"),
"systemID" : "87654321",
"err" : [
3, 7
],
"__v" : NumberInt(0)
},
]
"createdAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"updatedAt" : ISODate("2022-02-13T16:31:34.119+0000"),
"__v" : NumberInt(0)
},
I can create a foreach loop and call database each time but I suppose that wouldn't be a good practice.
I am new with this so please bear with me and explain it to me in details.
you can use aggregation with $match and $lookup to perform this task
db.users.aggregate([
{
"$match": {
"email": "xyz#gmail.com"
}
},
{
"$lookup": {
"from": "systems",
"localField": "systemIDs",
"foreignField": "systemID",
"as": "systemIDs"
}
}
])
demo

How to filter with data taken from $lookup

I am currently trying to aggregate list of documents by filtering them with data taken with $lookup
Product.aggregate([
{
$lookup: {
from: "categories",
localField: "category",
foreignField: "_id",
as: "category",
},
},
{ $unwind: "$category" }])
I was hoping adding { $match: { "category.left": {$gte: 3}} }, would be able to get all of the products with categories that's left property is greater than specified, but so far I get nothing. what would be the solution for this?
category documents
{ "_id" : ObjectId("570557d4094a4514fc1291d6"), "left" : 1, "right" : "2" }
{ "_id" : ObjectId("570557d4094a4514fc1291d7"), "left" : 3, "right" : "8"}
{ "_id" : ObjectId("570557d4094a4514fc1291d8"), "left" : 4, "right" : "5"}
{ "_id" : ObjectId("570557d4094a4514fc1291d9"), "left" : 6, "right" : "7" }
product documents
{ "_id" : ObjectId("570557d4094a4514fc129120"), "category": ObjectId("570557d4094a4514fc1291d6") }
{ "_id" : ObjectId("570557d4094a4514fc129121"), "category": ObjectId("570557d4094a4514fc1291d7")}
{ "_id" : ObjectId("570557d4094a4514fc129122"), "category": ObjectId("570557d4094a4514fc1291d8")}
{ "_id" : ObjectId("570557d4094a4514fc129123"), "category": ObjectId("570557d4094a4514fc1291d9") }
I was expecting to get
{ "_id" : ObjectId("570557d4094a4514fc129121"), "category": ObjectId("570557d4094a4514fc1291d7")}
{ "_id" : ObjectId("570557d4094a4514fc129122"), "category": ObjectId("570557d4094a4514fc1291d8")}
{ "_id" : ObjectId("570557d4094a4514fc129123"), "category": ObjectId("570557d4094a4514fc1291d9") }
for my response

How to return the variant values of each product if that product is a variant?

I have a database in MongoDB like this
{"productId" : 1,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "500 GB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
},
{"productId" : 2,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "1 TB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
},
{"productId" : 3,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "500 GB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "2.5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
},
{"productId" : 4,
"isVariant": 1,
"variantId" : 1,
"attributeSet" : [
{
"name" : "Capacity",
"value" : "1 TB",
"id" : 3
},
{
"name" : "Form Factor",
"value" : "2.5 inch",
"id" : 4
},
{
"id" : 5,
"name" : "Memory Components",
"value" : "3D NAND"
}
]
}
Now I want to return data where 500 GB has been in productId 1 and 3
The response should be like this:
variantValues : [{
attributeValue : "500 GB",
data : [
{productId : 1},
{productId : 3}
]},
{
attributeValue : "1 TB",
data : [
{productId : 2},
{productId : 4}
]},
{
attributeValue : "2.5 inch",
data : [
{productId : 3},
{productId : 4}
]},
{
attributeValue : "5 inch",
data : [
{productId : 1},
{productId : 2}
]}]
I have the possible values that I store in another collection for variantPossible values. The values that i am storing are like this:
"VariantValues" : {
"3" : [
"500 GB",
"1 TB"
],
"4" : [
"2.5 inch",
"5 inch"
]
},
I want to return the variant values of each product if that product is a variant with the above format. can anyone help me with this.
You should be able to achieve this using $unwind and $group in your aggregation pipeline. This first flattens each attribute into a single document and on those you can group by the attribute value.
Finally, you can use $project to get the desired name for attributeValue:
db.collection.aggregate([
{
$unwind: "$attributeSet"
},
{
$group: {
_id: "$attributeSet.value",
data: {
"$addToSet": {
productId: "$productId"
}
}
}
},
{
"$project": {
_id: 0,
data: 1,
attributeValue: "$_id"
}
}
])
See this simplifed example on mongoplayground: https://mongoplayground.net/p/VASadZnDedc

Group by Day and Item Total, but Output Item Names as Keys

I've been trying these examples : https://docs.mongodb.com/manual/reference/operator/aggregation/push/ and
https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/
Sample documents:
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-02-03T09:00:00Z") }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-03T09:05:00Z") }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }
{ "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T09:05:00Z") }
{ "_id" : 6, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-15T12:05:10Z") }
{ "_id" : 7, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T14:12:12Z") }
But my need is kind of mixes of them. In push example, the results look like:
{
"_id" : { "day" : 46, "year" : 2014 },
"itemsSold" : [
{ "item" : "abc", "quantity" : 10 },
{ "item" : "xyz", "quantity" : 10 },
{ "item" : "xyz", "quantity" : 5 },
{ "item" : "xyz", "quantity" : 10 }
]
}
{
"_id" : { "day" : 34, "year" : 2014 },
"itemsSold" : [
{ "item" : "jkl", "quantity" : 1 },
{ "item" : "xyz", "quantity" : 5 }
]
}
{
"_id" : { "day" : 1, "year" : 2014 },
"itemsSold" : [ { "item" : "abc", "quantity" : 2 } ]
}
And in $addToSet example, results look like:
{ "_id" : { "day" : 46, "year" : 2014 }, "itemsSold" : [ "xyz", "abc" ] }
{ "_id" : { "day" : 34, "year" : 2014 }, "itemsSold" : [ "xyz", "jkl" ] }
{ "_id" : { "day" : 1, "year" : 2014 }, "itemsSold" : [ "abc" ] }
What I want is going to be like:
{ "_id" : { "day" : 46, "year" : 2014 }, "itemsSold" : { "xyz": 25, "abc": 10 } }
{ "_id" : { "day" : 34, "year" : 2014 }, "itemsSold" : { "xyz": 5, "jkl": 1 ] }
{ "_id" : { "day" : 1, "year" : 2014 }, "itemsSold" : { "abc": 2 } }
Is this possible? If it is, any guide, direction would be helpful.
Based on your data you want two $group stages, in order to first collect per "item" and then to add those item details to an array.
Depending on your MongoDB version you have available is how you process the rest. For MongoDB 3.6 ( of from 3.4.7 ) you can use $arrayToObject in order to reshape the data:
db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$date" },
"dayOfYear": { "$dayOfYear": "$date" },
"item": "$item"
},
"total": { "$sum": "$quantity" }
}},
{ "$group": {
"_id": {
"year": "$_id.year",
"dayOfYear": "$_id.dayOfYear"
},
"itemsSold": { "$push": { "k": "$_id.item", "v": "$total" } }
}},
{ "$sort": { "_id": -1 } },
{ "$addFields": {
"itemsSold": { "$arrayToObject": "$itemsSold" }
}}
])
Or with earlier versions, you can simply post process the results. All the "aggregation" work is done before the last stage anyway:
db.collection.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$date" },
"dayOfYear": { "$dayOfYear": "$date" },
"item": "$item"
},
"total": { "$sum": "$quantity" }
}},
{ "$group": {
"_id": {
"year": "$_id.year",
"dayOfYear": "$_id.dayOfYear"
},
"itemsSold": { "$push": { "k": "$_id.item", "v": "$total" } }
}},
{ "$sort": { "_id": -1 } },
/*
{ "$addFields": {
"itemsSold": { "$arrayToObject": "$itemsSold" }
}}
*/
]).map( d => Object.assign( d,
{
itemsSold: d.itemsSold.reduce((acc,curr) =>
Object.assign(acc, { [curr.k]: curr.v }),
{}
)
}
))
Either way produces the same desired result:
{
"_id" : {
"year" : 2014,
"dayOfYear" : 46
},
"itemsSold" : {
"xyz" : 25,
"abc" : 10
}
}
{
"_id" : {
"year" : 2014,
"dayOfYear" : 34
},
"itemsSold" : {
"jkl" : 1,
"xyz" : 5
}
}
{
"_id" : {
"year" : 2014,
"dayOfYear" : 1
},
"itemsSold" : {
"abc" : 2
}
}
So you can do things with new aggregation features, but really that end result is just "reshaping" which is usually best left to client processing instead.

MongoDB average of embedded Array as extra field

My documents looks like this:
{
"_id" : "53ce85eda2579da8b40c1f0f",
"name" : "Autokino",
"tags" : [
"forMen"
],
"ratings" : [
{ "rating" : 5, "uuid" : "..."},
{ "rating" : 4, "uuid" : "..."},
{ "rating" : 4, "uuid" : "..."},
{ "rating" : 1, "uuid" : "..."},
]
}
Now I need the average of ratings.rating (here it should be 3.5). My query looks like this:
activities.aggregate([
{ $match: { _id: ObjectID(req.params.id) } },
{ $unwind: '$ratings' },
{ $group: {
_id: '$_id',
rating: { $avg: '$ratings.rating'},
}},
]);
It works, but what I get is:
{
"_id" : "53ce85eda2579da8b40c1f0f",
"rating" : 3.5
}
and this is what I need to get:
{
"_id" : "53ce85eda2579da8b40c1f0f",
"name" : "Autokino",
"tags" : [
"forMen"
],
"rating" : 3.5
}
(The original document without ratings array but with rating average)
How can I solve this problem?
Pipeline stages like $group and $project are "absolute" in that only the declared fields are emitted. You need another operator here. $first will do:
activities.aggregate([
{ "$match": { "_id": ObjectID(req.params.id) } },
{ "$unwind": "$ratings" },
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"tags": { "$first": "$tags" },
"rating": { "$avg": "$ratings.rating" },
}},
]);
Since $unwind makes many documents out of the array contents de-normalized, you can use $first here to just take the "first" occurrence of the additional field that you are not otherwise aggregating.
If you are worried about lots of fields to declare this way MongoDB 2.6 does offer the $$ROOT variable. I'ts usage and output are likely not what you really want:
activities.aggregate([
{ "$match": { "_id": ObjectID(req.params.id) } },
{ "$project": {
"_id": "$$ROOT",
"ratings": 1
}},
{ "$unwind": "$ratings" },
{ "$group": {
"_id": "$_id",
"rating": { "$avg": "$ratings.rating" },
}},
]);
This gives you something like:
{
"_id" : {
"_id": "53ce85eda2579da8b40c1f0f",
"name" : "Autokino",
"tags" : [
"forMen"
],
"ratings" : [
{ "rating" : 5, "uuid" : "..."},
{ "rating" : 4, "uuid" : "..."},
{ "rating" : 4, "uuid" : "..."},
{ "rating" : 1, "uuid" : "..."},
]
},
"rating": 3.5
}
This is okay here since grouping by _id is the same as grouping on the whole document. So you can always add a final $project to return to similar state. But there are no wildcards here.
I just went through this whole song and dance as well and ended up having to re-add all my fields back. Not ideal!
So I just found this - much easier: I have a reviews array field, which has a rating property of 1 - 5
{
$addFields: { avg: { $avg: '$reviews.rating'}}
},

Categories

Resources