Cade Embery
Cade Embery

Reputation: 450

Mongoose populate ObjectID from multiple possible collections

I have a mongoose model that looks something like this

var LogSchema = new Schema({
    item: {
        type: ObjectId,
        ref: 'article',
        index:true,
    },
});

But 'item' could be referenced from multiple collections. Is it possible to do something like this?

var LogSchema = new Schema({
    item: {
        type: ObjectId,
        ref: ['article','image'],
        index:true,
    },
});

The idea being that 'item' could be a document from the 'article' collection OR the 'image' collection.

Is this possible or do i need to manually populate?

Upvotes: 8

Views: 6902

Answers (3)

Llawliet
Llawliet

Reputation: 133

Dynamic References via refPath

Mongoose can also populate from multiple collections based on the value of a property in the document. Let's say you're building a schema for storing comments. A user may comment on either a blog post or a product.

  body: { type: String, required: true },
  on: {
    type: Schema.Types.ObjectId,
    required: true,
    // Instead of a hardcoded model name in `ref`, `refPath` means Mongoose
    // will look at the `onModel` property to find the right model.
    refPath: 'onModel'
  },
  onModel: {
    type: String,
    required: true,
    enum: ['BlogPost', 'Product']
  }
});

const Product = mongoose.model('Product', new Schema({ name: String }));
const BlogPost = mongoose.model('BlogPost', new Schema({ title: String }));
const Comment = mongoose.model('Comment', commentSchema);

Upvotes: 3

Lucas Matuszewski
Lucas Matuszewski

Reputation: 337

Question is old, but maybe someone else still looks for similar issues :)

I found in Mongoose Github issues this:

mongoose 4.x supports using refPath instead of ref:

var schema = new Schema({
  name:String,
  others: [{ value: {type:mongoose.Types.ObjectId, refPath: 'others.kind' } }, kind: String }]
})

In @CadeEmbery case it would be:

var logSchema = new Schema({
  item: {type: mongoose.Types.ObjectId, refPath: 'kind' } },
  kind: String
})

But I did't try it yet...

Upvotes: 9

Orelsanpls
Orelsanpls

Reputation: 23515

First of all some basics

The ref option says mongoose which collection to get data for when you use populate().

The ref option is not mandatory, when you do not set it up, populate() require you to give dynamically a ref to him using the model option.

@example

 populate({ path: 'conversation', model: Conversation }).

Here you say to mongoose that the collection behind the ObjectId is Conversation.

It is not possible to gives populate or Schema an array of refs.

Some others Stackoverflow people asked about it.


Soluce 1: Populate both (Manual)

Try to populate one, if you have no data, populate the second.


Soluce 2: Change your schema

Create two link, and set one of them.

var LogSchema = new Schema({
    itemLink1: {
        type: ObjectId,
        ref: 'image',
        index: true,
    },
    itemLink2: {
        type: ObjectId,
        ref: 'article',
        index: true,
    },
});


LogSchema.find({})
     .populate('itemLink1')
     .populate('itemLink2')
     .exec()

Upvotes: 4

Related Questions