skip to Main Content

I have a Project entity (projects collection). Each Project belongs to an organization represented by Project.orgId and has an author Project.author taken from users collection.

I want to query the projects that belong to an organization using Mongoose, and here’s the pipeline I came up with:

Project.aggregate([
  {
    $match: { organization: new mongoose.Types.ObjectId("an-object-id") }
  },
  {
    $lookup: {
      from: User.collection.collectionName,
      localField: "authorId",
      foreignField: "_id",
      as: "author",
    },
    /* Tried moving unset here. Doesn't work. Error: Arguments must be aggregate pipeline operators
    {
      "$unset": ["__v", "hash"],
    },
    */      
  },
  {
    $unset: ["author.__v", "author.hash"]
  }
])

The problem with this pipeline, is that instead of project.author being an object, it turns into a single-element array.

Expected

{
  "codename": "Longhorn",
  "launch": 1970-01-01T00:00:00Z,
  "author": {
    "firstName": "John",
    "lastName": "Smith"
  }
}

Actual

{
  "codename": "Longhorn",
  "launch": 1970-01-01T00:00:00Z,
  "author": [
    {
      "firstName": "John",
      "lastName": "Smith"
    }
  ]
}

What is the proper pipeline?

2

Answers


  1. To deconstruct the array into multiple documents, you need the $unwind stage:

    Project.aggregate([
      {
        $match: { organization: new mongoose.Types.ObjectId("an-object-id") }
      },
      {
        $lookup: {
          from: User.collection.collectionName,
          localField: "authorId",
          foreignField: "_id",
          as: "author",
        }
      },
      {
        $unwind: "$author",
      },
      {
        $unset: ["author.__v", "author.hash"]
      }
    ])
    
    Login or Signup to reply.
  2. That’s just how $lookup works. It will always give you an array since it can potentially match multiple documents in the other collection.

    You can convert the array to the first document in the array using $addFields in the step after.

    Project.aggregate([
      {
        $match: { organization: new mongoose.Types.ObjectId("an-object-id") }
      },
      {
        $lookup: {
          from: User.collection.collectionName,
          localField: "authorId",
          foreignField: "_id",
          as: "author",
        },    
      },
      {
        $addFields: {
          author: { $first: "$author" },
        },
      },
      {
        $unset: ["author.__v", "author.hash"]
      }
    ])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search