skip to Main Content

I need to change a few database fields in my backend controller before returning the object to the frontend.

Currently, I am doing it in the front end like this with my returned object:

       for (let contribution of contributions) {
          contribution["title"] = "You Added One"
          contribution["launchName"] = contribution.name
          contribution["launchId"] = contribution._id
          contribution["date"] = contribution.addedAt
          contribution["content"] = contribution.description
        }

But I am now trying to do this work in the backend using Mongo.

This is my controller:

const Launch = require('../models/launch')
const User = require('../models/user')
async function getRecentActivityByUserId (req, res) {
  const { userId } = req.params

  const user = await User.findOne({ userId }).lean() || []

  const contributions = await Launch.find({ _id: { $in: user.contributions } })

  return res.status(200).send(contributions.reverse())
}

So this correctly returns an object to the frontend but I still need to change the database field names.

So I tried this:

async function getRecentActivityByUserId (req, res) {
  let recents = []
  const { userId } = req.params

  const user = await User.findOne({ userId }).lean() || []

  const contributions = await Launch.find({ _id: { $in: user.contributions } }).aggregate([
    {
        $addFields: {
            plans: {
                $map:{
                    input: "$launch",
                    as: "l",
                    in: {
                        title: "You Added One",
                        launchName: "$$l.name",
                        launchId: "$$l._id",
                        date: "$$l.addedAt",
                        content: "$$l.description",
                    }
                }
            }
        }
    },
    {
        $out: "launch"
    }
])

  return res.status(200).send(contributions.reverse())
}

The above throws an error saying that I .aggregrate is not a function on .find. Even if I remove the .find, the object returned is just an empty array so I’m obviously not aggregating correctly.

How can I combine .find with .aggregate and what is wrong with my .aggregate function??

I also tried combining aggregate with find like this and get the error Arguments must be aggregate pipeline operators:

  const contributions = await Launch.aggregate([
    {
      $match: {
      _id: { $in: user.contributions }
      },
        $addFields: {
            plans: {
                $map:{
                    input: "$launch",
                    as: "l",
                    in: {
                        title: "You Added a Kayak Launch",
                        launchName: "$$l.name",
                        launchId: "$$l._id",
                        date: "$$l.addedAt",
                        content: "$$l.description",
                    }
                }
            }
        }
    },
    {
        $out: "launch"
    }
])

EDIT: Just realized that I have the word plans in the aggregate function and that is not relevant to my code. I copied this code from elsewhere so not sure what the value should be.

2

Answers


  1. Chosen as BEST ANSWER

    I figured it out. This is the solution:

    async function getRecentActivityByUserId (req, res) {
      let recents = []
      const { userId } = req.params
    
      const user = await User.findOne({ userId }).lean() || []
    
      const contributions = await Launch.aggregate([
        {
          $match: {
            _id: { $in: user.contributions }
          }
        },
        {
          $addFields: {
            title: "You Added One" ,
            launchName: "$name",
            launchId: "$_id",
            date: "$addedAt",
            content: "$description"
          }
        }
     ])
      if(contributions) {
        recents = recents.concat(contributions);
      }
    
      return res.status(200).send(recents.reverse())
    }
    

  2. The actual problem from the question was a small syntax error which has been noted and corrected in the self-reported answer here.

    I noted in the comments there that the current approach of issuing two separate operations (a findOne() followed by an aggregate() that uses the results) could be simplified into a single query to the database. The important thing here is that you will $match against the first collection (users or whatever the collection name is in your environment) and then use $lookup to perform the "match" against the subsequent launches collection.

    Here is a playground demonstrating the basic approach. Adjust as needed to the specifics of your environment.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search