mongoose aggregate query to filter and sort data

asela daskon

In my front-end react.js application use to filter and sort data fetching from mongoDB database. I want to move this logic into back-end mongoose query. how can I achieve this? I tried to refactor the code as below but it's making some errors in my code editor.

"error": "MongoServerError: Invalid $addFields :: caused by :: Unrecognized expression '$con'"

Front-end filter and sort

 try {
  const { data } = await getMobilePosts(skip);
  const filterData = await data?.filter(obj => obj.adPromote == "free-ad" || obj.adPromote == "reach-up-ad")
      .sort((f,p)=>{
          if (f.adPromote === 'reach-up-ad' && p.adPromote !== 'reach-up-ad') {
              return -1;
            } else if (f.adPromote !== 'reach-up-ad' && f.adPromote === 'reach-up-ad') {
              return 1;
            }
          return 0;
      })
      .map(obj => {
          return {
              ...obj,
              title: `${obj.title} (${obj.features.condition})`
          }
      });
  setPost([...post, ...filterData]);
  setSkip(filterData.length);
} catch (err) {
    console.log(err)
}

back-end query that tried so far.

           Post.aggregate([
                          {
                            $match: {
                              $and: [
                                {
                                  expiredAt: {$gte: currentDate}
                                },
                                {
                                  "status": "live"
                                }
                              ]
                            }
                          },
                          {
                            "$addFields": {
                              "adpromoTypes": {
                                "$con": {
                                   "if": {
                                      "$and": {
                                        "adPromote": {
                                          "$eq": 'reach-up-ad'
                                        },
                                      }
                                   }
                                }
                              }
                            }
                          },
                          {
                            $sort: {
                              "_id": -1,
                              "adpromoTypes": -1
                            }
                          },
                          {
                            "$skip": skip
                          },
                          {
                            "$limit": PER_REQUEST
                          },
                          {
                            $project: {
                              "title": 1,
                            }
                          }
              ]);

I have mongoDB collection with below fields:

POST document

  • title (string)
  • adPromote (string ) ex. 'reach-up-ad' , 'free-ad' , 'urgent-ad'
  • expiredAt (date)
  • status (string)

I want to filter 'reach-up' ads on the top of the list and then after display 'free-ad'

ex:
- reach up ad
- reach up ad
- free ad
.........

SKIP & LIMIT

skip and limit ads should work like this. When loading ads at the first time I want to show only 5 ads (limit 5). In this 5 ads I want to show if available 'reach-up-ad'. Next I want to skip 5 ads that already loaded. In this set of ad if available again I want to show another 'reach-up-ad'. After that I want to load 'free-ad' continuously but 5 at a time.

Fourchette

thanks to your update i worked on a solution.

You can check it on mongoplayground.

Here is my code with comments in it:

db.collection.aggregate([
  {
    //match documents not expired
    //with "live" status
    //where adPromote in ['reach-up-ad', 'free-ad'] to remove 'urgent-ad'
    $match: {
      $and: [
        {
          expiredAt: {
            $gte: ISODate("2023-08-04T12:02:50.000Z")
          }
        },
        {
          "status": "live"
        },
        {
          adPromote: {
            $in: [
              "reach-up-ad",
              "free-ad"
            ]
          }
        }
      ]
    }
  },
  {
    $addFields: {
      // add a "prioritySort" field to allow custom sort according the value of "adPromote" field
      prioritySort: {
        $switch: {
          branches: [
            {
              case: {
                $eq: [
                  "$adPromote",
                  "reach-up-ad"
                ]
              },
              then: 3
            },
            {
              case: {
                $eq: [
                  "$adPromote",
                  "free-ad"
                ]
              },
              then: 2
            },
            {
              case: {
                $eq: [
                  "$adPromote",
                  "urgent-ad"
                ]
              },
              then: 1
            }
          ],
          default: 0
        }
      },
      //add other custom field here
      //coolTitle: {$concat: ["$title"," is a cool title"]}
      
    }
  },
  // sort by prioritySort field added
  // sort then by _id to keep same order and have consistent result
  {
    $sort: {
      prioritySort: -1,
      _id: 1
    }
  },
  // skip 1 value
  {
    $skip: 1
  },
  // take the 3 next
  {
    $limit: 3
  },
  //optional remove added fields with unset
  {
    $unset: [
      "prioritySort",
      // "coolTitle"
      
    ]
  }
])

Make sure to change expiredDate, skip and limit values by your values. Change also db.collection by Posts to make it work.

I wasn't sure if you wanted to keep other type of ads so i made a filter to keep only 'reach-up-ad' and 'free-ad'. You can remove this part if it doesn't apply to your issue.

Hope that helps. Ask me any question if it doesn't !

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related