Java List to Reactor Flux to Identity Map

Java List to Reactor Flux to Identity Map

我正在尝试实现一些简单的 Spring Boot GraphQL BatchMapping。

@BatchMapping 接收一个 List<Book> 然后调用一个 @Repository 并将结果转换为 Map<Book, Author>。我正在努力使用 Reactor 的 FluxMono.

@BatchMapping
Mono<Map<Book, Author>> author(List<Book> books) {
  List<String> authorIds = books.stream().map(book -> book.authorId).toList();

  Flux<Author> authors = authorRepository.findAllById(authorIds);

  return authors.mapToTheBooksInTheCorrectOrder???();
}

每个Book有一个Author但是多个Book可以有相同的Author

我想return一个Map(更准确地说是一个Mono<Map>),其中键是每个Book,值为对应的Author.

您可以假设 Flux<Author> 中的 Author(return 由 authorRepository.findAllById 编辑)是唯一的, returned AuthorList<Book> 中的 authorId 具有相同的顺序(第一次出现):

book1 = { id: "1", name: "A Scanner Darkly", authorId = "2" }
book2 = { id: "2", name: "High Fidelity", authorId = "1" }
book3 = { id: "3", name: "The Man In The High Castle", authorId = "2" }

将导致:

authors = [
  { id: "2", name: "Philip K. Dick" }
  { id: "1", name: "Nick Hornby" } 
]

换句话说:

该方法的结果应该是(对于这个例子):

{
  book1 : author2,
  book2 : author1,
  book3 : author2
}

全部包裹在 Mono<Map>.

如果您在使用 FluxMono 时遇到困难,您可以使用 collectList() 运算符来获得 List<Author>。例如:

return authorRepository
    .findAllById(authorIds)
    .collectList() // Mono<List<Author>>
    .map(authors -> /* Use authors + books to get a Map<Book, Author> */);

这样做的好处是您可以使用 map() 运算符并使用您可能更熟悉的 List<Author>List<Book> 类型。

就我个人而言,我什至建议使用 collectMap() 运算符并创建一个 Map,其中作者的 ID 作为键,Author 作为 [= 的值21=]。这使得为​​每个 Book 检索相关的 Author 对象变得更加容易,因为这样您就可以使用 authorIdMap.get(book.getAuthorId())。例如:

return authorRepository
    .findAllById(authorIds)
    .collectMap(Author::getId) // Mono<Map<String, Author>>
    .map(authorIdMap -> /* Use authorIdMap + books to get a Map<Book, Author> */);

在这种情况下,authorIdMap 将是 Map<String, Author>

如果您不想在 map() 函数中使用常规 for-loop/streams,您也可以为此使用反应流。为此,您首先需要创建一个 Flux<Book>,然后您可以再次使用 collectMap() 运算符,但这次的键将是 Book 对象,而值将是相应的 Author 对象。

return authorRepository
    .findAllById(authorIds)
    .collectMap(Author::getId) // Mono<Map<String, Author>>
    .flatMap(authorIdMap -> Flux
        .fromIterable(books) // Flux<Book>
        .collectMap(identity(), book -> authorIdMap.get(book.getAuthorId()))); // Mono<Map<Book, Author>>

在这种情况下,您需要使用 flatMap() 而不是 map(),因为该函数将 return 反应流而不是普通对象。

此外,要将当前对象用作 collectMap() 运算符中的键,您可以编写 collectMap(book -> book, ...)。但是,编写这些 x -> x lambda 的一种更简洁的方法是使用 Function.identity()(如果使用静态导入,则使用 identity())。