I'm new to both MongoDB and Mongoose and trying to figure out the best way to handle creating my schema. I am more used to relational databases, but am hoping this will work with Mongo.
I am creating objects that "extend" other objects. For example, I have a person object that I want to use as the starting point for a parent or grandparent object. Both the parent and grandparent may have additional values beyond those that a base person has (I'm just including one example in each)...
const personSchema = new mongoose.Schema({
name: String,
birthDate: Date,
deathDate: Date,
});
const parentSchema = new mongoose.Schema({
//all the stuff a person has + below:
children: [personSchemas?] //[array of persons, important... parents can also be children,
multiple parents will share the same child]
parentingStyle: String,
})
const grandParentSchema = new mongoose.Schema({
// stuff that a parent has plus
grandparentingStyle: String,
})
I think the following post includes some answers that could be helpful to you:
Referencing another schema in Mongoose
Other than that, I might also suggest you'd define parents and grandparents as roles perhaps in this way:
var mongoose = require('mongoose');
const PersonSchema = new mongoose.Schema({
person_id: {
type: mongoose.Schema.Types.ObjectId
},
name: String,
birthDate: Date,
deathDate: Date,
//the following is if you want later to fetch persons by role
role: {
type: string,
enum: ["parent", "grandParent","child"],
}
});
//Then you could do it this way
const ParentSchema = new mongoose.Schema({
//all the stuff a person has + below:
children: [personSchemas],
parentingStyle: String
})
//Or this way
const ParentSchema = new mongoose.Schema({
//all the stuff a person has + below:
children: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Person'
}
parentingStyle: String,
})
const GrandParentSchema = new mongoose.Schema({
// same as for parents (ether way)
// stuff that a parent has plus
grandparentingStyle: String
})
In non-relational databases, it really depends on what you'd like to do with the data later on. The "joints" (in comaprison to relational DB) are much simpler to create, either by referencing an id or fetching a whole array (like you did) or simply by making more elaborate queries later on.
Related
I am looking for a way to populate documents beyond a document or the ref parameter.
const LibarySchema = new Schema({
books:[{
book: { type: Schema.Types.ObjectId},
bookType: { type: String }
}, { _id: false}]
});
const BookType1Schema = new Schema({
bookType: {
type: String,
default: 'lecture'
},
});
const BookType2Schema = new Schema({
bookType: {
type: String,
default: 'assingment'
},
});
This is an example of a similar project model, I am working on with 3 models. The first model hold all the information of the library books while the other models are the type of books. So during insertion of new book to the library array the bookType is added to know the collection to look for.
So my question is, is there anyway during population to check for the bookType and choose the right collection to find the book.
Also i am open for suggestion on the model.
you can just simply have a single schema for the thing you asks. Don't make unnecessary schema and make you collections bulky.. It will not be good when querying with tons of data when the system grows ...
Don't use separate schemas for storing just the types just make a single library book schema and use that to find the book you wan to find
What is the difference between nesting schema in schema (subdocuments) vs creating two separate models and referring to them, What about their performance?
subdocuments:
const postSchema = new Schema({
title: String,
content: String
});
const userSchema = new Schema({
name: String,
posts: [postSchema]
});
module.export = mongoose.model('User', userSchema);
nested models (Populating by reference):
const postSchema = new Schema({
title: String,
content: String,
author: { type: String, ref: 'User' }
});
module.export = mongoose.model('Post', postSchema);
const userSchema = new Schema({
name: String,
posts: [{ type: Schema.Types.ObjectId, ref: 'Post'}]
});
module.export = mongoose.model('User', userSchema);
Edit: This is not a duplicate question.
In this question: Mongoose subdocuments vs nested schema - mongoose subdocuments and nested schema is exactly the same.
BUT nested models creating a separate collection in database.
My question is what is diffrence in nested schema vs nested models, not subdocuments vs nested schema.
When using subdocuments, you actually have a copy of the data within your parent-document, wich allows you to get all the document + sub-document-data in a single query.
When using "nested models" you're not really nesting them, but referencing from the parent-model to the child-model. In this case you have to use population, which means you can't get all the data in a single query.
In short: subdocuments actually nest the data, and your "nested models" only reference them via their id
This question is similar to: this question
Working with Mongoose, I have something like the following code. (I've prefixed the files with numbers, so I can load them in the order they appear in the 'models' directory, but still control the loading order.)
In 100-employee.server.model.js:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var EmployeeSchema = new Schema({
name: {
type: String,
default: ''
},
company: {
type: Schema.ObjectId,
ref: 'Company'
},
subordinates: [EmployeeSchema],
});
mongoose.model('Employee', EmplyeeSchema);
Then, in 200-company-server.js, I have:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var CompanySchema = new Schema({
name: {
type: String,
default: ''
},
CEO: {
type: Schema.ObjectId,
ref: 'Employee'
}
});
mongoose.model('Company', CompanySchema);
Obviously, this doesn't work, since Company is referenced before it is registered. And, loading these in the opposite order doesn't work for the same reason. What I need, is a logical data structure like:
{
name: 'Acme, Inc',
CEO: {
name: 'Karen',
subordinates: [{
name: 'Bob',
subordinates: [{
name: 'Lisa',
subordinates: []
},
name: 'Jerry',
subordinates: []
}]
}]
}
}
(I think I got all of my brackets in place. I just typed that JSON to illustrate the need.)
I could just use an ObjectId for 'company' in EmployeeSchema, but it doesn't fix the problem. I still get a complaint that Company hasn't been registered.
Someone will ask for the use case, so here it is:
I have a bunch of companies.I have a hierarchy of employees of a company. And, I've got a bunch of companies. For each company, I need to know the CEO, without having to search all of my employees for the one with no parent, that has an ObjectId ref Company, but that still runs into the same problem.
Any suggestions?
OK, I've come up with an answer, or at least a workaround. Doesn't feel very elegant. In fact I actively dislike it, but it works. Would love to see a better solution. In 100-company-server.js, define the parent entity first, like so:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var CompanySchema = new Schema({
name: {
type: String,
default: ''
},
CEO: {
name: { // This is the company name, so probably a bad example
type: String,
default: ''
}, {
shoeSize: Number
}, etc ...
}
});
mongoose.model('Company', CompanySchema);
The key thing to notice here is that instead of making CEO an ObjectId ref 'Employee' I supplied an object that uses the same properties as my Employee schema. Depending on how you want to use it, you may have to coerce that into an Employee object in the controller. Or, there might be a clever way to use a virtual to do the same thing (http://mongoosejs.com/docs/guide.html) But, the virtual would just have to be defined AFTER the EmployeeSchema and model.
In 200-employee.server.js, something like the following:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
CompanySchema = mongoose.model('Company').schema; // so you can use this
// after your Employee
// is defined
var EmployeeSchema = new Schema({
name: {
type: String,
default: ''
},
company: {
type: Schema.ObjectId,
ref: 'Company'
},
subordinates: [], // Just an empty array. You fill it with Employees in the controller. You could use a virtual to do the same thing, probably.
});
mongoose.model('Employee', EmployeeSchema);
So, it's kind of ugly, but it works.
Key points:
In defining CompanySchema, you don't use an ObjectId ref 'Employee'.
Instead, you define an object that looks like a Company object.
Since this object doesn't have an _id, you have to work around that in the
controller. In my case, this works fine. But, for many/most use cases, this
probably would not. You'd have to work around this with clever virtuals
and/or the controller.
Use a simple array, without type, to store your children (suborinates in
this example.
The array works just fine, and returns objects just as if you used an array
of ObjectId ref Employee.
Making the Company refer to an Employee (which hasn't been defined yet), you
have to convert a generic object into an Employee object. But, there are
many ways to do that.
There is, undoubtedly, a better way to do this.
I have this 2 schemas in mongoose:
The Booking schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bookingSchema = new Schema({
bookingNO: { type: Number, unique: true},
plateNO: String,
startDate: String,
bookedTime: Number,
creator: {type: Schema.Types.ObjectId, ref: 'User'}
});
var Booking = mongoose.model('Booking', bookingSchema);
The User schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
username: String,
password: String,
balance: Number,
bookings: [{type: Schema.Types.ObjectId, ref: 'Booking'}]
});
var User = mongoose.model('User', userSchema);
The problem with this design is:
Some Bookings were NOT created by a user - in which case the 'creator' field in Booking would be empty. Likewise, some Users do NOT necessarily contain a Booking - they may later on.
I was thinking about deleting the creator and bookings fields from the 2 Schemas, and using the { strict: false } option in Mongoose. Would this be the best option ?
If this is the case, I would have to add the 'creator' property to the Booking model, and 'bookings' property to the User, which would then get saved to the DB.
MOST importantly, due to the fact that I've removed the references from the Schema, how do I go about creating the reference in the case of using { strict: false } ?
Thanks in advance.
Unless a field has require:true flag, it can be left empty.
If you have a field which isn't defined in the Mongoose schema but is present in a document in MongoDB, you'll have to use doc.get('field') instead of just doc.field. Similarly for saving doc.set('field', value) and strict:false will be required otherwise it won't persist.
IMO you should have your schema inclusive rather than exclusive. Mongoose is just a wrapper for your data in MongoDB which at its heart is already schemaless.
In your specific case, you can create a booking without specifying a 'creator', because its of type ObjectId Mongoose will simply create the document and will leave that field empty. The user.bookings is a different matter as it is an Array, in which case Mongoose will always default to an empty array, even if it was left undefined when creating the document. In this case, would it be that bad to just leave the empty array there? It still represents the data accurately, where the user simply has no bookings, but are still able to potentially have them. If you explicitly don't want bookings, then yes, you'll either have to deal with strict: false, or manually $unset/delete the field from the documents.
I am a bit confused by something in MongoDB, when using Mongoose, since I am new to it. I have two collections: authors and posts.
each author in the authors collection have the mongodb inbuilt id associated with them, which as per my understanding is of the schematype/datatype Objectid.
Now in my posts collection, I have a field which has is called author, which should have the value of the author's Objectid, similar to the foreign key concept in SQL.
My question is, which schematype/datatype should I declare the author field in the posts collection? Should I put it as Objectid? If so, would it not auto-increment and not be settable?
Mockup Of Schemas:
var authors = new mongoose.Schema({
name: String,
email: String
});
var posts = new mongoose.Schema({
title: String,
author: **??**
});
Any help would be hugely appreciated!
You can use population for that:
var authors = new mongoose.Schema({
name: String,
email: String
});
var posts = new mongoose.Schema({
title: String,
author: { type: Schema.Types.ObjectId, ref: 'Author' }
// assuming the model will be called 'Author'
});
To use:
// Get 'author' some how (create a new one, query the
// database for existing authors, etc);
...
// Create a new post and associate it with 'author' by referencing its `_id`:
var post = new Post({
...
author : author._id
});
post.save(...);
The documentation linked above also shows how you can get Mongoose to automatically retrieve the author document when you're querying posts.