Sort Sub Documents in MongoDB
Is there a way to sort sub documents in a mongo query?
For example:
{
"_id" : ObjectId("4c69d19532f73ad544000001"),
"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"}
]
}
{
"_id" : ObjectId("4c69d19532f73开发者_JS百科ad544000002"),
"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" : "07-24-1997"}
]
}
I want these documents to be ordered in however way I decide and then have comments ordered within my a document by reverse order of date or any other sorting I want.
Is that possible with mongoDB?
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/
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;
}
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 thebody
function takes as parameter. In our case"$comments"
.lang
, which is the language in which thebody
function is written. Onlyjs
is currently available.
It is possible when use in sequence $unwind
> $sort
> $group
specialty for array,
- $unwind deconstruct array
comments
db.collection.aggregate([
{ $unwind: "$comments" },
$addFields
to add new field fordate
, 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"
}
}
}
},
- $sort
comments.dateISO
in descending (-1) order
{ $sort: { "comments.dateISO": -1 } },
- $group by
_id
, and re-constructcomments
array using$push
objects and other fields remain using$first
{
$group: {
_id: "$_id",
comment: { $first: "$comment" },
comments: { $push: "$comments" }
}
}
])
Playground
精彩评论