Spring 响应式编程中如何从两个 Flux(Object) 获取 Flux 或 List?
How to get Flux or List from two Flux(Object) in Spring Reactive Programming?
我是 Spring Reactive 项目的新手。在我的 Spring 启动控制器 class 我有
Flux<House> ( list of all houses in the database) and Flux<Image>
来自服务层,其中给定 Flux<Image>
是给定房屋的图像,例如房屋列表,每个房屋都有自己的 collection 图像。这意味着我必须遍历 Flux<House>
来获取房屋的 id,然后在图像上调用 findByHouseId 来获取 Flux<Image>
,每个图像都有 houseId 属性。每个图像 object 都有一个带有字符串值 "yes" 或 "no" 的 属性 "cover" 和另一个具有各种字符串值的 属性 "type" "bedroom"。我要收藏一个
list of Flux<Image> like List<Flux<Image>> or List<List<Image>>
仅包含 "cover" == "yes" 或 "type" == "bedroom" 的图像。如何做到这一点?编程代码将回答这个问题。
下面是尝试作为解决方案的一部分,但即使这个部分解决方案也不起作用:
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));
请注意,我正在寻找不涉及调用 block() 的解决方案(否定响应式的好处),除非这是唯一的方法。
在 non-reactive 情况下,这就是我想要的:
List<House> houses --> List of all houses
Map<String,List<Image>> mapOfImages --> where key is houseId
所以对于每栋房子,我可以在视图模板中迭代房屋时轻松获取该房屋的图像。
这些(房屋和 mapOfImages)将被传递给模型
model.addAttribute("houses",houses);
model.addAttribute("mapOfImages",mapOfImages);
所以现在在 thyemleaf 视图模板中我可以:
<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>
正在使用的数据库是 Mongodb,它有两个独立的 collections:房子和图像,即没有嵌入的 collections 和唯一将图像绑定到房子是图片上的 houseId 属性。
您基本上想要将 House
个对象流转换为 Image
个对象流。
因此,使用 flatMap()
将 Flux<Flux<T>>
(或 Flux<Mono<T>>
)转换为 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>>
在这里使用 collectList()
是可以的,因为您很可能想将 images
转换为单个 Mono<ServerResponse>
对象:
return images.map(imageList -> ServerResponse.ok()
.body(fromObject(imageList)));
此外,我建议阅读 Project Reactor reference about when to use which operator。
编辑:
因此,您需要一个 Mono<Map<String, Collection<Image>>
。我的回答取决于 Image
class 是否引用了 House
。如果没有,那也没关系,不过答案会更冗长。
我将描述困难的方法,其中 Image
不包含 House
id。我们需要某物,它链接属于特定房屋的图像列表。您可以为此目的定义 class 或只使用 reactor.util.function.Tuple2
。有了这个,我们就可以使用 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());
当然,如果你有引用回House
,那么你可以再次省略map()
,直接使用collectMultiMap
:
...
.collectMultiMap(image -> image.getHouseId(), image -> image);
我是 Spring Reactive 项目的新手。在我的 Spring 启动控制器 class 我有
Flux<House> ( list of all houses in the database) and Flux<Image>
来自服务层,其中给定 Flux<Image>
是给定房屋的图像,例如房屋列表,每个房屋都有自己的 collection 图像。这意味着我必须遍历 Flux<House>
来获取房屋的 id,然后在图像上调用 findByHouseId 来获取 Flux<Image>
,每个图像都有 houseId 属性。每个图像 object 都有一个带有字符串值 "yes" 或 "no" 的 属性 "cover" 和另一个具有各种字符串值的 属性 "type" "bedroom"。我要收藏一个
list of Flux<Image> like List<Flux<Image>> or List<List<Image>>
仅包含 "cover" == "yes" 或 "type" == "bedroom" 的图像。如何做到这一点?编程代码将回答这个问题。
下面是尝试作为解决方案的一部分,但即使这个部分解决方案也不起作用:
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));
请注意,我正在寻找不涉及调用 block() 的解决方案(否定响应式的好处),除非这是唯一的方法。
在 non-reactive 情况下,这就是我想要的:
List<House> houses --> List of all houses
Map<String,List<Image>> mapOfImages --> where key is houseId
所以对于每栋房子,我可以在视图模板中迭代房屋时轻松获取该房屋的图像。
这些(房屋和 mapOfImages)将被传递给模型
model.addAttribute("houses",houses);
model.addAttribute("mapOfImages",mapOfImages);
所以现在在 thyemleaf 视图模板中我可以:
<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>
正在使用的数据库是 Mongodb,它有两个独立的 collections:房子和图像,即没有嵌入的 collections 和唯一将图像绑定到房子是图片上的 houseId 属性。
您基本上想要将 House
个对象流转换为 Image
个对象流。
因此,使用 flatMap()
将 Flux<Flux<T>>
(或 Flux<Mono<T>>
)转换为 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>>
在这里使用 collectList()
是可以的,因为您很可能想将 images
转换为单个 Mono<ServerResponse>
对象:
return images.map(imageList -> ServerResponse.ok()
.body(fromObject(imageList)));
此外,我建议阅读 Project Reactor reference about when to use which operator。
编辑:
因此,您需要一个 Mono<Map<String, Collection<Image>>
。我的回答取决于 Image
class 是否引用了 House
。如果没有,那也没关系,不过答案会更冗长。
我将描述困难的方法,其中 Image
不包含 House
id。我们需要某物,它链接属于特定房屋的图像列表。您可以为此目的定义 class 或只使用 reactor.util.function.Tuple2
。有了这个,我们就可以使用 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());
当然,如果你有引用回House
,那么你可以再次省略map()
,直接使用collectMultiMap
:
...
.collectMultiMap(image -> image.getHouseId(), image -> image);