'Populate a mongoose model with a field that isn't an id

Is it possible to populate a mongoose model with a field of a reference model that isn't the _id ... e.g. a username.

so something like

var personSchema = Schema({
  _id     : Number,
  name    : String,
  age     : Number,
  stories : { type: String, field: "username", ref: 'Story' }
});


Solution 1:[1]

This is supported since Mongoose 4.5, and is called virtuals population.

You have to define your foreign keys relationships after your schemas definitions and before creating models, like this:

// Schema definitions

BookSchema = new mongoose.Schema({
        ...,
        title: String,
        authorId: Number,
        ...
    },
    // schema options: Don't forget this option
    // if you declare foreign keys for this schema afterwards.
    {
        toObject: {virtuals:true},
        // use if your results might be retrieved as JSON
        // see http://stackoverflow.com/q/13133911/488666
        //toJSON: {virtuals:true} 
    });

PersonSchema = new mongoose.Schema({id: Number, ...});


// Foreign keys definitions

BookSchema.virtual('author', {
  ref: 'Person',
  localField: 'authorId',
  foreignField: 'id',
  justOne: true // for many-to-1 relationships
});


// Models creation

var Book = mongoose.model('Book', BookSchema);
var Person = mongoose.model('Person', PersonSchema);


// Querying

Book.find({...})
    // if you use select() be sure to include the foreign key field !
    .select({.... authorId ....}) 
    // use the 'virtual population' name
    .populate('author')
    .exec(function(err, books) {...})

Solution 2:[2]

It seems they enforce to use _id, and maybe we can customize it in the future.

Here is the issue on Github https://github.com/LearnBoost/mongoose/issues/2562

Solution 3:[3]

This is an example of using the $lookup aggregate to populate a model called Invite with the respective User based on the corresponding email field:

  Invite.aggregate(
      { $match: {interview: req.params.interview}},
      { $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} }
    ).exec( function (err, invites) {
      if (err) {
        next(err);
      }

      res.json(invites);
    }
  );

It's probably quite similar to what you're trying to do.

Solution 4:[4]

To add to Frosty's answer, if you're looking to refer to an array of documents of another collection you would make changes like so.

BookSchema = new mongoose.Schema(
{
    title: String,
    authorId: [Number],
},
// schema options: Don't forget this option
// if you declare foreign keys for this schema afterwards.
{
    toObject: { virtuals: true },
    // use if your results might be retrieved as JSON
    // see http://stackoverflow.com/q/13133911/488666
    toJSON: {virtuals:true}
});

PersonSchema = new mongoose.Schema({ id: Number, name: String });

BookSchema.virtual("author", {
ref: "Person",
localField: ["authorId"],
foreignField: ["id"],
// justOne: true, // Needs to be commented out in this scenario, 
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Lotus
Solution 4