Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to work with async code in Mongoose virtual properties?

I'm trying to work with associating documents in different collections (not embedded documents) and while there is an issue for that in Mongooose, I'm trying to work around it now by lazy loading the associated document with a virtual property as documented on the Mongoose website.

The problem is that the getter for a virtual takes a function as an argument and uses the return value for the virtual property. This is great when the virtual doesn't require any async calls to calculate it's value, but doesn't work when I need to make an async call to load the other document. Here's the sample code I'm working with:

TransactionSchema.virtual('notebook')   .get( function() { // <-- the return value of this function is used as the property value     Notebook.findById(this.notebookId, function(err, notebook) {       return notebook; // I can't use this value, since the outer function returns before we get to this code     })     // undefined is returned here as the properties value   }); 

This doesn't work since the function returns before the async call is finished. Is there a way I could use a flow control library to make this work, or could I modify the first function so that I pass the findById call to the getter instead of an anonymous function?

like image 559
Mikey P Avatar asked May 20 '11 21:05

Mikey P


People also ask

What is virtual property in mongoose?

In Mongoose, a virtual is a property that is not stored in MongoDB. Virtuals are typically used for computed properties on documents.

How do you use virtual populate?

For populating virtual, we have to specify three necessary options: ref: It contains the name of the model from which we want to populate the document. localField: It is any field of the current collection. foreignField: It is any field of the collection from which we want to populate the document.

What are virtual methods mongoose?

Virtuals are document properties that do not persist or get stored in the MongoDB database, they only exist logically and are not written to the document's collection.


2 Answers

You can define a virtual method, for which you can define a callback.

Using your example:

TransactionSchema.method('getNotebook', function(cb) {   Notebook.findById(this.notebookId, function(err, notebook) {     cb(notebook);   }) }); 

And while the sole commenter appears to be one of those pedantic types, you also should not be afraid of embedding documents. Its one of mongos strong points from what I understand.

One uses the above code like so:

instance.getNotebook(function(nootebook){     // hey man, I have my notebook and stuff }); 
like image 142
Josh Avatar answered Sep 22 '22 07:09

Josh


While this addresses the broader problem rather than the specific question, I still thought it was worth submitting:

You can easily load an associated document from another collection (having a nearly identical result as defining a virtual) by using Mongoose's query populate function. Using the above example, this requires specifying the ref of the ObjectID in the Transaction schema (to point to the Notebook collection), then calling populate(NotebookId) while constructing the query. The linked Mongoose documentation addresses this pretty thoroughly.

I'm not familiar with Mongoose's history, but I'm guessing populate did not exist when these earlier answers were submitted.

like image 24
Andrew Shooner Avatar answered Sep 24 '22 07:09

Andrew Shooner