Aggregate group by array and divide quantity to array length - arrays

Now I want to aggregate schema to group by users in array and divide items field to array length to create average..
This is simple json data ->
[{"users": ["5ea40086fc4b145b489da93d","5e8cb9a4462e45178c4d3405"],"isBuilt": true, "_id": "5eadd43b30f97f342cf663fc", "items": 3, ...},
{"users": ["5e8cb9a4462e45178c4d3405"], "isBuilt": true, "_id": "5ead419081eec52258b67f70", "items": 5, ...}]
And after aggregating with ->
Building.aggregate([
{
$match: {
updatedAt: {
$gte: startDate,
$lte: endDate
},
isBuilt: true
}
},
{
$unwind: "$users"
},
{
$group: {
_id: "$users",
items: {
$sum: '$items'
}
}
},
{
$project: {
user: '$_id',
items: 1,
_id: 0
}
}
])
I got this json ->
[{"items": 3, "user": "5ea40086fc4b145b489da93d"}, {"items": 8, "user": "5e8cb9a4462e45178c4d3405"}]
As you see here I got sum of items. In initial data Users "5ea40086fc4b145b489da93d" and "5e8cb9a4462e45178c4d3405" have 3 items, and user "5e8cb9a4462e45178c4d3405" has 5 items. And after aggregating they count by sum of items, that user "5e8cb9a4462e45178c4d3405" -> 8 items, and user "5ea40086fc4b145b489da93d" -> 3 items... Now I want make average items to users, like if length of array users is 2 or more it will divide items and give sum.. and final json will look like ->
[{"items": 1.5, "user": "5ea40086fc4b145b489da93d"}, {"items": 6.5, "user": "5e8cb9a4462e45178c4d3405"}]
PS if result of item is not integer, result should be rounded to ten

I've solved my problem with aggregation ->
Building.aggregate([
{
$match: {
updatedAt: {
$gte: startDate,
$lte: endDate
},
isBuilt: true
}
},
{
$addFields: {
itemsAvg: {
$divide: ["$items", {$size: "$users"}]
}
}
},
{
$addFields: {
roundedItemsAvg: {
$round: ["$itemsAvg", 1]
}
}
},
{
$unwind: "$users"
},
{
$group: {
_id: "$users",
items: {
$sum: '$roundedItemsAvg'
}
}
},
{
$project: {
user: '$_id',
items: 1,
_id: 0
}
}
])

Related

Mongoose | Find objects inside of an array, that each object has another array of objects to satisfy condition

I have a collection Shops. Each object in Shops collection has an array of Item objects called items.
{
_id: ObjectId(...),
shopName: 'Ice cream Shop',
items: [
<Item>{
itemName: 'Chocolate IC',
availabilities: [
{
city: 'NY',
arrivals: [
{
price: 3.99,
quantityLeft: 0,
date: 'yesterday'
},
{
price: 3.99,
quantityLeft: 40,
date: 'today'
}
]
},
{
city: 'LA',
arrivals: []
}
]
},
<Item>{
itemName: 'Strawberry IC',
availabilities: [
{
city: 'NY',
arrivals: [
{
price: 3.99,
quantityLeft: 0,
date: 'yesterday'
},
]
}
]
},
],
},
... anotherShops
I want to get list of Item objects which has overall quantityLeft more than 0 from a specific shop.
I tried this code to get all items with the name start with "Straw" from a Shop with shopName equal to 'Ice cream Shop':
const items = await Shop.aggregate()
.match({
shopName: 'Ice cream Shop',
})
.project({
items: {
$filter: {
input: "$items",
as: "item",
cond: {
$regexMatch: {
input: "$$item.itemName",
regex: `.*Straw.*`,
},
},
},
},
});
And it works. But I don't know how to sum up all quantityLeft values inside availabilities array of each item, and return only that items that has sum more than 0.
availabilities array can be an empty array [].
The city parameter also needs to be in condition. For example, only Items that are in stock in NY
I need this to get the list of items from a certain shop, and only the items that are still in stock.
Pretty hard.
I came up with this solution. If you have a better solution, please post it.
const shop = await GCShop.aggregate([
{
$match: {
shopName: 'Ice Cream Shop',
},
},
{
$unwind: "$items",
},
{
$unwind: "$items.availabilities",
},
{
$unwind: "$items.availabilities.arrivals",
},
{
$group: {
_id: "$items.id",
items_name: { $first: "$items.name" },
arrivals: {
$push: {
arrival_id: "$items.availabilities.arrivals.arrival_id",
price: "$items.availabilities.arrivals.price",
qtty: "$items.availabilities.arrivals.qtty",
},
},
totalQtty: { $sum: "$items.availabilities.arrivals.qtty" },
},
},
{
$project: {
offer_id: "$_id",
_id: 0,
offer_name: 1,
totalQtty: 1,
arrivals: 1,
},
},
{
$match: {
totalQtty: {
$gt: 0,
},
},
},
]).limit(20);

MongoDB using skip and distinct in a query based on values inside an array

So I have document that is structure like this
_id: ObjectId('62bbe17d8fececa06b91873d')
clubName: 'test'
staff:[
'62bbe47f8fececa06b9187d8'
'624f4b56ab4f5170570cdba3' //IDS of staff members
]
A single staff can be assigned to multiple clubs so what I'm trying to achieve is to get all staff that has been assigned to at least one club and display them on a table on the front end, I followed this solution since distinct and skip can't be used on a single query but it just returned this:
[
{ _id: [ '624f5054ab4f5170570cdd16', '624f5054ab4f5170570cdd16' ] } //staff from club 1,
{ _id: [ '624f5054ab4f5170570cdd16', '624f9194ab4f5170570cded1' ] } //staff from club 2,
{ _id: [ '624f4b56ab4f5170570cdba3' ]} //staff from club 3
]
my desired outcome would be like this:
[ _id : ['624f5054ab4f5170570cdd16', '624f9194ab4f5170570cded1', '624f4b56ab4f5170570cdba3'] ]
here's my query:
const query = this.clubModel.aggregate(
[{ $group: { _id: '$staff' } }, { $skip: 0}, { $limit: 10}],
(err, results) => {
console.log(results);
},
);
the values returned are not distinct at all, is there an operation that can evaluate the value inside an array and make them distinct?
Here's my new query after adding the 'createdAt' field in my document structure:
const query = this.clubModel.aggregate([
{ $sort: { createdAt: -1 } },
{
$unwind: '$drivers',
},
{
$project: {
isActive: true,
},
},
{
$group: {
_id: 'null',
ids: {
$addToSet: '$drivers',
},
},
},
{
$project: {
_id: 0,
},
},
{
$skip: skip,
},
{
$limit: limit,
},
]);
Does this works for you, first UNWIND the staff array, and then group on "_id" as null and add staff values using $addToSet:
db.collection.aggregate([
{
"$unwind": "$staff"
},
{
"$group": {
"_id": "null",
"ids": {
"$addToSet": "$staff"
}
}
},
{
"$project": {
"_id": 0,
}
},
{
$skip: 0
},
{
$limit: 10
}
])
Here's the working link.

How to get specific fields on document MangoDB&Mongoose and aggregate some of the fields?

My data looks like this:
[
{
"_id":"61717cafd351f3ae8b6d205a",
"restaurant":"Hogwarts",
"purchasedAt":"2021-10-20T17:47:40.166Z",
"products":[
{
"name":"Meat Samosa",
"price":3.95,
"quantity":1,
"_id":"61717cafd351f3ae8b6d205b"
},
{
"name":"Pilau Rice",
"price":2.95,
"quantity":1,
"_id":"61717cafd351f3ae8b6d205f"
}
]
},
{
"_id":"61717cb2d351f3ae8b6dd05b",
"restaurant":"Hogwarts",
"purchasedAt":"2021-10-20T03:14:11.111Z",
"products":[
{
"name":"Pilau Rice",
"price":2.95,
"quantity":1,
"_id":"61717cb2d351f3ae8b6dd05d"
}
]
},
]
I am trying to find a query that will get me all the products (no duplicates) and their quantities added up. Notice that the products id are different even when they are the same(same name) Ideally my response would look like this
[
{
name: "Meat Samosa",
price: 3.95,
quantity: 1
},
{
name: "Pilau Rice",
price: 2.95,
quantity: 2
}
]
$project to show required fields
$unwind deconstruct the products array
$group by name and get the first price and count the quantity sum
$project to show required fields
db.collection.aggregate([
{
$project: {
_id: 0,
products: 1
}
},
{ $unwind: "$products" },
{
$group: {
_id: "$products.name",
price: { $first: "$products.price" },
quantity: { $sum: "$products.quantity" }
}
},
{
$project: {
_id: 0,
name: "$_id",
price: 1,
quantity: 1
}
}
])
Playground

How can I group and count by most frequent values in mongoDB

Having a scheme like this
[{
id:1, category:['gourmet','mexican','breakfasts'], food:'eggs&Beans'
},
{
id:2, category:['breakfasts'], food:'waffles'
},
{
id:3, category:['mexican','breakfasts'], food:'burrito'
},]
How could I group and count categories with most frequent value to achieve something like this:
[
{category: 'breakfasts', count:3, foods:['eggs&beans','waffles','burrito']},
{category: 'mexican', count:2, foods:['eggs&beans','burrito']},
{category: 'gourmet', count:1, foods:['eggs&beans']},
]
Demo - https://mongoplayground.net/p/jYUjXUtzvru
db.collection.aggregate([
{ $unwind: "$category" }, // break into individual documents
{
$group: { // group by category
_id: "$category",
count: { $sum: 1 }, // get the count
foods: { $push: "$food" } // add food the array
}
},
{ $sort: { count: -1 } }, // sort by count
{
$project: {
_id: 0,
category: "$_id",
foods: 1,
count: 1
}
}
])

Get frequency for multiple elements in all documents inside a collection mongodb

So heres my problem.
I am new to mongodb and have a collection which documents are saved like this:
{
"_id": {
"$oid": "60626db173b4ca321c02ee3e"
},
"year": "2021",
"name": "Book 1",
"authors": ["Joe, B", "Jessica, K"],
"createdAt": {
"$date": "2021-03-30T00:15:45.859Z"
}
},
{
"_id": {
"$oid": "60626db173b4ca321c02ee4e"
},
"year": "2021",
"authors": ["Carl, B", "Jessica, K"],
"name": "Book 2"
"createdAt": {
"$date": "2021-03-30T00:15:45.859Z"
}
},
I need to get both the frequency of all authors and the years of the books.
The expected result would be something like this (as long as i can get each element frequency it doesn't really matter how the results are returned):
{
"authors": {
"Joe, B": 1,
"Carl, B": 1,
"Jessica, K": 2
},
"year": {
"2021": 2
}
}
I've seen this thread How to count occurence of each value in array? which does the job in one array but i have no idea if its possible to adapt to get the frequency of multiple elements (year, authors) at the same time or how to do it.
I appreciate any help. Thank you.
Demo - https://mongoplayground.net/p/95JtQEThxvV
$group by year $push authors into the array get $sum count of the year occurrence, $unwind into individuals documents.
$group by authors and get $sum count of the author occurrence
$group by null to combine all documents, use $addToSet to push unique values and convert $arrayToObject to get final output in $project
$first
db.collection.aggregate([
{
$group: {
_id: { year: "$year" },
authors: { $push: "$authors" },
yearCount: { $sum: 1 }
}
},
{ $unwind: "$authors" },
{ $unwind: "$authors"},
{
$group: {
_id: { author: "$authors" },
year: { $first: "$_id.year" },
yearCount: { $first: "$yearCount" },
authors: { $push: "$authors" },
authorCount: { $sum: 1 }
}
},
{
"$group": {
_id: null,
years: {
$addToSet: { k: "$year", v: "$yearCount" }
},
authors: {
$addToSet: { k: "$_id.author", v: "$authorCount" }
}
}
},
{
$project: {
_id: 0,
years: { $arrayToObject: "$years" },
authors: { $arrayToObject: "$authors" }
}
}
])
Demo 2 - For author count grouped by year- https://mongoplayground.net/p/_elnjmknroF

Resources