I'm looking for the best way to have a parent model that references variable child models. Is there a way to set the reference upon saving the parent model, then have it populate the child automatically? If not, what is the best way of achieving a parent model that has variable sub-fields.
var ChildSchema1 = new Schema({
field1: String,
field2: String
});
var ChildSchema2 = new Schema ({
field3: Number,
field4: String
});
var ParentSchema = new Schema({
name: {type: String, required: true},
child_ref: {type: ObjectId, ref: ChildSchema1 OR ChildSchema2}
});
I had a similar problem, where I needed to do schema inheritance. It seems like this github project will work for us both:
https://github.com/briankircho/mongoose-schema-extend
Related
If I have a model Attachment, which can be divided into 4 types: Link, YoutubeVideo, GoogleDriveFile, and GoogleDriveFolder, how can I use Mongoose to discriminate Attachment into these types, and allow them to be subdocuments in another schema; Post?
I've created the base Attachment model, and divided it into separate models using discriminators:
var AttachmentSchema = new Schema({
id: {type: String, required: true},
title: {type: String, required: true}
});
var Attachment = mongoose.model('Material', AttachmentSchema);
module.exports = {
DriveFile: Attachment.discriminator('GoogleDriveFile', new mongoose.Schema()),
DriveFolder: Attachment.discriminator('GoogleDriveFolder', new mongoose.Schema()),
Link: Attachment.discriminator('Link', new mongoose.Schema()),
YoutubeVideo: Attachment.discriminator('YoutubeVideo', new mongoose.Schema())
};
Now, in the Post schema, there should be an array of attachments, with varying types:
var Attachment = require('./attachment');
var PostSchema = new Schema(
text:{type: String},
attachments: [Material] // Could be Material.Link, Material.YoutubeVideo, etc
});
When I do this, I get an error saying "Undefined type Model at GoogleDriveFile. Did you try nesting Schemas? You can only nest using refs or arrays."
I don't know what this error means, and I can't find any docs explaining how to do this. Help?
Try doing the following:
var AttachmentSchema = new Schema({
id: {type: String, required: true},
title: {type: String, required: true}
});
var PostSchema = new Schema({
text: { type: String },
attachments: [ AttachmentSchema ] // Could be Material.Link, Material.YoutubeVideo, etc
});
var attachmentArray = PostSchema.path('attachments');
module.exports = {
Post: mongoose.model('Post', PostSchema),
DriveFile: attachmentArray.discriminator('GoogleDriveFile', new mongoose.Schema({})),
DriveFolder: attachmentArray.discriminator('GoogleDriveFolder', new mongoose.Schema({})),
Link: attachmentArray.discriminator('Link', new mongoose.Schema({})),
YoutubeVideo: attachmentArray.discriminator('YoutubeVideo', new mongoose.Schema({}))
};
The key is to NOT use a mongoose Model use the schema.path of the parent document schema as the base for your discriminators.
search for the term docArray on this link: Mongoose Discriminator documentation
I'm defining my schema in mongoose and I have an array of book objects, and then an "active book". Now setting it up wasn't an issue, but this seems like unnecessary repetition to define the exact same book object in two different parts of the schema.
var BookSchema = new Schema({
activeBook: {
id: String,
title: String,
author: String,
pages: Number
},
books: [{
id: String,
title: String,
author: String,
pages: Number
}]
});
Is there a cleaner way of writing this so I don't have to write out the same object everywhere I use it?
A cleaner way would be to create a subdocument, a document with a schema of its own which are elements of a parent's document array. So in your example above you can define the child/parent schema as follows:
var ChildSchema = new Schema({
id: String,
title: String,
author: String,
pages: Number
});
var ParentSchema = new Schema({
activeBook: ChildSchema,
books: [ChildSchema]
});
so I've been at it for like 4 hours, read the documentation several times, and still couldn't figure out my problem. I'm trying to do a simple populate() to my model.
I have a User model and Store model. The User has a favoriteStores array which contains the _id of stores. What I'm looking for is that this array will be populated with the Store details.
user.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UserSchema = new Schema({
username: String,
name: {first: String, last: String},
favoriteStores: [{type: Schema.Types.ObjectId, ref: 'Store'}],
modifiedOn: {type: Date, default: Date.now},
createdOn: Date,
lastLogin: Date
});
UserSchema.statics.getFavoriteStores = function (userId, callback) {
this
.findById(userId)
.populate('favoriteStores')
.exec(function (err, stores) {
callback(err, stores);
});
}
And another file:
store.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var StoreSchema = new Schema({
name: String,
route: String,
tagline: String,
logo: String
});
module.exports = mongoose.model('Store', StoreSchema);
After running this what I get is:
{
"_id": "556dc40b44f14c0c252c5604",
"username": "adiv.rulez",
"__v": 0,
"modifiedOn": "2015-06-02T14:56:11.074Z",
"favoriteStores": [],
"name": {
"first": "Adiv",
"last": "Ohayon"
}
}
The favoriteStores is empty, even though when I just do a get of the stores without the populate it does display the _id of the store.
Any help is greatly appreciated! Thanks ;)
UPDATE
After using the deepPopulate plugin it magically fixed it. I guess the problem was with the nesting of the userSchema. Still not sure what the problem was exactly, but at least it's fixed.
I think this issue happens when schemas are defined across multiple files. To solve this, try call populate this way:
.populate({path: 'favoriteStores', model: 'Store'})
How to write multi ref for one property of one mongoose schema, like this(but wrong):
var Schema = mongoose.Schema;
var PeopleSchema = new Schema({
peopleType:{
type: Schema.Types.ObjectId,
ref: ['A', 'B'] /*or 'A, B'*/
}
})
You should add string field to your model and store external model name in it, and refPath property - Mongoose Dynamic References
var Schema = mongoose.Schema;
var PeopleSchema = new Schema({
externalModelType:{
type: String
},
peopleType:{
type: Schema.Types.ObjectId,
refPath: 'externalModelType'
}
})
Now Mongoose will populate peopleType with object from corresponding model.
In the current version of Mongoose i still don't see that multi ref possible with syntax like you want. But you can use part of method "Populating across Databases" described here. We just need to move population logic to explicitly variant of population method:
var PeopleSchema = new Schema({
peopleType:{
//Just ObjectId here, without ref
type: mongoose.Schema.Types.ObjectId, required: true,
},
modelNameOfThePeopleType:{
type: mongoose.Schema.Types.String, required: true
}
})
//And after that
var People = mongoose.model('People', PeopleSchema);
People.findById(_id)
.then(function(person) {
return person.populate({ path: 'peopleType',
model: person.modelNameOfThePeopleType });
})
.then(populatedPerson) {
//Here peopleType populated
}
...
I'm designing a MongoDB schema to save a fairly large/nested document. I'm planning on embedding as much as possible into a single document, but wasn't sure what to do with code/lookup values. For example, if we have a code table representing "priority", with the possible values being:
low
medium
high
Is this something I should use a Mongoose reference for, and create a simple document to hold priority, eg something like:
var PrioritySchema = new Schema({
description: String
});
This would then be referenced with something like the following:
var AnotherSchema = new Schema({
name: String,
active: Boolean,
priority: { type: String, ref: 'Priority' }
});
Or is this overkill? The thing I want to avoid is directly storing these "descriptions" in the main/overall model, then having a requirement change sometime in the future. For example, someone decides that instead of "medium", we need to call it "somewhat". In that situation, I assume I'd be stuck doing some sort of data migration?
you can do this :
var PrioritySchema = new Schema({
description: String
});
and this
var AnotherSchema = new Schema({
name: String,
active: Boolean,
priority: { PrioritySchema }
});
But if you want what you described further I would advise you to do this instead :
var AnotherSchema = new Schema({
name: String,
active: Boolean,
priority: { type: Schema.Types.ObjectId, ref: 'Priority' } // see this : Schema.Types.ObjectId != String
});
Let's make it simple if you need those values to be cross-documents you need to use reference. If the values are only existing because of the parent document then you can choose embing.
For more information read this :
http://mongoosejs.com/docs/2.7.x/docs/embedded-documents.html
FYI : I used to struggle a lot with this. If you follow the path of embing all nested sub-document you will face a lot of "What Why I can't do that :'(. At the end I choosed the referencing way I felt more confortable with it. embing != referencing.