Discussion 13
NoSQL
Announcements
Vitamin 13 (NoSQL) due Monday, December 9 at 11:59pm
Course evals are out. Please take a few minutes to fill them out 🙏
Next week’s discussions are going to be topical review sessions. More details will be in next week’s ed announcement
Agenda
MapReduce and Spark (lecture 24) are also in scope for the finals but not covered in this section.
Background
Types of Workloads
Quick Summary Guide:
Question 1: OLTP vs. OLAP
For each of these workloads, choose whether it's better characterized as Online Transaction Processing or Online Analytical Processing:
a.) A social media site with millions of users needs to track all the "likes" and "dislikes" that each post receives.
Question 1: OLTP vs. OLAP
For each of these workloads, choose whether it's better characterized as Online Transaction Processing or Online Analytical Processing:
a.) A social media site with millions of users needs to track all the "likes" and "dislikes" that each post receives.
OLTP: OLTP workloads involve high numbers of transactions executed by many different users. Queries in these workloads involve simple lookups more often than complex joins. In this case, when a user likes or dislikes a post, the site would need to do a lookup on that post and update its likes/dislikes value.
Question 1: OLTP vs. OLAP
For each of these workloads, choose whether it's better characterized as Online Transaction Processing or Online Analytical Processing:
b.) An online book store needs to aggregate and analyze its users book purchases by genre over the last eight months.
Question 1: OLTP vs. OLAP
For each of these workloads, choose whether it's better characterized as Online Transaction Processing or Online Analytical Processing:
b.) An online book store needs to aggregate and analyze its users book purchases by genre over the last eight months.
OLAP: OLAP workloads involve read-only queries and typically include lots of joins and aggregations. Often, workloads executed for analysis and decision making are OLAP workloads. In this case, the book store needs to read from data stored over the last eight months regarding book purchases, and may need to perform some joins to analyze those purchases by genre.
Question 1: OLTP vs. OLAP
For each of these workloads, choose whether it's better characterized as Online Transaction Processing or Online Analytical Processing:
c.) A multiplayer online game has added updated areas to its map and now wants to assess how users behave in those areas, and how user playtime has changed as a result.
Question 1: OLTP vs. OLAP
For each of these workloads, choose whether it's better characterized as Online Transaction Processing or Online Analytical Processing:
c.) A multiplayer online game has added updated areas to its map and now wants to assess how users behave in those areas, and how user playtime has changed as a result.
OLAP: In this case, the game designers will need to read data that likely includes which players logged in after the updated areas were released, their average playtimes, and any other data that may be relevant to how players behave in the new area.
Scaling
Question 2: Scaling
a.) A small startup realizes that its current database can't sustain their growing workloads. Given that these workloads involve a lot of writes but few reads, should it invest in more partitioning or more replication?
Question 2: Scaling
a.) A small startup realizes that its current database can't sustain their growing workloads. Given that these workloads involve a lot of writes but few reads, should it invest in more partitioning or more replication?
Partitioning: More partitioning means that more queries can be executed in parallel on different machines. This is especially good for write-heavy workloads because these can often involve updates to only a few machines, meaning more of them can happen at once (higher throughput).
Question 2: Scaling
b.) A mechanical failure causes some of the startup's database machines to permanently crash, losing data in the process. If the startup wants to prevent similar losses in the future, should it invest more in partitioning or more replication?
Question 2: Scaling
b.) A mechanical failure causes some of the startup's database machines to permanently crash, losing data in the process. If the startup wants to prevent similar losses in the future, should it invest more in partitioning or more replication?
Replication: Replication means each database machine is no longer a single point of failure. Once data is replicated, the entire system is more resilient to data loss because if one machine crashes, its data can be recovered from the replicas. However, writes will become slower because the changes have to propagate through all replicas.
CAP Theorem and BASE
Distributed Systems– Desired Properties
CAP Theorem
BASE Semantics
Question 3: BASE
a.) Database designer Doug is annoyed with his distributed database because for some time after issuing a write, all his reads return different values. Does this violate any of the BASE properties?
Question 3: BASE
a.) Database designer Doug is annoyed with his distributed database because for some time after issuing a write, all his reads return different values. Does this violate any of the BASE properties?
No.
This is in fact the soft state and eventual consistency properties in action. As the write propagates through the system, reads may return different values because the database is inconsistent.
Question 3: BASE
Which properties of BASE do these scenarios violate?
b.) All reads and writes always have the same views of data, but they sometimes respond to valid inputs with timeout errors.
Question 3: BASE
Which properties of BASE do these scenarios violate?
b.) All reads and writes always have the same views of data, but they sometimes respond to valid inputs with timeout errors.
Basic Availability.
This database seems to prioritize consistency over availability. Since valid inputs sometimes receive an error response, it violates basic availability.
Question 3: BASE
Which properties of BASE do these scenarios violate?
c.) Writes propagate to only 3 replicas, but the system has 5 replicas of each piece of data.
Question 3: BASE
Which properties of BASE do these scenarios violate?
c.) Writes propagate to only 3 replicas, but the system has 5 replicas of each piece of data.
Eventual Consistency.
Since writes do not propagate to all 5 replicas, different pieces of data will always be stale on different replicas, so reads will never be consistent.
Question 3: BASE
Which properties of BASE do these scenarios violate?
d.) An empty database that has never been populated responds to a read query on some specific key with the message "Error: key nonexistent!"
Question 3: BASE
Which properties of BASE do these scenarios violate?
d.) An empty database that has never been populated responds to a read query on some specific key with the message "Error: key nonexistent!"
No properties are violated.
The basic availability property only guarantees that valid queries always get a non-error response. However, since the database is empty, a read query is invalid, so the error response is OK.
NoSQL Motivation
NoSQL Data Models
Key-Value Stores
Key-Value Stores Example
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
The Sales data can be represented in multiple ways as key, value pairs:
Choose depending on your use case!
Key-Value Stores Q1a
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
Sales data is stored with Key = sid, Value = entire Sales record, partitioned on hash function h and replicated across 3 servers. Describe how the operation get(sid1) would be executed. (Assume a Sale with sid1 exists in the data).
Key-Value Stores Q1a
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
Sales data is stored with Key = sid, Value = entire Sales record, partitioned on hash function h and replicated across 3 servers. Describe how the operation get(sid1) would be executed. (Assume a Sale with sid1 exists in the data).
We must first hash the key, h(sid1), to find which partition the data is stored on. Then we can retrieve the value from any of the replicas/servers.
Key-Value Stores Q1b
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
Sales data is stored with Key = sid, Value = entire Sales record, partitioned on hash function h and replicated across 3 servers. Describe how the operation put(sid2, saleRecord) would be executed.
Key-Value Stores Q1b
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
Sales data is stored with Key = sid, Value = entire Sales record, partitioned on hash function h and replicated across 3 servers. Describe how the operation put(sid2, saleRecord) would be executed.
We must first hash the key, h(sid2), to find which partition the data should be stored on. Then, we will insert the record into that partition, and propagate the change to the other replicas/servers. Note: Propagation of changes may not happen immediately. We only need to enforce eventual consistency.
Key-Value Stores Q1c
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
Sales data is stored with Key = sid, Value = entire Sales record, partitioned on hash function h and replicated across 3 servers. After put(sid2, saleRecord) is executed, is it guaranteed that every app will be able to access that new Sale data?
Key-Value Stores Q1c
Database Doug now has the following tables:
Sales (sid, date, quantity, customer, product)
Product (pid, name, price)
Customer (cid, name, address)
Sales data is stored with Key = sid, Value = entire Sales record, partitioned on hash function h and replicated across 3 servers. After put(sid2, saleRecord) is executed, is it guaranteed that every app will be able to access that new Sale data?
No. Since we’re only enforcing eventual consistency, the changes from the put operation may not have propagated to all replicas yet. Note: It is possible to perform checks to see whether the replica an app is pulling data from is up-to-date, if the app requires non-stale data. Requires communication with other replicas.
Extensible Record Stores
Document Stores
JSON
JSON Overview
JSON vs Relational
| JSON | Relational |
Flexibility | Very flexible, can represent complex structures and nested data | Less flexible |
Schema Enforcement | Self-describing; Each document can have unique structure | Schema is fixed |
Representation | Text-based (easily parsed and manipulated by many languages) | Binary representation (designed for efficient storage and retrieval from disk) |
| “Enforcing schema on read” | “Enforcing schema on write” |
Relational → JSON
name | grade |
Su Min | 86 |
Sarah | 55 |
Soumya | 91 |
{“student”: [
{“name”: “Su Min”, “grade”: 86},
{“name”: “Sarah”, “grade”: 55},
{“name”: “Soumya”, “grade”: 91}
]
}
Student
Student
name
“Su Min”
grade
86
grade
55
grade
91
name
“Sarah”
name
“Soumya”
Relational → JSON
For a many-to-many Relationship:
name | gpa |
Su Min | 3.0 |
Sarah | 3.5 |
Student
name | subject |
Su Min | Math |
Su Min | English |
Sarah | Math |
Classes Taken
name | teacher |
Math | Max |
English | Toby |
Subjects
Relational → JSON
For a many-to-many Relationship:
name | gpa |
Su Min | 3.0 |
Sarah | 3.5 |
Students
name | subject | grade |
Su Min | Math | A |
Su Min | English | B |
Sarah | Math | C |
Classes Taken
name | teacher |
Math | Max |
English | Toby |
Subjects
{"Students": [
{"name": "Su Min",
"gpa": 3.0,
"Classes Taken": [
{"Subject":
{"name": "Math",
"teacher": "Max"},
"grade": "A"},
{"Subject":
{"name": "English",
"teacher": "Toby"},
"grade": "B"}]},
...
]}
JSON → Relational
Structure of “address” is inconsistent across data, and so is the schema!
Example JSON Document that would be difficult to translate to a relational table
{“student”: [
{“name”: “Su Min”, “grade”: 86,
“address”: {“street”: “21 Milan St.”, “city”: “Berkeley”, “state”: ”CA”}},
{“name”: “Sarah”, “grade”: 55,
“address” : “12 Franklin St., Berkeley, CA”,
“email”: “sk@berkeley.edu”},
]
}
JSON Q1
Convert the following relational table into a JSON document.
name | debut | goals |
Tony | 10/12/09 | 43 |
Katy | 1/20/14 | 22 |
Players
JSON Q1
Convert the following relational table into a JSON document.
name | debut | goals |
Tony | 10/12/09 | 43 |
Katy | 1/20/14 | 22 |
{“players”: [
{“name”: “Tony”, “debut”: “10/12/09”,
“goals”: 43},
{“name”: “Katy”, “debut”: “1/20/14”,
“goals”: 22}
]
}
Players
JSON Q2
Convert the following JSON document into two relational tables, Players(name, debut) and Goals(name, goals).
{“players”: [
{“name”: “Abby”, “debut”: “10/12/09”,
“goals”: 43},
{“name”: “Babby”, “debut”: “1/20/14”,
“goals”: 22},
{“name”: “Cabby”, “debut”: “1/21/14”,
“goals”: 23}
]
}
JSON Q2
Convert the following JSON document into two relational tables: Players(name, debut) and Goals(name, goals).
{“players”: [
{“name”: “Abby”, “debut”: “10/12/09”,
“goals”: 43},
{“name”: “Babby”, “debut”: “1/20/14”,
“goals”: 22},
{“name”: “Cabby”, “debut”: “1/21/14”,
“goals”: 23}
]
}
name | debut |
Abby | 10/12/09 |
Babby | 1/20/14 |
Cabby | 1/21/14 |
Players
name | goals |
Abby | 43 |
Babby | 22 |
Cabby | 23 |
Goals
MongoDB Query Language (MQL)
MQL Data Model
MQL
MQL Syntax
MQL Syntax– Dot (.) notation
MQL Syntax– Dollar ($) Notation
MQL Queries
MQL: Retrieval Queries
MongoDB Query Language | Relational Database Equivalent |
find(<predicate>, optional <projection>) | SELECT <projection> FROM Collection WHERE predicate |
limit(<integer>) | LIMIT |
sort(<list of fields>) | ORDER BY |
MQL Retrieval: find()
db.collection.find(<predicate>, optional <projection>)
MQL Retrieval: find() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Retrieval: find() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Retrieval: find() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Retrieval: find() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Retrieval: find() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Retrieval: limit() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Retrieval: sort() Examples
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “Sold”, “seats” : {“num”: 5, “type”: “leather”}, “reviews” : [5, 4, 5, 3, 1] }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “reviews” : [2, 4, 3, 4, 2] }
MQL Aggregation
MQL Aggregation: Stage Types
MQL Aggregation Syntax
db.collection.aggregate([
{$stage1Op: {}},
{$stage2Op: {}},
…
{$stageNOp: {}}
])
MQL Aggregation: Grouping
Grouping is done with the $group operator:
$group : {
_id : <expression>, // Same as a relational GROUP BY
<field1> : {<aggregation_func1> : <expression1>},
…}
MQL Grouping Example
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “In Stock”, “seats” : {“num”: 5, “type”: “leather”}, “price” : 12.1 }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “price” : 15.2}
{“_id” : 2, “car” : “Audi”, “status” : “Sold”, “seats” : {“num”: 7, “type”: “polyester”}, “price” : 14.8}
db.dealership.aggregate([
{$group : {_id : “$status”, avgPrice : {$avg : “$price”}}},
{$match : {avgPrice : {$lte : 14}}
])
Note: remember to use “$field_name” format when referring to fields in the values of the $group stage.
MQL Grouping Example
> db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “In Stock”, “seats” : {“num”: 5, “type”: “leather”}, “price” : 12.1 }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “price” : 15.2}
{“_id” : 2, “car” : “Audi”, “status” : “Sold”, “seats” : {“num”: 7, “type”: “polyester”}, “price” : 14.8}
db.dealership.aggregate([
{$group : {_id : “$status”, avgPrice : {$avg : “$price”}}},
{$match : {avgPrice : {$lte : 14}}
])
Output:
{“_id” : “In Stock”, “avgPrice” : 13.65}
MQL Aggregation: Lookup
{$lookup : {
from : <collection to join>,
localField : <referencing field>,
foreignField : <referenced field>,
as : <output array field>
}}
MQL Aggregation: Lookup Example
db.dealership.find()
{“_id” : 1, “car” : “Honda”, “status” : “In Stock”, “seats” : {“num”: 5, “type”: “leather”}, “price” : 12.1 }
{“_id” : 2, “car” : “Audi”, “status” : “In Stock”, “seats” : {“num”: 7, “type”: “polyester”}, “price” : 15.2}
{“_id” : 2, “car” : “Audi”, “status” : “Sold”, “seats” : {“num”: 7, “type”: “polyester”}, “price” : 14.8}
db.dealership.aggregate([
{$lookup : {from : “dealership”, localField : “status”, foreignField : “status”, as : “stock”},
{$project : {_id : 0, seats: 0, “stock._id” : 0, “stock.seats” : 0}}
])
Output:
{“car” : “Honda”, “status” : “In Stock”, “price” : 12.1, “stock” : [
{“car” : “Honda”, “status” : “In Stock”, “price” : 12.1},
{“car” : “Audi”, “status” : “In Stock”, “price” : 15.2}
]}
...
MQL Updates
MQL Updates: insertMany
db.aquarium.insertMany([
{“fish” : “yellowtail”, “price” : “30”, status : “friend”},
{“fish” : “tuna”, “price” : 20, status : “food”}
])
MQL Updates: updateMany
db.aquarium.updateMany(
{price : {“$lt” : 35}},
{$set : {status : “food”}}
)
db.aquarium.find() → Output
{“fish” : “yellowtail”, “price” : “30”, status : “food”},
{“fish” : “tuna”, “price” : 20, status : “food”}
MQL Q1
Consider the following MongoDB collection teams.
(teamId: int, divisionId: int, stadiumCapacity: int, wins: int, �losses: int, coach: string, captain: string)
Write an MQL query to find the coach and captain of all teams from division 1 with at least 10 wins, sorted by coach DESC and ties broken by captain ASC.
MQL Q1
Consider the following MongoDB collection teams.
Write an MQL query to find the coach and captain of all teams from division 1 with at least 10 wins, sorted by coach DESC and ties broken by captain ASC.��
db.teams.aggregate([
{$match: {wins: {$gte: 10}, divisionId: 1}},
{$sort: {"coach": -1, "captain": 1}},
{$project: {"coach": 1, "captain": 1, "_id": 0}}
])
MQL Q2
Translate the following SQL query into an MQL query:��SELECT divisionId AS div, MAX(wins) AS maxWins �FROM teams�WHERE stadiumCapacity >= 20000 �GROUP BY divisionId�SORT BY MAX(wins), COUNT(*) DESC;
MQL Q2
Translate the following SQL query �into an MQL query:��SELECT divisionId AS div, MAX(wins) �AS maxWins �FROM teams�WHERE stadiumCapacity >= 20000 �GROUP BY divisionId�SORT BY MAX(wins), COUNT(*) DESC;
db.teams.aggregate([
{ $match: {
stadiumCapacity: {$gte: 20000}
}},
{ $group: {
_id: "$divisionId",
maxWins: {$max: "$wins"},
count: {$sum: 1}
}},
{ $sort: {
"maxWins": 1,
"count": -1
}},
{ $project: {
div: "$_id", ← similar to aliasing in SQL
_id: 0,
maxWins: 1
}}
])
Attendance Link