无法返回Mono<Compliance>求助:如何从MongoDB查询的Mono<PortCall>中过滤Compliance列表并返回目标对象
问题分析与解决方案
首先,咱们来拆解你代码里的几个关键问题,然后一步步修正:
1. 核心问题:误用doOnNext和subscribe()
doOnNext是副作用操作符,它只会在流元素通过时执行一些额外逻辑(比如打印日志、更新外部状态),但不会改变流中的元素类型或内容。你原来的代码里,doOnNext里的过滤操作完全没影响到后续的流,流里还是原来的PortCall对象,而不是你想要的Compliance。- 直接在Service层调用
subscribe()是错误的:subscribe()会立即触发流的执行,返回的是Disposable(用于取消订阅),而不是你需要的Mono<Compliance>。响应式编程的核心是构建流的管道,而不是提前触发执行,订阅应该交给上层调用者(比如WebFlux控制器)来做。
2. 隐藏问题:参数顺序错误
你的Repository方法定义是:
@Query("{vesselCode : ?0, arrivalVoyageCode: ?1}") Mono<PortCall> findDeadlineTimestamp(String vesselCode, String arrivalVoyageCode);
但你调用的时候传的是findDeadlineTimestamp(arrivalVoyageCode, vesselCode),参数顺序完全反了!这会导致MongoDB查询时用arrivalVoyageCode匹配vesselCode字段,用vesselCode匹配arrivalVoyageCode字段,大概率查不到数据,这也是你可能拿不到结果的原因之一。
修正后的代码
下面是修复后的Service层代码,完美实现你的需求:
// 注意参数顺序:vesselCode在前,arrivalVoyageCode在后 Mono<Compliance> cmp = portCallRepository.findDeadlineTimestamp(vesselCode, arrivalVoyageCode) // 把PortCall转换成匹配的Compliance(或空Mono) .flatMap(portCall -> { // 过滤Compliance列表,找到第一个匹配compId的元素 Optional<Compliance> matchingCompliance = portCall.getCompliance().stream() .filter(c -> c.getId().equalsIgnoreCase(compId)) .findFirst(); // 将Optional转换为Mono:有值则返回Mono.just,无值则返回Mono.empty return Mono.justOrEmpty(matchingCompliance); });
代码解释:
flatMap:用于将流中的PortCall对象转换为另一个Mono<Compliance>,这是响应式编程中转换流类型的正确方式。Optional<Compliance>:用findFirst()获取过滤后的第一个元素,避免空指针问题。Mono.justOrEmpty:把Optional无缝转换成Mono,如果没有匹配的Compliance,返回的Mono会处于empty状态,上层调用者可以通过defaultIfEmpty等操作符处理这种情况。- 移除了
subscribe():Service层只返回Mono<Compliance>,让上层(比如控制器)来触发订阅,符合响应式编程的最佳实践。
可选的简洁写法
如果你喜欢更紧凑的代码,也可以写成这样:
Mono<Compliance> cmp = portCallRepository.findDeadlineTimestamp(vesselCode, arrivalVoyageCode) .map(portCall -> portCall.getCompliance().stream() .filter(c -> c.getId().equalsIgnoreCase(compId)) .findFirst()) .flatMap(Mono::justOrEmpty);
这样处理后,你就能得到正确的Mono<Compliance>实例了,如果没有匹配的Compliance,Mono会是空的,你可以根据业务需求用defaultIfEmpty(new Compliance(...))来提供默认值。
内容的提问来源于stack exchange,提问作者user2891537
相关产品推荐
相关产品推荐

