sum up value in all subdocument in mongoose - mongodb

I am trying to sum up the value of a field in a subdocument but I get getting 0 are my summation.
Here is the DB.
[
{
"card": "master",
"payment": [
{
"amount": 220,
"status": "paid"
},
{
"amount": 100,
"status": "paid"
}
],
},
{
"card": "master",
"payment": [
{
"amount": 100,
"status": "paid"
},
{
"amount": 130,
"status": "paid"
}
],
},
]
Here is my aggregation.
db.collection.aggregate([
{
$match: {
card: "master",
},
},
{
$group: {
_id: null,
amount: {
$sum: "$payment.amount"
},
},
},
])
The response
[
{
"_id": null,
"amount": 0
}
]
I expect the amount to be 550 and not 0. Please what is wrong with my aggregation.

Related

How to do mongodb inner join and grouping

// orders
[
{
"id": 1,
"orderName": "a",
"seqId": 100,
"etc": [],
"desc": [],
},
{
"id": 2,
"orderName": "b",
"seqId": 200,
"etc": [],
"desc": []
},
{
"id": 3,
"orderName": "c",
"seqId": 100,
},
]
// goods collection
[
{
"id": 1,
"title": "example1",
"items": [
{
"id": 10,
"details": [
{
"id": 100
},
{
"id": 101,
}
]
},
{
"id": 20,
"details": [
{
"id": 102,
},
{
"id": 103,
}
]
},
]
},
[
{
"id": 2,
"title": "example2",
"items": [
{
"id": 30,
"details": [
{
"id": 200
},
{
"id": 201
}
]
},
{
"id": 40,
"details": [
{
"id": 202
},
{
"id": 203
}
]
},
]
},
]
When the etc field and desc field arrays of the orders collection are empty, or the non-empty document's seqId field value and the goods collection's "goods.details.id field value are the same.
I want to express the sum operation based on the title of the product collection and the sum if it is not empty.
{example1: 1, total: 2}
{example2: 1, total: 1}
For example, "example1" and "example2" represent the sum of the cases where the etc and desc field arrays are empty (the title of the goods collection), and the total represents the total regardless of whether the array is empty or not.
If so, it should be marked aboveas:
Following our discussion here, we can remove the early filtering for the 2 empty arrays and move it to a conditional sum at the $group stage.
db.orders.aggregate([
{
"$lookup": {
"from": "goods",
"localField": "seqId",
"foreignField": "items.details.id",
"as": "goodsLookup"
}
},
{
"$unwind": "$goodsLookup"
},
{
$group: {
_id: "$goodsLookup.title",
emptySum: {
$sum: {
"$cond": {
"if": {
$and: [
{
$eq: [
"$desc",
[]
]
},
{
$eq: [
"$etc",
[]
]
}
]
},
"then": 1,
"else": 0
}
}
},
total: {
$sum: 1
}
}
}
])
Mongo Playground

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 the balance and income using MongoDB?

I have a document exchange_history like:
[
{
"_id": {"$oid": "611fd7059ec255088af94e4d"},
"amount": {"$numberDecimal": 1.2},
"exchange_time": {"$date": "2021-08-20T16:02:00.448Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "611fed2a31ef85394c4f9b10"},
"amount": {"$numberDecimal": 1.02},
"exchange_time": {"$date": "2021-08-20T16:02:00.448Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "6120b47d4cf35f7f70312a71"},
"amount": {"$numberDecimal": -1},
"exchange_time": {"$date": "2021-08-21T08:08:16.397Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "6120b4834cf35f7f70312a72"},
"amount": {"$numberDecimal": -2},
"exchange_time": {"$date": "2021-08-21T08:08:16.397Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "6120b4e94cf35f7f70312a73"},
"amount": {"$numberDecimal": 10},
"exchange_time": {"$date": "2021-08-21T08:08:16.397Z"},
"user_id": "611df4d9ece358586d212d91"
}
]
How to calculate the balance (sum amount) and income (sum amount where amount is greater than 0) by user_id at the same time using MongoDB?
My MongoDB version is 4.4. Pls help me!
Try this ...
Document :
[
{
"_id": {
"oid": "611fd7059ec255088af94e4d"
},
"amount": {
"numberDecimal": 1.2
},
"exchange_time": {
"date": "2021-08-20T16:02:00.448Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "611fed2a31ef85394c4f9b10"
},
"amount": {
"numberDecimal": 1.02
},
"exchange_time": {
"date": "2021-08-20T16:02:00.448Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "6120b47d4cf35f7f70312a71"
},
"amount": {
"numberDecimal": -1
},
"exchange_time": {
"date": "2021-08-21T08:08:16.397Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "6120b4834cf35f7f70312a72"
},
"amount": {
"numberDecimal": -2
},
"exchange_time": {
"date": "2021-08-21T08:08:16.397Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "6120b4e94cf35f7f70312a73"
},
"amount": {
"numberDecimal": 10
},
"exchange_time": {
"date": "2021-08-21T08:08:16.397Z"
},
"user_id": "611df4d9ece358586d212d91"
}
]
Code :
db.collection.aggregate([
{
$group: {
_id: "$user_id",
balance: {
$sum: "$amount.numberDecimal"
},
income: {
$sum: {
$cond: [
{
$gt: [
"$amount.numberDecimal",
0
]
},
"$amount.numberDecimal",
0
]
}
}
}
},
])
And Output:
[
{
"_id": "611df4d9ece358586d212d91",
"balance": 10,
"income": 10
},
{
"_id": "60ef435299b1760752cbafe1",
"balance": -0.78,
"income": 2.2199999999999998
}
]
Try mongoplayground

MongoDB count value of attribute and group by month and year

I have documents in my mongodb and i try to count every same description i have and classify to month they created.
I want to return an array that include array of objects that includes month number with sub array of description value with the count.
I want to be able to choose which description value to count and choose by what year he will show me the data
for example this is my documents:
[
{
"username": "ron",
"skills": [
{
"rank": "high",
list: [
{
"subject": "Football"
},
{
"subject": "Swim"
}
]
},
{
"rank": "low",
list: [
{
"subject": "Baseball"
},
]
}
],
"duration": 0,
"date": ISODate("2021-07-24T12:05:27.127Z"),
"createdAt": ISODate("2021-07-24T12:05:49.985Z"),
"updatedAt": ISODate("2021-07-24T12:05:49.985Z"),
"__v": 0
},
{
"username": "john",
"skills": [
{
"rank": "low",
list: [
{
"subject": "Football"
},
]
}
],
"duration": 0,
"date": ISODate("2021-07-25T12:05:53.000Z"),
"createdAt": ISODate("2021-07-24T12:05:59.249Z"),
"updatedAt": ISODate("2021-07-24T12:05:59.249Z"),
"__v": 0
},
{
"username": "david",
"skills": [
{
"rank": "high",
list: [
{
"subject": "Football"
},
{
"subject": "Baseball"
}
]
},
{
"rank": "low",
list: [
{
"subject": "Swim"
},
]
}
],
"duration": 0,
"date": ISODate("2021-08-26T12:06:13.000Z"),
"createdAt": ISODate("2021-07-24T12:06:21.328Z"),
"updatedAt": ISODate("2021-07-24T12:06:21.328Z"),
"__v": 0
},
{
"username": "david",
"skills": [
{
"request": "high",
list: [
{
"subject": "Swim"
},
]
},
{
"request": "low",
list: [
{
"subject": "Football"
},
{
"subject": "Baseball"
},
]
}
],
"duration": 0,
"date": ISODate("2021-01-21T13:07:50.000Z"),
"createdAt": ISODate("2021-07-24T12:08:05.552Z"),
"updatedAt": ISODate("2021-07-24T12:14:51.285Z"),
"__v": 0
},
{
"username": "david",
"skills": [
{
"rank": "high",
list: [
{
"subject": "Football"
},
]
},
],
"duration": 0,
"date": ISODate("2022-01-21T13:07:50.000Z"),
"createdAt": ISODate("2022-07-24T12:08:05.552Z"),
"updatedAt": ISODate("2022-07-24T12:14:51.285Z"),
"__v": 0
}
]
The result I expect to get if i want to get match only the description that equal to "Football" or "Baseball":
{
[ {"month_number":7,"result":[{"description":'Football',"count":2},{"description":"Baseball","count":1}]},
{"month_number":8,"result":[{"description":'Football',"count":1}]}]
}
I'm new to mongodb ... so far I have been able to count how many there are of each value and display only the values I want but I do not know how to classify it into months depending on the year I choose.
I tried this:
db.exercises.aggregate([{$match:{ $and:[{description:{$in: ["Baseball","Football"]}}]}} ,{$group:{_id:"$description",count:{$sum:1}}}])
and this
db.exercises.aggregate(
[
{
$project:
{
_id: 0,
year: { $year: "$date" },
month: { $month: "$date" },
description:"$description"
}
},
{
$match:{$and:[{description:{$in: ["Baseball","Football","Swim"]},year:2021}]}
},
{$group:{_id:"$description" , count:{$sum:1}}}
]
)
You need add 1 more grouping i.e. by month.
Working playground
db.collection.aggregate([
{
$project: {
_id: 0,
year: {
$year: "$date"
},
month: {
$month: "$date"
},
description: "$description"
}
},
{
$match: {
$and: [
{
description: {
$in: [
"Baseball",
"Football",
"Swim"
]
},
year: 2021
}
]
}
},
{
$group: {
_id: {
description: "$description",
month: "$month"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: {
month_number: "$_id.month"
},
results: {
$push: {
description: "$_id.description",
count: "$count"
}
}
}
},
{
$project: {
_id: 0,
month_number: "$_id.month_number",
results: 1
}
}
])