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 68b2969..e59aca4 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java +++ b/src/main/java/ru/yandex/practicum/filmorate/controller/FilmController.java @@ -8,6 +8,7 @@ import ru.yandex.practicum.filmorate.service.FilmService; import java.util.Collection; +import java.util.List; @RestController @RequestMapping("/films") @@ -84,4 +85,11 @@ public Collection getFilmsByDirector(@PathVariable long directorId, log.info("GET /films/director/{}?sortBy={}", directorId, sortBy); return filmService.findByDirectorSorted(directorId, sortBy); } + + @GetMapping("/search") + public Collection searchFilms(@RequestParam String query, + @RequestParam List by) { + log.info("GET /films/search?query={}&by={}", query, by); + return filmService.searchFilms(query, by); + } } \ No newline at end of file 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 8de1336..b393165 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java +++ b/src/main/java/ru/yandex/practicum/filmorate/service/FilmService.java @@ -17,6 +17,7 @@ import java.time.LocalDate; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; @Service @@ -195,4 +196,23 @@ public Collection findByDirectorSorted(long directorId, String sortBy) { directorService.getDirectorById(directorId); // проверка существования режиссёра — иначе 404 return filmStorage.findFilmByDirector(directorId, sortBy); } + + private void validateSearchParameters(String query, List by) { + if (query == null || query.isBlank()) { + throw new ValidationException("Параметр 'query' не может быть пустым"); + } + if (by == null || by.isEmpty()) { + throw new ValidationException("Параметр 'by' не может быть пустым"); + } + for (String field : by) { + if (field == null || !field.equalsIgnoreCase("title") && !field.equalsIgnoreCase("director")) { + throw new ValidationException("Недопустимое значение в параметре 'by': " + field); + } + } + } + + public Collection searchFilms(String query, List by) { + validateSearchParameters(query, by); + return filmStorage.searchFilms(query, by); + } } \ No newline at end of file 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 787bd86..334f539 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 @@ -104,6 +104,14 @@ public class FilmDbStorage implements FilmStorage { "WHERE fd.director_id = ? " + "GROUP BY f.id, f.name, f.description, f.release_date, " + "f.duration, f.mpa_id, m.name "; + private static final String FIND_FILMS_BY_SEARCH_QUERY = + "SELECT f.id, f.name, f.description, f.release_date, f.duration, " + + "f.mpa_id, m.name AS mpa_name " + + "FROM films f " + + "LEFT JOIN mpa m ON f.mpa_id = m.id " + + "LEFT JOIN film_director fd ON f.id = fd.film_id " + + "LEFT JOIN directors d ON fd.director_id = d.id " + + "LEFT JOIN film_likes fl ON f.id = fl.film_id "; private final JdbcTemplate jdbc; private final FilmRowMapper mapper; @@ -238,6 +246,40 @@ public List findFilmByDirector(long directorId, String sortBy) { return films; } + @Override + public List searchFilms(String query, List by) { + StringBuilder sql = new StringBuilder(FIND_FILMS_BY_SEARCH_QUERY); + List conditions = new ArrayList<>(); + List params = new ArrayList<>(); + + if (by.contains("director")) { + conditions.add("LOWER(d.name) LIKE ?"); + params.add("%" + query.toLowerCase() + "%"); + } + + if (by.contains("title")) { + conditions.add("LOWER(f.name) LIKE ?"); + params.add("%" + query.toLowerCase() + "%"); + } + + if (conditions.isEmpty()) { + throw new ValidationException("Необходимо указать поиск: director или title"); + } + + sql.append("WHERE ") + .append(String.join(" OR ", conditions)) + .append(" "); + + sql.append("GROUP BY f.id, f.name, f.description, f.release_date, ") + .append("f.duration, f.mpa_id, m.name ") + .append("ORDER BY COUNT(DISTINCT fl.user_id) DESC, f.id"); + List films = jdbc.query(sql.toString(), mapper, params.toArray()); + + loadDirectors(films); + loadGenresForFilms(films); + return films; + } + //Собирает запрос: чтение + WHERE по условиям + сортировка по числу лайков, без LIMIT private String buildPopularQuery(List conditions) { StringBuilder sql = new StringBuilder(FIND_POPULAR_BASE_QUERY); diff --git a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java index 62f4a02..27bfdee 100644 --- a/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java +++ b/src/main/java/ru/yandex/practicum/filmorate/storage/film/FilmStorage.java @@ -27,4 +27,6 @@ public interface FilmStorage { boolean removeLike(long filmId, long userId); List findFilmByDirector(long directorId, String sortBy); + + List searchFilms(String query, List by); } \ No newline at end of file diff --git a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java index 8a786fd..3f3b36a 100644 --- a/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java +++ b/src/test/java/ru/yandex/practicum/filmorate/storage/film/FilmDbStorageTest.java @@ -348,4 +348,45 @@ void shouldReturnAllFilmsByDirectorSortedByYear() { assertEquals(film3.getId(), allFilms.get(1).getId()); assertEquals(film2.getId(), allFilms.get(2).getId()); } + + @Test + void searchFilmByTitle() { + Film film = newFilm("Интерстеллар"); + film.setReleaseDate(LocalDate.of(2000, 1, 1)); + film = filmStorage.add(film); + List foundFilms = filmStorage.searchFilms("инт", List.of("title")); + assertEquals(1, foundFilms.size()); + assertEquals(film.getId(), foundFilms.get(0).getId()); + assertEquals("Интерстеллар", foundFilms.get(0).getName()); + } + + @Test + void searchFilmByDirector() { + Director director = createDirector("Кристофер Нолан"); + directorStorage.addDirector(director); + Film film = newFilm("Начало"); + film.getDirectors().add(director); + film = filmStorage.add(film); + List foundFilms = filmStorage.searchFilms("нол", List.of("director")); + assertEquals(1, foundFilms.size()); + assertEquals(film.getId(), foundFilms.get(0).getId()); + assertEquals("Начало", foundFilms.get(0).getName()); + } + + @Test + void searchFilmByTitleAndDirector() { + Director director = createDirector("Кристофер Нолан"); + directorStorage.addDirector(director); + Film film = newFilm("Начало"); + film.getDirectors().add(director); + film = filmStorage.add(film); + Long filmId = film.getId(); + Film film1 = newFilm("Нолан и его фильмы"); + film1 = filmStorage.add(film1); + Long film1Id = film1.getId(); + List foundFilms = filmStorage.searchFilms("нол", List.of("title", "director")); + assertEquals(2, foundFilms.size()); + assertTrue(foundFilms.stream().anyMatch(f -> f.getId().equals(filmId))); + assertTrue(foundFilms.stream().anyMatch(f -> f.getId().equals(film1Id))); + } } \ No newline at end of file