Reputation: 11342
I'm using nodejs
with mongoose library for my mongoDB interaction.
I'm building a landing page query (think Netflix page) and need to get ~15 results for each categories (think Comedy, Horror, Drama, ...).
It seems not like a great solution to send a query for each "category" and deal with the callback issues as well as a heavy request on the database for a simple page load...as illustrated below:
const categories = ['horror','drama','comedy']
let counterCallback = 0
let allShows = []
categories.map(category =>{
Show.find({category: category},{limit: 15}, (err, shows)=>{
if(shows){ allShows.push(shows)}
allShows++
if(allShows === categories.length){ return res.json(allShows) }
})
})
I was thinking maybe of making an $or
request, but I can't limit for the number for each "or"s ...
const categories = ['horror','drama','comedy']
Show.find({
$or:categories.map(category=> {return {category: category} })
},{limit: 10})
How can I achieve a request that limits the number of results per "category"?
Thanks
Upvotes: 1
Views: 306
Reputation: 17915
You can take advantage of MongoDB's aggregation-framework to run complex queries, try below query :
var categories = ['horror', 'comedy', 'drama']
Show.aggregate([
/** This $match would help to limit docs to particular categories, as each pipeline in facet stage has to iterate to all docs in collection for thrice as 3 categories given, So at least limiting docs to needed categories. */
{ $match: { category: { $in: categories } } },
{
$facet: {
'horror': [{ $match: { category: 'horror' } }, { $limit: 1 }],
'comedy': [{ $match: { category: 'comedy' } }, { $limit: 1 }],
'drama': [{ $match: { category: 'drama' } }, { $limit: 1 }]
}
}])
Test : MongoDB-Playground
Alternatively you can achieve that using $group
as well :
Show.aggregate([
{ $match: { category: { $in: categories } } },
{ $group: { _id: '$category', shows: { $push: '$$ROOT' } } },
{ $project: { category: '$_id', _id: 0, shows: { $slice: ['$shows', 1] } } },
/** Below two stages are optional */
{ $group: { _id: '', data: { $push: '$$ROOT' } } },
{ $project: { _id: 0 } }
])
Test : MongoDB-Playground
Upvotes: 2
Reputation: 13103
You may use MongoDB Aggregation framework with $group
operation.
I assume you have MongoDB v4.2. For earlier versions, instead of {$replaceWith:"$data"}
use {$ReplaceRoot:{newRoot:"$data"}}
Show.aggregate([
{
$match: {
category: {
$in: [
"horror",
"comedy",
"drama"
]
}
}
},
{
$group: {
_id: "$category",
data: {
$push: "$$ROOT"
}
}
},
{
$addFields: {
data: {
$slice: [
"$data",
15
]
}
}
},
{
$unwind: "$data"
},
{
$replaceWith: "$data"
}
]).exec((err, shows) => {
if (err) throw err;
console.log(shows);
})
Upvotes: 2