JJ McGregor
JJ McGregor

Reputation: 192

Can't get populate() to fill out array in Mongoose

Let me begin by saying I know that this seems to be a frequently asked question and I've spent a couple of days trying to figure it out but no answer seems to work so I'm trying on here.

I have two models, User and Chapter: a Chapter can have have many members (Users). When I do router.get('/chapters') I want to see an array of all the Users associated with a Chapter as a property along with the other Chapter properties, like so:

[
    {
        "leads": [],
        "members": [
           {"_id":"someString1","firstName":"...", "lastName":"..."},
           {"_id":"someString2","firstName":"...", "lastName":"..."},
        ],
        "date": "2018-12-12T15:24:45.877Z",
        "_id": "5c11283d7d13687e60c186b3",
        "country": "5c11283d7d13687e60c185d6",
        "city": "Buckridgestad",
        "twitterURL": "qui",
        "bannerPic": "http://lorempixel.com/640/480/people",
        "__v": 0
    }
]

But what I'm getting is this:

  [
        {
            "leads": [],
            "members": [],
            "date": "2018-12-12T15:24:45.877Z",
            "_id": "5c11283d7d13687e60c186b3",
            "country": "5c11283d7d13687e60c185d6",
            "city": "Buckridgestad",
            "twitterURL": "qui",
            "bannerPic": "http://lorempixel.com/640/480/people",
            "__v": 0
        }
    ]

These are my Schemas:

Chapter

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

// Create Schema
const ChapterSchema = new Schema({
  country: {
    type: Schema.Types.ObjectId,
    ref: "countries"
  },
  city: {
    type: String,
    required: true
  },
  leads: [
    {
      type: Schema.Types.ObjectId,
      ref: "users"
    }
  ],
  members: [
    {
      type: Schema.Types.ObjectId,
      ref: "users"
    }
  ],
  twitterURL: {
    type: String,
    required: true
  },
  bannerPic: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now()
  }
});

module.exports = Chapter = mongoose.model("chapters", ChapterSchema);

User

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

// Create Schema
const UserSchema = new Schema({
  username: {
    type: String,
    required: true
  },
  firstName: {
    type: String,
    required: true
  },
  lastName: {
    type: String,
    required: true
  },
  organisation: {
    type: String,
    required: true
  },
  chapter: {
    type: Schema.Types.ObjectId,
    ref: "chapters"
  },
  email: {
    type: String,
    required: true
  },
  admin: {
    type: Boolean,
    default: false
  },
  lead: {
    type: Boolean,
    default: false
  },
  password: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now()
  }
});

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

Like I said, when I call the endpoint, I want it to return me all the Chapters with all the Users as a populated property.

I've tried a lot of variations of .populate() but to know luck. The closest I got was going through the early levels of callback hell which I know isn't necessary with today's tech, but nothing is working!

My routes/api/chapters.js

// @route    GET api/chapters
// @desc     Get all Chapters
// @access   Public

router.get("/", (req, res) => {
  Chapter.find()
    .populate("members")
    .then(chapters => {
      return res.json(chapters);
    })
    .catch(err =>
      res.status(404).json({ nochaptersfound: "No Chapters found" })
    );
});

I can get it to work the other way around:

My routes/api/users.js

// @route    GET api/users
// @desc     Return all users
// @access   Public

router.get("/", (req, res) => {
  User.find()
    .populate("chapter")
    .exec()
    .then(users => res.status(200).json(users))
    .catch(err => console.log(err));

Returns a user with the populated Chapter, but I can't populate the chapter.members array

Any help would be greatly appreciated!

Thanks!!

Upvotes: 0

Views: 67

Answers (1)

iagowp
iagowp

Reputation: 2494

From your comment, I believe you are not actually storing users in your chapters. What you are doing is this:

User.create({..., chapter: id})...

And assuming chapter now has a user. Its not the way it works with mongoose, so if you want to actually save in both place, you will need to do it yourself. You are thinking about this as if it were a relational database

You will need to do something like:

const user = await User.create({..., chapter: id})
const chapter = await Chapter.findOne({ _id: id })
chapter.members.push(user)
chapter.save()

If your populate wasn't working, you'd not get an empty array, you'd get an array with ids. Your current populate query is fine, you just don't have any data to populate

With promises, it would look like this:

var userPromise = User.create({..., chapter: id}).exec()
var chapterPromise = Chapter.findOne({ _id: id }).exec()
Promise.all([userPromise, chapterPromise]).then((user, chapter) => {
  chapter.members.push(user)
  return chapter.save()
}).then(chapter => {
  // send response
})

If you need 10 chapters with 10 to 50 users, I'd create 50 users, then push all of them into the chapters and save the chapter.

Upvotes: 1

Related Questions