web-dev-qa-db-fra.com

Comment obtenir un flux ou une liste à partir de deux flux (objets) dans la programmation réactive de Spring?

Je suis nouveau sur Spring Reactive Project. Dans ma classe Spring Boot Controller, j'ai

Flux<House> ( list of all houses in the database) and Flux<Image>

provenant de la couche de service où _ Flux<Image> sont des images pour une maison donnée comme une liste de maisons dont chacune a sa propre collection d'images. Cela signifie que je dois répéter sur Flux<House> pour obtenir l'identifiant de la maison, puis appelez findByHouseId sur Image pour obtenir Flux<Image>, chaque image a la propriété houseId. Chaque objet image a une propriété "cover" avec une valeur de chaîne "yes" ou "no" et une autre propriété "type" ayant différentes valeurs de chaîne comme "bedroom". Je veux récupérer un

list of Flux<Image> like List<Flux<Image>> or List<List<Image>>

ne contenant que les images pour lesquelles "couvrir" == "oui" OR "type" == "chambre". Comment y parvenir? Le code de programmation répondra à cette question.

Ci-dessous a été tenté dans le cadre de la solution, mais même cette solution partielle ne fonctionne pas:

List<Flux<Image>> imagesList= new ArrayList<Flux<Image>>();
        Flux<House> houses = houseService.findAllHouses();

        houses.map(house ->  house.getId()).subscribe(id -> imageService.findByHouseId(id))
        .map(imageFlux ->imagesList.add(imageFlux));

Veuillez noter que je recherche une solution qui n'implique pas d'invoquer block () (annulant l'avantage d'être réactif) sauf si c'est la seule façon.

En situation non réactive c'est ce que je veux:

List<House> houses  --> List of all houses
Map<String,List<Image>> mapOfImages  --> where key is houseId 

donc pour chaque maison, je peux facilement obtenir des images pour cette maison tout en itérant sur les maisons dans le modèle de vue.

Ces (maisons et mapOfImages) seront passés au modèle comme

model.addAttribute("houses",houses);
model.addAttribute("mapOfImages",mapOfImages);

Alors maintenant, dans le modèle de vue thyemleaf, je peux:

<div data-th-each="house : ${houses}">
        <h3 data-th-text="${house.title}"></h3>
        <div data-th-each="image : ${mapOfImages.get(house.houseId)}">
          <h5 data-th-text="${image.fileName}"></h5>
          <h5 data-th-text="${image.type}"></h5>
        </div>
 </div>   

La base de données utilisée est Mongodb qui a deux collections indépendantes: maison et image, c'est-à-dire qu'il n'y a pas de collections intégrées et la seule chose qui lie une image à une maison est la propriété houseId sur l'image.

10
ace

Vous voulez essentiellement transformer un flux d'objets House en un flux d'objets Image.

Donc, utilisez flatMap() qui convertit Flux<Flux<T>> (Ou Flux<Mono<T>>) En Flux<T>.

Mono<List<Image>> images = houseService.findAllHouses() // returns Flux<House>
  .flatMap(house -> imageService.findByHouseId(id)      // returns Flux<Image>
                        .filter(image -> "yes".equals(image.getCover()) && ... )) 
  .collectList();                                       //returns Mono<List<Image>>

C'est OK d'utiliser collectList() ici, parce que vous voulez très probablement convertir images en un seul objet Mono<ServerResponse>:

return images.map(imageList -> ServerResponse.ok()
                                       .body(fromObject(imageList)));

Aussi, je recommande de lire référence Project Reactor à propos quand utiliser quel opérateur .


MODIFIER:

Donc, vous voulez un Mono<Map<String, Collection<Image>> À la place. Ma réponse dépendrait de la référence de la classe Image à la House. C'est toujours OK si ce n'est pas le cas, la réponse serait cependant plus verbeuse.

Je vais décrire la manière difficile, où Image ne contient pas l'ID House. Nous avons besoin de qc, qui relie la liste des images appartenant à une maison spécifique. Vous pouvez définir une classe à cet effet ou simplement utiliser reactor.util.function.Tuple2. Cela dit, nous pouvons simplement utiliser collectMap():

Mono<Map<String, Collection<Image>>> images = houseService.findAllHouses()
     .flatMap(house -> imageService.findByHouseId(id)
                           .filter(...)
                           .map(image -> Tuples.of(id, image)))
     .collectMultiMap(Tuple -> Tuple.getT1(), 
                      Tuple -> Tuple.getT2());

Bien sûr, si vous avez une référence au House, vous pouvez omettre à nouveau la map() et utiliser collectMultiMap directement:

...
    .collectMultiMap(image -> image.getHouseId(), image -> image);
8
MuratOzkan