Introduction to MongoDB Virtuals
Transcript from the "Virtuals" Lesson
>> Scott Moss: All right, so we're gonna move on to the next part, which is gonna be working with hooks, virtuals, and indexes with our schemas. So up to this point, you can pretty much do anything with Mongo. What you've learned so far will take you 80% of the way.
[00:00:24] Even the connection logic, unless you're doing some advanced stuff in production, it's mostly the same even in production. You don't really have to change much, there's not much more you have to do other than what I've already taught you. What I'm gonna teach you now is just, take it to the next level just a little a bit, just thinking about it in general and how to get it there.
[00:01:00] So if I want a field on this school that just concatenated all the strings together, but I didn't wanna say that in a database, I could make a virtual for that. So when I ask for it, a function will run and give me back the value for it.
[00:01:14] So it's a getter, it's a virtual thing, it's not actually saved in a database, it's computed when I ask for it based on some properties. So that's a virtual, you can do the same thing for set, so you can set a virtual. So I can set a property that doesn't exist in a database, but when I actually set it, there's a function that runs and it sets real properties in the database.
[00:01:33] So it's like building a domain model around your actual database model, if you will. So that's what you can do, it's pretty handy for virtuals. If you've already seen GraphQL, GraphQL is literally just virtuals. So if you've ever used GraphQL, it's the same thing, so that's what we're gonna do.
[00:01:50] And one of the virtuals we've been using this whole time is .id. Any document that you get back has a .id property on it. But if you look in the database, there's no such thing as an id property, it's _id, so how did that get there? Well, Mongoose created a .id virtual for you because, who wants to write ._id all the time?
[00:02:08] So they just made one for you, and that's a built-in virtual, but you can make your own. So that's what you're gonna do on the next exercise. So I was gonna show you how to do some of those virtuals. So I'm just gonna clean some of this stuff up right quick so it's not so noisy in here.
>> Scott Moss: And we'll just stick with the school one, it's pretty easy.
>> Scott Moss: There we go, so to create virtuals, and pretty much everything dealing with the schema, all you have to do is use the schema object itself. So the schema instance that we created for the school is gonna be returned from mongoose.Schema.
[00:02:52] So this right here is more than just an object that we pass to mongoose.model. We have the schema, we can do some pretty amazing things with it. We can add different query methods that we want. We can make our own query methods, our own mutation methods, our own helpers, instance methods.
[00:03:07] Pretty much whatever we want, we could do here, and one of the things we're gonna do is, we're gonna add virtuals. So we're gonna add a virtual for the school, and the way you would do that is, you would grab the schema object itself. After you create the schema, you do the .virtual here.
[00:03:22] So if you do a .virtual, which is a function, you can pass in whatever value you want here, that's not a real field. So if I pass in a field called,
>> Scott Moss: staffCount, like this, now what I'll have to do is, I can define getters and setters on this virtual.
[00:03:43] A getter is a function that's gonna be called when someone tries to retrieve this property called staffCount. A setter's a function that's gonna be called when someone tries to set the value of staffCount. For right now, we're gonna do getters. So I'm gonna say, when something in my code tries to get the value for staffCount, run this function, and this part is important.
[00:04:03] So if you know ES6 and if you use the arrow function, does anybody what an arrow function does differently than a regular function in ES6?
>> Speaker 2: It binds this.
>> Scott Moss: Right, it keeps the same context of this. So in this case, the context inside of this function would be the same context of whatever is out here, and that's normally okay.
[00:04:25] In this situation, it's not okay, because we need to reference properties of the school instance. And in order for that to work, we're gonna use the this keyword. But we need to keep that context different, so we have to use a regular function here. So if you use the arrow function here, your code will not work, 100% won't work.
[00:04:43] Because the this inside of the arrow function will be the this of whatever is in this scope right here. And we're not in a browser so this won't refer to window. We're in Node, so this will refer to something else, but it for sure won't be the instance of a school.
[00:04:58] So make sure you use a regular function here, and if you're gonna be using a function that's gonna be using the keyword this, make sure you're using the right this. So what we wanna do here is, we want to really just return the length of the staff. So let's say return this.staff.length.
[00:05:19] And this, remember, is referring to whatever school instance it is that we're asking the property for. Staff is gonna be the property called staff, which should be array, and arrays have lengths, so we'll just get the length property on it. So I'm just gonna return this.staff.length. This is gonna give me a virtual field called staffCount.
[00:05:37] It's not in a database, it's not on a schema, I can't save it anywhere, it's computed for me. So to verify this, I'm gonna create a school.
>> Scott Moss: So create a school, we'll put a name, my school.
>> Scott Moss: And then we'll add some staff here, so we can actually see that count.
[00:06:08] And these are just strings, so I'll just add some strings here. There we go, actually, yeah, let's do that. So now that we have a school, we can see what that is. So I'm just gonna console.log mySchool.staffCount. And when I log this, it's going to actually execute this function right here.
[00:06:31] So we should be able to verify that if I put a log here, we can just say in virtual. So a log there, so this is me doing a getter. See, I'm accessing the property called staffCount, it's me trying to get it. So that's gonna trigger, you could think of this like middleware.
[00:06:46] It's like a listener that's like, you're trying to get staffCount, cool, I gotta run this function, I'm gonna return whatever value is here. So that's why that's gonna run, so actually run this code.
>> Scott Moss: So you can see it immediately, before it even returned the value, it first went inside the virtual, it was inside the virtual, in virtual, so it ran this first.
[00:07:10] Then the value was returned and then it logged here. So you can see, now I have 3, which is exactly how many strings I have in this array. So that's how you would do a virtual, so it's doing it synchronously. So that means you probably don't wanna put async code inside your virtuals, probably not.
[00:07:26] I mean, there's nothing stopping you from doing it, but I mean, you would have some pretty confusing code if you did that. I think actually, the runtime might even break, so yeah, I would probably avoid that. So yeah, keep things synchronous, use them if you just wanna do some quick computing on some virtual paths.
[00:07:43] id's a really good example, or anything like that. Any questions on virtuals?
>> Scott Moss: No, pretty straightforward, I like virtuals, they're really legit.