title: Mongoose getter function doesn't return updated array when making async calls
body: <p>I have an app which makes use of two models: Movie and Tag. The idea is that one can create a record of a movie and attach tags to it.  A Movie references one or more Tag models via the Movie.tags attribute which is a array containing the ObjectId of corresponding Tags.</p>
  
  <p>When displaying the properties of a Movie on the client side it makes sense to display the text of the tag, rather than the ObjectId of the tag (remember: Movie.tags is an array of ObjectIds).  I thought about the problem and concluded that the best method would be to make use of a getter function, so that when I retrieve a Movie document, the value of tags attribute is converted from an array of ObjectIds into an array of corresponding tag names.</p>
  
  <p>To do this I must perform a db query for each ObjectId in the array Movie.tags. Since db queries are done asynchronously in Mongoose, I tried implementing the getter function using <a href="https://github.com/caolan/async#forEach" rel="nofollow">async.forEach()</a> function from the Async module.  The problem is that the final value is not being returned at the end of the async.forEach function.  </p>
  
  <p>I have two questions regarding this problem:</p>
  
  <ol>
  <li>Given my overall objective, is using a getter function the best way of going about this?</li>
  <li>Why is async.forEach() failing to return the updated array?</li>
  </ol>
  
  <p>from model.js
  </p>
  
  <pre class="lang-js prettyprint-override"><code>/**
   * Mongo database models
   */
  
  function defineModels(mongoose, async, fn) {
    var Schema = mongoose.Schema,
        ObjectId = Schema.ObjectId;
  
    /**
     * Model - Movie
     */
  
    /**
     * Getter function
     * 
     * Gets tag text as well as tag ObjectId.
     */
    function getTagNames(tags) {
      var newArray = new Array();
      async.forEach(
        tags,
        function(id, done) {
          mongoose.models['Tag'].findOne({ _id: id }, function(err, doc) {
            if (err) {
              done(err);
            }
            else if (doc) {
              newArray.push(doc);
              done(null);
            }
            else {
              console.log(doc);
              // Just incase something weird like no document is found.  
              // Technically this condition should not occur in reality. But we 
              // put something here to catch it just in case.
              done(new Error('No tag document found.'));
            }
          });
        },
        function(err) {
          if (err) {
            throw err;
          }
          console.log(newArray);
          return newArray;
        }
      );
    }
  
    /**
     * Define schema
     */
    Movie = new Schema({
      'name': String,
      'machineFileName': String,
      'originalFileName': String,
      'size': Number,
      'type': String,
      'permanent': { 
        type: Boolean, 
        default: false 
      },
      'dateUploaded': Date,
      'amountUploaded': {
        type: [], 
        default: 0 
      },
      'viewed': Number,
      'uid': String,
      'flags': [],
      'tags': {
        type: Array, 
        get: getTagNames
      }
    }, { strict: true });
  
    mongoose.model('Movie', Movie);
  
    /**
     * Model - Tag
     */
    Tag = new Schema({
      'title': { type: String, unique: true, sparse: true }
    }, { strict: true });
  
    mongoose.model('Tag', Tag);
  
    fn();
  }
  
  exports.defineModels = defineModels; 
  </code></pre>
  
  <p>Retrieving the document:</p>
  
  <pre class="lang-js prettyprint-override"><code>/**
   * View individual movie.
   */
  exports.movie = function(req, res) {
    var Movie = mongoose.model('Movie');
    Movie.findById(req.params.id, function(err, doc) {
      if (err) {
        res.send('An error occured.', 500);
      }
      else {
        console.log('View movie');
        console.log(doc);
        res.render('movie', {
          locals: {
            title: 'Watch Movie',
            movie: doc
          }
        });
      }
    });
  }
  </code></pre>