- Compare and contrast relational to document based (NoSql) databases
- Setup local MongoDB server
- Define what a document is in the context of MongoDB
- CRUD documents using Mongo CLI
- Build a simple node CLI to query MongoDB
Well, we've come full circle ... again. What we're learning today isn't fundamentally that different from what we know. We're going to learn a different way to store information. This time, via a document-based, non-relational way.
We've learned a considerable amount of information about relational databases with Postgres. We've seen that schema's in SQL(relational DB's) are fairly rigid. Adding columns can be taxing(migrations). Also if we delete a row in a table that is being used as a foreign key in another table, we must delete all the rows associated with that foreign key before we can delete the parent object. With SQL, we can also join on foreign keys for relational tables in order to query our database. One of the negatives about these join queries, is that they can get really expensive and therefore slow down our app.
When dealing with less complex associations, non relational databases can be more effective. Mongo provides a more flexible, scalable solution for less complex domain models.
This is not to say that Mongo is a better solution than Postgres or other SQL libraries, but an alternative solution.
MongoDB is an open-source document database that provides:
- High Performance
- High Availability
- Automatic Scaling
In a nutshell, you should use Mongo if you are working with a flexible data model, that involves similar, but different objects.
{
"name": "Sue",
"age": 26,
"status": "Active",
"groups": ["sass", "express"]
}
TPS: What do you see?
- json
- different data types
- even arrays
{
"_id" : ObjectId("54c955492b7c8eb21818bd09"),
"address" : {
"street" : "2 Avenue",
"zipcode" : "10075",
"building" : "1480",
"coord" : [ -73.9557413, 40.7720266 ],
},
"borough" : "Manhattan",
"cuisine" : "Italian",
"grades" : [
{
"date" : ISODate("2014-10-01T00:00:00Z"),
"grade" : "A",
"score" : 11
},
{
"date" : ISODate("2014-01-16T00:00:00Z"),
"grade" : "B",
"score" : 17
}
],
"name" : "Vella",
"restaurant_id" : "41704620"
}
- a data structure composed of field(key) and value pairs.
- similar to JSON objects.
- stored as BJSON
- fields may include other documents, arrays, and arrays of documents.
- analogous to rows in a table
MongoDB stores documents in collections.
- Collections are analogous to tables in relational databases.
- does NOT require its documents to have the same schema.
- documents stored in a collection must have a unique
_id
field that acts as a primary key.
A SQL ___________ ...is like... A Mongo ____________
Great, now that we have a high level understanding of what Mongo is and what purpose it serves, let's look at how to use it!
-
Mac OS X
-
Install mongodb with brew
brew install mongodb
-
Create the folder mongo will be using to store your databases
sudo mkdir -p /data/db
-
Change permission so your user account owns this folder you just created
sudo chown -R $(whoami) /data/db
Type these commands exactly as displayed, you don't need to substitute anything.
-
$ mongod
You should see:
a bunch of output but with the prompt hanging
This is good news,
mongod
just starts up a mongo server locally. NOTE: you need this running in order to use the mongo cli
$ brew info mongo
$ mongo
feels a little bit like a JS REPL
You should see:
MongoDB shell version: 3.x.x
connecting to: test
>
> help
Based on what you see in the help menu:
- What jumps out as important?
- What might be useful for debugging?
show dbs
: show database namesshow collections
: show collections in current databaseuse <db_name>
: set current databasedb.foo.find()
: list objects in collection foo
Also:
<tab>
key completion<up-arrow>
and the<down-arrow>
for history.
In the Mongo REPL, let's go ahead and create our first database, one which we will be using to store information about restaurants.
In order to create/connect to a new database, we have to tell mongo to use
a specific database that we want to work with:
> use restaurant_db
Note:
use
will create the database it received as an argument if not already initialized and connect to it
Verify:
> db
restaurant_db
Note: the
db
variable is provided by mongo and will point to the currently connected database
Common Gotcha, what happens when we run:
$ show dbs
Note we don't see
restaurant_db
listed. It isn't until we add a document to our database that our db will show up inshow dbs
- use
insert()
to add documents to a collection
> db.restaurants.insert(
{
"name": "Cookies Corner",
"address" : {
"street" : "1970 2nd St NW",
"zipcode" : 20001,
},
"yelp": "http://www.yelp.com/biz/cookies-corner-washington"
})
Important to note:
The
db
is the database we’re connected to. In this case,restaurant_db
..restaurants
is then referring to a collection in ourrestaurant_db
. We use the.insert()
to add the document inside the parentheses.
> show collections
restaurants
Note: restaurants
was saved as a collection
> db.restaurants.find()
Returns documents with the following fields:
name
address
yelp
Q. What is surprising/unexpected?
- where did
restaurants
come from? _id
?- ObjectId
// insert
> db.your_collection_name.insert({ data as json })
// find
> db.your_collection_name.find()
New Record:
- If the document passed to the
insert()
method does not contain the_id
field the mongo shell automatically adds the field to the document and sets the field’s value to a generatedObjectId
.
New collection:
- If you attempt to add documents to a collection that does not exist, MongoDB will create the collection for you.
> use random_db
> db.dropDatabase()
Drops the current database.
Using the Mongo Shell CLI, add at least 4 new restaurant documents to your restaurants
collection.
ProTip: I recommend you construct your statements in your editor and copy / paste. It will help you now & later.
Prompt: Did anyone insert multiple at one time?
Let's recreate the steps together:
Q. How can we tell which database we are connected to currently?
db
- Create DB
- Use the appropriate DB
- Insert multiple restaurants
db.restaurants.remove({});
db.restaurants.insert([
{
"name": "Cookies Corner",
"address": {
"street" : "1970 2nd St NW",
"zipcode" : 20001
},
"yelp": "http://www.yelp.com/biz/cookies-corner-washington"
},
{
"name": "The Blind Dog Cafe",
"address": {
"street": "944 Florida Ave",
"zipcode": 20001
},
"yelp": "http://www.yelp.com/biz/the-blind-dog-cafe-washington-2?osq=cookies"
},
{
"name": "Birch & Barley",
"address": {
"street": "1337 14th St NW",
"zipcode": 20005
},
"yelp": "http://www.yelp.com/biz/birch-and-barley-washington?osq=Restaurants+cookies"
},
{
"name": "Captain Cookie and the Milk Man",
"address": {
"street": "Dupont Circle",
"zipcode": 20036
},
"yelp": "http://www.yelp.com/biz/captain-cookie-and-the-milk-man-washington-5"
},
{
"name": "J's Cookies",
"address": {
"street": "1700 N Moore St",
"zipcode": 22209
},
"yelp": "http://www.yelp.com/biz/js-cookies-arlington" }
])
> db.restaurants.count()
- A record’s unique immutable identifier.
- In relational databases: usually id field, typically an Integer
- In MongoDB: the _id field, usually a BSON ObjectId.
Breaking down the anatomy of a typical query with Mongo:
collection + operation + modification = results
In order to Find all restaurants:
> db.restaurants.find()
Note: we can format our output to be a little nicer on the eyes by chaining the
.pretty()
method to end of our query like so:db.restaurants.find().pretty()
We can add conditions to our query to target documents based on matching key-value pairs:
> db.restaurants.find({name: "Cookies Corner"});
> db.restaurants.find({"address.zipcode": 20001});
http://docs.mongodb.org/manual/core/write-operations-introduction/
> db.your_collection.update(
{ criteria },
{
$set: { assignments }
},
{ options }
)
Note: the first key value pair is the condition on which to find the document you'd like to update, the second is what values you'd like to set, and third is any additional options
Take time to think about and execute the appropriate commands so that you:
- Update all restaurants to have a new key-value pair
{state: "DC"}
- Add a property of
rating
to at least 2 documents and give it a numerical value between 1-5 - Change the street
address
of a specific restaurant
Bonus
- Add nested sub-documents to each restaurant to that it has many
reviews
- Store important information about each
review
Note this what a sample update might look like:
> db.restaurants.update(
{"name": "Cookies Corner"},
{ $set: { state: "DC" }}
)
Note: In order to update multiple documents at a time, make sure to pass the
multi
option as true, like so:
db.restaurants.update(
{},
{
$set: { "state": "DC" }
},
{multi: true}
)
Verify:
> db.restaurants.find().pretty()
> db.restaurants.remove({ conditions })
We already did this! (The address 'object' / 'subdocument')
- Usability
- High Performance
- High Availability
- Automatic Scaling
- No SQL :)
- Documents (i.e. objects) correspond to native data types in many programming languages.
- Schema-less, less need to manage migrations
- Dynamic schema supports fluent polymorphism.
- Embedded documents and arrays reduce need for expensive joins (reduces I/O).
- Indexes support faster queries and can include keys from embedded documents and arrays.
MongoDB’s replication facility, called replica sets, provide:
- automatic failover.
- data redundancy.
replica set:
is a group of MongoDB servers that maintain the same data set, providing redundancy and increasing data availability.
- Automatic sharding distributes data across a cluster of machines.
- Replica sets can provide eventually-consistent reads for low-latency high throughput deployments.
Interested in learning more about No SQL?
What database type would make sense for the following apps:
- Blog: Posts have_many Comments
- HR app: Companies have_many Managers have_many Employees
- Gallery: Artists have_many Paintings