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:
- [sponsored result #1]
- [sponsored result #2]
- [sponsored result #3]
- [organic result #1]
- [organic result #2]
- [organic result #3]
- [...]
What I want:
- [sponsored result #1]
- [organic result #1]
- [organic result #2]
- [organic result #3]
- [...]
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();
[...]
CodePudding user response:
One option is to change your $facet
a bit:
- You can get the
$match
out of the$facet
since it is relevant to all pipelines. - instead of two pipelines, one for the results and one for the counting, we have now three: one more for
sponsored
documents. $project
to concatenatesponsored
andnotSponsored
docs
db.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