How can I query all documents that contain same username after populate in Mongo

Viewed 589

I am trying to get all documents that match the username field with the same value.

Ex. if username is Mabel, then return every document where after populate, the username matches the name Mabel

Project Model:

{
    "_id": "5e641a033ecd5c172827eba6",
    "title": "My Post",
    "description": "First post",
    "userId": "5e431ba109c629018d664d89",
    "createdAt": "2020-03-07T22:02:43.758Z",
    "__v": 0
}

After Populate: (User model populated inside of Project Model)

{
    "_id": "5e641a033ecd5c172827eba6",
    "title": "My Post",
    "description": "First post",
    "userId": {
        "role": "user",
        "_id": "5e431ba109c629018d664d89",
        "username": "Mabel",
        "createdAt": "2020-02-11T21:24:49.932Z"
    },
    "createdAt": "2020-03-07T22:02:43.758Z",
    "__v": 0
}

What have I tried:

const projects = await Projects.find().populate({
    path: "userId",
    match: { username: { $in: username } }
  });

But end up getting all documents from all other usernames.

What I need?

Right now, Mabel has three projects created. Those three projects belong to the same userId.username. How can I query for all those documents from that username?

1 Answers

I guess you are trying to get user with his/her projects.

You can do this easily with aggregation framework.

We first match the user with username, and then use $lookup aggregation to get his/her projects.

const Users = require("../models/user");   //CHANGE this path according to your user model 
const Projects = require("../models/project");  //CHANGE this path according to your project model 

router.get("/users", async (req, res) => {
  let username = "Mabel";
  const result = await Users.aggregate([
    {
      $match: {
        username: username
      }
    },
    {
      $lookup: {
        from: "projects",  //MUST be the physical name of the collection
        localField: "_id",
        foreignField: "userId",
        as: "projects"
      }
    }
  ]);

  res.send(result);
});

Playground

Sample documents:

db={
  "users": [
    {
      "_id": "5e6a293637a3d101b488278d",
      "username": "Mabel",
      "role": "user",
      "__v": 0
    },
    {
      "_id": "5e6a294537a3d101b488278e",
      "username": "Tom",
      "role": "user",
      "__v": 0
    },
    {
      "_id": "5e6a294f37a3d101b488278f",
      "username": "Bob",
      "role": "user",
      "__v": 0
    }
  ],
  "projects": [
    {
      "_id": "5e6a298537a3d101b4882790",
      "title": "My Post",
      "description": "First post",
      "userId": "5e6a293637a3d101b488278d",
      "__v": 0
    },
    {
      "_id": "5e6a29a237a3d101b4882791",
      "title": "My Post 2",
      "description": "Second post",
      "userId": "5e6a293637a3d101b488278d",
      "__v": 0
    },
    {
      "_id": "5e6a29bd37a3d101b4882792",
      "title": "Tom post",
      "description": "Tom post description",
      "userId": "5e6a294537a3d101b488278e",
      "__v": 0
    },
    {
      "_id": "5e6a2a0837a3d101b4882793",
      "title": "Bob post",
      "description": "Bob post description",
      "userId": "5e6a294f37a3d101b488278f",
      "__v": 0
    }
  ]
}

Output:

[
  {
    "__v": 0,
    "_id": "5e6a293637a3d101b488278d",
    "projects": [
      {
        "__v": 0,
        "_id": "5e6a298537a3d101b4882790",
        "description": "First post",
        "title": "My Post",
        "userId": "5e6a293637a3d101b488278d"
      },
      {
        "__v": 0,
        "_id": "5e6a29a237a3d101b4882791",
        "description": "Second post",
        "title": "My Post 2",
        "userId": "5e6a293637a3d101b488278d"
      }
    ],
    "role": "user",
    "username": "Mabel"
  }
]

Second alternative:

If you want to use populate instead of aggregation, you need to setup virtual populate. Because we don't have project reference inside the user.

So to setup virtual populate, we make this changes in the user schema:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const UserSchema = new Schema(
  {
    username: String,
    role: String
  },
  {
    toJSON: { virtuals: true } //don't forget this option
  }
);

UserSchema.virtual("projects", {
  ref: "Project", // Project here must match mongoose.model("Project", ProjectSchema);
  foreignField: "userId",
  localField: "_id"
});

module.exports = mongoose.model("User", UserSchema);

And now we can get the user and his/her projects like this:

router.get("/users", async (req, res) => {
  let username = "Mabel"; 

  const result = await Users.find({ username }).populate("projects");

  res.send(result);
});
Related