8

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?

Cade Embery
  • 433
  • 1
  • 5
  • 18

3 Answers3

8

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...

Manglu
  • 10,744
  • 12
  • 44
  • 57
  • 1
    This is the option I was missing so badly in SQL RDBMS. Reference: https://mongoosejs.com/docs/populate.html#dynamic-ref – Kris Jobs Sep 23 '19 at 13:06
4

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()
Community
  • 1
  • 1
Orelsanpls
  • 22,456
  • 6
  • 42
  • 69
  • 1
    Ahhh! Thanks i didn't know that you could pass the model into the populate( ) function! That's a neat trick, unfortunately Solution 1 is the only option for me with this project. thanks! – Cade Embery Nov 18 '16 at 10:51
2

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);
Llawliet
  • 123
  • 1
  • 2
  • 8