Counting similar items in array with mongodb aggregation framework - mongodb

I'm just learning mongodb aggregation framework
There are data in the the format below:
{
"questionType": "multiple",
"multipleOptions": ["first", "second", "third", "forth"],
"answers": ["first", "second", "second", "first", "first", "forth"]
},
{
"questionType": "multiple",
"multipleOptions": ["awful", "bad", "soso", "good", "excellent"],
"answers": ["bad", "bad", "good", "soso", "bad", "excellent", "awful", "soso"]
}
I want to aggregate these to something like this:
{
"result": { "first": 3, "second": 2, "forth": 1 }
},
{
"result": { "awful": 1, "bad": 3, "soso": 2, "good": 1, "excellent": 1 }
}
Or like this (no difference):
{
"result": [["first", 3], ["second", 2], ["forth", 1]]
},
{
"result": [["awful", 1], ["bad", 3], ["soso", 2], ["good", 1], ["excellent", 1]]
}
Is there a way to do this in a $project stage?

This can be done with a cohort of array operators working in conjunction to produce the desired effect.
You essentially need an operation that creates an array of key/value pairs of the counts you need. This will then be converted to a hash map. The array of key value pairs is essentially a map which is constructed by looping through the multipleOptions array and checking the size of the elements that match in the answers array.
TLDR;
The final pipeline you need to run follows:
db.collection.aggregate([
{ "$project": {
"result": {
"$arrayToObject": {
"$map": {
"input": { "$range": [ 0, { "$size": "$multipleOptions" } ] },
"as": "idx",
"in": {
"$let": {
"vars": {
"k": {
"$arrayElemAt": [
"$multipleOptions",
"$$idx"
]
},
"v": {
"$size": {
"$filter": {
"input": "$answers",
"as": "ans",
"cond": {
"$eq": [
"$$ans",
{
"$arrayElemAt": [
"$multipleOptions",
"$$idx"
]
}
]
}
}
}
}
},
"in": { "k": "$$k", "v": "$$v" }
}
}
}
}
}
} }
])
To demonstrate this step by step, lets create an additional field in an aggregate operation, this field will be an array of the
counts of the corresponding array element. We need something like
{
"questionType": "multiple",
"multipleOptions": ["awful", "bad", "soso", "good", "excellent"],
"answersCount": [1, 3, 2, 1, 1],
"answers": ["bad", "bad", "good", "soso", "bad", "excellent", "awful", "soso"]
}
To get this we need a way to loop through the multipleOptions and for each option, iterate the answers array, filter it and count the number of elements in the filtered array. The pseudo-algorithm follows:
answersCount = []
for each elem in ["awful", "bad", "soso", "good", "excellent"]:
filteredAnswers = [<answers array containing only elem>]
count = filteredAnswers.length
answersCount.push(count)
In mongo, the filtering part can be done using $filter on the answers array and elements can be referenced with $arrayElemAt
{
"$filter": {
"input": "$answers",
"as": "ans",
"cond": {
"$eq": [
"$$ans",
{ "$arrayElemAt": [ "$multipleOptions", "$$idx" ] }
]
}
}
}
The counts are derived using $size on the above expression
{
"$size": {
"$filter": {
"input": "$answers",
"as": "ans",
"cond": {
"$eq": [
"$$ans",
{ "$arrayElemAt": [ "$multipleOptions", "$$idx" ] }
]
}
}
}
}
For getting the outer loop, we can use $range and $map as
{
"$map": {
"input": { "$range": [ 0, { "$size": "$multipleOptions" } ] },
"as": "idx",
"in": {
"$let": {
"vars": {
"v": {
"$size": {
"$filter": {
"input": "$answers",
"as": "ans",
"cond": {
"$eq": [
"$$ans",
{ "$arrayElemAt": [ "$multipleOptions", "$$idx" ] }
]
}
}
}
}
},
"in": "$$v"
}
}
}
}
This will produce the answersCount in the following aggregate operation
db.collection.aggregate([
{ "$addFields": {
"answersCount": {
"$map": {
"input": { "$range": [ 0, { "$size": "$multipleOptions" } ] },
"as": "idx",
"in": {
"$let": {
"vars": {
"v": {
"$size": {
"$filter": {
"input": "$answers",
"as": "ans",
"cond": {
"$eq": [
"$$ans",
{ "$arrayElemAt": [ "$multipleOptions", "$$idx" ] }
]
}
}
}
}
},
"in": "$$v"
}
}
}
}
} }
])
To then get to the desired output, you need the answersCount to be an array of key/value pairs i.e.
{
"answersCount": [
{ "k": "awful", "v": 1},
{ "k": "bad", "v": 3},
{ "k": "soso", "v": 2},
{ "k": "good", "v": 1},
{ "k": "excellent", "v": 1}
],
}
and when you apply $arrayToObject on the above expression i.e.
{ "$arrayToObject": {
"answersCount": [
{ "k": "awful", "v": 1},
{ "k": "bad", "v": 3},
{ "k": "soso", "v": 2},
{ "k": "good", "v": 1},
{ "k": "excellent", "v": 1}
],
} }
you get
{
"awful" : 1,
"bad" : 3,
"soso" : 2,
"excellent" : 1,
"good" : 1
}

This is a good use case for "multi-stage grouping." Let's begin with an $unwind of answers:
c = db.foo.aggregate([
{$unwind: "$answers"}
]);
{
"_id" : 0,
"questionType" : "multiple",
"multipleOptions" : [
"first",
"second",
"third",
"forth"
],
"answers" : "first"
}
{
"_id" : 0,
"questionType" : "multiple",
"multipleOptions" : [
"first",
"second",
"third",
"forth"
],
"answers" : "second"
}
{
"_id" : 0,
"questionType" : "multiple",
"multipleOptions" : [
"first",
"second",
"third",
"forth"
],
"answers" : "second"
}
// ...
Now we have answers and _id as peers ready to group:
db.foo.aggregate([
{$unwind: "$answers"}
,{$group: {_id: {Xid:"$_id", answer:"$answers"}, n:{$sum:1} }}
]);
{ "_id" : { "Xid" : 1, "answer" : "awful" }, "n" : 1 }
{ "_id" : { "Xid" : 1, "answer" : "excellent" }, "n" : 1 }
{ "_id" : { "Xid" : 1, "answer" : "soso" }, "n" : 2 }
{ "_id" : { "Xid" : 1, "answer" : "bad" }, "n" : 3 }
{ "_id" : { "Xid" : 0, "answer" : "forth" }, "n" : 1 }
Now we group again, this time by the _id.Xid and then use $push to construct the output array of results:
db.foo.aggregate([
{$unwind: "$answers"}
,{$group: {_id: {Xid:"$_id", answer:"$answers"}, n:{$sum:1} }}
,{$group: {_id: "$_id.Xid", result: {$push: {answer: "$_id.answer", n: "$n" }} }}
]);
{
"_id" : 0,
"result" : [
{
"answer" : "forth",
"n" : 1
},
{
"answer" : "second",
"n" : 2
},
{
"answer" : "first",
"n" : 3
}
]
}
{
"_id" : 1,
"result" : [
{
"answer" : "awful",
"n" : 1
},
{
"answer" : "excellent",
"n" : 1
},
{
"answer" : "soso",
"n" : 2
},
{
"answer" : "bad",
"n" : 3
},
{
"answer" : "good",
"n" : 1
}
]
}
So in spirit we have a solution but to really press the point, we will use the $arrayToObject function to turn the array of options from the values of the answer key to keys in their own right. To do so, we will name the $push object args k and v to properly drive the function:
db.foo.aggregate([
{$unwind: "$answers"}
,{$group: {_id: {Xid:"$_id", answer:"$answers"}, n:{$sum:1} }}
,{$group: {_id: "$_id.Xid", QQ: {$push: {k: "$_id.answer", v: "$n" }} }}
,{$project: {_id: true, result: {$arrayToObject: "$QQ"} }}
]);
which yields:
{ "_id" : 0, "result" : { "forth" : 1, "second" : 2, "first" : 3 } }
{
"_id" : 1,
"result" : {
"awful" : 1,
"excellent" : 1,
"soso" : 2,
"bad" : 3,
"good" : 1
}
}

Related

How to fix a MongoDB aggregate $project $filter

I have data that looks like this:
{
"_id" : 1,
"data" : [
{
"id" : 1,
"one" : [
{"user" : 2, "two" : 2},
{"user" : 1, "two" : 3}
]
}
]
}
{
"_id" : 2,
"data" : [
{
"id" : 2,
"one" : [
{"user" : 2, "two" : 2},
{"user" : 1, "two" : 3}
]
}
]
}
And I want to filter in an aggregate (since the data above is from previous stages) so that the one array is filtered to only show one array items where the user is equal to the _id.
The following returns empty one arrays, how to I get the filter to fill in the one array as I expect?
db.parks.aggregate([{$project: {_id: 1, 'data.id': 1, 'data.one':
{$filter: {
input: '$data.one',
as: 'un',
cond: {$eq: ['$$un.user', '$_id']}}
}}}]).pretty()
Which gives:
{ "_id" : 1, "data" : [ { "id" : 1, "one" : [ ] } ] }
{ "_id" : 2, "data" : [ { "id" : 2, "one" : [ ] } ] }
Replacing '$_id' with '$$un.user' shows all data.one items as expected, so it looks like the problem is in the $eq statement.
How to I get the filter to show the one array as I expect?
You can use below aggregation
db.collection.aggregate([
{ "$project": {
"data": {
"$filter": {
"input": {
"$map": {
"input": "$data",
"in": {
"id": "$$this.id",
"one": {
"$filter": {
"input": "$$this.one",
"as": "on",
"cond": { "$eq": ["$$on.user", "$_id"] }
}
}
}
}
},
"as": "d",
"cond": { "$eq": ["$$d.id", "$_id"] }
}
}
}}
])
MongoPlayground

MongoDB: Get the previous and next element in the embedded array

I have a database which has the following structure:
{
"_id" : ObjectId("59b8d72ab515211f3c161c4b"),
"Transport_event_id" : 1,
"Carrier_id" : 23,
"Payload_id" : 0,
"StartTime" : 214392.0,
"EndTime" : 362707.0,
"Move_events" : [
{
"Timestamp" : 214398,
"x_pos" : 13,
"y_pos" : 202
},{
"Timestamp" : 214845,
"x_pos" : 12,
"y_pos" : 202
},{
"Timestamp" : 216399,
"x_pos" : 12,
"y_pos" : 216
},{
"Timestamp" : 216842,
"x_pos" : 11,
"y_pos" : 216
},{
"Timestamp" : 219586,
"x_pos" : 10,
"y_pos" : 216
}
]
}
I've made the following query which will return the next 2 Elements form a Array after a specific TimeStamp.
var cursor = db.Transport_eventBeta.aggregate([
{ "$match": { "StartTime": { "$lte": query_time } } },
{ "$match": { "EndTime": { "$gte": query_time } } },
{
"$project": {
"Move_events": {
"$let": {
"vars": {
"filtered": {
"$filter": {
"input": "$Move_events",
"as": "event",
"cond": { "$lte": ["$$event.Timestamp" , query_time] }
}
}
},
"in": {
"$slice": [
"$Move_events",
{"$size": "$$filtered"},
2
]
}
}
},
"Carrier_id": 1
}
}
])
while (cursor.hasNext()) {
print(cursor.next());
}
What I need are the documents befor and after this specific TimeStamp.
Some kind of this:
"$slice": [
"$Move_events",
{"$size": "$$filtered"} - 1,
2
]
But this doesn't work. How can I solve this problem? 2 separate queries are no option because of the duration.
You can try below aggregation query in 3.4.
The query will filter Move_events to keep events with timestamp less than input timestamp followed by $arrayElemAt to get the Move_events after and before event.
db.Transport_eventBeta.aggregatee([
{
"$match": {
"StartTime": {
"$lte": query_time
},
"EndTime": {
"$gte": query_time
}
}
},
{
"$project": {
"Move_events": {
"$let": {
"vars": {
"filtered": {
"$filter": {
"input": "$Move_events",
"as": "event",
"cond": {
"$lte": [
"$$event.Timestamp",
query_time
]
}
}
}
},
"in": [
{
"$arrayElemAt": [
"$Move_events",
{
"$subtract": [
{
"$size": "$$filtered"
},
1
]
}
]
},
{
"$arrayElemAt": [
"$Move_events",
{
"$size": "$$filtered"
}
]
}
]
}
}
}
}
])

Filter Array Content to a Query containing $concatArrays

Given this function, I have a data set that I am querying. The data looks like this:
db.activity.insert(
{
"_id" : ObjectId("5908e64e3b03ca372dc945d5"),
"startDate" : ISODate("2017-05-06T00:00:00Z"),
"details" : [
{
"code" : "2",
"_id" : ObjectId("5908ebf96ae5003a4471c9b2"),
"walkDistance" : "03",
"jogDistance" : "01",
"runDistance" : "08",
"sprintDistance" : "01"
}
]
}
)
db.activity.insert(
{
"_id" : ObjectId("58f79163bebac50d5b2ae760"),
"startDate" : ISODate("2017-05-07T00:00:00Z"),
"details" : [
{
"code" : "2",
"_id" : ObjectId("58f7948fbebac50d5b2ae7f2"),
"walkDistance" : "01",
"jogDistance" : "02",
"runDistance" : "09",
"sprintDistance" : ""
}
]
}
)
Using this function, thanks to Neil Lunn, I am able to get my desired output:
db.activity.aggregate([
{ "$project": {
"_id": 0,
"unique": {
"$filter": {
"input": {
"$setDifference": [
{ "$concatArrays": [
"$details.walkDistance",
"$details.jogDistance",
"$details.runDistance",
"$details.sprintDistance"
]},
[]
]
},
"cond": { "$ne": [ "$$this", "" ] }
}
}
}},
{ "$unwind": "$unique" },
{ "$group": {
"_id": null,
"uniqueArray": { "$addToSet": "$unique" }
}}
])
However, I cannot add a match statement to the beginning.
db.activity.aggregate([
{$match: {"startDate" : ISODate("2017-05-06T00:00:00Z"), "details.code": "2" },
{$unwind: '$details'},
{$match: {"startDate" : ISODate("2017-05-06T00:00:00Z"), "details.code": "2" },
{ "$project": {
"_id": 0,
"unique": {
"$filter": {
"input": {
"$setDifference": [
{ "$concatArrays": [
"$details.walkDistance",
"$details.jogDistance",
"$details.runDistance",
"$details.sprintDistance"
]},
[]
]
},
"cond": { "$ne": [ "$$this", "" ] }
}
}
}},
{ "$unwind": "$unique" },
{ "$group": {
"_id": null,
"uniqueArray": { "$addToSet": "$unique" }
}}
])
Because it gives an error message of:
> $concatArrays only supports arrays, not string
How can I modify this query so that a $match statement can be added?
Don't $unwind the array you are feeding to $concatArrays. Instead apply $filter to only extract the matching values. And as stated, we can just use $setUnion for the 'unique concatenation' instead:
db.activity.aggregate([
{ "$match": { "startDate" : ISODate("2017-05-06T00:00:00Z"), "details.code": "2" } },
{ "$project": {
"_id": 0,
"unique": {
"$let": {
"vars": {
"filtered": {
"$filter": {
"input": "$details",
"cond": { "$eq": [ "$$this.code", "2" ] }
}
}
},
"in": {
"$setDifference": [
{ "$setUnion": [
"$$filtered.walkDistance",
"$$filtered.jogDistance",
"$$filtered.runDistance",
"$$filtered.sprintDistance"
]},
[""]
]
}
}
}
}},
{ "$unwind": "$unique" },
{ "$group": {
"_id": null,
"uniqueArray": { "$addToSet": "$unique" }
}}
])
Using $let makes things a bit cleaner syntax wise since you don't need to specify multiple $map and $filter statements "inline" as the source for $setUnion

MongoDB aggregate multiple group by top fields and array fields

My collection will look like this,
{
"_id" : ObjectId("591c5971240033283736860a"),
"status" : "Done",
"createdDate" : ISODate("2017-05-17T14:09:20.653Z")
"communications" : [
{
"communicationUUID" : "df07948e-4a14-468e-beb1-db55ff72b215",
"communicationType" : "CALL",
"recipientId" : 12345,
"createdDate" : ISODate("2017-05-18T14:09:20.653Z")
"callResponse" : {
"Status" : "completed",
"id" : "dsd45554545ds92a9bd2c12e0e6436d",
}
}
]}
{
"_id" : ObjectId("45sdsd59124003345121450a"),
"status" : "ToDo",
"createdDate" : ISODate("2017-05-17T14:09:20.653Z")
"communications" : [
{
"communicationUUID" : "45sds55-4a14-468e-beb1-db55ff72b215",
"communicationType" : "CALL",
"recipientId" : 1234,
"createdDate" : ISODate("2017-05-18T14:09:20.653Z")
"callResponse" : {
"Status" : "completed",
"id" : "84fe862f1924455dsds5556436d",
}
}
]}
Currently I am writing two aggregate query to achieve my requirement and my query will be below
db.collection.aggregate(
{ $project: {
dayMonthYear: { $dateToString: { format: "%d/%m/%Y", date: "$createdDate" } },
status: 1,
}},
{ $group: {
_id: "$dayMonthYear",
Pending: { $sum: { $cond : [{ $eq : ["$status", "ToDo"]}, 1, 0]} },
InProgress: { $sum: { $cond : [{ $eq : ["$status", "InProgress"]}, 1, 0]} },
Done: { $sum: { $cond : [{ $eq : ["$status", "Done"]}, 1, 0]} },
Total: { $sum: 1 }
}}
My output will be,
{"_id" : "17/05/2017", "Pending" : 1.0, "InProgress" : 0.0, "Done" : 1.0, "Total" : 2.0 }
Using above query I can able to get count but I need to find the count based on communication Status too so I am writing one more query to achieve,
db.collection.aggregate(
{"$unwind":"$communications"},
{ $project: {
dayMonthYear: { $dateToString: { format: "%d/%m/%Y", date: "$createdDate" } },
communications: 1
}},
{ "$group": {
_id: "$dayMonthYear",
"total_call": { $sum: { $cond : [{ $or : [ { $eq: [ "$communications.callResponse.Status", "failed"] },
{ $eq: [ "$communications.callResponse.Status", "busy"] },
{ $eq: [ "$communications.callResponse.Status", "completed"] },
{ $eq: [ "$communications.callResponse.Status", "no-answer"] }
]}, 1, 0 ] }},
"engaged": { $addToSet: { $cond : [{ $eq : ["$communications.callResponse.Status", "completed"]},
"$communications.recipientId", "null" ]} },
"not_engaged": { $addToSet: { $cond: [{ $or : [ { $eq: [ "$communications.callResponse.Status", "failed"] },
{ $eq: [ "$communications.callResponse.Status", "busy"] },
{ $eq: [ "$communications.callResponse.Status", "no-answer"] } ]},
"$communications.recipientId", "null" ] }}
}},
{ "$project": {
"_id": 1,
"total_call": 1,
"engaged": { "$setDifference": [ "$ngaged", ["null"] ] },
"not_engaged": { "$setDifference": [ "$not_engaged", ["null"] ] },
}},
{ "$project": {
"total_call": 1,
"engaged": { "$size": "$engaged" },
"not_engaged": { "$size": { "$setDifference": [ "$not_engaged", "$engaged" ] }},
}})
My output will be,
{"_id" : "18/05/2017", "total_call" : 2.0, "engaged" : 2, "not_engaged" : 0}
Using above query I can able to get count but I want to achieve it in single query
I am looking for output like
{"_id":"17/05/2017", "Pending" : 1.0, "InProgress" : 0.0, "Done" : 1.0, "total_call" : 0, "engaged" : 0, "not_engaged" : 0}
{"_id":"18/05/2017", "Pending" : 0.0, "InProgress" : 0.0, "Done" : 0.0, "total_call" : 2, "engaged" : 2, "not_engaged" : 0}
Can anyone suggest or provide me good way to get above result.
You can use $concatArrays to merge the status& createdDate documents followed by $group to count the occurrences.
db.collection.aggregate([
{
"$project": {
"statusandcreateddate": {
"$concatArrays": [
[
{
"status": "$status",
"createdDate": "$createdDate"
}
],
{
"$map": {
"input": "$communications",
"as": "l",
"in": {
"status": "$$l.callResponse.Status",
"createdDate": "$$l.createdDate"
}
}
}
]
}
}
},
{
"$unwind": "$statusandcreateddate"
},
{
"$group": {
"_id": {
"$dateToString": {
"format": "%d/%m/%Y",
"date": "$statusandcreateddate.createdDate"
}
},
"total_call": {
"$sum": {
"$cond": [
{
"$or": [
{
"$eq": [
"$statusandcreateddate.status",
"failed"
]
},
{
"$eq": [
"$statusandcreateddate.status",
"busy"
]
},
{
"$eq": [
"$statusandcreateddate.status",
"completed"
]
},
{
"$eq": [
"$statusandcreateddate.status",
"no-answer"
]
}
]
},
1,
0
]
}
},
"engaged": {
"$sum": {
"$cond": [
{
"$eq": [
"$statusandcreateddate.status",
"completed"
]
},
1,
0
]
}
},
"not_engaged": {
"$sum": {
"$cond": [
{
"$or": [
{
"$eq": [
"$statusandcreateddate.status",
"failed"
]
},
{
"$eq": [
"$statusandcreateddate.status",
"busy"
]
},
{
"$eq": [
"$statusandcreateddate.status",
"no-answer"
]
}
]
},
1,
0
]
}
},
"Pending": {
"$sum": {
"$cond": [
{
"$eq": [
"$statusandcreateddate.status",
"ToDo"
]
},
1,
0
]
}
},
"InProgress": {
"$sum": {
"$cond": [
{
"$eq": [
"$statusandcreateddate.status",
"InProgress"
]
},
1,
0
]
}
},
"Done": {
"$sum": {
"$cond": [
{
"$eq": [
"$statusandcreateddate.status",
"Done"
]
},
1,
0
]
}
}
}
}
])

How could I not only preserve the max value but also the record having the max value

I want to use $max operator to select the max value.
And also keep the max record with the key "original_document"
How could I do it in mongoDB
expect result
{ "_id" : "abc", "maxTotalAmount" : 100,
"maxQuantity" : 10,
"original_document": {{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }}}
current result
{ "_id" : "abc", "maxTotalAmount" : 100, "maxQuantity" : 10 }
documents
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }
aggregation
db.sales.aggregate(
[
{
$group:
{
_id: "$item",
maxTotalAmount: { $max: { $multiply: [ "$price", "$quantity" ] } },
maxQuantity: { $max: "$quantity" }
}
}
]
)
When you want detail from the same grouping item then you use $sort and $first for the field(s) from the document you wish to preserve:
db.sales.aggregate([
{ "$project": {
"item": 1,
"TotalAmount": { "$multiply": [ "$price", "$quantity" ] },
"quantity": 1
}},
{ "$sort": { "TotalAmount": -1 } },
{ "$group": {
"_id": "$item",
"maxTotalAmount": { "$max": "$TotalAmount" },
"maxQuantity": { "$max": "$quantity" },
"doc_id": { "$first": "$_id" },
"doc_quantity": { "$first": "$quantity" }
}}
])
The aggregation "accumulators" cannot use embedded fields, and pushing all to an array makes little sense. But you can name like above and even rename with another $project or in your code if you want to.
Just to demonstrate how impractical this is to do otherwise, there is this example:
db.sales.aggregate([
{ "$group": {
"_id": "$item",
"maxTotalAmount": { "$max": { "$multiply": [ "$price", "$quantity" ] } },
"maxQuantity": { "$max": "$quantity" },
"docs": { "$push": {
"_id": "$_id",
"quantity": "$quantity",
"TotalAmount": { "$multiply": [ "$price", "$quantity" ] }
}}
}},
{ "$project": {
"maxTotalAmount": 1,
"maxQuantity": 1,
"maxTotalDocs": {
"$setDifference": [
{ "$map": {
"input": "$docs",
"as": "doc",
"in": {
"$cond": [
{ "$eq": [ "$maxTotalAmount", "$$doc.TotalAmount" ] },
"$$doc",
false
]
}
}},
[false]
]
}
}}
])
Which is not a great idea since you are pushing every document within the grouping condition into an array, only to filter out the ones you want later. On any reasaonable data size this is not practical and likely to break.
Please check the below :
db.qt.aggregate([
{ "$project": { "maxTotalAmount" : { "$multiply" :
[ "$price", "$quantity" ]
} ,
"currentDocumnet" : { "_id" : "$_id" ,
"item" : "$item", "price" : "$price",
"quantity" : "$quantity",
"date" : "$date" } }
},
{"$sort" : { "currentDocumnet.item" : 1 , maxTotalAmount : -1}},
{"$group" :{ _id : "$currentDocumnet.item" ,
currentDocumnet : { "$first" : "$currentDocumnet"} ,
maxTotalAmount : { "$first" : "$maxTotalAmount"} ,
maxQuantity: { "$max" : "$currentDocumnet.quantity" }}
}
]);