From 1bc3244f5bf40ca5764c1bc499ec9daa0d3c01ef Mon Sep 17 00:00:00 2001 From: Nesailormun Date: Wed, 23 Apr 2025 23:02:22 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20getTopFilms=20=D0=B2?= =?UTF-8?q?=20=D1=86=D0=B5=D0=BF=D0=BE=D1=87=D0=BA=D0=B5=20FilmController-?= =?UTF-8?q?>FilmService->FilmDbStorage->FilmRepository=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D1=81=D0=BD=D0=BE=20=D0=BD=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20(=D1=84=D0=B8?= =?UTF-8?q?=D0=BB=D1=8C=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=20genreId=20=D0=B8=20year)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 +++ .../filmorate/controller/FilmController.java | 8 ++++--- .../filmorate/dal/FilmRepository.java | 14 +++++++++-- .../filmorate/service/film/FilmService.java | 23 ++++++++++--------- .../filmorate/storage/film/FilmDbStorage.java | 4 ++++ src/main/resources/application.properties | 8 ++++++- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 0c428fa..87a052d 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,7 @@ org.projectlombok lombok + 1.18.30 true @@ -81,6 +82,7 @@ org.projectlombok lombok + 1.18.30 @@ -93,6 +95,7 @@ org.projectlombok lombok + 1.18.30 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 79be9ce..1f5d2d4 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -63,8 +63,10 @@ public void deleteLikeToFilm(@PathVariable long filmId, @PathVariable long userI } @GetMapping("/popular") - public List getTopFilms(@RequestParam(defaultValue = "10") @Positive(message = "Count must be positive") int count) { - log.info("Received GET /films/popular?count={} request", count); - return filmService.getTopFilms(count); + public List getTopFilms(@RequestParam(defaultValue = "10") @Positive(message = "Count must be positive") int count, + @RequestParam @Positive(message = "genreId must be positive") int genreId, + @RequestParam @Positive(message = "year must be positive") int year) { + log.info("Received GET /films/popular?count={}&genreId={}&year={} request", count, genreId, year); + return filmService.getTopFilms(count, genreId, year); } } \ No newline at end of file diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java index 4adda28..6bc223a 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java @@ -30,6 +30,10 @@ public class FilmRepository extends BaseRepository { private static final String GET_LIKES_QUERY = "SELECT user_id FROM Likes WHERE film_id = ?"; private static final String ADD_LIKE_QUERY = "INSERT INTO Likes (user_id, film_id) VALUES (?, ?)"; private static final String REMOVE_LIKE_QUERY = "DELETE FROM Likes WHERE film_id = ? AND user_id = ?"; + private static final String GET_MOST_POPULAR_QUERY = "SELECT f.*, r.rating_id AS mpa_id, r.rating_name AS mpa_name " + + "FROM films AS f JOIN rating AS r on f.rating_id = r.rating_id JOIN film_genre AS fg ON f.id = fg.film_id " + + "JOIN likes AS l ON f.id = l.film_id WHERE fg.genre_id = ? AND EXTRACT(YEAR FROM f.release_date) = ? " + + "GROUP BY f.id, r.rating_id, r.rating_name ORDER BY COUNT(l.user_id) DESC LIMIT ?;"; private static final String FIND_GENRES_FOR_FILMS_QUERY = "SELECT fg.film_id, g.id as genre_id, g.name as genre_name " + @@ -93,7 +97,7 @@ public Optional findById(long id) { if (films.isEmpty()) { return Optional.empty(); } else { - Film film = films.get(0); + Film film = films.getFirst(); setGenresForFilms(List.of(film)); return Optional.of(film); } @@ -206,4 +210,10 @@ public void addLike(long filmId, long userId) { public void removeLike(long filmId, long userId) { jdbc.update(REMOVE_LIKE_QUERY, filmId, userId); } -} \ No newline at end of file + + public List getTopFilms(int count, int genreId, int year) { + List films = jdbc.query(GET_MOST_POPULAR_QUERY, filmWithRatingMapper, genreId, year, count); + setGenresForFilms(films); + return films; + } +} diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java index 4f83d6e..642e837 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java @@ -22,7 +22,6 @@ import java.time.LocalDate; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -89,23 +88,25 @@ public void deleteLikeToFilm(long filmId, long userId) { log.info("Like removed successfully"); } - public List getTopFilms(int count) { - log.info("Getting top {} popular films", count); + public List getTopFilms(int count, int genreId, int year) { + log.info("Getting top {} popular films with genreId = {} and release year = {}", count, genreId, year); if (count <= 0) { throw new ValidationException("Count parameter must be positive."); } - Collection allFilms = filmStorage.getFilms(); + if (genreId <= 0) { + throw new ValidationException("genreId parameter must be positive."); + } + if (year <= 0) { + throw new ValidationException("year parameter must be positive."); + } - return allFilms.stream() + return filmStorage.getTopFilms(count, genreId, year) + .stream() .map(film -> { Set likes = filmStorage.getLikes(film.getId()); - return Map.entry(film, likes.size()); + return FilmMapper.mapToFilmDto(film, likes); }) - .sorted(Map.Entry.comparingByValue().reversed()) - .limit(count) - .map(Map.Entry::getKey) - .map(film -> FilmMapper.mapToFilmDto(film, filmStorage.getLikes(film.getId()))) - .collect(Collectors.toList()); + .toList(); } public Collection getGenres() { diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java index 5cb7f56..a654a66 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorage.java @@ -92,6 +92,10 @@ public Rating getRatingById(int id) { .orElseThrow(() -> new NotFoundException("Rating with id " + id + " not found")); } + public List getTopFilms(int count, int genreId, int year) { + return filmRepository.getTopFilms(count, genreId, year); + } + private void validateRatingExists(Rating mpa) { if (mpa == null || mpa.getId() == 0) { throw new IllegalArgumentException("Film Rating (MPA) is required."); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1730186..73fd17b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,7 @@ -logging.level.org.zalando.logbook:ERROR \ No newline at end of file +logging.level.org.zalando.logbook=TRACE +logging.level.ru.yandex.practicum.filmorate=INFO +spring.sql.init.mode=always +spring.datasource.url=jdbc:h2:file:./db/filmorate +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password \ No newline at end of file From e8f73829ce1c6e8ebb12365fd767c41b396960e1 Mon Sep 17 00:00:00 2001 From: Nesailormun Date: Wed, 23 Apr 2025 23:41:08 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20getTopFilms=20=D0=B2?= =?UTF-8?q?=20=D1=86=D0=B5=D0=BF=D0=BE=D1=87=D0=BA=D0=B5=20FilmController-?= =?UTF-8?q?>FilmService->FilmDbStorage->FilmRepository=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B3=D0=BB=D0=B0=D1=81=D0=BD=D0=BE=20=D0=BD=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20(=D1=84=D0=B8?= =?UTF-8?q?=D0=BB=D1=8C=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE?= =?UTF-8?q?=20genreId=20=D0=B8=20year)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index 87a052d..4f7f5f6 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,6 @@ org.projectlombok lombok - 1.18.30 From bc596063dbd7e420b4cf1cb93c62c5916246452d Mon Sep 17 00:00:00 2001 From: Nesailormun Date: Wed, 23 Apr 2025 23:50:49 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D1=8E=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BC=D0=B8=D1=82=20=D0=BF=D1=81=C3=90=D0=BE=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D1=83=20force=20push=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=82=D1=80=D0=B8=D0=B3=D0=B3=D0=B5=D1=80=D0=B8=D1=82?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=B9=D0=BF=D0=BB=D0=B0=D0=B9=D0=BD=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=B2=D1=81=D0=B5=D0=B9=20=D0=B2=D0=B8=D0=B4=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From f96c992d72717c7a9bc01e9820d5f21b5befeb6d Mon Sep 17 00:00:00 2001 From: nesailormun Date: Thu, 24 Apr 2025 10:57:59 +0300 Subject: [PATCH 4/4] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20getTopFilms(...)=20(=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D1=8B=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0=20genreId=20and=20ye?= =?UTF-8?q?ar=20=D1=8F=D0=B2=D0=BB=D1=8F=D1=8E=D1=82=D1=81=D1=8F=20=D0=BD?= =?UTF-8?q?=D0=B5=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=8B=D0=BC=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filmorate/controller/FilmController.java | 4 +-- .../filmorate/dal/FilmRepository.java | 30 +++++++++++++++---- .../filmorate/service/film/FilmService.java | 6 ---- 3 files changed, 26 insertions(+), 14 deletions(-) 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 1f5d2d4..5fdc2e3 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -64,8 +64,8 @@ public void deleteLikeToFilm(@PathVariable long filmId, @PathVariable long userI @GetMapping("/popular") public List getTopFilms(@RequestParam(defaultValue = "10") @Positive(message = "Count must be positive") int count, - @RequestParam @Positive(message = "genreId must be positive") int genreId, - @RequestParam @Positive(message = "year must be positive") int year) { + @RequestParam(defaultValue = "-1") int genreId, + @RequestParam(defaultValue = "-1") int year) { log.info("Received GET /films/popular?count={}&genreId={}&year={} request", count, genreId, year); return filmService.getTopFilms(count, genreId, year); } diff --git a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java index 6bc223a..0ddf60e 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java +++ b/src/main/java/ru/yandex/practicum/filmorate/dal/FilmRepository.java @@ -30,10 +30,6 @@ public class FilmRepository extends BaseRepository { private static final String GET_LIKES_QUERY = "SELECT user_id FROM Likes WHERE film_id = ?"; private static final String ADD_LIKE_QUERY = "INSERT INTO Likes (user_id, film_id) VALUES (?, ?)"; private static final String REMOVE_LIKE_QUERY = "DELETE FROM Likes WHERE film_id = ? AND user_id = ?"; - private static final String GET_MOST_POPULAR_QUERY = "SELECT f.*, r.rating_id AS mpa_id, r.rating_name AS mpa_name " + - "FROM films AS f JOIN rating AS r on f.rating_id = r.rating_id JOIN film_genre AS fg ON f.id = fg.film_id " + - "JOIN likes AS l ON f.id = l.film_id WHERE fg.genre_id = ? AND EXTRACT(YEAR FROM f.release_date) = ? " + - "GROUP BY f.id, r.rating_id, r.rating_name ORDER BY COUNT(l.user_id) DESC LIMIT ?;"; private static final String FIND_GENRES_FOR_FILMS_QUERY = "SELECT fg.film_id, g.id as genre_id, g.name as genre_name " + @@ -211,9 +207,31 @@ public void removeLike(long filmId, long userId) { jdbc.update(REMOVE_LIKE_QUERY, filmId, userId); } - public List getTopFilms(int count, int genreId, int year) { - List films = jdbc.query(GET_MOST_POPULAR_QUERY, filmWithRatingMapper, genreId, year, count); + public List getTopFilms(int count, Integer genreId, Integer year) { + + StringBuilder queryBuilder = new StringBuilder("SELECT f.*, r.rating_id AS mpa_id, r.rating_name AS mpa_name " + + "FROM films AS f JOIN rating AS r on f.rating_id = r.rating_id " + + "JOIN film_genre AS fg ON f.id = fg.film_id " + + "JOIN likes AS l ON f.id = l.film_id WHERE 1=1"); + + List filterParams = new ArrayList<>(); + + if (genreId != -1) { + queryBuilder.append(" AND fg.genre_id = ?"); + filterParams.add(genreId); + } + + if (year != -1) { + queryBuilder.append(" AND EXTRACT(YEAR FROM f.release_date) = ?"); + filterParams.add(year); + } + + queryBuilder.append(" GROUP BY f.id, r.rating_id, r.rating_name ORDER BY COUNT(l.user_id) DESC LIMIT ?;"); + filterParams.add(count); + + List films = jdbc.query(queryBuilder.toString(), filmWithRatingMapper, filterParams.toArray()); setGenresForFilms(films); return films; } + } diff --git a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java index 642e837..39d8629 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/film/FilmService.java @@ -93,12 +93,6 @@ public List getTopFilms(int count, int genreId, int year) { if (count <= 0) { throw new ValidationException("Count parameter must be positive."); } - if (genreId <= 0) { - throw new ValidationException("genreId parameter must be positive."); - } - if (year <= 0) { - throw new ValidationException("year parameter must be positive."); - } return filmStorage.getTopFilms(count, genreId, year) .stream()