What's a NoSQL database again??
And what's an ODM? →
We're using MongoDB as our database and Mongoose as our ODM
What's a document… and what's a collection? →
In MongoDB
What's a schema, model… and object? →
In Mongoose…
A schemas is analogous to a collection. We create a schema with the mongoose.Schema
constructor.
Once you have a schema, you can then register a model. A model is a constructor for creating objects that represent MongoDB Documents.
Instance Methods
save
(create a new document)Static Methods
find
findOne
findOneAndUpdate
To add extra features to your schemas, you can use plug-ins.
One plug-in, mongoose-url-slugs…
Let's try creating a schema for a pizza and toppings. →
{
size: 'medium',
crust: 'thin',
slug: 'medium-thin-2'
toppings: [{name:'mushroom', extra:true}, {name:'peppers'}]
}
Make sure that you have the required modules for connecting to the database… and creating a slug!
npm install --save mongoose mongoose-url-slugs
Require and connect…
var mongoose = require('mongoose'),
URLSlugs = require('mongoose-url-slugs');
// more stuff goes here
mongoose.model('Pizza', Pizza);
mongoose.model('Topping', Topping);
mongoose.connect('mongodb://localhost/pizzadb');
Schemas represent collections (tables). Notice the different ways of specifying the type of a field:
var Topping = new mongoose.Schema({
name: String,
extra: {type: Boolean, default:false}
});
var Pizza = new mongoose.Schema({
size: {type: String, enum: ['small', 'medium', 'large']},
crust: String,
toppings: [Topping]
});
// note that we left out slug from the schema...
// (the plugin will add it for you!)
// this should go before registering model!
Pizza.plugin(URLSlugs('size crust'));
With mongoose, a model allows you to:
Pizza = mongoose.model('Pizza');
var pizza1 = new Pizza({
size: 'small',
crust: 'thin'
});
pizza1.save(function(err, pizza, count) {
console.log('made me some pizza', pizza, count, err);
});
// call mongoose.disconnect() in callback function to close
// database connection;
Ok… just like the commandline client, we can use find:
// find all (try with query/criteria)
Pizza.find(function(err, pizzas, count) {
console.log(err, pizzas, count);
});
Notice that we get back an Array!
But I only want one! Sometimes it's annoying to have to index into an Array if you only want one of something, so there's also findOne →
// find only one (returns first)
Pizza.findOne({slug: 'small-2' }, function(err, pizza, count) {
console.log(err, pizza, count);
});
In Mongoose… instead of using the push operator (like in the commandline client), we have a method, push, that can be called on a property if it represents a list / Array of embedded values:
// update one after finding (hello callbacks!)
Pizza.findOne({slug: 'small-2' }, function(err, pizza, count) {
// we can call push on toppings!
pizza.toppings.push({name: 'mushroom'});
pizza.save(function(saveErr, savePizza, saveCount) {
console.log(savePizza);
});
});
But of course… we can actually use push in an update query. In this case, we're using findOneAndUpdate to do the find and update all at once!
// find one and update it; maybe better than previous?
// ...notice $push?
Pizza.findOneAndUpdate({slug:'small-2'}, {$push: {toppings: {name:'peppers'}}}, function(err, pizza, count) {
console.log(err, pizza, count);
});
We can also adjust our query to find by an embedded document. In this case, we use the property of the list of embedded documents… and use another object that describes the embedded document that we'd like to match.
Pizza.find({toppings: {name:'mushroom'}}, function(err, pizzas, count) {
console.log(pizzas);
});
Notice that when we update an embedded document, before we save the parent, we have to let mongoose know that we made changes to embedded documents. (shrug)
Pizza.findOne({slug:'small-2'}, function(err, pizza, count) {
for (var i = 0; i < pizza.toppings.length; i++) {
pizza.toppings[i].extra = true;
}
pizza.markModified('toppings');
pizza.save(function(err, modifiedPizza, count) {
console.log(err, modifiedPizza);
});
});
(whew!)