diff --git a/ChatGPT Image.png b/ChatGPT Image.png
new file mode 100644
index 0000000..b4819ea
Binary files /dev/null and b/ChatGPT Image.png differ
diff --git a/README.md b/README.md
index 7a8723d..2f081e5 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,19 @@
# java-filmorate
-The Filmorate project — это приложение для работы с базой данных фильмов.
+
+
+
+
+
+
+
+
+
+
+**The Filmorate project** — это бэкенд приложения для работы с базой данных фильмов.
А также площадка для общения и взаимодействия пользователей.
+
+
### Основные возможности
- Хранение данных о фильмах
- Хранение данных о пользователях
@@ -45,15 +57,7 @@ The Filmorate project — это приложение для работы с б
- и подобные
### Примеры пользовательских запросов (Request Examples)

-### Архитектура и структура проекта (Architecture and Project Structure)
-Проект использует Spring Boot версии 3.5.9 и включает в себя следующие основные зависимости:
-- spring-boot-starter-web
-- spring-boot-starter-test
-- spring-boot-starter-validation
-- spring-boot-starter-jdbc
-- h2database - lombok
-- logback-classic
-- logbook-spring-boot-starter
+
### Схема базы данных (Database map)

### Примеры запросов к базе данных:
diff --git a/pom.xml b/pom.xml
index 417da8b..943d219 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,8 +20,6 @@
21
2.2.220
3.9.0
-
-
3.3.1
3.11.0
3.3.1
diff --git a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
index 2362097..206f30d 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java
@@ -7,6 +7,7 @@
import ru.yandex.practicum.filmorate.dto.FilmDto;
import ru.yandex.practicum.filmorate.dto.request.create.FilmCreateRequest;
import ru.yandex.practicum.filmorate.dto.request.update.FilmUpdateRequest;
+import ru.yandex.practicum.filmorate.model.Assessment;
import ru.yandex.practicum.filmorate.service.FilmService;
import ru.yandex.practicum.filmorate.service.util.LikeAction;
@@ -37,8 +38,8 @@ public Collection getTop(@RequestParam(defaultValue = "10", required =
}
@GetMapping("/common")
- public Collection getCommonLikedFilms(@RequestParam(required = true) Long userId,
- @RequestParam(required = true) Long friendId) {
+ public Collection getCommonLikedFilms(@RequestParam Long userId,
+ @RequestParam Long friendId) {
return filmService.getCommonLikedFilms(userId, friendId);
}
@@ -66,14 +67,18 @@ public FilmDto update(@RequestBody FilmUpdateRequest request) {
return filmService.update(request);
}
- @PutMapping("/{filmId}/like/{userId}")
- public void setLike(@PathVariable long filmId, @PathVariable long userId) {
- filmService.changeLike(LikeAction.SET, filmId, userId);
+ @PutMapping({"/{filmId}/like/{userId}/{assessment}", "/{filmId}/like/{userId}"})
+ public void setLike(
+ @PathVariable long filmId,
+ @PathVariable long userId,
+ @PathVariable(required = false) Integer assessment
+ ) {
+ filmService.changeLike(LikeAction.SET, filmId, userId, Assessment.of(assessment));
}
@DeleteMapping("/{filmId}/like/{userId}")
public void deleteLike(@PathVariable long filmId, @PathVariable long userId) {
- filmService.changeLike(LikeAction.REMOVE, filmId, userId);
+ filmService.changeLike(LikeAction.REMOVE, filmId, userId, null);
}
@DeleteMapping("/{filmId}")
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmStorage.java
index 1fe9ca9..83dde16 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmStorage.java
@@ -1,9 +1,10 @@
package ru.yandex.practicum.filmorate.dal;
+import ru.yandex.practicum.filmorate.model.Assessment;
import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Like;
import java.util.Collection;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -30,8 +31,8 @@ public interface FilmStorage {
/// Получить топ фильмов по фильтрам
Collection getTopByFilters(Integer count, Integer genreId, String year);
- /// Поставить лайк
- void setLike(long filmId, long userId);
+ /// Поставить лайк с оценкой (по умолчанию оценка 10)
+ void addLike(long filmId, long userId, Assessment assessment);
/// Убрать лайк
void removeLike(long filmId, long userId);
@@ -39,8 +40,8 @@ public interface FilmStorage {
/// Получить список фильмов по id режиссера
Collection getALLFilmsOfDirector(Integer directorId);
- /// Метод для получения матрицы лайков всех пользователей
- Map> getAllLikes();
+ /// Получить все лайки
+ Set getAllLikes();
/// Получить список филиьов, которые понравились обоим пользователям
Collection getCommonLikedFilms(long userId, long friendId);
@@ -51,5 +52,6 @@ public interface FilmStorage {
/// Удаление фильма по id
void removeFilm(long filmId);
+ /// Получить коллекцию фильмов по id фильмов
Collection getFilmsByIds(Collection ids);
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/database/FilmDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/dal/database/FilmDbStorage.java
index eac5594..57b42e1 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dal/database/FilmDbStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/database/FilmDbStorage.java
@@ -7,12 +7,17 @@
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import ru.yandex.practicum.filmorate.dal.FilmStorage;
+import ru.yandex.practicum.filmorate.dal.rowmappers.LikeRowMapper;
import ru.yandex.practicum.filmorate.exception.InternalServerException;
+import ru.yandex.practicum.filmorate.model.Assessment;
import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Like;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
import static ru.yandex.practicum.filmorate.dal.database.sql.FilmQueryes.*;
@@ -34,12 +39,13 @@ public Film createFilm(Film film) {
film.getDescription(),
film.getReleaseDate(),
film.getDuration().toMinutes(),
- film.getMpaId()
+ film.getMpaId(),
+ 0.0f
);
film.setId(id);
insertGenreIds(film);
insertDirectorIds(film);
- return findById(id).get();
+ return film;
}
@Override
@@ -56,15 +62,19 @@ public Film updateFilm(Film film) {
);
insertGenreIds(film);
insertDirectorIds(film);
- return findById(film.getId()).get();
+ return film;
}
@Override
@Transactional
public Optional findById(long filmId) {
Optional filmOptional = findOneByIdInTable(filmId, "films");
- filmOptional.ifPresent(film -> film.setGenreIds(getGenreIdsByFilmId(filmId)));
- filmOptional.ifPresent(film -> film.setDirectorIds(getDirectorIdsByFilmId(filmId)));
+ filmOptional.ifPresent(film -> {
+ film.setGenreIds(getGenreIdsByFilmId(filmId));
+ film.setDirectorIds(getDirectorIdsByFilmId(filmId));
+ film.setRate(calculateRating(getLikesByFilmId(filmId)));
+ });
+
return filmOptional;
}
@@ -73,9 +83,18 @@ public Collection findAll() {
return findManyFilms(SQL_FILMS_FIND_ALL);
}
+ @Transactional
@Override
- public void setLike(long filmId, long userId) {
- updateWithControl(SQL_FILMS_SET_LIKE, filmId, userId);
+ public void addLike(long filmId, long userId, Assessment assessment) {
+ jdbc.update(
+ SQL_FILMS_SET_LIKE,
+ filmId,
+ userId,
+ assessment.getValue()
+ );
+
+ float rate = calculateRating(getLikesByFilmId(filmId));
+ jdbc.update("UPDATE films SET rate = ? WHERE id = ?", rate, filmId);
}
@Override
@@ -95,16 +114,15 @@ public Collection getTop(int top) {
@Override
public Collection getTopByFilters(Integer top, Integer genreId, String year) {
- Collection films;
- if (genreId != null && year != null && !year.isEmpty()) {
+ Collection films = List.of();
+ if (genreId != null && year != null && !year.isBlank()) {
films = findManyByQuery(SQL_FILMS_FIND_TOP_BY_GENRE_AND_YEAR, Integer.parseInt(year), genreId, top);
- } else if (year != null && !year.isEmpty()) {
+ } else if (year != null && !year.isBlank()) {
films = findManyByQuery(SQL_FILMS_FIND_TOP_BY_YEAR, Integer.parseInt(year), top);
} else if (genreId != null) {
films = findManyByQuery(SQL_FILMS_FIND_TOP_BY_GENRES, genreId, top);
- } else {
- films = findManyByQuery(SQL_FILMS_FIND_TOP, top);
}
+
return films.stream().peek(film -> {
Set genres = getGenreIdsByFilmId(film.getId());
film.setGenreIds(new HashSet<>(genres));
@@ -118,18 +136,6 @@ public boolean checkFilmIsNotPresent(Long filmId) {
return checkIdIsNotPresentInTable(filmId, "films");
}
- @Override
- public Map> getAllLikes() {
- String sql = "SELECT user_id, film_id FROM likes";
- Map> allLikes = new HashMap<>();
- jdbc.query(sql, (rs) -> {
- long userId = rs.getLong("user_id");
- long filmId = rs.getLong("film_id");
- allLikes.computeIfAbsent(userId, k -> new HashSet<>()).add(filmId);
- });
- return allLikes;
- }
-
@Override
public Collection getCommonLikedFilms(long userId, long friendId) {
return findManyFilms(SQL_FILMS_FIND_COMMON_LIKED, userId, friendId);
@@ -156,6 +162,41 @@ public void removeFilm(long filmId) {
updateWithControl("DELETE FROM films WHERE id = ?", filmId);
}
+ @Override
+ public Set getAllLikes() {
+ String sql = "SELECT user_id, film_id, assessment FROM likes";
+ Set allLikes = new HashSet<>();
+ jdbc.query(sql, (rs) -> {
+ long filmId = rs.getLong("film_id");
+ long userId = rs.getLong("user_id");
+ int assessment = rs.getInt("assessment");
+ Like like = Like.builder()
+ .filmId(filmId)
+ .userId(userId)
+ .assessment(Assessment.of(assessment))
+ .build();
+ allLikes.add(like);
+ });
+ return allLikes;
+ }
+
+ @Override
+ public Collection getFilmsByIds(Collection ids) {
+ if (ids == null || ids.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ String inSql = String.join(",", Collections.nCopies(ids.size(), "?"));
+ String sql = String.format(SQL_FILMS_GET_BY_IDS, inSql);
+
+ return findManyFilms(sql, ids.toArray());
+ }
+
+ private Set getLikesByFilmId(long filmId) {
+ return jdbc.query(SQL_FILMS_FIND_LIKES_BY_FILM_ID, new LikeRowMapper(), filmId).stream()
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
private void insertGenreIds(Film film) {
jdbc.update("DELETE FROM genres_of_films WHERE film_id = ?", film.getId());
Set genreIds = film.getGenreIds();
@@ -187,8 +228,6 @@ private Collection findManyFilms(String query, Object... params) {
throw new InternalServerException("Не удалось получить все фильмы из базы.\n" + e.getMessage());
}
});
- long userId = rs.getLong("user_id");
- if (!rs.wasNull() && film != null) film.addLike(userId);
int genreId = rs.getInt("genre_id");
if (!rs.wasNull() && film != null) film.addGenreId(genreId);
int directorId = rs.getInt("director_id");
@@ -206,24 +245,23 @@ private Set getDirectorIdsByFilmId(long filmId) {
return findColumnByQuery(SQL_FILMS_FIND_DIRECTORIDS_BY_FILM_ID, Integer.class, filmId);
}
- @Override
- public Collection getFilmsByIds(Collection ids) {
- if (ids == null || ids.isEmpty()) {
- return Collections.emptyList();
+ private float calculateRating(Set likes) {
+ if (likes.isEmpty()) {
+ return 0f;
+ } else if (likes.size() == 1) {
+ return likes.iterator().next().getAssessment().getValue();
}
+ AtomicLong count = new AtomicLong();
+ AtomicLong sum = new AtomicLong();
+ likes.stream()
+ .map(Like::getAssessment)
+ .filter(assessment -> !assessment.equals(Assessment.UNDEFINED))
+ .map(Assessment::getValue).forEach(value -> {
+ sum.addAndGet(value);
+ count.getAndIncrement();
+ });
- String inSql = String.join(",", Collections.nCopies(ids.size(), "?"));
-
- String sql = String.format("""
- SELECT f.id, f.film_name, f.description, f.release_date, f.duration, f.mpa_id,
- l.user_id, gof.genre_id, dof.director_id
- FROM films f
- LEFT JOIN likes l ON f.id = l.film_id
- LEFT JOIN genres_of_films gof ON f.id = gof.film_id
- LEFT JOIN directors_of_films dof ON f.id = dof.film_id
- WHERE f.id IN (%s)
- """, inSql);
-
- return findManyFilms(sql, ids.toArray());
+ float average = (float) sum.get() / count.get();
+ return Math.round(average * 100) / 100.0f;
}
}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/database/sql/FilmQueryes.java b/src/main/java/ru/yandex/practicum/filmorate/dal/database/sql/FilmQueryes.java
index 6b21bf1..9a04a0e 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dal/database/sql/FilmQueryes.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/database/sql/FilmQueryes.java
@@ -2,6 +2,22 @@
public class FilmQueryes {
+ public static final String SQL_FILMS_GET_BY_IDS = """
+ SELECT f.id,
+ f.film_name,
+ f.description,
+ f.release_date,
+ f.duration,
+ f.mpa_id,
+ f.rate,
+ gof.genre_id,
+ dof.director_id
+ FROM films f
+ LEFT JOIN genres_of_films gof ON f.id = gof.film_id
+ LEFT JOIN directors_of_films dof ON f.id = dof.film_id
+ WHERE f.id IN (%s)
+ """;
+
public static final String SQL_FILMS_FIND_ALL = """
SELECT mrg.id,
mrg.FILM_NAME,
@@ -9,23 +25,12 @@ public class FilmQueryes {
mrg.RELEASE_DATE,
mrg.DURATION,
mrg.MPA_ID,
- gof.GENRE_ID,
- dof.DIRECTOR_ID,
- user_id
- FROM (
- SELECT f.ID,
- f.FILM_NAME,
- DESCRIPTION,
- RELEASE_DATE,
- DURATION,
- MPA_ID,
- l.USER_ID
- FROM FILMS f
- LEFT JOIN LIKES l ON f.ID = l.FILM_ID
- ORDER BY f.ID
- ) AS mrg
+ mrg.RATE,
+ dof.DIRECTOR_ID,
+ gof.GENRE_ID
+ FROM FILMS mrg
LEFT JOIN GENRES_OF_FILMS gof ON mrg.id = gof.FILM_ID
- LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.ID = dof.FILM_ID
+ LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.ID = dof.FILM_ID;
""";
public static final String SQL_FILMS_FIND_ALL_OF_DIRECTOR = """
@@ -35,6 +40,7 @@ public class FilmQueryes {
mrg.RELEASE_DATE,
mrg.DURATION,
mrg.MPA_ID,
+ mrg.RATE,
gof.GENRE_ID,
dof.DIRECTOR_ID,
user_id
@@ -45,6 +51,7 @@ public class FilmQueryes {
RELEASE_DATE,
DURATION,
MPA_ID,
+ f.RATE,
l.USER_ID
FROM FILMS f
LEFT JOIN LIKES l ON f.ID = l.FILM_ID
@@ -64,45 +71,28 @@ WHERE f1.ID IN (
""";
public static final String SQL_FILMS_FIND_TOP = """
- SELECT mrg.id,
+ SELECT mrg.ID,
mrg.FILM_NAME,
mrg.DESCRIPTION,
mrg.RELEASE_DATE,
mrg.DURATION,
mrg.MPA_ID,
- gof.GENRE_ID,
- dof.DIRECTOR_ID,
- user_id
+ mrg.RATE,
+ dof.DIRECTOR_ID,
+ gof.GENRE_ID
FROM (
- SELECT f.ID,
- f.FILM_NAME,
- DESCRIPTION,
- RELEASE_DATE,
- DURATION,
- MPA_ID,
- l.USER_ID
- FROM FILMS f
- LEFT JOIN LIKES l ON f.ID = l.FILM_ID
+ SELECT *
+ FROM FILMS flm
+ ORDER BY RATE DESC
+ LIMIT ?
) AS mrg
LEFT JOIN GENRES_OF_FILMS gof ON mrg.id = gof.FILM_ID
- LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.ID = dof.FILM_ID
- WHERE mrg.id IN (
- SELECT rte.ID
- FROM (
- SELECT flm.*,
- COUNT(lks.ID) AS rate
- FROM FILMS flm
- LEFT JOIN LIKES lks ON flm.ID = lks.FILM_ID
- GROUP BY flm.ID
- ) AS rte
- ORDER BY rate DESC, rte.ID
- LIMIT ?
- )
+ LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.ID = dof.FILM_ID;
""";
public static final String SQL_FILMS_SET_LIKE = """
- INSERT INTO LIKES (FILM_ID, USER_ID)
- VALUES (?, ?)
+ INSERT INTO LIKES (FILM_ID, USER_ID, ASSESSMENT)
+ VALUES (?, ?, ?)
""";
public static final String SQL_FILMS_DELETE_LIKE = """
@@ -129,6 +119,12 @@ MERGE INTO GENRES_OF_FILMS (FILM_ID, GENRE_ID) KEY (FILM_ID, GENRE_ID) VALUES
WHERE dof.FILM_ID = ?
""";
+ public static final String SQL_FILMS_FIND_LIKES_BY_FILM_ID = """
+ SELECT *
+ FROM LIKES
+ WHERE FILM_ID = ?
+ """;
+
public static final String SQL_FILMS_INSERT_DIRECTORIDS = """
MERGE INTO DIRECTORS_OF_FILMS (FILM_ID, DIRECTOR_ID) KEY (FILM_ID, DIRECTOR_ID) VALUES
""";
@@ -139,9 +135,10 @@ INSERT INTO films (
description,
release_date,
duration,
- mpa_id
+ mpa_id,
+ rate
)
- VALUES (?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?)
""";
public static final String SQL_FILMS_UPDATE = """
@@ -155,34 +152,92 @@ INSERT INTO films (
""";
public static final String SQL_FILMS_FIND_TOP_BY_GENRES = """
- SELECT f.*, COUNT(fl.user_id) as likes_count
- FROM films f
- LEFT JOIN likes fl ON f.id = fl.film_id
- LEFT JOIN genres_of_films gr ON f.id = gr.film_id
- WHERE gr.genre_id = ?
- GROUP BY f.id
- ORDER BY likes_count DESC
- LIMIT ?
+ SELECT mrg.ID,
+ mrg.FILM_NAME,
+ mrg.DESCRIPTION,
+ mrg.RELEASE_DATE,
+ mrg.DURATION,
+ mrg.MPA_ID,
+ mrg.RATE,
+ dof.DIRECTOR_ID,
+ gof.GENRE_ID
+ FROM (
+ SELECT *
+ FROM (
+ SELECT *
+ FROM films flm
+ WHERE flm.id IN (
+ SELECT f.id
+ FROM films f
+ LEFT JOIN GENRES_OF_FILMS g ON f.ID = g.FILM_ID
+ WHERE g.GENRE_ID = ?
+ )
+ )
+ ORDER BY RATE DESC
+ LIMIT ?
+ ) AS mrg
+ LEFT JOIN GENRES_OF_FILMS gof ON mrg.id = gof.FILM_ID
+ LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.id = dof.FILM_ID;
""";
public static final String SQL_FILMS_FIND_TOP_BY_YEAR = """
- SELECT f.*, COUNT(fl.user_id) as likes_count
- FROM (SELECT * FROM films WHERE EXTRACT(YEAR FROM release_date) = ?) f
- LEFT JOIN likes fl ON f.id = fl.film_id
- GROUP BY f.id
- ORDER BY likes_count DESC
- LIMIT ?
+ SELECT mrg.ID,
+ mrg.FILM_NAME,
+ mrg.DESCRIPTION,
+ mrg.RELEASE_DATE,
+ mrg.DURATION,
+ mrg.MPA_ID,
+ mrg.RATE,
+ dof.DIRECTOR_ID,
+ gof.GENRE_ID
+ FROM (
+ SELECT *
+ FROM (
+ SELECT *
+ FROM films
+ WHERE EXTRACT(
+ YEAR
+ FROM release_date
+ ) = ?
+ )
+ ORDER BY RATE DESC
+ LIMIT ?
+ ) AS mrg
+ LEFT JOIN GENRES_OF_FILMS gof ON mrg.id = gof.FILM_ID
+ LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.id = dof.FILM_ID;
""";
public static final String SQL_FILMS_FIND_TOP_BY_GENRE_AND_YEAR = """
- SELECT f.*, COUNT(fl.user_id) as likes_count
- FROM (SELECT * FROM films WHERE EXTRACT(YEAR FROM release_date) = ?) f
- LEFT JOIN likes fl ON f.id = fl.film_id
- LEFT JOIN genres_of_films gr ON f.id = gr.film_id
- WHERE gr.genre_id = ?
- GROUP BY f.id
- ORDER BY likes_count DESC
- LIMIT ?
+ SELECT mrg.ID,
+ mrg.FILM_NAME,
+ mrg.DESCRIPTION,
+ mrg.RELEASE_DATE,
+ mrg.DURATION,
+ mrg.MPA_ID,
+ mrg.RATE,
+ dof.DIRECTOR_ID,
+ gof.GENRE_ID
+ FROM (
+ SELECT *
+ FROM (
+ SELECT *
+ FROM films flm
+ WHERE EXTRACT(
+ YEAR
+ FROM flm.release_date
+ ) = ?
+ AND flm.id IN (
+ SELECT f.id
+ FROM films f
+ LEFT JOIN GENRES_OF_FILMS g ON f.ID = g.FILM_ID
+ WHERE g.GENRE_ID = ?
+ )
+ )
+ ORDER BY RATE DESC
+ LIMIT ?
+ ) AS mrg
+ LEFT JOIN GENRES_OF_FILMS gof ON mrg.id = gof.FILM_ID
+ LEFT JOIN DIRECTORS_OF_FILMS dof ON mrg.id = dof.FILM_ID;
""";
public static final String SQL_FILMS_FIND_COMMON_LIKED = """
@@ -192,6 +247,7 @@ INSERT INTO films (
f.release_date,
f.duration,
f.mpa_id,
+ f.rate,
l_all.user_id,
g.genre_id,
d.director_id,
@@ -216,16 +272,15 @@ WHERE f.id IN (
f.release_date,
f.duration,
f.mpa_id,
+ f.rate,
m.mpa_name,
gof.genre_id,
- dof.director_id,
- l.user_id
+ dof.director_id
FROM films f
LEFT JOIN mpa m ON f.mpa_id = m.id
LEFT JOIN genres_of_films gof ON f.id = gof.film_id
LEFT JOIN directors_of_films dof ON f.id = dof.film_id
LEFT JOIN directors d ON dof.director_id = d.id
- LEFT JOIN likes l ON f.id = l.film_id
WHERE %s
ORDER BY (SELECT COUNT(*) FROM likes WHERE film_id = f.id) DESC, f.id ASC;
""";
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/inmemory/InMemoryFilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/dal/inmemory/InMemoryFilmStorage.java
index bdbb0ed..df1784f 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dal/inmemory/InMemoryFilmStorage.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/inmemory/InMemoryFilmStorage.java
@@ -3,10 +3,11 @@
import org.springframework.stereotype.Component;
import ru.yandex.practicum.filmorate.dal.FilmStorage;
import ru.yandex.practicum.filmorate.exception.MethodNotImplementedException;
+import ru.yandex.practicum.filmorate.model.Assessment;
import ru.yandex.practicum.filmorate.model.Film;
+import ru.yandex.practicum.filmorate.model.Like;
import java.util.Collection;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -49,7 +50,7 @@ public Collection getTopByFilters(Integer count, Integer genreId, String y
}
@Override
- public void setLike(long filmId, long userId) {
+ public void addLike(long filmId, long userId, Assessment assessment) {
throw new MethodNotImplementedException();
}
@@ -64,7 +65,7 @@ public Collection getALLFilmsOfDirector(Integer directorId) {
}
@Override
- public Map> getAllLikes() {
+ public Set getAllLikes() {
throw new MethodNotImplementedException();
}
@@ -87,4 +88,5 @@ public void removeFilm(long filmId) {
public Collection getFilmsByIds(Collection ids) {
throw new MethodNotImplementedException();
}
+
}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/EventRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/EventRowMapper.java
index 679de25..18165e8 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/EventRowMapper.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/EventRowMapper.java
@@ -4,8 +4,8 @@
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import ru.yandex.practicum.filmorate.model.Event;
-import ru.yandex.practicum.filmorate.model.EventType;
-import ru.yandex.practicum.filmorate.model.Operation;
+import ru.yandex.practicum.filmorate.service.util.EventType;
+import ru.yandex.practicum.filmorate.service.util.Operation;
import java.sql.ResultSet;
import java.sql.SQLException;
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/FilmRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/FilmRowMapper.java
index 13224bb..bbc2881 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/FilmRowMapper.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/FilmRowMapper.java
@@ -21,7 +21,7 @@ public Film mapRow(ResultSet resultSet, int rowNum) throws SQLException {
.releaseDate(resultSet.getDate("release_date").toLocalDate())
.duration(Duration.ofMinutes(resultSet.getInt("duration")))
.mpaId(resultSet.getInt("mpa_id"))
+ .rate(resultSet.getFloat("rate"))
.build();
}
-
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/LikeRowMapper.java b/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/LikeRowMapper.java
new file mode 100644
index 0000000..9559054
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/dal/rowmappers/LikeRowMapper.java
@@ -0,0 +1,20 @@
+package ru.yandex.practicum.filmorate.dal.rowmappers;
+
+import org.springframework.jdbc.core.RowMapper;
+import ru.yandex.practicum.filmorate.model.Assessment;
+import ru.yandex.practicum.filmorate.model.Like;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class LikeRowMapper implements RowMapper {
+ @Override
+ public Like mapRow(ResultSet resultSet, int rowNum) throws SQLException {
+ return Like.builder()
+ .id(resultSet.getLong("id"))
+ .filmId(resultSet.getLong("film_id"))
+ .userId(resultSet.getLong("user_id"))
+ .assessment(Assessment.of(resultSet.getInt("assessment")))
+ .build();
+ }
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java
index 6f66634..11f005c 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dto/EventDto.java
@@ -3,8 +3,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
-import ru.yandex.practicum.filmorate.model.EventType;
-import ru.yandex.practicum.filmorate.model.Operation;
+import ru.yandex.practicum.filmorate.service.util.EventType;
+import ru.yandex.practicum.filmorate.service.util.Operation;
@Data
@Builder
diff --git a/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java b/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java
index ba35e8f..82583f2 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/dto/FilmDto.java
@@ -20,9 +20,7 @@ public class FilmDto {
private String description;
private LocalDate releaseDate;
private Duration duration;
-
- @JsonProperty("rate")
- int likesCount;
+ float rate;
@Builder.Default
private MpaDto mpa = new MpaDto();
diff --git a/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java b/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java
index 056a560..cc6c2f9 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/mapper/FilmMapper.java
@@ -43,7 +43,6 @@ public static FilmDto mapToFilmDto(Film film, Mpa mpa, Set genres, Set genres, Set new DirectorDto(director.getId(), director.getName()))
.toList()
)
+ .rate(film.getRate())
.build();
}
@@ -87,6 +87,8 @@ public static Film updateFilmFields(Film film, FilmUpdateRequest request) {
.map(Director::getId)
.collect(Collectors.toSet()));
+ film.getRate();
+
return film;
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Assessment.java b/src/main/java/ru/yandex/practicum/filmorate/model/Assessment.java
new file mode 100644
index 0000000..419b9a9
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Assessment.java
@@ -0,0 +1,35 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.Getter;
+
+@Getter
+public enum Assessment {
+ UNDEFINED, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN;
+
+ private int value;
+
+ Assessment() {
+ this.value = ordinal();
+ }
+
+ public static Assessment of(Integer value) {
+ if (value == null) {
+ value = 10;
+ }
+
+ Assessment result = Assessment.UNDEFINED;
+ if (value < 1 || value > 10) {
+ result.setValue(value);
+ return result;
+ }
+
+ result = values()[value];
+ result.setValue(result.ordinal());
+ return result;
+ }
+
+ private void setValue(int value) {
+ this.value = value;
+ }
+}
+
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Event.java b/src/main/java/ru/yandex/practicum/filmorate/model/Event.java
index a033a30..c3c5265 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Event.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Event.java
@@ -4,6 +4,8 @@
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import ru.yandex.practicum.filmorate.service.util.EventType;
+import ru.yandex.practicum.filmorate.service.util.Operation;
import java.time.Instant;
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/EventType.java b/src/main/java/ru/yandex/practicum/filmorate/model/EventType.java
deleted file mode 100644
index e6cfb0b..0000000
--- a/src/main/java/ru/yandex/practicum/filmorate/model/EventType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ru.yandex.practicum.filmorate.model;
-
-public enum EventType {
- LIKE,
- REVIEW,
- FRIEND
-}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
index a16f4e6..429d01d 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Film.java
@@ -1,6 +1,5 @@
package ru.yandex.practicum.filmorate.model;
-import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -25,33 +24,16 @@ public class Film {
@Builder.Default
private Duration duration = Duration.ZERO;
- @Builder.Default
- private Set genreIds = new HashSet<>();
-
private Integer mpaId;
@Builder.Default
- private Set directorIds = new HashSet<>();
+ private Float rate = 0.0f;
@Builder.Default
- private Set likes = new HashSet<>();
+ private Set genreIds = new HashSet<>();
- @JsonProperty(value = "rate")
@Builder.Default
- private int likesCount = 0;
-
- public int getLikesCount() {
- likesCount = likes.size();
- return likesCount;
- }
-
- public void setLike(User user) {
- likes.add(user.getId());
- }
-
- public void removeLike(User user) {
- likes.remove(user.getId());
- }
+ private Set directorIds = new HashSet<>();
public void addGenreId(int genreId) {
genreIds.add(genreId);
@@ -60,8 +42,4 @@ public void addGenreId(int genreId) {
public void addDirectorId(int directorId) {
directorIds.add(directorId);
}
-
- public void addLike(long userId) {
- likes.add(userId);
- }
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Like.java b/src/main/java/ru/yandex/practicum/filmorate/model/Like.java
new file mode 100644
index 0000000..4b17652
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/model/Like.java
@@ -0,0 +1,13 @@
+package ru.yandex.practicum.filmorate.model;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class Like {
+ Long id;
+ long filmId;
+ long userId;
+ Assessment assessment;
+}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/model/Operation.java b/src/main/java/ru/yandex/practicum/filmorate/model/Operation.java
deleted file mode 100644
index c0bc9bc..0000000
--- a/src/main/java/ru/yandex/practicum/filmorate/model/Operation.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ru.yandex.practicum.filmorate.model;
-
-public enum Operation {
- REMOVE,
- ADD,
- UPDATE
-}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/EventService.java b/src/main/java/ru/yandex/practicum/filmorate/service/EventService.java
index b50e86b..0156ac7 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/EventService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/EventService.java
@@ -8,8 +8,8 @@
import ru.yandex.practicum.filmorate.dto.EventDto;
import ru.yandex.practicum.filmorate.mapper.EventMapper;
import ru.yandex.practicum.filmorate.model.Event;
-import ru.yandex.practicum.filmorate.model.EventType;
-import ru.yandex.practicum.filmorate.model.Operation;
+import ru.yandex.practicum.filmorate.service.util.EventType;
+import ru.yandex.practicum.filmorate.service.util.Operation;
import java.time.Instant;
import java.util.Collection;
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
index 569e0c5..f198fac 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java
@@ -15,8 +15,10 @@
import ru.yandex.practicum.filmorate.exception.ValidationException;
import ru.yandex.practicum.filmorate.mapper.FilmMapper;
import ru.yandex.practicum.filmorate.model.*;
+import ru.yandex.practicum.filmorate.service.util.EventType;
import ru.yandex.practicum.filmorate.service.util.FilmSortingAction;
import ru.yandex.practicum.filmorate.service.util.LikeAction;
+import ru.yandex.practicum.filmorate.service.util.Operation;
import java.util.*;
@@ -82,7 +84,7 @@ public Collection getSortedFilmsOfDirector(Integer directorId, String s
log.trace("Применение сортировки");
Comparator comparator = switch (sortingAction) {
case YEAR -> Comparator.comparing(Film::getReleaseDate);
- case LIKES -> Comparator.comparing(Film::getLikesCount).reversed();
+ case RATE -> Comparator.comparing(Film::getRate).reversed();
case UNDEFINED -> throw new ParameterNotValidException(
sortingAction.getSortingName(),
"не подходит. Допустимые значения: " +
@@ -114,23 +116,22 @@ private Collection getSortedFilmDtosOfDirector(Integer directorId) {
return films;
}
- public void changeLike(LikeAction action, Long filmId, Long userId) {
+ public void changeLike(LikeAction action, Long filmId, Long userId, Assessment assessment) {
switch (action) {
case SET -> log.info("Добавление лайка пользователя id={} к фильму id={}.", userId, filmId);
case REMOVE -> log.info("Удаление лайка пользователя id={} у фильма id={}.", userId, filmId);
}
- if (filmStorage.checkFilmIsNotPresent(filmId)) {
- throw new NotFoundException("Фильм с id=" + filmId + " не найден.");
+ if (assessment != null && assessment.equals(Assessment.UNDEFINED)) {
+ throw new ParameterNotValidException("assessment", "может быть только от 1 до 10, либо отсутствовать.");
}
- if (userStorage.checkUserIsNotPresent(userId)) {
- throw new NotFoundException("Пользователь с id=" + userId + " не найден.");
- }
+ easyCheckFilm(filmId);
+ easyCheckUser(userId);
switch (action) {
case SET -> {
- filmStorage.setLike(filmId, userId);
+ filmStorage.addLike(filmId, userId, assessment);
eventService.addUserEvent(userId, EventType.LIKE, Operation.ADD, filmId);
}
case REMOVE -> {
@@ -150,7 +151,7 @@ public Collection getTopFilms(Integer count, Integer genreId, String ye
}
Collection films;
- if (genreId != null || year != null) {
+ if (genreId != null || (year != null && !year.isBlank())) {
films = filmStorage.getTopByFilters(count, genreId, year);
} else {
films = filmStorage.getTop(count);
@@ -163,7 +164,7 @@ public Collection getTopFilms(Integer count, Integer genreId, String ye
getGenresByIds(film.getGenreIds()),
getDirectorsByIds(film.getDirectorIds())
)
- ).sorted(Comparator.comparing(FilmDto::getLikesCount).reversed()).toList();
+ ).sorted(Comparator.comparing(FilmDto::getRate).reversed()).toList();
}
public Collection findAll() {
@@ -368,4 +369,16 @@ public Collection getFilmsByIds(Collection ids) {
))
.toList();
}
+
+ private void easyCheckFilm(long filmId) {
+ if (filmStorage.checkFilmIsNotPresent(filmId)) {
+ throw new NotFoundException("Фильм с id=" + filmId + " не найден.");
+ }
+ }
+
+ private void easyCheckUser(long userId) {
+ if (userStorage.checkUserIsNotPresent(userId)) {
+ throw new NotFoundException("Пользователь с id=" + userId + " не найден.");
+ }
+ }
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java b/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java
index 2dd323c..201f02a 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/ReviewService.java
@@ -12,8 +12,8 @@
import ru.yandex.practicum.filmorate.dto.request.update.ReviewUpdateRequest;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.mapper.ReviewMapper;
-import ru.yandex.practicum.filmorate.model.EventType;
-import ru.yandex.practicum.filmorate.model.Operation;
+import ru.yandex.practicum.filmorate.service.util.EventType;
+import ru.yandex.practicum.filmorate.service.util.Operation;
import ru.yandex.practicum.filmorate.model.Review;
import java.util.Collection;
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
index aa2485f..f6e77f1 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/UserService.java
@@ -14,8 +14,9 @@
import ru.yandex.practicum.filmorate.dto.request.update.UserUpdateRequest;
import ru.yandex.practicum.filmorate.exception.*;
import ru.yandex.practicum.filmorate.mapper.UserMapper;
-import ru.yandex.practicum.filmorate.model.EventType;
-import ru.yandex.practicum.filmorate.model.Operation;
+import ru.yandex.practicum.filmorate.model.Like;
+import ru.yandex.practicum.filmorate.service.util.EventType;
+import ru.yandex.practicum.filmorate.service.util.Operation;
import ru.yandex.practicum.filmorate.model.User;
import ru.yandex.practicum.filmorate.service.util.FriendsAction;
@@ -210,8 +211,14 @@ public Collection getRecommendations(long userId) {
log.info("Получение рекомендаций для пользователя id={}.", userId);
easyCheckUser(userId);
- Map> allLikes = filmStorage.getAllLikes();
- Set targetUserLikes = allLikes.getOrDefault(userId, Collections.emptySet());
+ Set likes = filmStorage.getAllLikes();
+
+ Map> userTofilmsLikes = new HashMap<>();
+ likes.forEach(like ->
+ userTofilmsLikes.computeIfAbsent(like.getUserId(), k -> new HashSet<>()).add(like.getFilmId())
+ );
+
+ Set targetUserLikes = userTofilmsLikes.getOrDefault(userId, Collections.emptySet());
if (targetUserLikes.isEmpty()) {
return Collections.emptyList();
@@ -219,7 +226,7 @@ public Collection getRecommendations(long userId) {
Map similarityMap = new HashMap<>();
- for (Map.Entry> entry : allLikes.entrySet()) {
+ for (Map.Entry> entry : userTofilmsLikes.entrySet()) {
long otherUserId = entry.getKey();
if (otherUserId == userId) continue;
@@ -245,7 +252,7 @@ public Collection getRecommendations(long userId) {
Set recommendedFilmIds = new HashSet<>();
for (Long simId : similarUserIds) {
- Set likedBySimilarUser = allLikes.get(simId);
+ Set likedBySimilarUser = userTofilmsLikes.get(simId);
likedBySimilarUser.stream()
.filter(filmId -> !targetUserLikes.contains(filmId))
.forEach(recommendedFilmIds::add);
@@ -255,6 +262,8 @@ public Collection getRecommendations(long userId) {
return Collections.emptyList();
}
- return filmService.getFilmsByIds(recommendedFilmIds);
+ return filmService.getFilmsByIds(recommendedFilmIds).stream()
+ .filter(filmDto -> filmDto.getRate() > 5.0f)
+ .toList();
}
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/util/EventType.java b/src/main/java/ru/yandex/practicum/filmorate/service/util/EventType.java
new file mode 100644
index 0000000..78f7013
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/util/EventType.java
@@ -0,0 +1,7 @@
+package ru.yandex.practicum.filmorate.service.util;
+
+public enum EventType {
+ LIKE,
+ REVIEW,
+ FRIEND
+}
\ No newline at end of file
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/util/FilmSortingAction.java b/src/main/java/ru/yandex/practicum/filmorate/service/util/FilmSortingAction.java
index 4803a27..0157cbb 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/util/FilmSortingAction.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/util/FilmSortingAction.java
@@ -6,7 +6,7 @@
import java.util.List;
public enum FilmSortingAction {
- YEAR, LIKES, UNDEFINED;
+ UNDEFINED, YEAR, RATE;
@Getter
private String sortingName;
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/util/LikeAction.java b/src/main/java/ru/yandex/practicum/filmorate/service/util/LikeAction.java
index dd910e2..76fd67e 100644
--- a/src/main/java/ru/yandex/practicum/filmorate/service/util/LikeAction.java
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/util/LikeAction.java
@@ -1,5 +1,5 @@
package ru.yandex.practicum.filmorate.service.util;
public enum LikeAction {
- SET, REMOVE
+ SET, RATE, REMOVE
}
diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/util/Operation.java b/src/main/java/ru/yandex/practicum/filmorate/service/util/Operation.java
new file mode 100644
index 0000000..30df4e0
--- /dev/null
+++ b/src/main/java/ru/yandex/practicum/filmorate/service/util/Operation.java
@@ -0,0 +1,7 @@
+package ru.yandex.practicum.filmorate.service.util;
+
+public enum Operation {
+ REMOVE,
+ ADD,
+ UPDATE
+}
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index ac774a1..81afd31 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -20,7 +20,8 @@ CREATE TABLE films (
description varchar(200) NOT NULL,
release_date date NOT NULL,
duration int NOT NULL,
- mpa_id int NOT NULL
+ mpa_id int NOT NULL,
+ rate real NOT NULL
);
CREATE TABLE genres (
@@ -52,8 +53,9 @@ CREATE TABLE mpa (
CREATE TABLE likes (
id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ film_id bigint NOT NULL,
user_id bigint NOT NULL,
- film_id bigint NOT NULL
+ assessment int NOT NULL
);
CREATE TABLE reviews (
@@ -132,6 +134,8 @@ CREATE INDEX IF NOT EXISTS idx_friends_friend_id ON friends (friend_id);
CREATE INDEX IF NOT EXISTS idx_films_film_name ON films (film_name);
+CREATE UNIQUE INDEX IF NOT EXISTS idx_likes_film_id_user_id ON likes (film_id, user_id);
+
CREATE INDEX IF NOT EXISTS idx_reviews_film_id ON reviews (film_id);
CREATE INDEX IF NOT EXISTS idx_reviews_user_id ON reviews (user_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_directors_name ON directors (director_name);
diff --git a/src/test/java/ru/yandex/practicum/filmorate/FilmoRateApplicationTests.java b/src/test/java/ru/yandex/practicum/filmorate/FilmoRateApplicationTests.java
index 46e71d6..38ab019 100644
--- a/src/test/java/ru/yandex/practicum/filmorate/FilmoRateApplicationTests.java
+++ b/src/test/java/ru/yandex/practicum/filmorate/FilmoRateApplicationTests.java
@@ -1,5 +1,6 @@
package ru.yandex.practicum.filmorate;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -7,21 +8,12 @@
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.context.annotation.Import;
-import ru.yandex.practicum.filmorate.dal.database.FilmDbStorage;
-import ru.yandex.practicum.filmorate.dal.database.GenreDbStorage;
-import ru.yandex.practicum.filmorate.dal.database.MpaDbStorage;
-import ru.yandex.practicum.filmorate.dal.database.UserDbStorage;
-import ru.yandex.practicum.filmorate.dal.rowmappers.FilmRowMapper;
-import ru.yandex.practicum.filmorate.dal.rowmappers.GenreRowMapper;
-import ru.yandex.practicum.filmorate.dal.rowmappers.MpaRowMapper;
-import ru.yandex.practicum.filmorate.dal.rowmappers.UserRowMapper;
+import ru.yandex.practicum.filmorate.dal.database.*;
+import ru.yandex.practicum.filmorate.dal.rowmappers.*;
import ru.yandex.practicum.filmorate.dto.request.update.FilmUpdateRequest;
import ru.yandex.practicum.filmorate.mapper.FilmMapper;
import ru.yandex.practicum.filmorate.mapper.UserMapper;
-import ru.yandex.practicum.filmorate.model.Film;
-import ru.yandex.practicum.filmorate.model.Genre;
-import ru.yandex.practicum.filmorate.model.Mpa;
-import ru.yandex.practicum.filmorate.model.User;
+import ru.yandex.practicum.filmorate.model.*;
import java.time.Duration;
import java.time.LocalDate;
@@ -34,6 +26,7 @@
FilmDbStorage.class, FilmRowMapper.class,
GenreDbStorage.class, GenreRowMapper.class,
MpaDbStorage.class, MpaRowMapper.class,
+ DirectorDbStorage.class, DirectorRowMapper.class,
FilmMapper.class, UserMapper.class
})
@JdbcTest
@@ -44,18 +37,21 @@ class FilmoRateApplicationTests {
private final FilmDbStorage filmStorage;
private final GenreDbStorage genreStorage;
private final MpaDbStorage mpaStorage;
+ private final DirectorDbStorage directorStorage;
@Autowired
FilmoRateApplicationTests(
UserDbStorage userStorage,
FilmDbStorage filmStorage,
GenreDbStorage genreStorage,
- MpaDbStorage mpaStorage
+ MpaDbStorage mpaStorage,
+ DirectorDbStorage directorStorage
) {
this.userStorage = userStorage;
this.filmStorage = filmStorage;
this.genreStorage = genreStorage;
this.mpaStorage = mpaStorage;
+ this.directorStorage = directorStorage;
}
private String generateRandomText(int length, String addAfterStr, Character... addSynbols) {
@@ -118,6 +114,14 @@ private Film addRandomFilm() {
.build());
}
+ private Director addRandomDirector() {
+ return directorStorage.createDirector(
+ Director.builder()
+ .name(generateRandomText(20, "", ' '))
+ .build()
+ );
+ }
+
@Nested
@DisplayName("Тест пользователей.")
class Users {
@@ -199,6 +203,23 @@ public void friends() {
assertThat(userStorage.getFriendIds(id2)).isEqualTo(Set.of(id1, id3));
}
+ @Test
+ void recomendations() {
+ User user1 = addRandomUser();
+ User user2 = addRandomUser();
+ User user3 = addRandomUser();
+
+ Film film1 = addRandomFilm();
+ Film film2 = addRandomFilm();
+ Film film3 = addRandomFilm();
+
+ filmStorage.addLike(film1.getId(), user1.getId(), Assessment.SEVEN);
+ filmStorage.addLike(film2.getId(), user2.getId(), Assessment.EIGHT);
+ filmStorage.addLike(film3.getId(), user3.getId(), Assessment.NINE);
+
+
+ }
+
}
@Nested
@@ -206,7 +227,7 @@ public void friends() {
class Films {
@Test
- public void testFindUserById() {
+ public void testFindFilmById() {
long id = addFifstFilm().getId();
Optional filmOptional = filmStorage.findById(id);
@@ -226,10 +247,7 @@ public void testFindUserById() {
);
assertThat(film).hasFieldOrPropertyWithValue("mpaId", 1);
assertThat(film).hasFieldOrPropertyWithValue("genreIds", Set.of(1, 2));
- assertThat(film).hasFieldOrPropertyWithValue(
- "likes", HashSet.newHashSet(0)
- );
- assertThat(film).hasFieldOrPropertyWithValue("likesCount", 0);
+ assertThat(film).hasFieldOrPropertyWithValue("rate", 0.0f);
}
);
}
@@ -270,43 +288,26 @@ public void updateFilm() {
}
@Test
- public void likes() {
+ void likes() {
+ Film film1 = addRandomFilm();
+
User user1 = addRandomUser();
User user2 = addRandomUser();
User user3 = addRandomUser();
- addRandomFilm();
- addRandomFilm();
-
- Film film1 = addRandomFilm();
- Film film2 = addRandomFilm();
- Film film3 = addRandomFilm();
-
- filmStorage.setLike(film2.getId(), user1.getId());
- filmStorage.setLike(film2.getId(), user2.getId());
- filmStorage.setLike(film2.getId(), user3.getId());
- film2.setLikes(Set.of(user1.getId(), user2.getId(), user3.getId()));
+ filmStorage.addLike(film1.getId(), user1.getId(), Assessment.ONE);
+ filmStorage.addLike(film1.getId(), user2.getId(), Assessment.of(3));
+ filmStorage.addLike(film1.getId(), user3.getId(), Assessment.of(null));
- filmStorage.setLike(film3.getId(), user1.getId());
- filmStorage.setLike(film3.getId(), user2.getId());
- film3.setLikes(Set.of(user1.getId(), user2.getId()));
+ film1 = filmStorage.findById(film1.getId()).orElse(null);
+ Assertions.assertNotNull(film1);
+ assertThat(film1.getRate()).isEqualTo(4.67f);
- filmStorage.setLike(film1.getId(), user2.getId());
- film1.setLikes(Set.of(user2.getId()));
+ filmStorage.removeLike(film1.getId(), user1.getId());
- Collection top = filmStorage.getTop(4);
- assertThat(top)
- .contains(film1, film2, film3)
- .hasSize(4);
-
- filmStorage.removeLike(film1.getId(), user2.getId());
- film1.setLikes(HashSet.newHashSet(0));
-
- Collection newTop = filmStorage.getTop(4);
- assertThat(newTop)
- .contains(film2, film3)
- .doesNotContain(film1)
- .hasSize(4);
+ film1 = filmStorage.findById(film1.getId()).orElse(null);
+ Assertions.assertNotNull(film1);
+ assertThat(film1.getRate()).isEqualTo(6.5f);
}
@Test
@@ -324,7 +325,6 @@ void genres() {
Collection genreIds = genreStorage.getAllGenres().stream().map(Genre::getId).toList();
assertThat(genreIds).containsAll(film1.getGenreIds());
-
}
@Test
@@ -345,6 +345,38 @@ void mpa() {
}
+ @Test
+ void top() {
+
+ User user1 = addRandomUser();
+
+ Film film1 = addRandomFilm();
+ Film film2 = addRandomFilm();
+ Film film3 = addRandomFilm();
+
+ Director director = addRandomDirector();
+ film1.setDirectorIds(Set.of(director.getId()));
+ film2.setDirectorIds(Set.of(director.getId()));
+ film3.setDirectorIds(Set.of(director.getId()));
+
+ film1 = filmStorage.updateFilm(film1);
+ film2 = filmStorage.updateFilm(film2);
+ film3 = filmStorage.updateFilm(film3);
+
+ filmStorage.addLike(film1.getId(), user1.getId(), Assessment.ONE);
+ filmStorage.addLike(film2.getId(), user1.getId(), Assessment.NINE);
+ filmStorage.addLike(film3.getId(), user1.getId(), Assessment.SIX);
+
+ assertThat(filmStorage.findById(film1.getId()).orElseThrow().getRate()).isEqualTo(1);
+ assertThat(filmStorage.findById(film2.getId()).orElseThrow().getRate()).isEqualTo(9);
+ assertThat(filmStorage.findById(film3.getId()).orElseThrow().getRate()).isEqualTo(6);
+
+ Collection films = filmStorage.getTop(2);
+
+ assertThat(films).containsExactly(film2, film3);
+
+ }
+
}
}
\ No newline at end of file