Home > Software design >  Rename database field within array in MongoDB
Rename database field within array in MongoDB

Time:10-04

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.

CodePudding user response:

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())
}

CodePudding user response:

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.

  • Related