Skip to content

Commit 4766475

Browse files
author
Ayzat Murtazin
committed
refactor: resolve conflicts
2 parents e90a394 + 35a870d commit 4766475

File tree

9 files changed

+172
-12
lines changed

9 files changed

+172
-12
lines changed

src/main/java/ru/yandex/practicum/filmorate/controller/UserController.java

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import lombok.RequiredArgsConstructor;
55
import org.springframework.http.HttpStatus;
66
import org.springframework.web.bind.annotation.*;
7+
import ru.yandex.practicum.filmorate.dto.FilmDto;
78
import ru.yandex.practicum.filmorate.dto.UserDto;
89
import ru.yandex.practicum.filmorate.service.UserService;
910

@@ -63,4 +64,9 @@ public void removeUser(@PathVariable long id) {
6364
userService.removeUser(id);
6465
}
6566

67+
@GetMapping("/{id}/recommendations")
68+
public Collection<FilmDto> showRecommendations(@PathVariable long id) {
69+
return userService.showRecommendations(id);
70+
}
71+
6672
}

src/main/java/ru/yandex/practicum/filmorate/dao/FilmLikeStorage.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ru.yandex.practicum.filmorate.dao;
22

33
import java.util.Map;
4+
import java.util.Set;
45

56
public interface FilmLikeStorage {
67
void add(long filmId, long userId);
@@ -10,4 +11,6 @@ public interface FilmLikeStorage {
1011
Map<Long, Long> findAll();
1112

1213
void remove(long filmId, long userId);
14+
15+
Map<Long, Set<Long>> getUsersAndFilmLikes();
1316
}

src/main/java/ru/yandex/practicum/filmorate/dao/FilmStorage.java

+3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import ru.yandex.practicum.filmorate.model.Film;
44

55
import java.util.Collection;
6+
import java.util.Set;
67

78
public interface FilmStorage extends Dao<Film> {
89
Collection<Film> findMostLikedFilmsLimitBy(int count);
10+
11+
Collection<Film> findFilmsByIds(Set<Long> filmIds);
912
}

src/main/java/ru/yandex/practicum/filmorate/dao/impl/FilmDbStorage.java

+15
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,21 @@ public Collection<Film> findMostLikedFilmsLimitBy(final int count) {
122122
return setGenresForFilms(films);
123123
}
124124

125+
public Collection<Film> findFilmsByIds(Set<Long> filmIds) {
126+
final String ids = String.join(",", Collections.nCopies(filmIds.size(), "?"));
127+
final String sql = String.format(
128+
"SELECT " +
129+
"f.ID, f.TITLE, f.DESCRIPTION, f.RELEASE_DATE, f.DURATION, f.MPA_ID, m.RATING_NAME, COUNT(fl.USER_ID) AS likes " +
130+
"FROM " +
131+
"FILM f LEFT JOIN MPA m ON f.MPA_ID = m.ID " +
132+
"LEFT JOIN film_like fl on f.id = fl.film_id " +
133+
"WHERE f.ID IN (%s)" +
134+
"GROUP BY f.id, m.rating_name ", ids);
135+
136+
Collection<Film> films = jdbcTemplate.query(sql, this::mapToFilm, filmIds.toArray());
137+
return setGenresForFilms(films);
138+
}
139+
125140
private List<Film> setGenresForFilms(Collection<Film> films) {
126141
Map<Long, Film> filmMap = films.stream().collect(Collectors.toMap(Film::getId, identity()));
127142
Map<Long, List<Genre>> filmIdGenreMap = filmGenreStorage.findGenresInIdList(filmMap.keySet());

src/main/java/ru/yandex/practicum/filmorate/dao/impl/FilmLikeDbStorage.java

+21-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
import lombok.RequiredArgsConstructor;
44
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.dao.DataAccessException;
56
import org.springframework.dao.EmptyResultDataAccessException;
67
import org.springframework.jdbc.core.JdbcTemplate;
78
import org.springframework.stereotype.Repository;
89
import ru.yandex.practicum.filmorate.dao.FilmLikeStorage;
910

1011
import java.sql.ResultSet;
1112
import java.sql.SQLException;
12-
import java.util.LinkedHashMap;
13-
import java.util.Map;
13+
import java.util.*;
1414

1515
@Repository
1616
@RequiredArgsConstructor
@@ -49,6 +49,11 @@ public void remove(long filmId, long userId) {
4949
jdbcTemplate.update(sql, filmId, userId);
5050
}
5151

52+
@Override
53+
public Map<Long, Set<Long>> getUsersAndFilmLikes() {
54+
String filmsIdsSql = "SELECT user_id, film_id FROM film_like";
55+
return jdbcTemplate.query(filmsIdsSql, this::extractToMap);
56+
}
5257

5358
private Map<Long, Long> mapRowToIdCount(ResultSet rs) throws SQLException {
5459
final Map<Long, Long> result = new LinkedHashMap<>();
@@ -57,4 +62,18 @@ private Map<Long, Long> mapRowToIdCount(ResultSet rs) throws SQLException {
5762
}
5863
return result;
5964
}
65+
66+
private Map<Long, Set<Long>> extractToMap(ResultSet rs) throws SQLException, DataAccessException {
67+
final Map<Long, Set<Long>> userFilmLikesMap = new HashMap<>();
68+
while (rs.next()) {
69+
final Long userId = rs.getLong("user_id");
70+
Set<Long> filmLikes = userFilmLikesMap.get(userId);
71+
if (filmLikes == null) {
72+
filmLikes = new HashSet<>();
73+
}
74+
filmLikes.add(rs.getLong("film_id"));
75+
userFilmLikesMap.put(userId, filmLikes);
76+
}
77+
return userFilmLikesMap;
78+
}
6079
}

src/main/java/ru/yandex/practicum/filmorate/dao/impl/UserDbStorage.java

-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ public Collection<User> findCommonFriends(final long userId, final long anotherU
109109
return jdbcTemplate.query(sql, this::extractToUserList, userId, anotherUserId);
110110
}
111111

112-
113112
private User extractToUser(ResultSet rs) throws SQLException, DataAccessException {
114113

115114
User user = null;

src/main/java/ru/yandex/practicum/filmorate/service/UserService.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ru.yandex.practicum.filmorate.service;
22

3+
import ru.yandex.practicum.filmorate.dto.FilmDto;
34
import ru.yandex.practicum.filmorate.dto.UserDto;
45

56
import java.util.Collection;
@@ -23,4 +24,6 @@ public interface UserService {
2324
void removeFriend(long userId, long friendId);
2425

2526
void removeUser(long userId);
27+
28+
Collection<FilmDto> showRecommendations(long id);
2629
}

src/main/java/ru/yandex/practicum/filmorate/service/impl/UserServiceImpl.java

+45-3
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33
import lombok.RequiredArgsConstructor;
44
import lombok.extern.slf4j.Slf4j;
55
import org.springframework.stereotype.Service;
6+
import ru.yandex.practicum.filmorate.dao.FilmLikeStorage;
7+
import ru.yandex.practicum.filmorate.dao.FilmStorage;
68
import ru.yandex.practicum.filmorate.dao.FriendshipStorage;
79
import ru.yandex.practicum.filmorate.dao.UserStorage;
10+
import ru.yandex.practicum.filmorate.dto.FilmDto;
811
import ru.yandex.practicum.filmorate.dto.UserDto;
12+
import ru.yandex.practicum.filmorate.mapper.FilmMapper;
913
import ru.yandex.practicum.filmorate.mapper.UserMapper;
14+
import ru.yandex.practicum.filmorate.model.Film;
1015
import ru.yandex.practicum.filmorate.model.Friendship;
1116
import ru.yandex.practicum.filmorate.model.User;
1217
import ru.yandex.practicum.filmorate.service.UserService;
1318

14-
import java.util.ArrayList;
15-
import java.util.Collection;
19+
import java.util.*;
1620
import java.util.stream.Collectors;
1721

1822
import static ru.yandex.practicum.filmorate.model.FriendshipStatus.ACKNOWLEDGED;
@@ -24,8 +28,9 @@
2428
public class UserServiceImpl implements UserService {
2529

2630
private final UserStorage userStorage;
27-
31+
private final FilmStorage filmStorage;
2832
private final FriendshipStorage friendshipStorage;
33+
private final FilmLikeStorage filmLikeStorage;
2934

3035
/**
3136
* Сохранение пользователя в БД.
@@ -161,6 +166,43 @@ public void removeUser(long userId) {
161166
userStorage.remove(userId);
162167
}
163168

169+
/**
170+
* Создание рекомендаций пользователю фильмов, которые ему могут понравиться. Для подготовки рекомендаций
171+
* выгружаем данные из таблицы film_like, находим пользователей с максимальным количеством одинаковых с нашим
172+
* пользователем лайков и выбираем у них для рекомендации, которые они тоже залайкали, но наш пользователь
173+
* в запросе их еще не видел
174+
*
175+
* @param id идентификатор пользователя
176+
* @return коллекцию FilmDto
177+
*/
178+
179+
@Override
180+
public Collection<FilmDto> showRecommendations(long id) {
181+
log.info("Получение списка рекомендаций фильмов для пользователя с id {}.", id);
182+
Map<Long, Set<Long>> usersLikes = filmLikeStorage.getUsersAndFilmLikes();
183+
int maxLikes = 0;
184+
Set<Long> recommendations = new HashSet<>();
185+
Set<Long> userLikedFilms = usersLikes.get(id);
186+
for (Long user : usersLikes.keySet()) {
187+
if (user != id) {
188+
Set<Long> likedFilms = usersLikes.get(user);
189+
Set<Long> currentUserLikedFilms = new HashSet<>(userLikedFilms);
190+
currentUserLikedFilms.retainAll(likedFilms);
191+
if (currentUserLikedFilms.size() > maxLikes && currentUserLikedFilms.size() < likedFilms.size()) {
192+
recommendations.clear();
193+
maxLikes = currentUserLikedFilms.size();
194+
likedFilms.removeAll(currentUserLikedFilms);
195+
recommendations.addAll(likedFilms);
196+
} else if (currentUserLikedFilms.size() == maxLikes) {
197+
likedFilms.removeAll(currentUserLikedFilms);
198+
recommendations.addAll(likedFilms);
199+
}
200+
}
201+
}
202+
Collection<Film> filmsRecommendation = filmStorage.findFilmsByIds(recommendations);
203+
return filmsRecommendation.stream().map(FilmMapper::toDto).collect(Collectors.toList());
204+
}
205+
164206
private UserDto validateUserName(final UserDto userDto) {
165207
final String validatedName = userDto.getName() == null || userDto.getName().isBlank() ?
166208
userDto.getLogin() : userDto.getName();

src/test/java/ru/yandex/practicum/filmorate/storage/UserDbStorageTest.java

+76-6
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@
88
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
99
import org.springframework.jdbc.core.JdbcTemplate;
1010
import org.springframework.test.annotation.DirtiesContext;
11-
import ru.yandex.practicum.filmorate.dao.FriendshipStorage;
12-
import ru.yandex.practicum.filmorate.dao.UserStorage;
13-
import ru.yandex.practicum.filmorate.dao.impl.FriendshipDbStorage;
14-
import ru.yandex.practicum.filmorate.dao.impl.UserDbStorage;
11+
import ru.yandex.practicum.filmorate.dao.*;
12+
import ru.yandex.practicum.filmorate.dao.impl.*;
1513
import ru.yandex.practicum.filmorate.exception.NotFoundException;
14+
import ru.yandex.practicum.filmorate.model.Film;
1615
import ru.yandex.practicum.filmorate.model.Friendship;
16+
import ru.yandex.practicum.filmorate.model.Mpa;
1717
import ru.yandex.practicum.filmorate.model.User;
18+
import ru.yandex.practicum.filmorate.service.impl.UserServiceImpl;
1819

1920
import java.time.LocalDate;
20-
import java.util.Collection;
21-
import java.util.List;
21+
import java.util.*;
2222

2323
import static org.assertj.core.api.Assertions.assertThat;
2424
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -34,16 +34,26 @@ class UserDbStorageTest {
3434

3535
private final JdbcTemplate jdbcTemplate;
3636
private UserStorage userStorage;
37+
private UserServiceImpl userService;
3738
private FriendshipStorage friendshipStorage;
39+
private FilmGenreStorage filmGenreStorage;
40+
private FilmLikeStorage filmLikeStorage;
41+
private FilmStorage filmDbStorage;
3842
private User user;
3943
private User updatedUser;
4044
private User anotherUser;
45+
private Film filmOne;
46+
private Film filmTwo;
4147

4248

4349
@BeforeEach
4450
void setUp() {
51+
filmLikeStorage = new FilmLikeDbStorage(jdbcTemplate);
52+
filmGenreStorage = new FilmGenreDbStorage(jdbcTemplate);
53+
filmDbStorage = new FilmDbStorage(jdbcTemplate, filmGenreStorage);
4554
userStorage = new UserDbStorage(jdbcTemplate);
4655
friendshipStorage = new FriendshipDbStorage(jdbcTemplate);
56+
userService = new UserServiceImpl(userStorage, filmDbStorage, friendshipStorage, filmLikeStorage);
4757
user = User.builder()
4858
.id(1)
4959
.email("email")
@@ -65,6 +75,26 @@ void setUp() {
6575
.name("another_name")
6676
.birthday(LocalDate.now())
6777
.build();
78+
79+
Mpa mpa = new Mpa(1, "G");
80+
81+
filmOne = Film.builder()
82+
.id(1)
83+
.name("film")
84+
.description("film description")
85+
.releaseDate(LocalDate.of(2020, 12, 12))
86+
.duration(123)
87+
.mpa(mpa)
88+
.build();
89+
90+
filmTwo = Film.builder()
91+
.id(2)
92+
.name("film two")
93+
.description("film two description")
94+
.releaseDate(LocalDate.of(2020, 12, 12))
95+
.duration(123)
96+
.mpa(mpa)
97+
.build();
6898
}
6999

70100
@Test
@@ -334,4 +364,44 @@ void testDeleteNotExistingUser() {
334364
NotFoundException e = assertThrows(NotFoundException.class, () -> userStorage.remove(userId));
335365
assertEquals(formattedResponse, e.getMessage());
336366
}
367+
368+
@Test
369+
@DisplayName("Тест получения мапы с ключами userId и сетом с списком айдишников залайканных фильмов.")
370+
void testGetRecommendationsList() {
371+
userStorage.add(user);
372+
userStorage.add(anotherUser);
373+
374+
filmDbStorage.add(filmOne);
375+
filmDbStorage.add(filmTwo);
376+
377+
filmLikeStorage.add(filmOne.getId(), user.getId());
378+
filmLikeStorage.add(filmOne.getId(), anotherUser.getId());
379+
filmLikeStorage.add(filmTwo.getId(), anotherUser.getId());
380+
381+
Map<Long, Set<Long>> filmRecommendations = filmLikeStorage.getUsersAndFilmLikes();
382+
383+
assertThat(filmRecommendations.get(1L))
384+
.isNotNull()
385+
.isNotEmpty()
386+
.containsExactly(filmOne.getId());
387+
388+
assertThat(filmRecommendations.get(2L))
389+
.isNotNull()
390+
.isNotEmpty()
391+
.containsExactly(filmOne.getId(), filmTwo.getId());
392+
}
393+
394+
@Test
395+
@DisplayName("Тест получения мапы с ключами userId и сетом с списком айдишников залайканных фильмов, когда лайков нет.")
396+
void testGetRecommendationsListNoLikes() {
397+
userStorage.add(user);
398+
userStorage.add(anotherUser);
399+
filmDbStorage.add(filmOne);
400+
401+
Map<Long, Set<Long>> filmRecommendations = filmLikeStorage.getUsersAndFilmLikes();
402+
403+
assertThat(filmRecommendations)
404+
.isNotNull()
405+
.isEmpty();
406+
}
337407
}

0 commit comments

Comments
 (0)