Is there a way to sort sub documents in a mongo query?
For example:
{
\"_id\" : ObjectId(\"4c69d19532f73ad544000001\"),
\"content\" : \"blah bla
It's not possible directly out of MongoDB, however when you pull the document you can then sort the array as if it was an array of objects, using whatever native sort method your language has. This is how I do comments on my blog with MongoDB.
PHP Code
/* Takes an array of comments and turns them into native objects */
public static function loadMany($pcomments)
{
$comments = array();
if(isset($pcomments) && count($pcomments) > 0)
{
foreach($pcomments as $key => $comment)
{
$comment['data']['index'] = $key;
$comments[] = Comment::loadOne($comment['data']);
}
}
usort($comments, "comment_compare");
return $comments;
}
/* Compares comment timestamps */
function comment_compare($a, $b)
{
if($a->timestamp->sec == $b->timestamp->sec)
{
return 0;
}
return ($a->timestamp->sec < $b->timestamp->sec) ? -1 : 1;
}
In mongo version 2.4 or above, you can use $sort while updating. The new modifier let you sort sub document.
More information could read this page, http://docs.mongodb.org/manual/reference/operator/sort/
I dont think you can. I raised same question on the forums.
Starting in Mongo 4.4
, the $function aggregation operator allows applying a custom javascript function to implement behaviour not supported by the MongoDB Query Language.
For instance, in order to sort an array of objects by one of their fields:
// {
// "content": "blah blah blah",
// "comments": [
// { "author": "jim", "content": "comment content 1", "date": "07-24-1995" },
// { "author": "joe", "content": "comment content 2", "date": "07-24-1996" },
// { "author": "amy", "content": "comment content 3", "date": "09-10-1999" }
// ]
// }
db.collection.aggregate(
{ $sort: { content: 1 } }, // or whatever sort order
{ $set:
{ "comments":
{ $function: {
body: function(comments) {
return comments.sort((a, b) => a.date < b.date);
},
args: ["$comments"],
lang: "js"
}}
}
}
)
// {
// "content": "blah blah blah",
// "comments": [
// { "author": "amy", "content": "comment content 3", "date": "09-10-1999" },
// { "author": "joe", "content": "comment content 2", "date": "07-24-1996" },
// { "author": "jim", "content": "comment content 1", "date": "07-24-1995" }
// ]
// }
$function
takes 3 parameters:
body
, which is the function to apply, whose parameter is the array to modify.args
, which contains the fields from the record that the body
function takes as parameter. In our case "$comments"
.lang
, which is the language in which the body
function is written. Only js
is currently available.It is possible when use in sequence $unwind
> $sort
> $group
specialty for array,
comments
db.collection.aggregate([
{ $unwind: "$comments" },
$addFields
to add new field for date
, field is in string and we need to convert it to an ISO date for proper sorting, to convert string date to ISO date use $dateFromString {
$addFields: {
"comments.dateISO": {
$dateFromString: {
dateString: "$comments.date",
format: "%m-%d-%Y"
}
}
}
},
comments.dateISO
in descending (-1) order { $sort: { "comments.dateISO": -1 } },
_id
, and re-construct comments
array using $push
objects and other fields remain using $first
{
$group: {
_id: "$_id",
comment: { $first: "$comment" },
comments: { $push: "$comments" }
}
}
])
Playground