My app can search through a database of resources using MongoDB's aggregation pipeline. Some of these resources are marked as sponsored
via a property.
I want to show these sponsored entries first (already done) but I want to show only one of them.
What I have now is this:
What I want:
Below is my aggregation code (with Mongoose syntax). How can I skip elements with sponsored: true
except for the first one?
[...]
const matchFilter: { approved: boolean, type?: QueryOptions, format?: QueryOptions, difficulty?: QueryOptions, language?: QueryOptions }
= { approved: true }
if (typeFilter) matchFilter.type = { $in: typeFilter };
if (formatFilter) matchFilter.format = { $in: [...formatFilter, 'videoandtext'] };
if (difficultyFilter) matchFilter.difficulty = { $in: difficultyFilter };
if (languageFilter) matchFilter.language = { $in: languageFilter };
const aggregationResult = await Resource.aggregate()
.search({
compound: {
must: [
[...]
],
should: [
[...]
]
}
})
[...]
.sort(
{
sponsored: -1,
_id: 1
}
)
.facet({
results: [
{ $match: matchFilter },
{ $skip: (page - 1) * pageSize },
{ $limit: pageSize },
],
totalResultCount: [
{ $match: matchFilter },
{ $group: { _id: null, count: { $sum: 1 } } }
],
[...]
})
.exec();
[...]
One option is to change your $facet
a bit:
$match
out of the $facet
since it is relevant to all pipelines.sponsored
documents.$project
to concatenate sponsored
and notSponsored
docsdb.collection.aggregate([
{$sort: {sponsored: -1, _id: 1}},
{$match: matchFilter },
{$facet: {
notSponsored: [
{$match: {sponsored: false}},
{$skip: (page - 1) * pageSize },
{$limit: pageSize },
],
sposerted: [
{$match: {sponsored: true}},
{$limit: numberOfSponsoreditemsWanted}
],
count: [
{$match: {sponsored: false}},
{$count: "total"}
]
}
},
{$project: {
results: {$concatArrays: ["$sposerted", "$notSponsored"]},
totalResultCount: {$first: "$count.total"}
}
}
])
See how it works on the playground example