Jeffrey Kevin Pry Jeffrey Kevin Pry - 1 year ago 65
Node.js Question

Node Mongoose virtual property return children matching criteria

In a Mongoose application I can use virtual functions to look up objects that are children by ref.

The question I am have is, given a parent object that has a ref relationship to many children objects with two dates (start_date, end_date).

Parent Object:

{
"id": 12345,
"children": [...] // <= A virtual property to the child objects below.
}


Child Objects

[{
"parent": 12345,
"start_date": "2016-01-01",
"end_date": "2016-02-01"
},
{
"parent": 12345,
"start_date": "2016-02-02",
"end_date": "2016-03-01"
}]


Ideally I would like to have a virtual property called current that returns the child object where the current date falls between start_date and end_date.

As an example, if today is "2016-02-20", I would like the result to look like this:

{
"id": 12345,
"children": [...], // <= A virtual property to the child objects below.
"current": {
"parent": 12345,
"start_date": "2016-02-02",
"end_date": "2016-03-01"
}
}


I tried looking up the child property in the virtual function, but it seems that since it is a promise, it always returns null. I wasn't sure if there was an easier way to do this, but I would really appreciate any ideas.

This is what I tried, but returns null always. Even if I log to the console and the result shows there:

ParentSchema
.virtual('current')
.get(function () {
var result = null;
ChildModel.find({parent: this._id}, function (err, results) {
// ... some logic here to find the correct item. (Omitted for brevity).
result = foundItem;
});
return result;
})


Thank you very much!

Answer Source

Remember mongoose operations are asynchronous, so you need to wait for their callback to be called before getting the result.

ParentSchema.virtual('current').get(function () {
    var result = null;
    ChildModel.find({parent: this._id}, function callback(err, children) {
        // ...
        result = child;
    });
    // by the time it reaches this point, the async function ^ will not yet be finished -- so result will always be null
    return result; 
})

(1) To use a virtual property, you would have to return a Promise instead of the value.

ParentSchema.virtual('current').get(function () {
    var self = this;
    return ChildModel.find({ parent: self._id }, function (err, children) {
        // ...
        self.current = child;
    });
})

You would then use it like

parent.current.then(function () {
    console.log(parent.current);
}).catch(function (err) {
    // ...
})

(2) I think it's better to do use a method instead.

ParentSchema.methods.getCurrent(function (callback) {
    var self = this;
    ChildModel.find({ parent: self._id }, function (err, children) {
        if (err) return callback(err);
        // ...
        self.current = child;
        callback();
    });
});

You would then use it like

parent.getCurrent(function (err) {
    console.log(parent.current);
})
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download