tsurantino
tsurantino

Reputation: 1027

Mongoose: Merging two documents that reference each other

I am currently trying to learn how to work with NoSQL, coming from a relational database background. In this project, I am using Express with Mongoose.

I am struggling with callbacks as I try to merge two models together, which reference each other. I am trying to edit each item in a group of one model (Ribbits) to contain the attributes of another (Users who posted a Ribbit). Because the call to find the User associated with a Ribbit is asynchronous, I am unable to return the collection of edited Ribbits (with user info).

In my website, I have ribbits (a.k.a. tweets) which belong to users. Users can have many ribbits. In one of my pages, I would like to list all of the ribbits on the service, and some information associated with the user who posted that ribbit.

One solution I found was embedded documents, but I discovered that this is, in my case, limited to showing ribbits which belong to a user. In my case, I want to start by getting all of the ribbits first, and then, for each ribbit, attach info about who posted that.

Ideally, I'd want my schema function to return an array of Ribbit objects, so that I can then render this in my view.

// models/user.js
var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var userSchema = Schema({
  username: String,
  email: String,
  password: String,
  name: String,
  profile: String,
  ribbits: [{
    type: Schema.Types.ObjectId,
    ref: 'Ribbit',
  }]
});

module.exports = mongoose.model('User', userSchema);

// models/ribbit.js
var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    User = require('./user');

var ribbitSchema = Schema({
  content:  { type: String, maxlength: 140 },
  created:  { type: Date, default: Date.now() },
  owner:    { type: Schema.Types.ObjectId, ref: 'User' },
});

ribbitSchema.methods.getOwnerObj = function(cb) {
  return User.findOne({ _id: this.owner }, cb);
}

ribbitSchema.statics.getAllRibbits = function(cb) {
  this.find({}, function(err, ribbits) {
    console.log('Before Transform');
    console.log(ribbits);

    ribbits.forEach(function(ribbit) {
      ribbit.getOwnerObj(function(err, owner) {
        ribbit = {
          content: ribbit.content,
          created: ribbit.created,
          owner: {
            username: owner.username,
            email: owner.email,
            name: owner.name,
            profile: owner.profile,
          }
        };
      });
    });
  });
}

module.exports = mongoose.model('Ribbit', ribbitSchema);

Upvotes: 0

Views: 782

Answers (1)

hassansin
hassansin

Reputation: 17498

If I understand correctly, you can use Mongoose populate method for this scenario:

ribbitSchema.statics.getAllRibbits = function(cb) {
  this.find({}).populate('owner').exec(function(err, ribbits){
    console.log(ribbits[0].owner)
    return cb(err, ribbits);
  })
}

Upvotes: 1

Related Questions