Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ru.yandex.practicum.filmorate.controller;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -13,6 +14,7 @@
import ru.yandex.practicum.filmorate.service.film.FilmService;

import java.util.Collection;
import java.util.List;

@RestController
@RequestMapping("/films")
Expand Down Expand Up @@ -69,6 +71,22 @@ public void deleteLike(@PathVariable long id,
filmService.removeLike(id, userId);
}

@GetMapping("/search")
public Collection<FilmDto> searchFilms(@RequestParam @NotBlank String query,
@RequestParam(defaultValue = "title") String by) {
log.trace("Search films requested with query: {}, by: {}", query, by);

List<SearchFilmsBy> parsedInput = SearchFilmsBy.parseStr(by);

for (SearchFilmsBy searchFilmsBy : parsedInput) {
if (searchFilmsBy == null) {
throw new IllegalArgumentException("Unknown search params %s".formatted(by));
}
}

return filmService.searchFilms(query, parsedInput);
}

@GetMapping("/common")
public Collection<FilmDto> findCommonFilms(@RequestParam(name = "userId") @Positive long userId,
@RequestParam(name = "friendId") @Positive long friendId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ru.yandex.practicum.filmorate.controller;

import lombok.Getter;

import java.util.ArrayList;
import java.util.List;

@Getter
public enum SearchFilmsBy {
TITLE("title"),
DIRECTOR("director");

private final String value;

SearchFilmsBy(String value) {
this.value = value;
}

public static List<SearchFilmsBy> parseStr(String value) {
String[] splittedStr = value.trim().toLowerCase().split(",");
List<SearchFilmsBy> parsedList = new ArrayList<>();

for (String str : splittedStr) {
SearchFilmsBy searchFilmsBy = SearchFilmsBy.fromString(str);
parsedList.add(searchFilmsBy);
}

return parsedList;
}

public static SearchFilmsBy fromString(String value) {
return switch (value) {
case "title" -> SearchFilmsBy.TITLE;
case "director" -> SearchFilmsBy.DIRECTOR;
default -> null;
};
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package ru.yandex.practicum.filmorate.repository.film;

import ru.yandex.practicum.filmorate.controller.FilmsSortBy;
import ru.yandex.practicum.filmorate.controller.SearchFilmsBy;
import ru.yandex.practicum.filmorate.model.Film;

import java.util.Collection;
import java.util.List;
import java.util.Optional;

public interface FilmRepository {
Expand All @@ -24,4 +26,6 @@ public interface FilmRepository {
Collection<Film> findFilmsOfDirector(long directorId, FilmsSortBy sortFilmsBy);

Collection<Film> findFilmRecommendations(long userId, long similarUserId);

Collection<Film> searchFilms(String query, List<SearchFilmsBy> searchBy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.stereotype.Repository;
import ru.yandex.practicum.filmorate.controller.FilmsSortBy;
import ru.yandex.practicum.filmorate.controller.SearchFilmsBy;
import ru.yandex.practicum.filmorate.model.Director;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.model.Genre;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -185,6 +187,37 @@ AND f.film_id NOT IN (
return jdbc.query(sqlRecommendations, params, filmRowMapper);
}

@Override
public Collection<Film> searchFilms(String query, List<SearchFilmsBy> searchBy) {
String searchParamName = "query";
String searchFilmsSql = buildSearchSql(searchBy, searchParamName);
String searchPattern = "%%%s%%".formatted(query.toLowerCase());

MapSqlParameterSource params = new MapSqlParameterSource()
.addValue(searchParamName, searchPattern);

return jdbc.query(searchFilmsSql, params, filmRowMapper);
}

private String buildSearchSql(List<SearchFilmsBy> searchFilmsBy, String searchParamName) {
List<String> conditions = new ArrayList<>();

for (SearchFilmsBy searchBy : searchFilmsBy) {
switch (searchBy) {
case TITLE -> conditions.add("LOWER(f.name) LIKE :%s".formatted(searchParamName));
case DIRECTOR -> conditions.add("LOWER(d.name) LIKE :%s".formatted(searchParamName));
}
}

String whereClause = String.join(" OR ", conditions);

return BASE_SELECT_SQL.concat("""
WHERE (%s)
GROUP BY f.film_id
ORDER BY (SELECT COUNT(*) FROM likes l WHERE l.film_id = f.film_id) DESC, f.film_id""".formatted(
whereClause));
}

private void saveGenres(Collection<Genre> genres, long filmId) {
String insertGenresSql = """
INSERT INTO film_genres (film_id, genre_id)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ru.yandex.practicum.filmorate.service.film;

import ru.yandex.practicum.filmorate.controller.FilmsSortBy;
import ru.yandex.practicum.filmorate.controller.SearchFilmsBy;
import ru.yandex.practicum.filmorate.dto.film.FilmDto;
import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest;
import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest;

import java.util.Collection;
import java.util.List;

public interface FilmService {
Collection<FilmDto> findAll();
Expand All @@ -27,4 +29,6 @@ public interface FilmService {
Collection<FilmDto> findCommonFilms(long userId, long friendId);

Collection<FilmDto> findFilmsOfDirector(long directorId, FilmsSortBy sortFilmsBy);

Collection<FilmDto> searchFilms(String query, List<SearchFilmsBy> searchFilmsBy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.yandex.practicum.filmorate.controller.FilmsSortBy;
import ru.yandex.practicum.filmorate.controller.SearchFilmsBy;
import ru.yandex.practicum.filmorate.dto.director.DirectorDto;
import ru.yandex.practicum.filmorate.dto.film.FilmDto;
import ru.yandex.practicum.filmorate.dto.film.NewFilmRequest;
import ru.yandex.practicum.filmorate.dto.film.UpdateFilmRequest;
import ru.yandex.practicum.filmorate.dto.genre.GenreDto;
import ru.yandex.practicum.filmorate.exception.NotFoundException;
import ru.yandex.practicum.filmorate.mapper.FilmMapper;
import ru.yandex.practicum.filmorate.model.Director;
import ru.yandex.practicum.filmorate.model.EventType;
import ru.yandex.practicum.filmorate.model.Film;
import ru.yandex.practicum.filmorate.model.Genre;
import ru.yandex.practicum.filmorate.model.Operation;
import ru.yandex.practicum.filmorate.model.*;
import ru.yandex.practicum.filmorate.repository.director.DirectorRepository;
import ru.yandex.practicum.filmorate.repository.film.FilmRepository;
import ru.yandex.practicum.filmorate.repository.genre.GenreRepository;
Expand Down Expand Up @@ -183,6 +180,14 @@ public Collection<FilmDto> findFilmsOfDirector(long directorId, FilmsSortBy sort
.toList();
}

@Override
public Collection<FilmDto> searchFilms(String query, List<SearchFilmsBy> searchFilmsBy) {
return filmRepository.searchFilms(query, searchFilmsBy)
.stream()
.map(FilmMapper::toFilmDto)
.toList();
}

private void throwIfDirectorNotFound(long directorId) {
directorRepository.findById(directorId)
.orElseThrow(NotFoundException.supplier("Director with id %d not found", directorId));
Expand Down