ALX Back End Storage
ALX Back End Storage
By: Guillaume Plessis, Senior Cloud & System Engineer at WeWork and
Guillaume, CTO at Holberton school
Weight: 1
Project over - took place from Feb 8, 2023 5:00 AM to Feb 10, 2023 5:00 AM
In a nutshell…
Auto QA review: 53.0/53 mandatory & 8.0/8 optional
Altogether: 200.0%
Mandatory: 100.0%
Optional: 100.0%
Concepts
Advanced SQL
MySQL cheatsheet
Stored Procedure
Triggers
Views
Back-end - Storage 1
CREATE VIEW Statement
Resources
Read or watch:
MySQL cheatsheet
Stored Procedure
Triggers
Views
Learning Objectives
At the end of this project, you are expected to be able to explain to anyone, without
the help of Google:
General
How to create tables with constraints
Requirements
Back-end - Storage 2
General
All your files will be executed on Ubuntu 18.04 LTS using MySQL 5.7 (version
5.7.30)
All your SQL queries should have a comment just before (i.e. syntax above)
More Info
In the container, you should start MySQL before playing with it:
Back-end - Storage 3
performance_schema
sys
$
Tasks
0. We are all unique!
mandatory
Score: 100.0% (Checks completed: 100.0%)
Write a SQL script that creates a table users following these requirements:
Back-end - Storage 4
Context: Make an attribute unique directly in the table schema will enforced your
business rules and avoid bugs in your application
Repo:
Directory: 0x00-MySQL_Advanced
File: 0-uniq_users.sql
Back-end - Storage 5
name , string (255 characters)
Repo:
Directory: 0x00-MySQL_Advanced
File: 1-country_users.sql
Back-end - Storage 6
Score: 100.0% (Checks completed: 100.0%)
Write a SQL script that ranks country origins of bands, ordered by the number of
(non-unique) fans
Requirements:
Repo:
Directory: 0x00-MySQL_Advanced
File: 2-fans.sql
Write a SQL script that lists all bands with Glam rock as their main style, ranked by
their longevity
Back-end - Storage 7
Requirements:
Column names must be: band_name and lifespan (in years until 2022 - please
use 2022 instead of YEAR(CURDATE()) )
You should use attributes formed and split for computing the lifespan
Repo:
Directory: 0x00-MySQL_Advanced
File: 3-glam_rock.sql
Write a SQL script that creates a trigger that decreases the quantity of an item after
adding a new order.
Back-end - Storage 8
bob@dylan:~$ cat 4-init.sql
-- Initial
DROP TABLE IF EXISTS items;
DROP TABLE IF EXISTS orders;
bob@dylan:~$
bob@dylan:~$ cat 4-init.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 4-store.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 4-main.sql
Enter password:
-- Show and add orders
SELECT * FROM items;
SELECT * FROM orders;
SELECT "--";
bob@dylan:~$
bob@dylan:~$ cat 4-main.sql | mysql -uroot -p holberton
Enter password:
name quantity
apple 10
pineapple 10
pear 10
--
--
name quantity
apple 6
pineapple 10
pear 8
item_name number
apple 1
apple 3
Back-end - Storage 9
pear 2
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 4-store.sql
Write a SQL script that creates a trigger that resets the attribute valid_email only
when the email has been changed.
Context: Nothing related to MySQL, but perfect for user email validation - distribute
the logic to the database itself!
bob@dylan:~$
bob@dylan:~$ cat 5-init.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 5-valid_email.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 5-main.sql
Enter password:
-- Show users and update (or not) email
SELECT * FROM users;
Back-end - Storage 10
UPDATE users SET valid_email = 1 WHERE email = "[email protected]";
UPDATE users SET email = "[email protected]" WHERE email = "[email protected]";
UPDATE users SET name = "Jannis" WHERE email = "[email protected]";
SELECT "--";
SELECT * FROM users;
SELECT "--";
SELECT * FROM users;
bob@dylan:~$
bob@dylan:~$ cat 5-main.sql | mysql -uroot -p holberton
Enter password:
id email name valid_email
1 [email protected] Bob 0
2 [email protected] Sylvie 1
3 [email protected] Jeanne 1
--
--
id email name valid_email
1 [email protected] Bob 1
2 [email protected] Sylvie 0
3 [email protected] Jannis 1
--
--
id email name valid_email
1 [email protected] Bob 1
2 [email protected] Sylvie 0
3 [email protected] Jannis 1
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 5-valid_email.sql
6. Add bonus
mandatory
Write a SQL script that creates a stored procedure AddBonus that adds a new
correction for a student.
Requirements:
Back-end - Storage 11
Procedure AddBonus is taking 3 inputs (in this order):
Back-end - Storage 12
INSERT INTO corrections (user_id, project_id, score) VALUES (@user_bob, @project_c, 8
0);
INSERT INTO corrections (user_id, project_id, score) VALUES (@user_bob, @project_py, 9
6);
bob@dylan:~$
bob@dylan:~$ cat 6-init.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 6-bonus.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 6-main.sql
Enter password:
-- Show and add bonus correction
SELECT * FROM projects;
SELECT * FROM corrections;
SELECT "--";
CALL AddBonus((SELECT id FROM users WHERE name = "Jeanne"), "Python is cool", 100);
CALL AddBonus((SELECT id FROM users WHERE name = "Jeanne"), "Bonus project", 100);
CALL AddBonus((SELECT id FROM users WHERE name = "Bob"), "Bonus project", 10);
CALL AddBonus((SELECT id FROM users WHERE name = "Jeanne"), "New bonus", 90);
SELECT "--";
bob@dylan:~$
bob@dylan:~$ cat 6-main.sql | mysql -uroot -p holberton
Enter password:
id name
1 C is fun
2 Python is cool
user_id project_id score
1 1 80
1 2 96
2 1 91
2 2 73
--
--
--
--
id name
1 C is fun
2 Python is cool
3 Bonus project
4 New bonus
Back-end - Storage 13
user_id project_id score
1 1 80
1 2 96
2 1 91
2 2 73
2 2 100
2 3 100
1 3 10
2 4 90
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 6-bonus.sql
7. Average score
mandatory
Score: 100.0% (Checks completed: 100.0%)
Back-end - Storage 14
id int not null AUTO_INCREMENT,
name varchar(255) not null,
PRIMARY KEY (id)
);
bob@dylan:~$
bob@dylan:~$ cat 7-init.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 7-average_score.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 7-main.sql
-- Show and compute average score
SELECT * FROM users;
SELECT * FROM corrections;
SELECT "--";
CALL ComputeAverageScoreForUser((SELECT id FROM users WHERE name = "Jeanne"));
SELECT "--";
SELECT * FROM users;
Back-end - Storage 15
bob@dylan:~$
bob@dylan:~$ cat 7-main.sql | mysql -uroot -p holberton
Enter password:
id name average_score
1 Bob 0
2 Jeanne 0
user_id project_id score
1 1 80
1 2 96
2 1 91
2 2 73
--
--
--
--
id name average_score
1 Bob 0
2 Jeanne 82
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 7-average_score.sql
Requirements:
Context: Index is not the solution for any performance issue, but well used, it’s really
powerful!
Back-end - Storage 16
Enter password:
mysql> SELECT COUNT(name) FROM names WHERE name LIKE 'a%';
+-------------+
| COUNT(name) |
+-------------+
| 302936 |
+-------------+
1 row in set (2.19 sec)
mysql>
mysql> exit
bye
bob@dylan:~$
bob@dylan:~$ cat 8-index_my_names.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ mysql -uroot -p holberton
Enter password:
mysql> SHOW index FROM names;
+-------+------------+----------------+--------------+-------------+-----------+------
-------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardi
nality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------+--------------+-------------+-----------+------
-------+----------+--------+------+------------+---------+---------------+
| names | 1 | idx_name_first | 1 | name | A |
25 | 1 | NULL | YES | BTREE | | |
+-------+------------+----------------+--------------+-------------+-----------+------
-------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
mysql>
mysql> SELECT COUNT(name) FROM names WHERE name LIKE 'a%';
+-------------+
| COUNT(name) |
+-------------+
| 302936 |
+-------------+
1 row in set (0.82 sec)
mysql>
mysql> exit
bye
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 8-index_my_names.sql
Back-end - Storage 17
Score: 100.0% (Checks completed: 100.0%)
Write a SQL script that creates an index idx_name_first_score on the table names and
the first letter of name and the score .
Requirements:
Back-end - Storage 18
mysql> exit
bye
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 9-index_name_score.sql
a , INT
b , INT
And returns a / b or 0 if b == 0
Back-end - Storage 19
bob@dylan:~$
bob@dylan:~$ cat 10-div.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ echo "SELECT (a / b) FROM numbers;" | mysql -uroot -p holberton
Enter password:
(a / b)
5.0000
0.8000
0.6667
2.0000
NULL
0.7500
bob@dylan:~$
bob@dylan:~$ echo "SELECT SafeDiv(a, b) FROM numbers;" | mysql -uroot -p holberton
Enter password:
SafeDiv(a, b)
5
0.800000011920929
0.6666666865348816
2
0
0.75
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 10-div.sql
Write a SQL script that creates a view need_meeting that lists all students that have a
score under 80 (strict) and no last_meeting or more than 1 month.
Requirements:
Back-end - Storage 20
bob@dylan:~$ cat 11-init.sql
-- Initial
DROP TABLE IF EXISTS students;
SELECT "--";
SELECT "--";
SELECT "--";
SELECT "--";
SELECT "--";
SELECT "--";
Back-end - Storage 21
bob@dylan:~$
bob@dylan:~$ cat 11-main.sql | mysql -uroot -p holberton
Enter password:
name
Jean
Steeve
--
--
name
Bob
Jean
Steeve
--
--
name
Bob
Jean
--
--
name
Bob
--
--
name
Bob
Jean
--
--
View Create View character_set_client collation_connection
XXXXXX<yes, here it will display the View SQL statement :-) >XXXXXX
--
--
Table Create Table
students CREATE TABLE `students` (\n `name` varchar(255) NOT NULL,\n `score` int
(11) DEFAULT '0',\n `last_meeting` date DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET
=latin1
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 11-need_meeting.sql
Back-end - Storage 22
Write a SQL script that creates a stored
procedure ComputeAverageWeightedScoreForUser that computes and store the average
weighted score for a student.
Requirements:
Tips:
Calculate-Weighted-Average
Back-end - Storage 23
SET @project_c = LAST_INSERT_ID();
bob@dylan:~$
bob@dylan:~$ cat 100-init.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 100-average_weighted_score.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 100-main.sql
-- Show and compute average weighted score
SELECT * FROM users;
SELECT * FROM projects;
SELECT * FROM corrections;
SELECT "--";
SELECT * FROM users;
bob@dylan:~$
bob@dylan:~$ cat 100-main.sql | mysql -uroot -p holberton
Enter password:
id name average_score
1 Bob 0
2 Jeanne 82
id name weight
1 C is fun 1
2 Python is cool 2
user_id project_id score
1 1 80
1 2 96
2 1 91
2 2 73
--
--
id name average_score
1 Bob 0
2 Jeanne 79
bob@dylan:~$
Repo:
Back-end - Storage 24
GitHub repository: alx-backend-storage
Directory: 0x00-MySQL_Advanced
File: 100-average_weighted_score.sql
Tips:
Calculate-Weighted-Average
Back-end - Storage 25
CONSTRAINT fk_project_id FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) O
N DELETE CASCADE
);
bob@dylan:~$
bob@dylan:~$ cat 101-init.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 101-average_weighted_score.sql | mysql -uroot -p holberton
Enter password:
bob@dylan:~$
bob@dylan:~$ cat 101-main.sql
-- Show and compute average weighted score
SELECT * FROM users;
SELECT * FROM projects;
SELECT * FROM corrections;
CALL ComputeAverageWeightedScoreForUsers();
SELECT "--";
SELECT * FROM users;
bob@dylan:~$
bob@dylan:~$ cat 101-main.sql | mysql -uroot -p holberton
Enter password:
id name average_score
1 Bob 0
2 Jeanne 0
id name weight
1 C is fun 1
2 Python is cool 2
user_id project_id score
1 1 80
1 2 96
2 1 91
Back-end - Storage 26
2 2 73
--
--
id name average_score
1 Bob 90.6667
2 Jeanne 79
bob@dylan:~$
Repo:
Directory: 0x00-MySQL_Advanced
File: 101-average_weighted_score.sql
0x01. NoSQL
Back-endNoSQLMongoDB
By: Emmanuel Turlay, Staff Software Engineer at Cruise and Guillaume, CTO at
Holberton school
Weight: 1
Project over - took place from Feb 13, 2023 5:00 AM to Feb 15, 2023 5:00 AM
In a nutshell…
Auto QA review: 133.95/135 mandatory & 32.0/32 optional
Altogether: 198.44%
Mandatory: 99.22%
Optional: 100.0%
Resources
Read or watch:
Back-end - Storage 27
What is NoSQL ?
Aggregation
Learning Objectives
At the end of this project, you are expected to be able to explain to anyone, without
the help of Google:
General
What NoSQL means
What is ACID
Requirements
Back-end - Storage 28
All your files should end with a new line
Python Scripts
All your files will be interpreted/compiled on Ubuntu 18.04 LTS
using python3 (version 3.7) and PyMongo (version 3.10)
The first line of all your files should be exactly #!/usr/bin/env python3
'print(__import__("my_module").__doc__)' )
'print(__import__("my_module").my_function.__doc__)'
Your code should not be executed when imported (by using if __name__ ==
"__main__" :)
More Info
Back-end - Storage 29
$ mongo --version
MongoDB shell version v4.2.8
git version: 43d25964249164d76d5e04dd6cf38f6111e21f5f
OpenSSL version: OpenSSL 1.1.1 11 Sep 2018
allocator: tcmalloc
modules: none
build environment:
distmod: ubuntu1804
distarch: x86_64
target_arch: x86_64
$
$ pip3 install pymongo
$ python3
>>> import pymongo
>>> pymongo.__version__
'3.10.1'
Potential issue if documents creation doesn’t work or this error: Data directory
#!/bin/sh
### BEGIN INIT INFO
# Provides: mongod
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Should-Start: $named
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: An object/document-oriented database
# Description: MongoDB is a high-performance, open source, schema-free
# document-oriented data store that's easy to deploy, manage
# and use. It's network accessible, written in C++ and offers
# the following features:
#
# * Collection oriented storage - easy storage of object-
# style data
# * Full index support, including on inner objects
# * Query profiling
# * Replication and fail-over support
# * Efficient storage of binary data including large
# objects (e.g. videos)
# * Automatic partitioning for cloud-level scalability
#
# High performance, scalability, and reasonable depth of
# functionality are the goals for the project.
Back-end - Storage 30
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/mongod
DESC=database
NAME=mongod
# Defaults. Can be overridden by the /etc/default/$NAME
# Other configuration options are located in $CONF file. See here for more:
# http://dochub.mongodb.org/core/configurationoptions
CONF=/etc/mongod.conf
PIDFILE=/var/run/$NAME.pid
ENABLE_MONGOD=yes
. /lib/lsb/init-functions
STARTTIME=1
DIETIME=10 # Time to wait for the server to die, in seconds
# If this value is set too low you might not
# let some servers to die gracefully and
# 'restart' will not work
DAEMONUSER=${DAEMONUSER:-mongodb}
DAEMONGROUP=${DAEMONGROUP:-mongodb}
set -e
Back-end - Storage 31
running_pid() {
# Check if a given process pid's cmdline matches a given name
pid=$1
name=$2
[ -z "$pid" ] && return 1
[ ! -d /proc/$pid ] && return 1
cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
# Is this the expected server
[ "$cmd" != "$name" ] && return 1
return 0
}
running() {
# Check if the process is running looking at /proc
# (works for all users)
start_server() {
# Start the process using the wrapper
start-stop-daemon --background --start --quiet --pidfile $PIDFILE \
--make-pidfile --chuid $DAEMONUSER:$DAEMONGROUP \
--exec $NUMACTL $DAEMON $DAEMON_OPTS
errcode=$?
return $errcode
}
stop_server() {
# Stop the process using the wrapper
start-stop-daemon --stop --quiet --pidfile $PIDFILE \
--retry 300 \
--user $DAEMONUSER \
--exec $DAEMON
errcode=$?
return $errcode
}
force_stop() {
# Force the process to die killing it manually
[ ! -e "$PIDFILE" ] && return
if running ; then
kill -15 $pid
# Is it really dead?
sleep "$DIETIME"s
if running ; then
kill -9 $pid
sleep "$DIETIME"s
if running ; then
echo "Cannot kill $NAME (pid=$pid)!"
exit 1
fi
fi
fi
Back-end - Storage 32
rm -f $PIDFILE
}
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
# Check if it's running first
if running ; then
log_progress_msg "apparently already running"
log_end_msg 0
exit 0
fi
if start_server ; then
# NOTE: Some servers might die some time after they start,
# this code will detect this issue if STARTTIME is set
# to a reasonable value
[ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time
if running ; then
# It's ok, the server started and is running
log_end_msg 0
else
# It is not running after we did start
log_end_msg 1
fi
else
# Either we could not start it
log_end_msg 1
fi
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
if running ; then
# Only stop the server if we see it running
errcode=0
stop_server || errcode=$?
log_end_msg $errcode
else
# If it's not running don't do anything
log_progress_msg "apparently not running"
log_end_msg 0
exit 0
fi
;;
force-stop)
# First try to stop gracefully the program
$0 stop
if running; then
# If it's still running try to kill it more forcefully
log_daemon_msg "Stopping (force) $DESC" "$NAME"
errcode=0
force_stop || errcode=$?
log_end_msg $errcode
fi
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
errcode=0
Back-end - Storage 33
stop_server || errcode=$?
# Wait some sensible amount, some server need this
[ -n "$DIETIME" ] && sleep $DIETIME
start_server || errcode=$?
[ -n "$STARTTIME" ] && sleep $STARTTIME
running || errcode=$?
log_end_msg $errcode
;;
status)
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0
In the container, you should start MongoDB before playing with it:
Back-end - Storage 34
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongo
db
Implicit session: session { "id" : UUID("70f14b38-6d0b-48e1-a9a4-0534bcf15301") }
MongoDB server version: 4.2.8
admin 0.000GB
config 0.000GB
local 0.000GB
bye
$
Tasks
0. List all databases
mandatory
Repo:
Directory: 0x01-NoSQL
File: 0-list_databases
1. Create a database
mandatory
Back-end - Storage 35
guillaume@ubuntu:~/0x01$ cat 0-list_databases | mongo
MongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
admin 0.000GB
config 0.000GB
local 0.000GB
logs 0.005GB
bye
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ cat 1-use_or_create_database | mongo
MongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
switched to db my_db
bye
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 1-use_or_create_database
2. Insert document
mandatory
Score: 100.0% (Checks completed: 100.0%)
The document must have one attribute name with value “Holberton school”
Repo:
Back-end - Storage 36
Directory: 0x01-NoSQL
File: 2-insert
3. All documents
mandatory
Score: 100.0% (Checks completed: 100.0%)
Repo:
Directory: 0x01-NoSQL
File: 3-all
4. All matches
mandatory
Score: 100.0% (Checks completed: 100.0%)
Write a script that lists all documents with name="Holberton school" in the
collection school :
Back-end - Storage 37
bye
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 4-match
5. Count
mandatory
Repo:
Directory: 0x01-NoSQL
File: 5-count
6. Update
mandatory
The script should update only document with name="Holberton school" (all of them)
Back-end - Storage 38
The update should add the attribute address with the value “972 Mission street”
Repo:
Directory: 0x01-NoSQL
File: 6-update
7. Delete by match
mandatory
Back-end - Storage 39
MongoDB server version: 3.6.3
bye
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 7-delete
if __name__ == "__main__":
client = MongoClient('mongodb://127.0.0.1:27017')
school_collection = client.my_db.school
schools = list_all(school_collection)
for school in schools:
print("[{}] {}".format(school.get('_id'), school.get('name')))
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ ./8-main.py
[5a8f60cfd4321e1403ba7ab9] Holberton school
[5a8f60cfd4321e1403ba7aba] UCSD
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
Back-end - Storage 40
File: 8-all.py
Write a Python function that inserts a new document in a collection based on kwargs :
if __name__ == "__main__":
client = MongoClient('mongodb://127.0.0.1:27017')
school_collection = client.my_db.school
new_school_id = insert_school(school_collection, name="UCSF", address="505 Parnass
us Ave")
print("New school created: {}".format(new_school_id))
schools = list_all(school_collection)
for school in schools:
print("[{}] {} {}".format(school.get('_id'), school.get('name'), school.get('a
ddress', "")))
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ ./9-main.py
New school created: 5a8f60cfd4321e1403ba7abb
[5a8f60cfd4321e1403ba7ab9] Holberton school
[5a8f60cfd4321e1403ba7aba] UCSD
[5a8f60cfd4321e1403ba7abb] UCSF 505 Parnassus Ave
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 9-insert_school.py
Back-end - Storage 41
Done? Help Check your code Get a sandbox QA Review
Write a Python function that changes all topics of a school document based on the
name:
topics (list of strings) will be the list of topics approached in the school
if __name__ == "__main__":
client = MongoClient('mongodb://127.0.0.1:27017')
school_collection = client.my_db.school
update_topics(school_collection, "Holberton school", ["Sys admin", "AI", "Algorith
m"])
schools = list_all(school_collection)
for school in schools:
print("[{}] {} {}".format(school.get('_id'), school.get('name'), school.get('t
opics', "")))
schools = list_all(school_collection)
for school in schools:
print("[{}] {} {}".format(school.get('_id'), school.get('name'), school.get('t
opics', "")))
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ ./10-main.py
[5a8f60cfd4321e1403ba7abb] UCSF
[5a8f60cfd4321e1403ba7aba] UCSD
[5a8f60cfd4321e1403ba7ab9] Holberton school ['Sys admin', 'AI', 'Algorithm']
[5a8f60cfd4321e1403ba7abb] UCSF
[5a8f60cfd4321e1403ba7aba] UCSD
[5a8f60cfd4321e1403ba7ab9] Holberton school ['iOS']
guillaume@ubuntu:~/0x01$
Back-end - Storage 42
Repo:
Directory: 0x01-NoSQL
File: 10-update_topics.py
Write a Python function that returns the list of school having a specific topic:
if __name__ == "__main__":
client = MongoClient('mongodb://127.0.0.1:27017')
school_collection = client.my_db.school
j_schools = [
{ 'name': "Holberton school", 'topics': ["Algo", "C", "Python", "React"]},
{ 'name': "UCSF", 'topics': ["Algo", "MongoDB"]},
{ 'name': "UCLA", 'topics': ["C", "Python"]},
{ 'name': "UCSD", 'topics': ["Cassandra"]},
{ 'name': "Stanford", 'topics': ["C", "React", "Javascript"]}
]
for j_school in j_schools:
insert_school(school_collection, **j_school)
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ ./11-main.py
[5a90731fd4321e1e5a3f53e3] Holberton school ['Algo', 'C', 'Python', 'React']
Back-end - Storage 43
[5a90731fd4321e1e5a3f53e5] UCLA ['C', 'Python']
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 11-schools_by_topic.py
Write a Python script that provides some stats about Nginx logs stored in MongoDB:
Database: logs
Collection: nginx
5 lines with the number of documents with the method = ["GET", "POST",
"PUT", "PATCH", "DELETE"] in this order (see example below - warning: it’s a
method=GET
path=/status
The output of your script must be exactly the same as the example
Back-end - Storage 44
inflating: dump/logs/nginx.metadata.json
inflating: dump/logs/nginx.bson
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ mongorestore dump
2018-02-23T20:12:37.807+0000 preparing collections to restore from
2018-02-23T20:12:37.816+0000 reading metadata for logs.nginx from dump/logs/nginx.m
etadata.json
2018-02-23T20:12:37.825+0000 restoring logs.nginx from dump/logs/nginx.bson
2018-02-23T20:12:40.804+0000 [##......................] logs.nginx 1.21MB/13.4MB
(9.0%)
2018-02-23T20:12:43.803+0000 [#####...................] logs.nginx 2.88MB/13.4MB
(21.4%)
2018-02-23T20:12:46.803+0000 [#######.................] logs.nginx 4.22MB/13.4MB
(31.4%)
2018-02-23T20:12:49.803+0000 [##########..............] logs.nginx 5.73MB/13.4MB
(42.7%)
2018-02-23T20:12:52.803+0000 [############............] logs.nginx 7.23MB/13.4MB
(53.8%)
2018-02-23T20:12:55.803+0000 [###############.........] logs.nginx 8.53MB/13.4MB
(63.5%)
2018-02-23T20:12:58.803+0000 [#################.......] logs.nginx 10.1MB/13.4MB
(74.9%)
2018-02-23T20:13:01.803+0000 [####################....] logs.nginx 11.3MB/13.4MB
(83.9%)
2018-02-23T20:13:04.803+0000 [######################..] logs.nginx 12.8MB/13.4MB
(94.9%)
2018-02-23T20:13:06.228+0000 [########################] logs.nginx 13.4MB/13.4MB
(100.0%)
2018-02-23T20:13:06.230+0000 no indexes to restore
2018-02-23T20:13:06.231+0000 finished restoring logs.nginx (94778 documents)
2018-02-23T20:13:06.232+0000 done
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ ./12-log_stats.py
94778 logs
Methods:
method GET: 93842
method POST: 229
method PUT: 0
method PATCH: 0
method DELETE: 0
47415 status check
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 12-log_stats.py
Back-end - Storage 45
#advanced
Write a script that lists all documents with name starting by Holberton in the
collection school :
Repo:
Directory: 0x01-NoSQL
File: 100-find
Write a Python function that returns all students sorted by average score:
The average score must be part of each item returns with key = averageScore
Back-end - Storage 46
top_students = __import__('101-students').top_students
if __name__ == "__main__":
client = MongoClient('mongodb://127.0.0.1:27017')
students_collection = client.my_db.students
j_students = [
{ 'name': "John", 'topics': [{ 'title': "Algo", 'score': 10.3 },{ 'title':
"C", 'score': 6.2 }, { 'title': "Python", 'score': 12.1 }]},
{ 'name': "Bob", 'topics': [{ 'title': "Algo", 'score': 5.4 },{ 'title': "C",
'score': 4.9 }, { 'title': "Python", 'score': 7.9 }]},
{ 'name': "Sonia", 'topics': [{ 'title': "Algo", 'score': 14.8 },{ 'title':
"C", 'score': 8.8 }, { 'title': "Python", 'score': 15.7 }]},
{ 'name': "Amy", 'topics': [{ 'title': "Algo", 'score': 9.1 },{ 'title': "C",
'score': 14.2 }, { 'title': "Python", 'score': 4.8 }]},
{ 'name': "Julia", 'topics': [{ 'title': "Algo", 'score': 10.5 },{ 'title':
"C", 'score': 10.2 }, { 'title': "Python", 'score': 10.1 }]}
]
for j_student in j_students:
insert_school(students_collection, **j_student)
students = list_all(students_collection)
for student in students:
print("[{}] {} - {}".format(student.get('_id'), student.get('name'), student.g
et('topics')))
top_students = top_students(students_collection)
for student in top_students:
print("[{}] {} => {}".format(student.get('_id'), student.get('name'), student.
get('averageScore')))
guillaume@ubuntu:~/0x01$
guillaume@ubuntu:~/0x01$ ./101-main.py
[5a90776bd4321e1ec94fc408] John - [{'title': 'Algo', 'score': 10.3}, {'title': 'C', 's
core': 6.2}, {'title': 'Python', 'score': 12.1}]
[5a90776bd4321e1ec94fc409] Bob - [{'title': 'Algo', 'score': 5.4}, {'title': 'C', 'sco
re': 4.9}, {'title': 'Python', 'score': 7.9}]
[5a90776bd4321e1ec94fc40a] Sonia - [{'title': 'Algo', 'score': 14.8}, {'title': 'C',
'score': 8.8}, {'title': 'Python', 'score': 15.7}]
[5a90776bd4321e1ec94fc40b] Amy - [{'title': 'Algo', 'score': 9.1}, {'title': 'C', 'sco
re': 14.2}, {'title': 'Python', 'score': 4.8}]
[5a90776bd4321e1ec94fc40c] Julia - [{'title': 'Algo', 'score': 10.5}, {'title': 'C',
'score': 10.2}, {'title': 'Python', 'score': 10.1}]
[5a90776bd4321e1ec94fc40a] Sonia => 13.1
[5a90776bd4321e1ec94fc40c] Julia => 10.266666666666666
[5a90776bd4321e1ec94fc408] John => 9.533333333333333
[5a90776bd4321e1ec94fc40b] Amy => 9.366666666666665
[5a90776bd4321e1ec94fc409] Bob => 6.066666666666667
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
Back-end - Storage 47
File: 101-students.py
guillaume@ubuntu:~/0x01$ ./102-log_stats.py
94778 logs
Methods:
method GET: 93842
method POST: 229
method PUT: 0
method PATCH: 0
method DELETE: 0
47415 status check
IPs:
172.31.63.67: 15805
172.31.2.14: 15805
172.31.29.194: 15805
69.162.124.230: 529
64.124.26.109: 408
64.62.224.29: 217
34.207.121.61: 183
47.88.100.4: 166
45.249.84.250: 160
216.244.66.228: 150
guillaume@ubuntu:~/0x01$
Repo:
Directory: 0x01-NoSQL
File: 102-log_stats.py
Back-end - Storage 48
By: Emmanuel Turlay, Staff Software Engineer at Cruise
Weight: 1
Project over - took place from Feb 15, 2023 5:00 AM to Feb 16, 2023 5:00 AM
In a nutshell…
Auto QA review: 27.0/27 mandatory & 4.0/6 optional
Altogether: 166.67%
Mandatory: 100.0%
Optional: 66.67%
Resources
Read or watch:
Back-end - Storage 49
Redis commands
Learning Objectives
Learn how to use redis for basic operations
Requirements
All of your files will be interpreted/compiled on Ubuntu 18.04 LTS using python3
(version 3.7)
The first line of all your files should be exactly #!/usr/bin/env python3
'print(__import__("my_module").__doc__)' )
'print(__import__("my_module").MyClass.__doc__)' )
'print(__import__("my_module").MyClass.my_function.__doc__)' )
A documentation is not a simple word, it’s a real sentence explaining what’s the
purpose of the module, class or method (the length of it will be verified)
Back-end - Storage 50
$ sudo apt-get -y install redis-server
$ pip3 install redis
$ sed -i "s/bind .*/bind 127.0.0.1/g" /etc/redis/redis.conf
Tasks
0. Writing strings to Redis
mandatory
Create a Cache class. In the __init__ method, store an instance of the Redis client
as a private variable named _redis (using redis.Redis() ) and flush the instance
using flushdb .
Create a store method that takes a data argument and returns a string. The
method should generate a random key (e.g. using uuid ), store the input data in
Redis using the random key and return the key.
Cache = __import__('exercise').Cache
cache = Cache()
data = b"hello"
key = cache.store(data)
print(key)
local_redis = redis.Redis()
print(local_redis.get(key))
Back-end - Storage 51
bob@dylan:~$ python3 main.py
3a3e8231-b2f6-450d-8b0e-0f38f16e8ca2
b'hello'
bob@dylan:~$
Repo:
Directory: 0x02-redis_basic
File: exercise.py
Redis only allows to store string, bytes and numbers (and lists thereof). Whatever
you store as single elements, it will be returned as a byte string. Hence if you
store "a" as a UTF-8 string, it will be returned as b"a" when retrieved from the
server.
In this exercise we will create a get method that take a key string argument and an
optional Callable argument named fn . This callable will be used to convert the data
back to the desired format.
Remember to conserve the original Redis.get behavior if the key does not exist.
Also, implement 2 new methods: get_str and get_int that will automatically
parametrize Cache.get with the correct conversion function.
cache = Cache()
TEST_CASES = {
b"foo": None,
123: int,
"bar": lambda d: d.decode("utf-8")
}
Back-end - Storage 52
Repo:
Directory: 0x02-redis_basic
File: exercise.py
2. Incrementing values
mandatory
Familiarize yourself with the INCR command and its python equivalent.
In this task, we will implement a system to count how many times methods of
the Cache class are called.
Above Cache define a count_calls decorator that takes a
single method Callable argument and returns a Callable .
As a key, use the qualified name of method using the __qualname__ dunder method.
Create and return function that increments the count for that key every time the
method is called and returns the value returned by the original method.
Remember that the first argument of the wrapped function will be self which is the
instance itself, which lets you access the Redis instance.
Cache = __import__('exercise').Cache
cache = Cache()
cache.store(b"first")
print(cache.get(cache.store.__qualname__))
cache.store(b"second")
cache.store(b"third")
print(cache.get(cache.store.__qualname__))
Back-end - Storage 53
bob@dylan:~$ ./main.py
b'1'
b'3'
bob@dylan:~$
Repo:
Directory: 0x02-redis_basic
File: exercise.py
3. Storing lists
mandatory
In this task, we will define a call_history decorator to store the history of inputs and
outputs for a particular function.
Everytime the original function will be called, we will add its input parameters to one
list in redis, and store its output into another list.
call_history has a single parameter named method that is a Callable and returns
a Callable .
In the new function that the decorator will return, use rpush to append the input
arguments. Remember that Redis can only store strings, bytes and numbers.
Therefore, we can simply use str(args) to normalize. We can ignore
potential kwargs for now.
Execute the wrapped function to retrieve the output. Store the output using rpush in
the "...:outputs" list, then return the output.
Cache = __import__('exercise').Cache
Back-end - Storage 54
cache = Cache()
s1 = cache.store("first")
print(s1)
s2 = cache.store("secont")
print(s2)
s3 = cache.store("third")
print(s3)
print("inputs: {}".format(inputs))
print("outputs: {}".format(outputs))
bob@dylan:~$ ./main.py
04f8dcaa-d354-4221-87f3-4923393a25ad
a160a8a8-06dc-4934-8e95-df0cb839644b
15a8fd87-1f55-4059-86aa-9d1a0d4f2aea
inputs: [b"('first',)", b"('secont',)", b"('third',)"]
outputs: [b'04f8dcaa-d354-4221-87f3-4923393a25ad', b'a160a8a8-06dc-4934-8e95-df0cb8396
44b', b'15a8fd87-1f55-4059-86aa-9d1a0d4f2aea']
bob@dylan:~$
Repo:
Directory: 0x02-redis_basic
File: exercise.py
4. Retrieving lists
mandatory
In this tasks, we will implement a replay function to display the history of calls of a
particular function.
Back-end - Storage 55
Cache.store(*('bar',)) -> dcddd00c-4219-4dd7-8877-66afbe8e7df8
Cache.store(*(42,)) -> 5e752f2b-ecd8-4925-a3ce-e2efdee08d20
Tip: use lrange and zip to loop over inputs and outputs.
Repo:
Directory: 0x02-redis_basic
File: exercise.py
In this tasks, we will implement a get_page function (prototype: def get_page(url: str)
-> str: ). The core of the function is very simple. It uses the requests module to
Start in a new file named web.py and do not reuse the code written in exercise.py .
Inside get_page track how many times a particular URL was accessed in the
key "count:{url}" and cache the result with an expiration time of 10 seconds.
Repo:
Directory: 0x02-redis_basic
File: web.py
Back-end - Storage 56