Как суммировать голоса песен с использованием операторов Collectors.summarizingInt и flatMap - PullRequest
0 голосов
/ 26 февраля 2020

Допустим, у меня есть классы, иллюстрирующие проблему песен и голосов, за которые проголосовали.

Пользователь. java

@EqualsAndHashCode
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Document(collection = "users")
public class User {

  @Id private String id;

  private String username;

  private String email;

  private Integer age;

  private String password;

  @DBRef(db = "interview", lazy = true)
  @EqualsAndHashCode.Exclude
  @ToString.Exclude
  private Set<Song> songs;
}

Песня. java

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Song {

  @Id private String id;

  private String title;

  private String author;

  private String songGenre;

  Set<Vote> votesOfSong;
}

Голосовать. java

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "votes")
@Getter
public class Vote {

  @Id private String id;

  private User user;

  private Song song;

  private VoteKind voteKind;

  @Default private LocalDateTime dateOfVote = LocalDateTime.now();
}

и

VoteKind. java

@RequiredArgsConstructor
@Getter
public enum VoteKind {
  LIKE(1),
  DISLIKE(-1);

  private final Integer voteValue;
}

Я хочу создать метод Flux, который найдет x песен с самым высоким рейтингом в целом.

Пока я не завершил метод:

public Flux<Vote> findHighestRatedSongs(int numberOfTopSongs) {

    return songRepository.findAll()
                         .limitRate(numberOfTopSongs)
                         .collect(Collectors.summarizingInt(value -> value.getVotesOfSong()
     .stream()
     .flatMap(vote -> vote.getVoteKind()
                          .getVoteValue(), ...missing expression))))
}

Насколько я понимаю, я должен использовать toMap метод, подобный:

Mono<Map<Set<Vote>, Song>> collect = songRepository.findAll()
        .collect(Collectors.toMap(Song::getVotesOfSong, Function.identity())); 

после всех плоских getVotesOfSong с оператором flatMap для VoteKinds и в конце reduce результат суммирования симпатий и антипатий, но у меня есть проблема с окончательным синтаксисом, из-за которого я застрял. Все, что я хочу вернуть, это Flux<Map<String, Integer>>, которое будет соответствовать SongTitle и количеству голосов для конкретной песни.

Буду благодарен за предложения о том, как достичь желаемой цели.

1 Ответ

1 голос
/ 26 февраля 2020

Вне концепции Flux ядро ​​использования Stream здесь будет выглядеть следующим образом (комментарии вписаны).

Stream<Song> songCollection = songRepository.findAll();

Map<String, Integer> songTitleToVotes = songCollection
        // create a song to its total vote Map
        .collect(Collectors.toMap(Function.identity(), this::getSumOfVotes))
        // stream the entries of this map
        .entrySet().stream()
        // sort them in descending order of votes
        .sorted(Map.Entry.<Song,Integer>comparingByValue().reversed())
        // then limit the top N songs
        .limit(numberOfTopSongs)
        // collect these N songs while mapping song to its title
        .collect(Collectors.toMap(e -> e.getKey().getId(), Map.Entry::getValue,
                (a, b) -> a, // check for duplicate title when using this
                LinkedHashMap::new));

, где метод getSumOfVotes может быть реализован как

private int getSumOfVotes(Song song) {
    return song.getVotesOfSong()
            .stream()
            .mapToInt(vote -> vote.getVoteKind().getVoteValue())
            .sum();
}
...