How do I sum up two fields in the Mongoshell? - mongodb-query

I have a find statement in a store database that looks like this:
db.Purchases.find( {}, { store: 1, total: 1, _id: 0 } ).sort( { "store" : 1} ) =
{ "store" : DBRef("Location", ObjectId("5dae22702486f7d89ba7633c")), "total" : "$1500" }
{ "store" : DBRef("Location", ObjectId("5dae227f2486f7d89ba7633d")), "total" : "$156.88" }
{ "store" : DBRef("Location", ObjectId("5dae22992486f7d89ba7633e")), "total" : "$1510" }
{ "store" : DBRef("Location", ObjectId("5dae22992486f7d89ba7633e")), "total" : "$3000" }
{ "store" : DBRef("Location", ObjectId("5dae22cd2486f7d89ba76340")), "total" : "$156.88" }
I need to sum the totals from output 3 and 4 (i.e $1510 and $3000) and display the result as one line in the output along all the other outputs. How do I do this?

Try this:
db.Purchases.aggregate(
[
{
$group:
{
_id: "$store",
totalAmount: { $sum: "$total"}
}
}
]
)
Observation: total must be a numeric type in MongoDB.

Related

Mongo Group By query

I have data stored in a Mongo collection that is structured like this:
{
"numberAtPending" : 3,
"numberAtInProgress" : 5,
"numberAtCancelled" : 1,
"numberAtShipped" : 50,
"timeOfRequest" : ISODate("2022-01-10T12:52:15.813Z"),
"requestingSupplier" : "SUPPLIER_1",
},
{
"numberAtPending" : 5,
"numberAtInProgress" : 3,
"numberAtCancelled" : 4,
"numberAtShipped" : 35,
"timeOfRequest" : ISODate("2022-01-15T09:11:02.992Z"),
"requestingSupplier" : "SUPPLIER_1",
},
{
"numberAtPending" : 12,
"numberAtInProgress" : 3,
"numberAtCancelled" : 1,
"numberAtShipped" : 21,
"timeOfRequest" : ISODate("2022-01-10T14:21:55.221Z"),
"requestingSupplier" : "SUPPLIER_2",
}
I wish to construct a query that would let me sum up each count in each entry and group by requestingSupplier.
For example, I would like to answer the question, for the month of January '22, what was the sum of each entity and get a response similar to:-
"TotalNumberAtPending": 300
"TotalNumberAtInProgress" : 150,
"TotalNumberAtCancelled" : 70,
"TotalNumberAtShipped" : 400
"Supplier" : "SUPPLIER_1",
"TotalNumberAtPending": 230
"TotalNumberAtInProgress" : 110,
"TotalNumberAtCancelled" : 40,
"TotalNumberAtShipped" : 300
"Supplier" : "SUPPLIER_2",
Any help most appreciated!
thanks and regards
You can try this query (also I'm assuming the output you show is an example and not the real values because I don't know from where can you get 300, 150, 400...)
So, try this:
You have two options to match values in the range of two dates. If you want to input the name of the month and the year you can try something like:
Use $expr and $eq with $year and $month. And then you can use as input exaclty the number of the desired month or year.
{
"$match": {
"$expr": {
"$and": [
{
"$eq": [
{
"$month": "$timeOfRequest"
},
1
]
},
{
"$eq": [
{
"$year": "$timeOfRequest"
},
2022
]
}
]
}
}
}
Or you can match by the date range. If you want to get all documents from 01-2022 you can use this $match stage where the range is from the first second of January (equal) to the first second of February (not equal, so i.e. is the last second of January).
{
"$match": {
"timeOfRequest": {
"$gte": ISODate("2022-01-01T00:00:00Z"),
"$lt": ISODate("2022-02-01T00:00:00Z")
}
}
}
So, with the filter done you only need to use $group like this to generate the desired fields values.
{
"$group": {
"_id": "$requestingSupplier",
"TotalNumberAtPending": {
"$sum": "$numberAtPending"
},
"TotalNumberAtInProgress": {
"$sum": "$numberAtInProgress"
},
"TotalNumberAtCancelled": {
"$sum": "$numberAtCancelled"
},
"TotalNumberAtShipped": {
"$sum": "$numberAtShipped"
},
"Supplier": {
"$first": "$requestingSupplier"
}
}
}
Example here and here

mongodb aggregate distinct count

Realise this topic has been asked many times - but the advice hasn't helped me solve this problem.
The following query is trying to determine the presence of sales on a given weekday using ISODay. Because the query will be run at the start of the month, I need to know how many occurrences of the specific ISOday occur in the month.
var query = { eventType: 'Sale', site : 4, tank: 1, txnDate : { "$gt" : new Date('2018-08-01T00:00:00') } };
db.tankevent.aggregate([
{ $match: query },
{ $project : {
isoDay: { $isoDayOfWeek: "$txnDate" },
dayDate: { $dateToString: { format: "%d", date:"$txnDate" } }
}
},
{ $group:
{ _id : { isoday: "$isoDay", dday: "$dayDate" }, count: { "$sum" : 1 } }
},
{ $sort: { "_id.isoday": 1, "_id.dday": 1 } }
])
provides the following output
/* 1 */
{
"_id" : {
"isoday" : 1,
"dday" : "06"
},
"count" : 62.0
}
/* 2 */
{
"_id" : {
"isoday" : 1,
"dday" : "13"
},
"count" : 69.0
}
/* 3 */
{
"_id" : {
"isoday" : 1,
"dday" : "20"
},
"count" : 72.0
}
/* 4 */
{
"_id" : {
"isoday" : 2,
"dday" : "07"
},
"count" : 75.0
}
I am trying to have "count" represent the number of unique "dday" records - so using the output above, I want count to be "3" for isoDay = 1. At the moment count is reporting number of sales events that occurred for the group combination
All you need to do is have the grouping twice.
db.tankevent.aggregate([
{ $match: query },
{ $project : {
isoDay: { $isoDayOfWeek: "$txnDate" },
dayDate: { $dateToString: { format: "%d", date:"$txnDate" } }
}
},
{ $group:
{ _id : { isoday: "$isoDay", dday: "$dayDate" }, count: { "$sum" : 1 } }
},
{ $project : {
isoDay_Final: "$_id.isoday"
}
},
{ $group:
{ _id : "$isoDay_Final", count: { "$sum" : 1 } }
},
{ $sort: { "_id": 1 } }
])

how to count number of keys in embedded mongodb document

I have a mongodb query: (Give me the settings where account='test')
db.collection_name.find({"account" : "test1"}, {settings : 1}).pretty();
where I get the following sample output:
{
"_id" : ObjectId("49830ede4bz08bc0b495f123"),
"settings" : {
"clusterData" : {
"us-south-1" : "cluster1",
"us-east-1" : "cluster2"
},
},
What I'm looking for now, is to give me the account where the clusterData has more than 1 key.
I'm only interested in listing those accounts with (2) or more keys.
I've tried this: (but this doesn't work)
db.collection_name.find({'settings.clusterData.1': {$exists: true}}, {account : 1}).pretty();
Is this possible to do with the current data structure? I don't have the option to redesign this schema.
Your clusterData field is not an array which is why you cannot just filter the number of elements it has. There is a way, though, to get what you want via the aggregation framework. Try this:
db.collection_name.aggregate({
$match: {
"account" : "test1"
}
}, {
$project: {
"settingsAsArraySize": { $size: { $objectToArray: "$settings.clusterData" } },
"settings.clusterData": 1
}
}, {
$match: {
"settingsAsArraySize": { $gt: 1 }
}
}, {
$project: {
"_id": 0,
"settings.clusterData": 1
}
}).pretty();

db.find vs db.aggregation to select nested array Object

I'v tried to perform the following query :
db.getCollection('fxh').find({"username": "user1", "pf.acc.accnbr" : 915177},{userid: true, "pf.pfid": true, "pf.acc.accid":true})
and my collection is the following :
{
"_id" : ObjectId("5932fd8f381d4c0a7de21942"),
"userid" : 1496513894,
"username" : "user1",
"email" : "user1#gmail.com",
"fullname" : "User 1",
"pf" : {
"acc" : [
{
"cyc" : [
{
"det" : {
"status" : "New",
"dcycid" : 1496513941
},
"status" : "New",
"name" : "QPT202017_M1",
"cycid" : 1496513940
}
],
"status" : "New",
"accnbr" : 915177,
"accid" : 1496513939
},
{
"cyc" : [
{
"det" : {
"status" : "New",
"dcycid" : 1496552643
},
"status" : "New",
"name" : "QPT202017_S8",
"cycid" : 1496552642
}
],
"status" : "New",
"accnbr" : 73497,
"accid" : 1496552641
}
],
"pfid" : 1496513935,
},
"lastupdate" : ISODate("2017-06-03T18:18:55.080Z"),
"__v" : 0
}
When I execute the query the result is the following :
{
"_id" : ObjectId("5932fd8f381d4c0a7de21942"),
"userid" : 1496513894,
"portfolio" : {
"acc" : [
{
"accid" : 1496513939
},
{
"accid" : 1496552641
}
],
"pfid" : 1496513935
}
}
And my problem is that I need to see only the concerned accid and the result returns the all accid !.
Any idea how just to return the selected accid of accnbr ?
NB : I have also tried to add $ sign at the end of my query , it
selects the right acc but it returns the all objects or I need just
only ONE returned object.
On 6/5/17
I also used the aggregate command instead of find and it get result by using this :
db.getCollection('fxh').aggregate([ { $unwind : "$pf.acc"} , { $match : {"username":"adh1", "pf.acc.accbr": 915177 } }, {$project : {_id:0, accid: "$pf.acc.accid"}}])
But could NOT get a lower level result, when I ran this :
db.getCollection('fxh').aggregate([ { $unwind : "$pf.acc.cyc"} , { $match : {"username":"adh1", "pf.acc.accbr": 915177, "pf.acc.cyc.name": "QPT202017_M1" } }, {$project : {_id:0, cycid: "$pf.acc.cyc.cycid"}}])
Any idea ?
You can try the below aggregation pipeline.
The idea is to $unwind one nested level at a time, starting from the outermost to the innermost.
For each nested level unwinding, you can apply the$match to limit the documents and continue till you have the desired shape.
You can $group it together at the end to get back to the original shape.
db.getCollection('fxh').aggregate([
{ $match : {"username":"adh1"} },
{ $unwind : "$pf.acc"} ,
{ $match : {"pf.acc.accbr": 915177 } },
{ $unwind : "$pf.acc.cyc"},
{ $match : {"pf.acc.cyc.name": "QPT202017_M1" } },
{$project : {_id:0, accid: "$pf.acc.accid", cycid: "$pf.acc.cyc.cycid"}}])

SQL Where clause equivalent for Elastic Search

I am trying to create a aggregate results in elastic search but filter option is not working for me.
I can aggregate data without filter e.g.
select name , material ,sum(price)
from products group by name , material
curl -XGET 'http://localhost:9200/products/_search?pretty=true' -d'
{
"aggs" : {
"product" : {
"terms" : {
"field" : "name"
},
"aggs" : {
"material" : {
"terms" : {
"field" : "material"
},
"aggs" : {
"sum_price" : {
"sum" : {
"field" : "price"
}
}
}
}
}
}
},
"size" : 0
}'
but I am facing problems to write equivalent DSL query of :
select name , material ,sum(price)
from products
where material = "wood"
group by name , material
Should be something like this:
{
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"term": {
"material": "wood"
}
}
}
},
"aggs" : {
"product" : {
"terms" : {
"field" : "name"
},
"aggs" : {
"material" : {
"terms" : {
"field" : "material"
},
"aggs" : {
"sum_price" : {
"sum" : {
"field" : "price"
}
}
}
}
}
}
},
"size" : 0
}
Use a filter if you know the exact value and do not need a match, else use a match query instead of the filtered query.
You can use match
{
"query": {
"bool": {
"must": [
{
"match": {
"material": "wood"
}
}
],
"filter": [
{
"match_all": {}
},
]
}
},
"aggs" : {
"product" : {
"terms" : {
"field" : "name"
},
"aggs" : {
"material" : {
"terms" : {
"field" : "material"
},
"aggs" : {
"sum_price" : {
"sum" : {
"field" : "price"
}
}
}
}
}
}
},
"size" : 0
}