MONGODB - adding a field in collection to a field in Item array - arrays

Suppose I have a document of the following prototype.
{
cust_id: "abc123",
ord_date: ISODate("2012-11-02T17:04:11.102Z"),
status: 'A',
price: 50,
items: [
{ sku: "xxx", qty: 25, price: 1 },
{ sku: "yyy", qty: 25, price: 1 }
]
}
My requirement is to get the total price for SKU "XXX" which is 50 * 25 (Price times quantity). How can I achieve this query's result in MONGODB?

db.YOUR_COLLECTION.aggregate({
$match: {
items.sku: "xxx"
},
$project: {
"product": {$multiply: ["$items.qty","$items.price"]},
_id: 0
}
});

I am able to achieve the solution to this by separating the query into two blocks as below.
var pipeline1 = [
{
{
"$unwind": "$items"
},
$match: {
items.sku: "xxx"
},
$project: {
"product":
{
$multiply: ["$items.qty","$items.price"]
},
_id: 0
}
}];
R = db.tb.aggregate( pipeline );

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

Aggregate group by array and divide quantity to array length

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

Resources