Reputation: 596
I'm attempting to write some query that will take documents about YouTubers and their videos, and then return a single list of all their videos in chronological order.
So far with my query (in Mongoose):
return mongoose.model('creators').find({
id: {}
})
I get these results:
[
{
"_id": "5b0891a3dcec4912601e32ce",
"name": "CaptainDisillusion",
"id": "UCEOXxzW2vU0P-0THehuIIeg",
"videos": [
{
"_id": "5b0891a3dcec4912601e32cf",
"name": "Quick D: A Fighter Jet Says Hi",
"id": "-NPmBkYb_mY",
"date": "2018-05-10T16:30:30.000Z"
}
],
"__v": 0
},
{
"_id": "5b08967b66d8321034176afb",
"name": "Curious Droid",
"id": "UC726J5A0LLFRxQ0SZqr2mYQ",
"videos": [
{
"_id": "5b0891a3dcec4912601e32cf",
"name": "Guest post bloggers wanted for the Curious Droid website",
"id": "sNLvdoValBw",
"date": "2018-05-23T16:30:30.000Z"
}
],
"__v": 0
}
]
Ideally what I'd like to do is return something along the lines of:
"videos": [
{
"_id": "5b0891a3dcec4912601e32cf",
"name": "Guest post bloggers wanted for the Curious Droid website",
"id": "sNLvdoValBw",
"date": "2018-05-23T16:30:30.000Z"
},
{
"_id": "5b0891a3dcec4912601e32cf",
"name": "Quick D: A Fighter Jet Says Hi",
"id": "-NPmBkYb_mY",
"date": "2018-05-10T16:30:30.000Z"
}
]
Returning just the one array of all the documents combined and in newest-first order.
Is that possible to do and if so how do I accomplish it?
Thanks!
Upvotes: 0
Views: 259
Reputation: 18235
You need to use $unwind
to flat the array items, then promote inner $videos
as new root item along with $sort
to order your videos item.
db.tests.aggregate([
{$match: ...},
{$unwind: "$videos"},
{$replaceRoot: {newRoot: "$videos"}},
{$sort: {date: -1}} // Newest first
])
Upvotes: 1
Reputation: 151112
You need aggregate()
instead. Either using $unwind
and $replaceRoot
where you have it:
return mongoose.model('creators').aggregate([
// { "$match": { ... } }, // A real condition to match the document
{ "$unwind": "$videos" },
{ "$replaceRoot": { "newRoot": "$videoes" } }
])
Or using $project
with all fields explicitly:
return mongoose.model('creators').aggregate([
// { "$match": { ... } }, // A real condition to match the document
{ "$unwind": "$videos" },
{ "$project": {
"_id": "$videos._id",
"name": "$videos.name",
"id": "$videos.id",
"date": "$videos.date"
}}
])
Those are the only things that promote the embedded array content to the "top level" of the document in output.
NOTE: Any query conditions on either the document or array content should be done "before" the $unwind
. This means applying the $match
valid for query conditions and possibly applying a $filter
projection on the array content first. If you don't do those "first" you are creating unnecessary overhead for your application.
The whole process of using $unwind
is really quite expensive anyway. It would be far wiser to actually take a good look at your query patterns and really make a decision if it is in fact worth keeping the documents embedded or if your queries are indeed more frequently suited to placing the documents in a separate collection.
Upvotes: 2