Professional Documents
Culture Documents
Potion Reviews
Invisibility
Score: 70
Taste: 4 Strength: 1
More Info
Love
Score: 84
More Info
Taste: 3 Strength: 5
Shrinking
Score: 94
Taste: 2 Strength: 3
More Info
Course Outline
01 Conjuring MongoDB
02 Mystical Modications
03 Materializing Potions
04 Morphing Models
05 Aggregation Apparitions
Conjuring MongoDB
Level 1 Section 1
Introducing MongoDB
What Is MongoDB?
Catch-all term for databases that generally arent
relational and dont have a query language like SQL
Document-oriented
2007
2009
X
2013
Renamed to MongoDB
MongoDB
database
database
table
collection
row
document
Potions Collection
Potions Table
potion_id
name
price
Love
3.99
Invisibility
15.99
Shrinking
9.99
Vendors Table
vendor_id
1
2
vendor_id
Name: "Love"
Name: "Love"
Price: 3.99
Name: Love
Price: 3.99
Vendor: "Brewers"
Price: 3.99
Vendor: "Brewers"
Vendor: Brewers
name
Kettlecooked
Brewers
Potions Collection
Name:
Love
Price:
3.99
Vendor:
Brewer
Name:
Luck
Price:
59.99
Danger:
High
Name:
Sleeping
Price:
3.99
This is referred to as a
dynamic schema.
Your terminal
$ mongo
>
Mongo commands
go after the >
Download MongoDB here:
http://go.codeschool.com/download-mongodb
SHELL
Regular JavaScript
variable assignment
> potion
{.
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
This is all just normal JavaScript!
SHELL
...
{.
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
Field
Surrounded by
curly braces
Value
Separated by a
comma
> help
db.help()
...
show dbs
> show dbs
test
reviews
SHELL
Show list of
databases
Name and size of databases
SHELL
...
...
0.078GB
0.078GB
Potion Document
{.
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
Document must be placed
in a collection
Potions Collection
SHELL
> db.potions.insert(
{.
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
)
WriteResult({ "nInserted": 1 })
Whats a WriteResult?
Whenever we write to the database, well always be returned a WriteResult object that tells us
if the operation was successful or not.
SHELL
> db.potions.insert(
{.
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
)
WriteResult({ "nInserted": 1 })
1 document
successfully inserted
SHELL
> db.potions.find()
{.
"_id": ObjectId("559f07d741894edebdd8aa6d"),
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
> db.potions.insert(...)
WriteResult({ "nInserted": 1 })
> db.potions.find()
{ "name": "Invisibility" ... }
{ "name": "Love" ... }
{ "name": "Shrinking" ... }
Conjuring MongoDB
Level 1 Section 2
Queries and Data Types
SHELL
> db.potions.find()
{.
"_id": ObjectId("559f07d741894edebdd8aa6d"),
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
SHELL
> db.potions.find({"name": "Invisibility"} )
{.
"_id": ObjectId("559f07d741894edebdd8aa6d"),
"name": "Invisibility",
"vendor": "Kettlecooked"
}/
SHELL
> db.potions.find({"vendor": "Kettlecooked"})
{
"_id": ObjectId("55d232a5819aa726..."),
"name": "Invisibility",
"vendor": "Kettlecooked"
Two separate documents
}
are returned
{
"_id": ObjectId("55c3b9501aad0cb0..."),
"name": "Shrinking",
"vendor": "Kettlecooked"
}
Queries are case sensitive.
Strings
"Invisibility"
ObjectID
ObjectId(...)
Numbers
1400
3.14
Date
ISODate(...)
Booleans
true
false
Arrays
Objects
{"type" : "potion"}
Null
null
tryDate
Name: Invisibility
Ratings
Price: 15.99
Vendor: Kettlecooked
Price
Ingredients
Score
{.
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99,
"score": 59
}
Adding a tryDate
Dates can be added using the JavaScript Date object and get saved in the database as an
ISODate object.
{.
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99,
"score": 59,
"tryDate": new Date(2012, 8, 13)
}
"tryDate": ISODate("2012-09-13T04:00:00Z")
{.
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99,
"score": 59,
"tryDate": new Date(2012, 8, 13),
"ingredients": ["newt toes", 42, "laughter"]
}
{.
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99,
"score": 59,
...
}
{
"strength": 2,
"flavor": 5
}
Embedded Documents
We embed documents simply by adding the document as a value for a given eld.
{.
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99,
"score": 59,
"tryDate": new Date(2012, 8, 13),
"ingredients": ["newt toes", 42, "laughter"],
"ratings": {"strength": 2, "flavor": 5}
}
SHELL
> db.potions.insert(
{.
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99,
"score": 59,
"tryDate": new Date(2012, 8, 13),
"ingredients": ["newt toes", 42, "laughter"],
"ratings": {"strength": 2, "flavor": 5}
}
Document successfully inserted!
)
WriteResult({ "nInserted": 1 })
SHELL
{.
"_id": "ObjectId(...)",
"name": "Invisibility",
...
"ratings": {"strength": 2, "flavor": 5}
}
ratings.strength
We can easily query
embedded documents
ratings.flavor
db.potions.find({"ratings.flavor": 5})
SHELL
> db.potions.insert({
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": "Ten dollars",
"score": 59
})
WriteResult({ "nInserted": 1 })
The document still got saved
to the database!
{.
"_id": ObjectId("55c3b9561...") ,
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99
}
{/
"_id": ObjectId("55d232a51...") ,
"name": "Shrinking",
"vendor": "Kettlecooked",
"price": 9.99
}-
{.
"_id": ObjectId("55c3b9561...") ,
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99
,
{/
No syntax errors
"_id": ObjectId("55d232a51...") ,
"name": "Shrinking",
"vendor": "Kettlecooked",
"price": 9.99
}-
{.
"_id": 1,
"name": "Invisibility",
"vendor": "Kettlecooked",
"price": 10.99
},
{/
Duplicate _id
"_id": 1,
"name": "Shrinking",
"vendor": "Kettlecooked",
"price": 9.99
}-
Mystical Modications
Level 2 Section 1
Removing and Modifying Documents
Potion Catastrophe
Uh-oh we sneezed while performing a spell and ruined some potions in our database!
Potion Reviews
Invisibility
4
Sc
or
e:
Taste: 3
Love
Taste: 2
Strength: 1
:5
gth
en
Str
70
Ta
st
e:
More Info
More Info
84
:
e
r
o
c
S
Shrinking
4
9
:
re
o
c
S
More Info
Stre
ngth
:3
Ruined
Potions
Name:
Love
Name:
Invisibility
Name:
Shrinking
Vendor:
Brewers
...
Vendor:
Kettlecooked
...
Vendor:
Kettlecooked
...
SHELL
> db.potions.remove(
1 document
{"name": "Love"}
successfully removed
)
WriteResult({ "nRemoved": 1 })
Query matches
single document
Ruined
Potions
Name:
Invisibility
Name:
Shrinking
Vendor:
Kettlecooked
...
Vendor:
Kettlecooked
...
SHELL
> db.potions.remove(
1 document
{"name": "Love"}
successfully removed
)
WriteResult({ "nRemoved": 1 })
Query matches
single document
Name:
Invisibility
Vendor:
Kettlecooked
...
Name:
Shrinking
Vendor:
Kettlecooked
...
SHELL
> db.potions.remove(
{"vendor": "Kettlecooked"}
)
WriteResult({ "nRemoved": 2})
Removed 2 documents
Name:
Love
Potion Reviews
Invisibility
Score: 70
Taste: 4 Strength: 1
More Info
Price: 40.99
...
Love
Score: 84
Vendor:
Brewers
More Info
Taste: 3 Strength: 5
Shrinking
Score: 94
Taste: 2 Strength: 3
More Info
Updating a Document
We can use the update() collection method to modify existing documents.
Name:
Love
Vendor:
Brewers
Price: 40.99
...
Name:
Love
Update
Vendor:
Brewers
Price: 3.99
...
Price is updated!
Update only applied to
first matching document
SHELL
Query parameter
Update parameter
> db.potions.update(
{"name": "Love"},
{"$set": {"price": 3.99 }}
).
Update operators
always begin with a $
SHELL
> db.potions.update(
{"name": "Love"},
{"$set": {"price": 3.99 }}
).
WriteResult({
"nMatched": 1,
"nUpserted": 0,
"nModified": 1
})
Name:
Love
Vendor:
Brewers
Update
Price: 3.99
Price: 40.99
...
Useful for
importing data
> db.potions.update(
{"name": "Love"},
{"price": 3.99 }
).
SHELL
Notice
WE ARE NOT
CALLED KC
SHELL
> db.potions.update(
{"vendor": "KC"},
{"$set": { "vendor": "Kettlecooked" }},
When multi is true, the update
{"multi": true}
modifies all matching documents
)
WriteResult({
4 documents matched
and modified
"nMatched": 4,
"nUpserted": 0,
"nModified": 4
})
Create or update
existing log document
Potion Reviews
Logs Collection
Invisibility
Score: 70
Taste: 4 Strength: 1
More Info
Love
Score: 84
Taste: 3 Strength: 5
Shrinking
More Info
Score: 94
Taste: 2 Strength: 3
More Info
"_id": ObjectId(...),
"potion": "Frog Tonic",
"count": 1
}
Count: 1
Update
Increments field by
specified value
Count incremented by 1 !
Potion: Shrink..
Potion: Shrink
Count: 2
...
...
SHELL
> db.logs.update(
{"potion": "Shrinking"},
{"$inc": {"count": 1}}
).
If field doesnt exist, it gets
created with the value.
SHELL
> db.logs.update(
{"potion": "Love"},
{"$inc": {"count": 1}},
).
WriteResult({
"nMatched": 0,
"nUpserted": 0,
"nModified": 0
})
> db.logs.update(
{"potion": "Love"},
{"$inc": {"count": 1}},
{"upsert": true}
).
WriteResult({
"nMatched": 0,
"nUpserted": 1,
"nModified": 0
})
SHELL
Results in new
document
Name: Love
Count: 1
...
1 document created
SHELL
> db.logs.update(
{"potion": "Love"},
{"$inc": {"count": 1}},
{"upsert": true}
).
WriteResult({
"nMatched": 1,
"nUpserted": 0,
"nModified": 1
})
Name: Love
Result
Count: 2
Count of 2
...
Mystical Modications
Level 2 Section 2
Advanced Modication
Improving Potions
We rushed a bit during development and need to x up our potions. Luckily, we keep a to-do
list for what to work on.
TO DO
-
Rename score
Query for
all potions
Update all
potions
Name:
Love
Name:
Invisibility
Name:
Shrinking
Color: red
...
Color: black
...
Color: green
...
Color field
removed from
documents
SHELL
> db.potions.update(
{},
{"$unset": {"color": ""}},
{"multi": true}
).
"_id": ObjectId(...),
"name": "Love",
"grade": 84,
Renamed to
...
"_id": ObjectId(...),
"name": "Love",
"score": 84,
...
}
grade!
SHELL
Renames
specified
field
> db.potions.update(
{},
{"$rename": {"score": "grade"}},
{"multi": true}
).
New field
name
Field to
rename
Notice
{
"_id": ObjectId(...),
"name": "Shrinking",
...
"ingredients": ["hippo", "secret", "mouse feet"]
SHELL
> db.potions.update(
{"ingredients": "secret"},
{"$set": {"ingredients": "42"}}
)
"ingredients": 42
{/
"_id": ObjectId(...),
"name": "Shrinking",
"vendor": "Kettlecooked",
"score": 94,
...
"ingredients": ["hippo", "secret", "mouse feet"]
}
ingredients.0
BSON arrays start with an index of 0
ingredients.1
ingredients.2
SHELL
> db.potions.update(
{"name": "Shrinking"},
{"$set": {"ingredients.1" : 42}}
)
WriteResult({"nMatched": 1,"nUpserted": 0,"nModified: 1})
{/
"_id": ObjectId(...),
"name": "Shrinking",
...
"ingredients": ["hippo", 42, "mouse feet"]
}
Successful
update
ingredients.1
Potions Collection
SHELL
> db.potions.update(
{"ingredients": "secret"},
{"$set": {"ingredients.$" : 42}},
{"multi": true}
)
Potions Collection
Shrunken Conundrum
Uh-oh the shrinking potion hasnt worn o, and we keep on shrinking! We better update
that strength rating.
{
"_id": ObjectId(...),
"name": "Shrinking",
...
"ratings": {
"strength": 1,
"flavor": 5
}.
}
Update strength
to 5
Name: Shrink
Vendor: Kettle
Ratings...
...
"ratings": {
"strength": 1,
"flavor": 5
}.
> db.potions.update(
{"name": "Shrinking"},
{"$set": {"ratings.strength" : 5}}
)
ratings.strength
SHELL
$max
$min
$mul
Operator documentation:
http://go.codeschool.com/update-operators
MongoDBs
documentation is great
Modifying Arrays
Weve added categories to the potions but need a way to easily manipulate the values.
Name:
Shrink
Vendor:
Kettle
Categories...
...
Categories...
...
> db.potions.update(
{"name": "Shrinking"},
{"$pop": {"categories": 1}})
Result
"categories": ["tasty"]
-1
1
"categories": ["tasty"]
Categories...
...
> db.potions.update(
{"name": "Shrinking"},
{"$push": {"categories": "budget"}})
Result
Categories...
...
> db.potions.update(
{"name": "Shrinking"},
{"$addToSet": {"categories": "budget"}})
Result
Categories...
...
> db.potions.update(
{"name": "Shrinking"},
{"$pull": {"categories": "tasty"}})
Result
"categories": ["budget"]
Materializing Potions
Level 3 Section 1
Query Operators
Potion Reviews
Vendor
X
Kettlecooked
Brewers
+ more
Strength
X
5
4
+ more
Invisibility
Score: 70
Taste: 4 Strength: 1
More Info
Love
Score: 84
Taste: 3 Strength: 5
More Info
Shrinking
Score: 94
Taste: 2 Strength: 3
More Info
Matches both
documents
Name:
Invisibility
Vendor:
Kettlecooked
Strength: 5
...
Name:
Shrinking
Vendor:
Kettlecooked
Strength: 5
...
Name:
Luck
Vendor:
Kettlecooked
Strength: 4
...
Name:
Love
Vendor:
Brewers
Strength: 2
...
SHELL
> db.potions.find(
{
We can pass in more
"vendor": "Kettlecooked",
than 1 query
"ratings.strength": 5
}
)
Potion Reviews
Ingredients
Laughter
Unicorn
+ more
Vendor
Kettlecooked
Brewers
+ more
Price
Under $10
Under $20
Invisibility
Score: 70
Taste: 4 Strength: 1
More Info
Love
Score: 84
Taste: 3 Strength: 5
More Info
Shrinking
Score: 94
Taste: 2 Strength: 3
More Info
$gt
greater than
$lt
less than
$gte
$lte
$ne
not equal to
Name:
Invisibility
Vendor:
Kettlecooked
Price: 15.99
...
Name:
Shrinking
Vendor:
Kettlecooked
Price: 9.99
...
Name:
Luck
Vendor:
Kettlecooked
Price: 59.99
...
Name:
Love
Vendor:
Brewers
Price: 3.99
...
SHELL
> db.potions.find({"price": {"$lt": 20}} )
Name:
Invisibility
Vendor:
Kettlecooked
Price: 15.99
...
Name:
Shrinking
Vendor:
Kettlecooked
Price: 9.99
...
Name:
Luck
Vendor:
Kettlecooked
Price: 59.99
...
Name:
Love
Vendor:
Brewers
Price: 3.99
...
SHELL
> db.potions.find({"price": {"$gt":10, "$lt": 20}} )
Queries of Non-equality
We can use the $ne operator to nd potions with elds that dont equal the specied value.
Name:
Invisibility
Vendor:
Kettlecooked
Price: 15.99
...
Name:
Shrinking
Vendor:
Kettlecooked
Price: 9.99
...
Name:
Luck
Vendor:
Kettlecooked
Price: 59.99
...
Name:
Love
Vendor:
Brewers
Price: 3.99
...
SHELL
> db.potions.find({"vendor": { "$ne": "Brewers"}})
Name:
Invisibility
Price: 15.99
Sizes: [34,64,80]
...
Potion sizes
Name:
Shrinking
Price: 9.99
Sizes:[32,64,112]
...
Name:
Luck
Price: 59.99
Sizes: [10,16,32]
...
Name:
Love
Price: 3.99
Sizes: [2,8,16]
...
SHELL
> db.potions.find(
{"sizes" : {"$elemMatch": {"$gt": 8, "$lt": 16}}}
)
Name:
Invisibility
Price: 15.99
Sizes: [34,64,80]
...
Name:
Shrinking
Price: 9.99
Sizes:[32,64,112]
...
Name:
Luck
Price: 59.99
Sizes: [10,16,32]
...
Name:
Love
Price: 3.99
Sizes: [2,8,16]
...
SHELL
> db.potions.find(
{"sizes" : {"$gt": 8, "$lt": 16} }
)
Name:
Love
Price: 3.99
Sizes: [2,8,16]
...
SHELL
> db.potions.find(
{"sizes" : {"$gt": 8, "$lt": 16} }
)
Range Query
Name:
Invisibility
Price: 15.99
Sizes: [32,64,80]
...
Range Query
Materializing Potions
Level 3 Section 2
Customizing Queries
Potions Collection
Best potions
______
______
______
Introducing Projections
nd() takes a second parameter called a projection that we can use to specify the exact
elds we want back by setting their value to true.
SHELL
> db.potions.find(
{"grade": {"$gte": 80}},
{"vendor": true, "name": true}
)
{
"_id": ObjectId(...),
"vendor": "Kettlecooked",
"name": "Shrinking"
}
...
Only retrieve what s needed
Excluding Fields
Sometimes you want all the elds except for a few. In that case, we can exclude specic elds.
SHELL
> db.potions.find(
{"grade": {"$gte": 80}},
{"vendor": false, "price": false}
)
{/
"_id": ObjectId(...),
"name": "Shrinking",
"grade": 94,
"ingredients": [...],
...
}
SHELL
> db.potions.find(
{"grade": {"$gte": 80}},
{"vendor": true, "price": true, "_id": false}
)
{
"vendor": "Homebrewed",
"price": 9.99
}
The only time we can mix
an exclusion with selections
Removing the id is common when preparing data reports for non-developers.
SHELL
> db.potions.find(
{"grade": {"$gte": 80}},
{"name": true, "vendor": false}
)
Causes an error to be
raised
ERROR
"$err": "Can't canonicalize query: BadValue
Projection cannot have a mix of inclusion
and exclusion."
Potion Reviews
Ingredients
Laughter
Unicorn
+ more
Vendor
Kettlecooked
Brewers
+ more
Price
Under $10
Under $20
Invisibility
Score: 70
Taste: 4 Strength: 1
More Info
Love
Score: 84
Taste: 3 Strength: 5
More Info
Shrinking
Score: 94
Taste: 2 Strength: 3
More Info
SHELL
> db.potions.find({"vendor": "Kettlecooked"})
{"_id": ObjectId(...), ... }
{"_id": ObjectId(...), ... }
{"_id": ObjectId(...), ... }
...
First 20
documents
db.potions.find()
Sends 20
documents
SHELL
...
{"_id": ObjectId(...), "name": ... }
{"_id": ObjectId(...), "name": ... }
{"_id": ObjectId(...), "name": ... }
type "it" for more
db.potions.find()
Next batch
sent
SHELL
Cursor Methods
Since the cursor is actually an object, we can chain methods on it.
SHELL
> db.potions.find().count()
80
Cursor methods always come after find() since it returns the cursor object.
Sort by Price
We want to implement a way for users to sort potions by price.
Potion Reviews
Ingredients
+ more
Vendor
Invisibility
Score: 70
Taste: 4 Strength: 1
+ more
Love
+ more
Score: 84
Taste: 3 Strength: 5
Price
Sort
Price high
Price low
More Info
More Info
Shrinking
Score: 94
Taste: 2 Strength: 3
More Info
Sorting Potions
We can use the sort() cursor method to sort documents.
Name:
Love
Vendor:
Brewers
Price: 3.99
...
Name:
Shrinking
Vendor:
Kettlecooked
Price: 9.99
...
Name:
Invisibility
Vendor:
Kettlecooked
Price: 15.99
...
Name:
Luck
Vendor:
Leprechau"
Price: 59.99
...
SHELL
> db.potions.find().sort({"price": 1})
Field to sort
-1 to order descending
1 to order ascending
Potion Reviews
Ingredients
Invisibility
+ more
Score: 70
Taste: 4 Strength: 1
Vendor
+ more
Love
+ more
Score: 84
Taste: 3 Strength: 5
Price
Sort
More Info
More Info
Shrinking
Price high
Score: 94
Taste: 2 Strength: 3
Price low
< Back
Next >
More Info
Basic Pagination
We can implement basic pagination by limiting and skipping over documents. To do this, well
use the skip() and limit() cursor methods.
Page 1
Skip 0, Limit 3
SHELL
> db.potions.find().limit(3)
Since were not skipping, we
can leave off the skip method
and just limit 3
Basic Pagination
We can implement basic pagination by limiting and skipping over documents.
Page 2
Skip 3, Limit 3
SHELL
> db.potions.find().skip(3).limit(3)
Basic Pagination
We can implement basic pagination by limiting and skipping over documents.
Page 3
Skip 6, Limit 3
SHELL
> db.potions.find().skip(6).limit(3)
This approach can become really expensive
with large collections.
Morphing Models
Level 4 Section 1
Data Modeling
User Document
Potion Reviews
User Profile
Username
Azrius
E-mail
azrius@example.com
"_id": ObjectId(...),
"username": "Azrius",
"email": "azrius@example.com",
"favorites": "Luck"
Favorite Potion
Luck
Update
Favorites
User Document
{
"_id": ObjectId(...),
"username": "Azrius",
"email": "azrius@example.com",
"favorites": [
"Newt Tonic",
"Sleeping",
"Love"
]
}
Use an array to hold
multiple values
{
Vendor
"_id": ObjectId(...),
"name": "Invisibility",
...
"vendor": {
"name": "Kettlecooked",
"phone": 5555555555,
"organic": true
}
Name:
Invisi
Name:
Shrink...
Vendor:
Kettlecooked,
Organic
...
Vendor:
Kettlecooked,
Organic
...
Name:
Love
Vendor:
Vendor:
Poof
Brewers,
Non-Organic
Non-Organic
...
Name:
Luck
Vendor:
Leprechau,
Organic
...
Dangers of Duplication
Duplicate data can be hard to keep consistent throughout the database.
Name:
Invisi
Name:
Shrink...
Name:
Love
Name:
Luck
Vendor:
Kettlecooked,
Organic
...
Vendor:
Kettlecooked,
Non-Organic
...
Vendor:
Brewers,
Non-Organic
...
Vendor:
Leprechau,
Organic
...
Potion
Vendor
Potion
{
"_id": ObjectId(...),
"name": "Invisibility",
"vendor_id": "Kettlecooked",
...
}
Vendor
{
"_id": "Kettlecooked",
"phone": 5555555555,
"organic": true
}
SHELL
> db.potions.insert({
"name": "Invisibility",
"vendor_id": "Kettlecooked"
...
})
Referenced document
Potion
Vendor
{
"_id": ObjectId(...),
"name": "Invisibility",
"vendor_id": "Kettlecooked",
...
}
User
SHELL
{
"_id": ObjectId(...),
"email": "azrius@example.com"
"favorites": [
"Newt Tonic",
"Sleeping",
"Love"
]
}
SHELL
SHELL
db.users.insert({
"username": "Azrius",
"email": "azrius@example.com",
"favorites": ["Newt...", "Sleeping", "Love"]
})
Guarantee that the document write
completely happens or doesnt at all
Document remains
unchanged
SHELL
Something
bad happens
db.users.update(...)
SHELL
db.users.update(...)
Successful
WriteResult returned
db.vendors.update({"_id": ObjectId(...)},{...})
New Potion
Vendor
SHELL
db.potions.insert({...})
New Potions Vendor
db.vendors.insert({...})
SHELL
SHELL
Successful write
db.potions.insert(...)
New potion created!
Adding the new vendor
SHELL
Error occurs
db.vendors.insert(...)
No vendor document
created
Potion document
{
"_id": ObjectId(...),
"name": "Time Travel",
"vendor_id": "Magical Inc.",
...
}
Morphing Models
Level 4 Section 2
Data Modeling Decisions
Embedding
Referencing
Single query
Requires 2 queries
Atomic writes
Comments
Potion
Embed
Reference
Always
Sometimes
Rarely
Potion Reviews
Invisibility
Score: 70
Taste: 4 Strength: 1
Comments
I don't agree. You are wrong.
-Sauron
Referencing
We would be forced to
perform multiple queries
Whenever we display potions, well
always want to display comments
Username displayed for each
comment
Expected Size
Embed
Reference
More than a
few hundred
Thousands
Potion Reviews
Invisibility
Score: 70
Taste: 4 Strength: 1
Comments
I don't agree. You are wrong.
-Sauron
Referencing
Frequency of Change
Embed
Never/Rarely
Occasionally
Reference
Constantly
Prevents inconsistencies
from duplication
Comments
Comments
Embedding
Embedding
Referencing
User
Referencing
.{
"name": "Invisibility",
...
"comments":[
.{
"title": "The best potion!",
"body": "Lorem ipsum abra cadrbra"
},
...
].
}.
Comments readily
available
.{
"name": "Invisibility",
...
"comments":[
.{
"title": "The best potion!",
"body": "Lorem ipsum abra cadrbra",
"user_id": "Azrius"
},
...
].
}.
Usernames cant be
changed and are unique
Aggregation Apparitions
Level 5 Section 1
Common Aggregations
Name:
Love
Vendor:
Brewers
Price: 3.99
...
Name:
Shrinking
Vendor:
Kettlecooked
Price: 9.99
...
Name:
Invisibility
Vendor:
Kettlecooked
Price: 15.99
...
Name:
Luck
Vendor:
Leprechau
Price: 59.99
...
SHELL
Go within an array
> db.potions.aggregate(
[{"$group": {"_id": "$vendor_id" }}]
)
Results
$group
{"_id": "Kettlecooked"},
{"_id": "Brewers"},
{"_id": "Leprechaun Inc"}
Using Accumulators
Anything specied after the group key is considered an accumulator. Accumulators take a
single expression and compute the expression for grouped documents.
SHELL
> db.potions.aggregate([
{"$group": { "_id": "$vendor_id", "total": {"$sum": 1}}}
])
Will add 1 for each
matching document
Group key
Inventory
Results
$group
Accumulator
SHELL
> db.potions.aggregate([
{"$group": {"_id": "$vendor_id", "total": {"$sum": 1}}}
])
> db.potions.aggregate([
Field
path
{ "$group": {
"_id": "$vendor_id",
Sums the grade
"total": {"$sum": 1},
values for potions in
"grade_total": {"$sum": "$grade"}
their group
}
}
Second accumulator
])
Results
{"_id": "Kettlecooked", "total": 2, "grade_total": 400},
{"_id": "Brewers","total: 1, "grade_total": 340},
{"_id": "Leprechaun Inc, "total": 1, "grade_total": 92}
Name:
Invisibility
Vendor:
Kettlecooked
Grade: 70
...
Name:
Love
Vendor:
Brewers
Grade: 84
...
Name:
Shrinking
Vendor:
Kettlecooked
Grade: 94
...
Name:
Sleep
Vendor:
Brewers
Grade: 30
...
> db.potions.aggregate([
{ "$group": {
"_id": "$vendor_id",
"avg_grade": {"$avg": "$grade"}
}
}
])
Results
{"_id": "Kettlecooked", "avg_grade": 82 },
{"_id": "Brewers","avg_grade": 57 }
Name:
Invisibility
Vendor:
Kettlecooked
Grade: 70
...
Name:
Love
Vendor:
Brewers
Grade: 84
...
> db.potions.aggregate([
{ "$group": {
"_id": "$vendor_id",
"max_grade": {"$max": "$grade"}
}
}
])
Name:
Shrinking
Vendor:
Kettlecooked
Grade: 94
...
Name:
Sleep
Vendor:
Brewers
Grade: 30
...
Results
{"_id": "Kettlecooked", "max_grade": 94},
{"_id": "Brewers","max_grade": 84}
Name:
Love
Vendor:
Brewers
Grade: 84
...
Results
Name:
Shrinking
Vendor:
Kettlecooked
Grade: 94
...
Name:
Sleep
Vendor:
Brewers
Grade: 30
...
SHELL
> db.potions.aggregate([
{ "$group": {
"_id": "$vendor_id",
"max_grade": {"$max": "$grade"},
"min_grade": {"$min": "$grade"}
}
}
])
We can use the same field in
multiple accumulators
Aggregation Apparitions
Level 5 Section 2
The Aggregation Pipeline
Notice
All potions containing
unicorn are strictly
forbidden
1) Query potions
2) Group by vendor
3) Sum the number of
potions per vendor
db.potions.aggregate([
{"$group": {"_id": "$vendor_id", "total": {"$sum": 1}}}
])
Grouping
stage
Query
stage
Potions
collection
Filtered
documents
Grouped
documents
SHELL
db.potions.aggregate([
{"$match": {"ingredients": "unicorn"}}
We can use the same query
])
we would use with find()
Use match early to reduce the number of documents for
better performance.
-----
-----
-----
Potions
collection
Filtered
documents
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}}
])
Its good practice to limit the
number of results early on
Filtered
documents
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}}
])
Filtered
documents
Grouped
documents
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}},
{"$group": {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}}
])
avg_grade from
highest to lowest
Sort stage
Grouped documents
Sorted documents
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}},
{"$group": {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}},
{"$sort": {"avg_grade": -1}}
])
We can sort the field we created
during the group stage
Limit
Sorted documents
Limited documents
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}},
{"$group": {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}},
{"$sort": {"avg_grade": -1}},
{"$limit": 3}
Specify the number of
])
documents to limit
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}},
{"$group": {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}},
{"$sort": {"avg_grade": -1}},
{"$limit": 3}
])
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}},
{"$project":{"_id": false, "vendor_id": true, "grade": true}},
{"$group": {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}},
{"$sort": {"avg_grade": -1}},
{"$limit": 3}
])
Aggregation Results
SHELL
db.potions.aggregate([
{"$match": {"price": {"$lt": 15}}},
{"$project":{"_id": false, "vendor_id": true, "grade": true}},
{"$group": {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}},
{"$sort": {"avg_grade": -1}},
{"$limit": 3}
])
Group key