Mongodb - Aggregation and Sum for each object - mongodb

I need to aggregate for each "Product" object from the sales database, and sum "Price" and "Quantity" to make a product classification.
I was able to group by "Products" but I can't sum Price and Quantity to the each object.
{$group: {
_id: '$products.items',
totalprice:{$sum: "$products.items.price"},
}}
Below sample of sales database, where I need to return the sum of the "Price" and "Quantity" fields sold for each "Products".
{
"_id": {
"$oid": "5d753707c0cd851e80da914c"
},
"created_on": {
"$date": {
"$numberLong": "1567962886000"
}
},
"custumer": {
"name": "Teste",
"cep": "teste",
"address": "teste",
"district": "test",
"city": "test",
"numb": "50",
"comple": "test",
"state": "test",
"cpf": "test",
"birth": "30/09/1977",
"email": "test#gmail.com",
"phone": {
"$numberDouble": "1111111111111"
},
"gender": "M",
"portalt": {
"status": "true",
"vendor": "test",
"phone": {
"$numberDouble": "11111111111"
},
"sim": "011111111111",
"salesnumb": "1222222222222222222"
}
},
"payment": {
"method": "Boleto",
"type": "Parcelado",
"installments": "5",
"billing_date": "15"
},
**"products": {
"items": {
"5d515979736802000415a561": {
"item": {
"_id": "5d515979736802000415a561",
"name_produto": "Product 1",
"resumo": "Minutos ilimitados,20GB + 2GB",
"price": "110",
"_image": "images/test.jpg"
},
"quantity": {
"$numberInt": "2"
},
"price": {
"$numberInt": "220"
}
},
"5d515aba736802000415a562": {
"item": {
"_id": "5d515aba736802000415a562",
"name_produto": "Product 2",
"resumo": "Minutos ilimitados,3GB + 1GB",
"price": "80",
"_image": "images/test.jpg"
},
"quantity": {
"$numberInt": "1"
},
"price": {
"$numberInt": "80"
}
},
"5d515dbf736802000415a564": {
"item": {
"_id": "5d515dbf736802000415a564",
"name_produto": "Product 3",
"resumo": "Minutos ilimitados,30GB + 3GB",
"price": "150",
"_image": "images/test.jpg"
},
"quantity": {
"$numberInt": "1"
},
"price": {
"$numberInt": "150"
}
}
},**
"totalItems": {
"$numberInt": "4"
},
"totalPrice": {
"$numberInt": "450"
}
},
"seller": {
"_id": {
"$oid": "5cd086787dc59921bcad94d8"
},
"name": "test"
}
}
I need output something like:
_id:Object
5d515979736802000415a561:{sum_price: 300, sum_quantity: 30 }
5d515aba736802000415a562:{sum_price: 500, sum_quantity: 60 }
5d515dbf736802000415a564:{sum_price: 600, sum_quantity: 70 }
Thanks so much!

So let's go with the OP ask to sum the individual product price and quantity. Stripping away the other fields which are not relevant to the ask, we arrive at something like this:
var r =
[
{
_id:0,
"products": {
"items": {
"5d515979736802000415a561": {
"quantity": 2,
"price": 220,
},
"5d515aba736802000415a562": {
"quantity": 1,
"price": 80
}
}
}
}
, {
_id:1,
"products": {
"items": {
"5d515979736802000415a561": { // deliberately same id as doc above but different quantity and price
"quantity": 3,
"price": 330
},
"5d515aba736802000415a562": { // same
"quantity": 2,
"price": 160
},
"5d515979736802000415ZZZZ": { // different than above; adds "third item"
"quantity": 4,
"price": 200
}
}
}
}
];
Note that the whole inner item field is basically not important, not the least of which it only contains the unit price, not the total price (amount) and quantity per product.
"5d515dbf736802000415a564": {
"item": {
"_id": "5d515dbf736802000415a564",
// etc
So now we employ the $objectToArray to turn the keys into rvals. That gives us something we can $group on, and so here is a solution:
db.foo.aggregate([
{$project: {X: {$objectToArray: "$products.items"}}}
,{$unwind: "$X"}
,{$group: {_id: "$X.k", tot_q: {$sum:"$X.v.quantity"}, tot_amt: {$sum:"$X.v.price"}} }
]);
which given the input above yields:
{ "_id" : "5d515979736802000415ZZZZ", "tot_q" : 4, "tot_amt" : 200 }
{ "_id" : "5d515aba736802000415a562", "tot_q" : 3, "tot_amt" : 240 }
{ "_id" : "5d515979736802000415a561", "tot_q" : 5, "tot_amt" : 550 }

Related

Remove unwanted key on nested unique keys MongoDB

I have this kind of mongodb document example
"data": {
"2023-02-01": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
"2023-02-02": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
"2023-02-03": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
}
I have list of mapped ID on my aystem, it should be like
ids = [123]
I want to remove the key that not in the list (ids) from the document, started from a specific date (today/"2023-02-02"), the date always updated and so the ID, my expected result is
"data": {
"2023-02-01": {
"123": {
"price": 100,
},
"234": {
"price": 100,
},
},
"2023-02-02": {
"123": {
"price": 100,
},
},
"2023-02-03": {
"123": {
"price": 100,
},
},
}
Could I achieve that on MongoDB aggregation? I'm using pymongo
Following the discussion in comments, if refactoring the schema is an option, you can achieve what you need in very simple query.
db.collection.update({
"date": {
$gte: ISODate("2023-02-02")
}
},
[
{
$set: {
value: {
$filter: {
input: "$value",
as: "v",
cond: {
$in: [
"$$v.key",
[
"123"
]
]
}
}
}
}
}
],
{
multi: true
})
Mongo Playground
The schema I am proposing:
[
{
"date": ISODate("2023-02-01"),
"value": [
{
"key": "123",
"price": 100
},
{
"key": "234",
"price": 100
}
]
},
{
"date": ISODate("2023-02-02"),
"value": [
{
"key": "123",
"price": 100
},
{
"key": "234",
"price": 100
}
]
},
{
"date": ISODate("2023-02-03"),
"value": [
{
"key": "123",
"price": 100
},
{
"key": "234",
"price": 100
}
]
}
]
You can see there is a few things:
avoided using dynamic value as field name
formatted date as proper date objects
avoided highly nesting arrays/objects

MongoDB Select By Group along with that Count Unique match exclude array and object fields Get data sort by latest objects

I have a collection where from the backend user can input multiple same name bikes but with different registration number but in front-End I want them to be grouped by matching the same name but as user updates separately display image changes but I want only one display image as it is 1 vehicle
provided there is a node created I will implement it we can sort it by the latest and take the price and image of it
Activa -2 Count
KTM -1 Count
but there is a catch.
Activa 2 bikes but I want only count 2 and the price as it is the same in an array I want only 1 and the same applies to displayimage here display image file path is different but I want the latest one only Sharing data below
Data:
[
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62e69ee3edfe4d0f3cb4994a",
"bikename": "KTM",
"bikenumber": "KA05HM2034",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-2020-honda-city-exterior-8-1659281111883.jpg",
"file_path": "https://www.example.com/images/upload/bike-2020-honda-city-exterior-8-1659281111883.jpg",
"idx": 1
}
},
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62dba8418ef8f51f454ed757",
"bikename": "Activa",
"bikenumber": "KA05HM2033",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658562557459.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658562557459.jpg",
"idx": 0
}
},
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62d7ff7e70b9ab38c6ab0cb1",
"bikename": "Activa",
"bikenumber": "KA05HM2223",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"afterfreekmprice": 22,
"descreption": "Activa",
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658322798414.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658322798414.jpg",
"idx": 0
}
}
]
Expected:
[
{
"_id":{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62dba8418ef8f51f454ed757",
"bikename": "Activa",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658562557459.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658562557459.jpg",
"idx": 0
}
},
"count": 2
},
{
"_id":{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62e69ee3edfe4d0f3cb4994a",
"bikename": "KTM",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-2020-honda-city-exterior-8-1659281111883.jpg",
"file_path": "https://www.example.com/images/upload/bike-2020-honda-city-exterior-8-1659281111883.jpg",
"idx": 1
}
}
"count": 1
}
]
You can use the aggregation pipeline,
$sort by _id in descending order
$group by bikename and get the first root document that is latest one in root and count total documents in count
$project to show required documents
db.collection.aggregate([
{ $sort: { _id: -1 } },
{
$group: {
_id: "$bikename",
root: { $first: "$$ROOT" },
count: { $sum: 1 }
}
},
{
$project: {
_id: "$root",
count: 1
}
}
])
Playground
You can use $group for this:
db.collection.aggregate([
{$group: {
_id: "$bikename",
count: {$sum: 1},
data: {$first: "$$ROOT"}
}
},
{$set: {"data.count": "$count"}},
{$replaceRoot: {newRoot: "$data"}}
])
See how it works on the playground example

How to sum values using unwind in MongoDB with Spring Data

When using unwind("items") each item produces a duplicate line. This leads to revenue being counted as many times as there are items.
Example: Someone buys 1 item of A and 1 item of B, resulting in a cummulative value of 10. Unwind now inserts a row for each item, leading to a sum(cummulative) of 20.
I tried grouping the elements directly after unwind but have not managed to get it to work properly.
How can I sum each array element without duplication every other value?
I have this data structure
{
"_id": {
"$binary": "VEE6CsjHPvjzS2JYso7mnQ==",
"$type": "3"
},
"sequentialId": {
"$numberLong": "1"
},
"date": "2022-02-04",
"invoiceTotal": {
"$numberDecimal": "9.85"
},
"vatTotal": {
"$numberDecimal": "0"
},
"vatPercentage": {
"$numberDecimal": "19.00"
},
"invoiceNumber": "1111111",
"type": "ELEKTRONISCH",
"aktivkonto": 2800,
"passivkonto": 5200,
"buyerEmail": "",
"username": "",
"shop": "",
"externalId": "",
"shipped": false,
"actualShippingCost": {
"$numberDecimal": "1"
},
"filename": "",
"isReported": false,
"deliveryCostTotal": {
"$numberDecimal": "4.35"
},
"items": [
{
"lineItemId": "",
"amount": "1",
"sku": "A123123",
"title": "",
"priceTotal": {
"$numberDecimal": "4.50"
},
"vatTotal": {
"$numberDecimal": "0"
},
"hardwareCostPerPiece": {
"$numberDecimal": "0.22"
},
"hardwareCostTotal": {
"$numberDecimal": "0.22"
}
},
{
"lineItemId": "",
"amount": "1",
"sku": "B212312",
"title": "",
"priceTotal": {
"$numberDecimal": "1.00"
},
"vatTotal": {
"$numberDecimal": "0"
},
"hardwareCostPerPiece": {
"$numberDecimal": "0.22"
},
"hardwareCostTotal": {
"$numberDecimal": "0.22"
}
}
],
"packagingCost": {
"$numberDecimal": "0.15"
},
"hasInvoiceSent": false,
"tenant": "you!",
"createdAt": {
"$date": "2022-02-04T15:23:40.716Z"
},
"modifiedAt": {
"$date": "2022-02-04T15:23:40.716Z"
},
"_class": "_.RevenueEntity"
}
and this query
fun sumAllByWeeklyAndTenant(tenant: String): Flux<DashboardRevenue> {
val aggregation = newAggregation(
match(findByTenant(tenant)),
unwind("items"),
group("invoiceNumber")
.sum("items.hardwareCostTotal").`as`("hardwareCostTotal"),
project()
.andExpression("year(createdAt)").`as`("year")
.andExpression("week(createdAt)").`as`("week")
.andInclude(bind("hardwareCostTotal", "items.hardwareCostTotal"))
.andInclude(
"invoiceTotal",
"vatTotal",
"actualShippingCost",
"packagingCost",
"marketplaceFeesTotal",
"tenant"
),
group("year", "week", "tenant")
.sum("invoiceTotal").`as`("umsatz")
.sum("actualShippingCost").`as`("portokosten")
.sum("packagingCost").`as`("verpackung")
.sum("marketplaceFeesTotal").`as`("marketplaceFees")
.sum("hardwareCostTotal").`as`("hardwareCost")
.sum("vatTotal").`as`("vatTotal")
.count().`as`("numberOfInvoices"),
sort(Sort.Direction.DESC, "year", "week"),
limit(8),
sort(Sort.Direction.ASC, "year", "week")
)
return reactiveMongoTemplate
.aggregate(aggregation, "revenue", DashboardRevenue::class.java)
.toFlux()
}
Using the data above the query results in
[
{
"_id": {
"year": 2022,
"month": 2,
"week": 0,
"tenant": "einsupershop"
},
"umsatz": 19.70,
"portokosten": 2,
"verpackung": 0.30,
"marketplaceFees": 3.42,
"hardwareCost": 0.22,
"vatTotal": 0,
"numberOfInvoices": 2
}
]
Where the expected value is "invoiceTotal": { "$numberDecimal": "9.85" }

how to filter databse from mongodb between to string formatted date

[
{ "item": "journal", "qty": 25,"date":"1/1/2016", "status": "A" },
{ "item": "notebook", "qty": 50,"date":"10/1/2016", "status": "A" },
{ "item": "paper", "qty": 100,"date":"20/1/2016", "status": "D" },
{ "item": "planner", "qty": 75,"date":"1/2/2016", "status": "D" },
{ "item": "postcard", "qty": 45,"date":"10/2/2016", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"20/5/2016", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"30/7/2016", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"2/3/2017", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"5/5/2017", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"6/5/2017", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"8/10/2017", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"11/10/2017", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"12/11/2017", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"4/3/2018", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"5/6/2018", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"6/7/2018", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"7/7/2018", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"17/11/2018", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"19/12/2018", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"5/1/2019", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"7/1/2019", "status": "A" },
{ "item": "postcard", "qty": 45,"date":"14/3/2019", "status": "A" }
]
Above Is my database structure.
db.lichi.find({date: {$gte : '1/1/2016', $lt : '1/1/2019'}})
Here is the query i am trying to fetch data from database.
Here in database i have string formatted date.
I am tring to fetch using above way that, didn't work.
gave result
{ "item": "journal", "qty": 25,"date":"1/1/2016", "status": "A" },
{ "item": "notebook", "qty": 50,"date":"10/1/2016", "status": "A" }
Only two documents.
PLease have a look.
Storing dates as string is not the best idea since in case like this you have to compare strings instead of dates. If for some reason you have to keep date as string then you can convert it in your query using $dateFromString and then apply your filtering condition:
db.lichi.aggregate([
{
$addFields: {
date: {
$dateFromString: {
dateString: "$date",
format: "%d/%m/%Y"
}
}
}
},
{
$match: {
date: { $gte: ISODate("2016-01-01T00:00:00Z"), $lt: ISODate("2019-01-01T00:00:00Z") }
}
}
])

MongoDB Aggregation Error Returning wrong result

I have my json object like this
{
"_id": "5c2e811154855c0012308f00",
"__pclass": "QXRzXFByb2plY3RcTW9kZWxcUHJvamVjdA==",
"id": 44328,
"name": "Test project via postman2//2",
"address": "some random address",
"area": null,
"bidDate": null,
"building": {
"name": "Health Care Facilities",
"type": "Dental Clinic"
},
"collaborators": [],
"createdBy": {
"user": {
"id": 7662036,
"name": "Someone Here"
},
"firm": {
"id": 2520967,
"type": "ATS"
}
},
"createdDate": "2019-01-03T21:39:29Z",
"customers": [],
"doneBy": null,
"file": null,
"firm": {
"id": 1,
"name": "MyFirm"
},
"leadSource": {
"name": "dontknow",
"number": "93794497"
},
"location": {
"id": null,
"city": {
"id": 567,
"name": "Bahamas"
},
"country": {
"id": 38,
"name": "Canada"
},
"province": {
"id": 7,
"name": "British Columbia"
}
},
"modifiedBy": null,
"modifiedDate": null,
"projectPhase": {
"id": 1,
"name": "pre-design"
},
"quotes": [{
"id": 19,
"opportunityValues": {
"Key1": 100,
"Key2 Key2": 100,
"Key3 Key3 Key3": 200,
}
}],
"specForecast": [],
"specIds": [],
"tags": [],
"valuation": "something"
}
I am trying to aggregate using this query in MongoDB. My aggregation key is 4 level deep and also contains spaces. On all online examples shows me the aggregation at the first level. Looking to the online codes, I tried to re-iterate the same with my 4th level deep key.
db.mydata.aggregate([
{$match: {"id": 44328 } } ,
{$group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2"},
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3"}
}
}
]);
This should return
_id totalKey2 totalKey3
0 19 100 300
But it is returning
_id totalKey2 totalKey3
0 19 0 0
What am I doing Wrong?
Although it's not recommended to use space in field names in Mongo, it works as expected.
The problem with your query is that "quotes" is an array and you should first unwind it before grouping it.
This works as expected:
db.mydata.aggregate([
{ $match: { "id": 44328 } } ,
{ $unwind: "$quotes" },
{ $group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2" },
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3" } }
}
]);