28

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' }
});
angulord
  • 453
  • 1
  • 4
  • 7

5 Answers5

102

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) {...})
Maxime Pacary
  • 22,336
  • 11
  • 85
  • 113
3

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

Leo Gao
  • 629
  • 1
  • 6
  • 17
  • 1
    This github issue was closed in favor of using the new [virtuals population](https://mongoosejs.com/docs/populate.html) – steampowered Aug 19 '19 at 18:42
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.

Lotus
  • 2,596
  • 1
  • 22
  • 20
1

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, 
});
JLange
  • 47
  • 1
  • 6
-7

You may use the populate() API. The API is more flexible, you don't have to specify ref and field in the Schema.

http://mongoosejs.com/docs/api.html#document_Document-populate http://mongoosejs.com/docs/api.html#model_Model.populate

You can mix and match with find().

leesei
  • 6,020
  • 2
  • 29
  • 51