How to work with async code in Mongoose virtual properties? How to work with async code in Mongoose virtual properties? mongoose mongoose

How to work with async code in Mongoose virtual properties?


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});


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.


Josh's approach works great for single document look-ups, but my situation was a little more complex. I needed to do a look-up on a nested property for an entire array of objects. For example, my model looked more like this:

var TransactionSchema = new Schema({  ...  , notebooks: {type: [Notebook]}});var NotebookSchema = new Schema({  ...  , authorName: String  // this should not necessarily persist to db because it may get stale  , authorId: String});var AuthorSchema = new Schema({  firstName: String  , lastName: String});

Then, in my application code (I'm using Express), when I get a Transaction, I want all of the notebooks with author last name's:

...TransactionSchema.findById(someTransactionId, function(err, trans) {  ...  if (trans) {    var authorIds = trans.notebooks.map(function(tx) {        return notebook.authorId;      });    Author.find({_id: {$in: authorIds}, [], function(err2, authors) {      for (var a in authors) {        for (var n in trans.notebooks {          if (authors[a].id == trans.notebooks[n].authorId) {            trans.notebooks[n].authorLastName = authors[a].lastName;            break;          }        }      }      ...    });

This seems wildly inefficient and hacky, but I could not figure out another way to accomplish this. Lastly, I am new to node.js, mongoose, and stackoverflow so forgive me if this is not the most appropriate place to extend this discussion. It's just that Josh's solution was the most helpful in my eventual "solution."