In our homework and class examples, where did we store our data? →
What are some downsides to storing data as part of our application in memory? →
What are some other options… let's list as many as we can. →
We'll be using a database in this part of the class…
We can categorize databases as: →
nosql databases can be further categorized by the data model they use: →
So, the following slides include high level overviews of different kinds of databases.
Relational databases organize data in a collection of tables (relations). Can you describe characterstics of a relational database? →
Regarding additional relational database features… →
Maybe we want to store these fields:
Let's get to it! →
What are some examples of relational databases? →
These are all great choices for storing highly structured data, related data.
They are all in common usage for conventional web applications. However, there's a bit of a learning curve, and some are difficult to set up.
NoSQL databases can be categorized by how they store their data:
We'll focus on key-value and document stores…
Probably the most simple conceptually… data is stored in key/value pairs. This should sound similar to some data structures that you've seen before. →
They're typically good at scaling to handle large amounts of data and dealing with high volumes of changes in data.
What may be some good applications for key value stores? →
Some key value databases include:
As you might guess by the name, document stores organize data semi-structured documents.
They're particularly good for applications where flexible data storage or constantly changing data storage is required.
Two of the most popular NoSQL databases are:
Of course, there are a bunch of others
Some use cases for document stores include:
We're using MongoDB. Not for all of the reasons we previously mentioned, though… We're using it because… →
All this can pretty much be summed up by saying that it's easy to use! (As an aside, I'm a bit biased to using relational databases, specifically Postgres)
A couple of terms to remember (yay, definitions again!)
Although MongoDB doesn't require you to pre-define the types of values that your documents will have, it does have data types. These types are inferred from the value. Some available types include:
More about Object ID: a 12-byte binary value which has a very rare chance of duplication; consists of a 4-byte timestamp (seconds since epoch), a 3-byte machine id, a 2-byte process id, and a 3-byte counter
brew install mongodb
Working with MongoDB on the commandline…
If your OS doesn't autostart by default, you can run:
mongod
To connect via the commandline MongoDB client and connect to a locally running instance:
mongo
This drops you into the MongoDB shell (yay… more shell). You can issue commands that
The following commands can be used to navigate, create and remove databases and collections →
show databases
- show available databases (remember, there can be more than one database)use db
- work with a specific database (if unspecified, the default database connected to is test)show collections
- once a db is selected, show the collections within the databasedb.dropDatabase()
- drop (remove) the database that you're currently indb.collectionName.drop()
- drop (remove) the collection named collectionName
To get some inline help:
help
- get help on available commandsTo begin using the commandline client to inspect your data: →
mongod
is running in a different window (or running in the background or as a daemon)mongo
use databaseName
to switch to the database that you're looking throughFrom there, you can start querying for data, inserting documents, etc. These basic create, read, update, and delete operations are called CRUD operations…
(C)reate, (R)ead, (U)pdate, and (D)elete operations: →
db.Person.insert({'first':'bob', 'last':'bob'})
db.Person.find({'last':'bob'})
db.Person.find() // finds all!
db.Person.update({'first':'foo'}, {$set: {'last':'bar'}})
db.Person.remove({'last':'bob'})
Where queryObj
is a name value pair that represents the property you're searching on… with a value that matches the value you specify
As prep for the next part, some insert and finds (with a test for greater than!) →
Inserting, finding all, then finding by exact number of lives:
> db.Cat.insert({name:'foo', lives:9})
WriteResult({ "nInserted" : 1 })
> db.Cat.find()
{ "_id" : ObjectId("57ff86a14639d0fd263f87a0"), "name" : "foo", "lives" : 9 }
> db.Cat.find({lives:9})
{ "_id" : ObjectId("57ff86a14639d0fd263f87a0"), "name" : "foo", "lives" : 9 }
Inserting more, then using greater than!
> db.Cat.insert({name:'bar', lives:2})
WriteResult({ "nInserted" : 1 })
> db.Cat.insert({name:'qux', lives:5})
WriteResult({ "nInserted" : 1 })
> db.Cat.find({lives: {$gt: 4}})
{ "_id" : ObjectId("57ff86a14639d0fd263f87a0"), "name" : "foo", "lives" : 9 }
{ "_id" : ObjectId("57ff86c14639d0fd263f87a2"), "name" : "qux", "lives" : 5 }
As with everything else we've done in node, there's a module for our specific task. If we'd like to use MongoDB in our application, there are a few options:
We'll be using mongoose, as it seems to have the most traction out of the three. (But it's a bit more complicated than it needs to be).
Has anyone heard of ORM or ODM before? →
Both map objects in your application to their counterparts in your database (tables, collections). Mongoose is our ODM.
Let's use MongoDB and Mongoose to store our classic list of cat names.
npm install --save mongoose
For simplicity, we'll dump everything in a file called [PROJECT ROOT]/db.js
for now. We'll see other ways of laying things out.
In db.js
:
// as always, require the module
const mongoose = require('mongoose');
// some extra stuff goes here...
// connect to the database (catdb)
mongoose.connect('mongodb://localhost/catdb');
Between your require and connect… create a schema. A schema represents a MongoDB collection. Here we're specifying a collection for Cats.
// define the data in our collection
const Cat = new mongoose.Schema({
name: String,
updated_at: Date
});
// "register" it so that mongoose knows about it
mongoose.model('Cat', Cat);
In app.js
, simply:
require( './db' );
Ostensibly, we would want to create, update, read or delete data based on what page (path/url/etc.) we're on. Let's start by adding some setup to our index.js
routes. →
const mongoose = require('mongoose');
const Cat = mongoose.model('Cat');
Let's create a simple site that saves a bunch of cat names. →
(for when we adopt three new adorable cats for our class)
What pages do you think we should have? →
So what kind of routes will we need? →
Use the schema's find method to read objects (of course, we have to define a callback that gets triggered when the read is done)!
router.get('/cats', function(req, res) {
Cat.find(function(err, cats, count) {
res.render( 'cats', {
cats: cats
});
});
});
We'll also need a form. We can handle this one pretty easily. →
router.get('/cat/create', function(req, res) {
res.render('create');
});
Let's accept posts to /cat/create
. We'll use our schema to create an object:
router.post('/cat/create', function(req, res) {
console.log(req.body.catName);
new Cat({
name: req.body.catName,
updated_at : Date.now()
}).save(function(err, cat, count){
res.redirect('/cats');
});
});
Our is essentially the same as all of the previous forms we've had →
<form method="POST" action="">
cat name plz
<input type="text" name="catName">
<input type="submit">
</form>
As with our previous templates, we'll just loop through all of the objects that we retrieve. →
<ul>
{{#each cats}}
<li>{{name}}</li>
{{/each}}
</ul>