Sorting an array in spring-data mongodb - mongodb

I have a collection like :
{"x":{"y":[
{"date":ISODate("2014-07-24T21:00:00.000Z"),"k":5 },
{"date":ISODate("2014-07-22T21:00:00.000Z"),"k":6 } ] }}
I want to sort y array according to "date" parameter.So i made the code like this :
query.with(new Sort(Sort.Direction.ASC, "y.date"));
I want to output like this:
{"x":{"y":[
{"date":ISODate("2014-07-22T21:00:00.000Z"),"k":5 },
{"date":ISODate("2014-07-24T21:00:00.000Z"),"k":6 } ] }}
How can i make the output like this ? is the code that i made ,it is ok ?

Using aggregation, your query should look like this, since you want to sort elements of 'y' based on 'date' in ascending order.
db.test.aggregate([ {$unwind: "$y"},
{$sort: {"y.date":1}},
{$group: {_id:"$_id", y: {$push:"$y"}}} ]);

Related

How do I project an element of an array in mongo?

I have a mongo document that contains something like
{date: [2018, 3, 22]}
and when I try to project this into a flat JSON structure with these fields concatenated, I always get an array with 0 elements, eg. just extracting the year with
db.getCollection('blah').aggregate([
{$project: {year: "$date.0"}}
])
I get
{"year" : []}
even though matching on a similar expression works fine, eg.
db.getCollection('blah').aggregate([
{$match: {"$date.0": 2018}}
])
selects the documents I would expect just fine.
What am I doing wrong? I've searched mongo documentation and stackoverflow but could find nothing.
For $project you should use $arrayElemAt instead of dot notation which works only for queries.
db.getCollection('blah').aggregate([
{$project: {year: { $arrayElemAt: [ "$date", 0 ] }}}
])
More here

Group by calculated dates in MongoDB

I have data that looks like this:
[
{
"start_time" : ISODate("2017-08-22T19:43:41.442Z"),
"end_time" : ISODate("2017-08-22T19:44:22.150Z")
},
{
"start_time" : ISODate("2017-08-22T19:44:08.344Z"),
"end_time" : ISODate("2017-08-22T19:46:25.500Z")
}
]
Is there any way to run an aggregation query that will give me a frequency result like:
{
ISODate("2017-08-22T19:43:00.000Z"): 1,
ISODate("2017-08-22T19:44:00.000Z"): 2,
ISODate("2017-08-22T19:45:00.000Z"): 1,
ISODate("2017-08-22T19:46:00.000Z"): 1
}
Essentially I want to group by minute, with a sum, but the trick is that each record might count toward multiple groups. Additionally, as in the case with 19:45, the date is not explicitly mentioned in the data (it is calculated as being between two other dates).
At first I thought I could do this with a function like $minute. I could group by minute and check to see if the data fits in that range. However, I'm stuck on how that would be accomplished, if that's possible at all. I'm not sure how to turn a single entry into multiple date groups.
You can use below aggregation in 3.6 mongo version.
db.col.aggregate([
{"$addFields":{"date":["$start_time","$end_time"]}},
{"$unwind":"$date"},
{"$group":{
"_id":{
"year":{"$year":"$date"},
"month":{"$month":"$date"},
"day":{"$dayOfMonth":"$date"},
"hour":{"$hour":"$date"},
"minute":{"$minute":"$date"}
},
"count":{"$sum":1}
}},
{"$addFields":{
"_id":{
"$dateFromParts":{
"year":"$_id.year",
"month":"$_id.month",
"day":"$_id.day",
"minute":"$_id.minute"
}
}
}}
])

MongoDB query with sort on multiple keys

I've got a MongoDB setup and running and I'm issuing a query that looks like this:
db.indexCollection.aggregate([
{$match:
{'term': {$regex: 'gas'},
'term': {$regex: 'carbon'},
'term': {$regex: 'hydro'}}
},
{$sort:
{'documentFrequency': 1,
'postingsList._id.termFrequency': 1}
},
{$group:
{_id: {_id: '$postingsList._id.documentID'}}
}
],
{allowDiskUse: true});
The documentIDs that are returned should contain all of the three words (or subwords) specified in the match expression (logical AND), sorted by document frequency in ascending order (priority to small document frequencies) and term frequency in descending order (priority to documents that make the most use of these terms, thus have the highest term frequency).
However, I notice that when I change the value associated with 'postingsList._id.termFrequency' from 1 to -1, nothing changes.. which makes me think that I'm doing something wrong with the sorting. The order of the output (just a list of documentIDs) does change if I change the value associated with the 'documentFrequency'.
Any ideas?
EDIT:
Sample output format when I execute a simple find query on the key 'term',
{
“_id” : ObjectId(“5941b6ad3de5cb1799f8ea26"),
“term” : “gases”,
“documentFrequency” : 2,
“postingsList” : [
{
“documentID” : 8317982,
“termFrequency” : 4
},
{
“documentID” : 9587169,
“termFrequency” : 1
}
]
}
The output after the $group stage is not as you were hoping, because a $group stage's output may not reflect the order of the input data; to be specific, the documentation says:
$group does not order its output documents.
If you want the output to be ordered after you do a $group, then you need to put a $sort after the $group.

$split and return the first array element in mongodb query

[
{lecture : "427#Math"},
{lecture : "217#Science"},
{lecture : "7#History"},
{lecture : "12#Music"}
]
Assume I have the database structure above. I want to return only the lecture code.
What have I done so far?
db.collection.aggregate([
{$project: {"lecture": {$split: ["$lecture" , "#"]}}}
])
But this returns as collection ["427" , "Math"]. How can I return only the lecture code which is the part that comes before the # character.
You can use $arrayElemAt to return only first item from $split result:
db.collection.aggregate([
{$project: {"lecture": {$arrayElemAt:[{$split: ["$lecture" , "#"]}, 0]}}}
])

Retrieve Array Of Documents in MongoDB

I have a MongoDB Document like as follows
{
"_id":1,
"name":"XYZ"
ExamScores:[
{ExamName:"Maths", UnitTest:1, Score:100},
{ExamName:"Maths", UnitTest:2, Score:80},
{ExamName:"Science", UnitTest:1, Score:90}
]
}
I Need to retrieve this document so that it has to show only Maths Array. Like as follows
{
"_id":1,
"name":"XYZ"
ExamScores:[
{ExamName:"Maths", UnitTest:1, Score:100},
{ExamName:"Maths", UnitTest:2, Score:80},
]
}
How Can I Do That ?
As #karin states there is no, normal, in query method of doing this.
In version 2.2 you can use $elemMatch to project the first matching result from ExamScores but you cannot get multiple.
That being said, the aggregation framework can do this:
db.col.aggregate([
{$unwind: '$ExamScores'},
{$match: {'ExamScores.ExamName':"Maths"}},
{$group: {_id: '$_id', name: '$name', ExamScores: {$push: '$ExamScores'}}}
])
Something like that anyway.
This has been asked before MongoDB query to limit values based on condition, the only answer there says it is not possible, but that there is a request to implement that.