- The surprise problem:
- Mongo is a document database.
- Postgres is a relational database.
- Neo is a graph database.
- Redis is a key-value database.
- Sarah Mei thinks you should never use Mongo.
- Redis (and similar) databases make really good caches.
- ACID stands for atomicity consistency isolation durability.
- Mongo doesn’t have joins because it favors horizontal scaling.
- If you use promises, you don’t need callbacks (T/F): F.
- Databases with schemas generally mean you must suffer with migrations.
- Mongo.
db.movies.insertOne({
title: 'The Incredibles', year: 2004, studio: 'Pixar',
directors: [{name: 'Brad Bird', birthdate: new Date('1957-09-24')}],
actors: [
{name: 'Holly Hunter', birthdate: new Date('1958-03-20'), roles: ['Elastigirl', 'Helen Parr']},
{name: 'Samuel L. Jackson', roles: ['Lucius Best', 'Frozone']},
{name: 'Sarah Vowell', roles: ['Violet Parr']}
]
});
// Titles and studios for sequels with actors born before 1940
db.movies.find(
{'actors.birthdate': {$lt: new Date('1940')}, sequel_of: {$exists: 0}},
{_id: 0, title: 1, studio: 1}
);
// Names of directors that directed a movie made in 2000
db.movies.find({year: 2000}, {'_id': 0, 'directors.name': 1});
// Names of all Pixar movies that John Ratzenberger starred in,
// together with the roles he played in each, sorted by movie
// release year, most recent first
db.movies.aggregate([
{$match: {studio: 'Pixar', 'actors.name': {$eq: 'John Ratzenberger'}}},
{$project: {_id: 0, title: 1, 'actors.roles': 1, year: 1}},
{$sort: {year: -1}},
{$project: {_id: 0, title: 1, 'actors.roles': 1}}
]);
- Postgres. We don’t need a separate table for the sequel relationship because it is one-to-many and not many-to-many. We can just use a nullable column in the movie table to indicate which movie it is a sequel of, if any.
create table person (
id serial primary key not null,
name varchar not null,
birthdate date,
headshot_url varchar
);
create table movie (
id serial primary key not null,
title varchar not null,
year int,
studio varchar,
sequel_of int references movie (id)
);
create table director (
person_id int not null,
movie_id int not null,
primary key (person_id, movie_id),
foreign key (person_id) references person (id),
foreign key (movie_id) references movie (id)
);
create table actor (
person_id int not null,
movie_id int not null,
roles jsonb,
primary key (person_id, movie_id),
foreign key (person_id) references person (id),
foreign key (movie_id) references movie (id)
);
-- Title and studio for non-sequels with actors born before 1940
select distinct m.title, m.studio
from movie m
join actor a on m.id = a.movie_id
join person p on p.id = a.person_id
where m.sequel_of is null
and p.birthdate < '1940-01-01';
-- For each director, the number of movies he or she directed,
-- ordered by the number of movies descending.
select p.name, count(d.movie_id) as number_of_movies
from person p
join director d on p.id = d.person_id
group by p.name
order by 2 desc;
-- Names of all Pixar movies that John Ratzenberger starred in,
-- together with the roles he played in, sorted by movie release
-- year, most recent first.
select m.title, a.roles
from person p
join actor a on p.id = a.person_id
join movie m on a.movie_id = m.id
where p.name = 'John Ratzenberger'
and m.studio = 'Pixar'
order by m.year desc;
- Neo.
create (incredibles:Movie {title: 'The Incredibles', studio: 'Pixar', year: '2004'}),
(cars:Movie {title: 'Cars', studio: 'Pixar', year: '2006'}),
(cars2:Movie {title: 'Cars 2', studio: 'Pixar', year: '2011'}),
(hh:Person {name: 'Holly Hunter', birthday: '19580320'}),
(jr:Person {name: 'John Ratzenberger', birthday: '19470406'}),
(hh)-[:ACTED_IN {roles: ['Elastigirl', 'Helen Parr']}]->(incredibles),
(jr)-[:ACTED_IN {roles: ['The Underminer']}]->(incredibles),
(jr)-[:ACTED_IN {roles: ['Mack']}]->(cars),
(jr)-[:ACTED_IN {roles: ['Mack', 'Hamm Tuck']}]->(cars2),
(cars2)-[:SEQUEL_OF]->(cars);
// Title and studio for non-sequels with actors born before 1940.
match (m:Movie)<-[:ACTED_IN]-(p:Person)
where p.birthday < '19400101'
and not exists ((m)-[:SEQUEL_OF]->(:Movie))
return distinct m.title, m.studio;
// For each director, the number of movies he or she directed,
// ordered by the number of movies descending.
match (p:Person)-[:DIRECTED]->(m:Movie)
return p.name as director, count(*) as number_of_movies_directed
order by number_of_movies_directed desc;
// Names of all Pixar movies that John Ratzenberger starred in,
// together with the roles he played in each, sorted by movie
// release year, most recent first.
match (p:Person {name: 'John Ratzenberger'})-[a:ACTED_IN]->(m:Movie {studio: 'Pixar'})
return m.title, m.year, a.roles
order by m.year desc;
// Titles of all movies that were sequels of sequels of sequels.
match (m:Movie)-[:SEQUEL_OF*3]->(:Movie)
return m.title;
- Redis.
hmset m:the-incredibles title "The Incredibles" studio "Pixar" year 2004
hmset p:holly-hunter name "Holly Hunter" birthdate "1958-03-20"
hset p:sarah-vowell name "Sarah Vowell"
hmset p:brad-bird name "Brad Bird" birthdate "1957-09-24"
lpush m:the-incredibles:p:holly-hunter "Helen Parr" "Elastigirl"
lpush m:the-incredibles:p:holly-hunter
sadd m:the-incredibles:d "p:brad-bird"
scard m:the-prestige:d
exists m:gone-with-the-wind:p:hugh-jackman
hset m:the-circle year 2017
zrevrangebyscore rt-scores +inf -inf limit 0 5
/* OR */
zrevrange rt-scores 0 4 withscores
- In a Sequelize application, it’s super annoying that you have to hand-code migrations for join tables in a many-to-many relationship. Regular tables can be pretty much auto-generated, but not the join tables (as far as we were able to tell).