mongodb sorting array documents - mongodb

This is my document i want to sort array documents by ascending order to get so for that my queries are in following code.but i am not getting the docs in sorted way.
The query is
db.sample.find({_id: ObjectId("55b32f5957e47fabd30c5d2e")}).sort({'naresh.ts':1}).pretty();
This is the result I am getting
{
"_id" : ObjectId("55b32f5957e47fabd30c5d2e"),
"naresh" : [
{
"ts" : "hi",
"created_by" : 1437806425105
},
{
"ts" : "hello",
"created_by" : 1437806425105
},
{
"ts" : "waht",
"created_by" : 1437807757261
},
{
"ts" : "lefo",
"created_by" : 1437807768514
},
{
"ts" : "lefow",
"created_by" : 1437807775719
}
]
}

You can use $aggregation like following query:
db.collection.aggregate({
"$match": {
"_id": ObjectId("55b32f5957e47fabd30c5d2e")
}
}, {
$unwind: "$naresh"
}, {
$sort: {
"naresh.ts": 1
}
}, {
"$group": {
_id: "$_id",
"naresh": {
$push: "$naresh"
}
}
})

The cursor .sort() only looks at the values in the array to decide to use the "smallest" value of the specified field ( in ascending order ) to determine how to "sort" the documents in the response. This does not "sort" the array content itself.
In order to sort the array, you need to use the aggregation framework to manipulate the document:
db.sample.aggregate([
{ "$match": { "_id": ObjectId("55b32f5957e47fabd30c5d2e") },
{ "$unwind": "$naresh" },
{ "$sort": { "$naresh.ts": 1 } },
{ "$group": {
"_id": "$_id",
"naresh": { "$push": "$naresh" }
}}
])
That sorts the array.
Better yet, if you "always" want then results sorted then do it as you update the document:
db.sample.update({},{ "$push": { "$each": [], "$sort": { "ts": 1 } } },{ "multi": true })
And use those same, $each and $sort modifiers when adding new elements to the array and the content will remain sorted.

If you want just query the collection and get the output sorted, then Blackes Seven's answer will work perfectly for you.
However if you want to update the documents in the sorted order, go with this update query:
update(
{
_id: ObjectId("55b32f5957e47fabd30c5d2e")
},
{
$push: {
naresh: {
$each: [],
$sort: {created_by: 1}
}
}
}
)

Related

Mongo query how to retrieve the latest inserted array value?

I have a mongodb collection which contains some array values such as ActivityType, Note and ActivityDate. The array name is called activities. I need to rename some fields so I used aggregate and $project to rename some columns for the output. But I only need to return the latest inserted ActivityDate for the array value.
My current query returns all the array value in the Activity array:
db.test.aggregate([
{$match: {}
}, {$unwind: "$activities"},
{$match: {}},
{ "$project": {
"_id" : 0,
"Project Number": "$ProjectNumber" ,
"Activity Type": "$activities.activityTypeDesc" ,
"Date of Activity": {
"$dateToString": { "format": "%Y-%m-%d", "date": "$activities.dateOfActivity" }
}
}}
])
It is sort of like getting the top 1 order by in sql server. How do I do that in Mongodb? After some reading seems like I need to use $sort and $group, but I don't know how to fit in here.
I have some sample data below:
{
"_id" : ObjectId("5fd289a93f7cf02c36837ca7"),
"ProjectNumber" : "ABC1234567",
"activities" : [
{
"activityTypeDesc" : "Type1",
"dateOfActivity" : ISODate("2021-02-20T06:00:00.000Z"),
"activityNote" : ""
},
{
"activityTypeDesc" : "Type2",
"dateOfActivity" : ISODate("2021-03-04T06:00:00.000Z"),
"activityNote" : ""
},
{
"activityTypeDesc" : "Type3",
"dateOfActivity" : ISODate("2021-01-04T06:00:00.000Z"),
"activityNote" : ""
},
{
"activityTypeDesc" : "Type4",
"dateOfActivity" : ISODate("2021-04-15T05:00:00.000Z"),
"activityNote" : ""
}
]
}
{
"_id" : ObjectId("5fd2ca65d1a01d157c0179be"),
"ProjectNumber" : "12345",
"activities" : []
}
The result of the query should return two rows, one with the lastest activitydate , one with no activitydate (as no array value)
Any help will be appreciated!
$unwind deconstruct activities array
$sort by dateOfActivity in descending order
$group by _id and get first activity required fields
db.collection.aggregate([
{
$unwind: {
path: "$activities",
preserveNullAndEmptyArrays: true
}
},
{ $sort: { "activities.dateOfActivity": -1 } },
{
$group: {
_id: "$_id",
"Project Number": { $first: "$ProjectNumber" },
"Activity Type": { $first: "$activities.activityTypeDesc" },
"Date Of Activity": {
$first: {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$activities.dateOfActivity"
}
}
}
}
}
])
Playground

Need to sum from array object value in mongodb

I am trying to calculate total value if that value exits. But query is not working 100%. So can somebody help me to solve this problem. Here my sample document. I have attached two documents. Please these documents & find out best solution
Document : 1
{
"_id" : 1"),
"message_count" : 4,
"messages" : {
"data" : [
{
"id" : "11",
"saleValue": 1000
},
{
"id" : "112",
"saleValue": 1400
},
{
"id" : "22",
},
{
"id" : "234",
"saleValue": 111
}
],
},
"createdTime" : ISODate("2018-03-18T10:18:48.000Z")
}
Document : 2
{
"_id" : 444,
"message_count" : 4,
"messages" : {
"data" : [
{
"id" : "444",
"saleValue" : 2060
},
{
"id" : "444",
},
{
"id" : 234,
"saleValue" : 260
},
{
"id" : "34534",
}
]
},
"createdTime" : ISODate("2018-03-18T03:11:50.000Z")
}
Needed Output:
{
total : 4831
}
My query :
db.getCollection('myCollection').aggregate([
{
"$group": {
"_id": "$Id",
"totalValue": {
$sum: {
$sum: "$messages.data.saleValue"
}
}
}
}
])
So please if possible help me to solve this problem. Thanks in advance
It's not working correctly because it is aggregating all the documents in the collection; you are grouping on a constant "_id": "tempId", you just need to reference the correct key by adding the $ as:
db.getCollection('myCollection').aggregate([
{ "$group": {
"_id": "$tempId",
"totalValue": {
"$sum": { "$sum": "$messages.data.saleValue" }
}
} }
])
which in essence is a single stage pipeline version of an aggregate operation with an extra field that holds the sum expression before the group pipeline then calling that field as the $sum operator in the group.
The above works since $sum from MongoDB 3.2+ is available in both the $project and $group stages and when used in the $project stage, $sum returns the sum of the list of expressions. The expression "$messages.data.value" returns a list of numbers [120, 1200] which are then used as the $sum expression:
db.getCollection('myCollection').aggregate([
{ "$project": {
"values": { "$sum": "$messages.data.value" },
"tempId": 1,
} },
{ "$group": {
"_id": "$tempId",
"totalValue": { "$sum": "$values" }
} }
])
You can add a $unwind before your $group, in that way you will deconstructs the data array, and then you can group properly:
db.myCollection.aggregate([
{
"$unwind": "$messages.data"
},
{
"$group": {
"_id": "tempId",
"totalValue": {
$sum: {
$sum: "$messages.data.value"
}
}
}
}
])
Output:
{ "_id" : "tempId", "totalValue" : 1320 }
db.getCollection('myCollection').aggregate([
{
$unwind: "$messages.data",
$group: {
"_id": "tempId",
"totalValue": { $sum: "$messages.data.value" }
}
}
])
$unwind
According to description as mentioned into above question, as a solution please try executing following aggregate query
db.myCollection.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
path: '$messages.data'
}
},
// Stage 2
{
$group: {
_id: {
pageId: '$pageId'
},
total: {
$sum: '$messages.data.saleValue'
}
}
},
// Stage 3
{
$project: {
pageId: '$_id.pageId',
total: 1,
_id: 0
}
}
]
);
You can do it without using $group. Grouping made other data to be managed and addressed. So, I prefer using $sum and $map as shown below:
db.getCollection('myCollection').aggregate([
{
$addFields: {
total: {
$sum: {
$map: {
input: "$messages.data",
as: "message",
in: "$$message.saleValue",
},
},
},
},
},
}
])

How can I get max value in nested documents?

I have a collection(named menucategories) in MongoDB 3.2.11:
{
"_id" : ...
"menus" : [
{
"code":0
},
{
"code":1
},
{
"code":2
},
{
"code":3
}
]
},
{
"_id" : ...
"menus" : [
{
"code":4
},
{
"code":5
},
{
"code":6
},
{
"code":7
}
]
},
{
"_id" : ...
"menus" : [
{
"code":8
},
{
"code":9
},
{
"code":10
},
{
"code":11
}
]
}
Every menucategory has array named menus. And every menu(element of the array) has code. The 'code' of menus is unique in every menu. I wanna get the maximum value of menu's code(in this case, 11). How can I achieve this?
If you want to find maximum value of code from all menus code then probable query will be as follows:
db.menucategories.aggregate([
{ $unwind: '$menus' },
{ $group: { _id: null, max: { $max: '$menus.code' } } },
{ $project: { max: 1, _id:0 } }
])
Click below links for more information regarding different operators:
$unwind, $group, $project
You don't need to use the $unwind aggregation pipeline operator here because starting from MongoDB 3.2, some accumulator expressions are available in the $project stage.
db.collection.aggregate([
{"$project": {"maxPerDoc": {"$max": "$menus.code"}}},
{"$group": {"_id": null, "maxValue": {"$max": "$maxPerDoc"}}}
])
Responding a previous now deleted comment, you don't need to put your pipeline in an array so the following query will work as well.
db.collection.aggregate(
{"$project": {"maxPerDoc": {"$max": "$menus.code"}}},
{"$group": {"_id": null, "maxValue": {"$max": "$maxPerDoc"}}}
)
Try with aggregation:
db.collection.aggregate({ $group : { _id: 1, max: { $max: {$max : "$menus.code"}}}});
No need of any unwind, if you need find only maximum value.

Removing duplicates in mongodb with aggregate query

db.games.aggregate([
{ $unwind : "$rounds"},
{ $match: {
"rounds.round_values.gameStage": "River",
"rounds.round_values.decision": "BetPlus" }
},
{ $project: {"FinalFundsChange":1, "GameID":1}
}])
The resulting output is:
{ "_id" : ObjectId("57cbce66e281af12e4d0731f"), "GameID" : "229327202", "FinalFundsChange" : 0.8199999999999998 }
{ "_id" : ObjectId("57cbe2fce281af0f34020901"), "FinalFundsChange" : -0.1599999999999997, "GameID" : "755030199" }
{ "_id" : ObjectId("57cbea3ae281af0f340209bc"), "FinalFundsChange" : 0.10000000000000009, "GameID" : "231534683" }
{ "_id" : ObjectId("57cbee43e281af0f34020a25"), "FinalFundsChange" : 1.7000000000000002, "GameID" : "509975754" }
{ "_id" : ObjectId("57cbee43e281af0f34020a25"), "FinalFundsChange" : 1.7000000000000002, "GameID" : "509975754" }
As you can see the last element is a duplicate, that's because the unwind creates two elements of it, which it should. How can I (while keeping the aggregate structure of the query) keep the first element of the duplicate or keep the last element of the duplicate only?
I have seen that the ways to do it seem to be related to either $addToSet or $setUnion (any details how this works exactly are appreciated as well), but I don't understand how I can choose the 'subset' by which I want to identify the duplicates (in my case that's the 'GameID', other values are allowed to be different) and how I can select whether I want the first or the last element.
You could group by _id via $group and then use the $last and $first operator respectively to keep the last or first values.
db.games.aggregate([
{ $unwind : "$rounds"},
{ $match: {
"rounds.round_values.gameStage": "River",
"rounds.round_values.decision": "BetPlus" }
},
{ $group: {
_id: "$_id",
"FinalFundsChange": { $first: "$FinalFundsChange" },
"GameID": { $last: "$GameID" }
}
}
])
My problem was find all users who purchase same product, where a user can purchase a product multiple time.
https://mongoplayground.net/p/UTuT4e_N6gn
db.payments.aggregate([
{
"$lookup": {
"from": "user",
"localField": "user",
"foreignField": "_id",
"as": "user_docs"
}
},
{
"$unwind": "$user_docs",
},
{
"$group": {
"_id": "$user_docs._id",
"name": {
"$first": "$user_docs.name"
},
}
},
{
"$project": {
"_id": 0,
"id": "$_id",
"name": "$name"
}
}
])

MongoDB - get $max among fields at different levels

I have a MongoDB collection with documents of this (simplified) form
{
"_id": "Doc"
"created": NumberLong("1422526079335")
}
Additionally, this documents may have an additional edited field
{
"_id": "Doc"
"created": NumberLong("1422526079335")
"edited": {
"date": NumberLong("1458128507498")
}
}
What I need is to get the most recent timestamp (among created and edited.date) for a subset of these documents, matching certain conditions.
What I achieved so far is to get the most recent created timestamp
db.myCollection.aggregate([ { $match: { ... } },
{ $project: { _id:0, created: 1 } },
{ $group: { _id: 'latest', latest: { $max: '$created' } } }
])
which returns
{ "_id" : "latest", "latest" : NumberLong("1422526079335") }
How can I integrate the check against edited.date in the $max logic above? Or alternatively is there another solution? Thanks is advance!
Try this script. it's simple $max operator.
I have following documents in collection
{
"_id" : "Doc",
"created" : NumberLong(1422526079335),
"edited" : {
"date" : NumberLong(1458128507498)
}
}
{
"_id" : "Doc1",
"created" : NumberLong(1422526079335)
}
Try running following query:
db.doc.aggregate([
{
$match: { ... }
},
{
$project:{
latest:{ $max:["$created", "$edited.date"]}
}
}
])
Output will be:
{
"_id" : "Doc",
"latest" : NumberLong(1458128507498)
}
{
"_id" : "Doc1",
"latest" : NumberLong(1422526079335)
}
you can use $cond in last $project pipe - lastModifedDate makes the trick :-)
db.ill.aggregate([{
$project:{
lastModifedDate:{
$cond: {
if: { $gte: [ "$created", "$edited.date"] },
then: "$created", else: "$edited.date" }
}}}])