【问题标题】:Building a Recursive Data Structure with Spring WebFlux使用 Spring WebFlux 构建递归数据结构
【发布时间】:2023-09-09 06:14:01
【问题描述】:

我有一个使用 Spring WebFlux 框架构建的 REST API,我有一个返回 Flux<ChannelResponse> 的端点,其中 ChannelResponse 是一个树形结构的对象,如下所示:

public record ChannelResponse(
        long id,
        List<ChannelResponse> children
) {}

现在,我对反应式编程范式没有太多经验,但这就是我将如何使用同步逻辑实现这样一个端点,这样每个*通道(那些没有父通道的通道)都被转换为一个ChannelResponse 对象树:

public Flux<ChannelResponse> getAll() {
    return channelRepository.findAllByParentChannelIdOrderByOrdinality(null)
        .map(channel -> getChannelDataRecursive(channel));
}

private FullChannelResponse getChannelDataRecursive(Channel channel) {
    var children = channelRepository.findAllByParentChannelIdOrderByOrdinality(channel.getId())
            .collectList().block();
    List<ChannelResponse> childData = new ArrayList<>();
    for (var child : children) {
        childData.add(getChannelDataRecursive(child));
    }
    return new ChannelResponse(channel.getId(), childData);
}

显然这在 WebFlux 中不起作用,因为我正在尝试进行阻塞存储库调用。

有没有办法以异步方式生成这种递归数据结构?或者如果没有,我有什么选择可以混合同步和异步代码来实现这个结果?

【问题讨论】:

  • 看看expandexpandDeep 运算符。
  • @Ikatiforis,我看过expand,但似乎它用于遍历递归数据结构以产生一组平坦的结果。这不是我想要达到的目标。

标签: java recursion reactive-programming spring-webflux project-reactor


【解决方案1】:

我能够以一种有点奇怪的方式解决这个问题,即使用expandDeep 运算符生成所有频道的平面列表,排序后每个父级紧随其后的是他们的一组子级。然后我使用普通的同步递归方法将这些数据转换为所需的格式:

public Flux<ChannelResponse> getAll() {
    return channelRepository.findAllByParentChannelIdOrderByOrdinality(null)
            .expandDeep(channel -> channelRepository.findAllByParentChannelIdOrderByOrdinality(channel.getId()))
            .collectList()
            .flatMapMany(channels -> Flux.fromIterable(buildRecursiveChannelResponse(null, channels)));
}

public List<ChannelResponse> buildRecursiveChannelResponse(Long parent, List<Channel> channels) {
    List<ChannelResponse> responses = new ArrayList<>();
    while (!channels.isEmpty()) {
        Channel c = channels.get(0);
        if (!Objects.equals(c.getParentChannelId(), parent)) return responses;
        channels.remove(0);
        var children = buildRecursiveChannelResponse(c.getId(), channels);
        responses.add(new ChannelResponse(c.getId(), children));
    }
    return responses;
}

我觉得这个解决方案不是最优的,因为它需要非常具体地了解频道列表的排序方式才能生成树形结构。如果有更清洁的方法,请告诉我。

【讨论】: