Home > Net >  Apply match filter but ignore first element
Apply match filter but ignore first element

Time:07-19

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:

  1. You can get the $match out of the $facet since it is relevant to all pipelines.
  2. instead of two pipelines, one for the results and one for the counting, we have now three: one more for sponsored documents.
  3. $project to concatenate sponsored and notSponsored 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

  • Related