6

I am learning some node and have been trying to use mongoose. Currently, my goal is to learn how to use populate.

I have a projects definition and milestone require:

projectSchema = new mongoose.Schema({
    id: String,
    title: String,
    description: String,
    owner: String,
    site: String,
    creation_date: Date,
    milestone_ids: Array,
    milestones: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: "Milestone"
    }]
})

Project = mongoose.model("Project", projectSchema)
milestones = require(__dirname + "/milestones.js")();

Then I do this at some point in the projects.js:

Project.find(query, {}, {sort: {_id: -1}},
    function (error, results) {
        callback(results);
    }
).populate("milestones");

How do I populate the milestones?


Here is the project data from mongo:

{
    "title": "sitename",
    "description": "online thing",
    "creation_date": {
        "$date": "2013-07-11T19:45:42.139Z"
    },
    "_id": {
        "$oid": "51df0b66dbdd7c4f14000001"
    },
    "milestones": [],
    "milestone_ids": [],
    "__v": 0
}

And this one is the milestone that is basically connected to the project:

{
    "title": "Proof of concept",
    "description": "Make it work.",
    "due_date": {
        "$date": "2013-07-11T19:46:38.535Z"
    },
    "project_id": "51df0b66dbdd7c4f14000001",
    "_id": {
        "$oid": "51df0b9edbdd7c4f14000002"
    },
    "__v": 0
}

Also, this is the milestone schema:

milestoneschema = new mongoose.Schema({
    id: String,
    title: String,
    description: String,
    owner: String,
    site: String,
    due_date: Date,
    project_id: {
        type: String,
        ref: "Project"
    }
})

Milestone = mongoose.model("Milestone", milestoneschema);
Kyll
  • 7,036
  • 7
  • 41
  • 64
Eduárd Moldován
  • 1,495
  • 3
  • 13
  • 29

1 Answers1

13

You need to get the order right of defining query options then executing, and chainable APIs such as mongoose Query can't know what additional methods you might call AFTER the query fires. So when you pass the callback to .find, mongoose sends the query right away.

Pass a callback to find

  • query defined by arguments to find
  • since callback is there, query immediately executes and issues command to DB
  • then .populate happens, but it has no effect as the query has already been sent to mongo

Here's what you need to do:

Project.find(query, {}, {
    sort: {
        _id: -1
    }
}).populate("milestones").exec(function (error, results) {
    callback(results);
});

Or a little more readable:

Project
    .find(query)
    .sort('-_id')
    .populate('milestones')
    .exec(function(error, results) {                  
        callback(results);
    });

Omit callback and use .exec

  • query passed to .find creates query object with parameters
  • additional chained calls to .sort, .populate etc further configure the query
  • .exec tells mongoose you are done configuring the query and mongoose issues the DB command
Community
  • 1
  • 1
Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • Ok, all the things you wrote make perfect sense. The bad thing is that the milestones are still not populated. I get an empty array there exactly like before. – Eduárd Moldován Jul 11 '13 at 19:55
  • Also if you post more code we can look for simple typos and whatnot. The devil is in the details here as mongoose wires all this together based on string names with confounding things like auto-pluralization. One thing lowercase or singular incorrectly and you get incorrect behavior and no errors. From your snippet all looks well at first glance to me though. – Peter Lyons Jul 11 '13 at 21:38
  • You've got `milestone_ids` as well which is probably confusing you and seems like a duplicate. I would remove that from the schema and migrate it off of your data once you have `milestones` populating correctly. – Peter Lyons Jul 11 '13 at 21:40
  • I checked the data, looks fine to me. I pasted it in the post. Do you see anything there that is wrong? – Eduárd Moldován Jul 11 '13 at 23:04
  • Yes `"milestones": [],` your milestones array is empty. Populate is looking at that (the refs in the collection you are querying), one-to-many style, not the reverse. – Peter Lyons Jul 11 '13 at 23:30
  • I'm lost a bit. That should be set on save? Isn't this like a sql join, which joins data from a different table? – Eduárd Moldován Jul 11 '13 at 23:33
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/33314/discussion-between-peter-lyons-and-eduard-moldovan) – Peter Lyons Jul 11 '13 at 23:49
  • I am going to mark this as the right answer, because you provided me with guidance and the right way to do it in the chat. Thank you! – Eduárd Moldován Jul 12 '13 at 22:14
  • 1
    chat link doesnt work. I would like to know how this was resolved. Thanks – ibolton336 Dec 10 '15 at 04:08