spring-webfluxreactive-programmingproject-reactor

Why is switchIfEmpty not being executed?


In the below code logic switchIfEmpty is not getting executed. As per below code logic I am expecting lowerCase names when filter(__ -> names.size() > 5) evaluates to false and upperCase names when filter(__ -> names.size() > 3) evaluates to true. It is working as expected if filter evaluates true but it is not working when filter evaluates to false. Why is that?

List<String> names = Arrays.asList("google", "abc", "fb", "stackoverflow");

Flux.fromIterable(names)
                .filter(name -> name.length() > 5)
                .collectList()
                .flatMap(commonAppliedFilters -> Mono.just(commonAppliedFilters)
                    .filter(__ -> names.size() > 5)
                    .flatMapIterable(list -> list)
                    .map(String::toUpperCase)
                    .collectList()
                    .switchIfEmpty(Mono.defer(() -> Mono.just(commonAppliedFilters))))
                .subscribe(System.out::println);

Solution

  • What happened is that the collectList returned a Mono with an empty list, but not a Mono.empty, so we have:

    ...
    .collectList()
    .filter(list -> !list.isEmpty())
    

    or

    ...
    .collectList()
    .flatMap(list -> !list.isEmpty() ? Mono.just(list) : Mono.empty())
    

    With this we successfully force a Mono.empty() that will trigger your switchIfEmpty().

    I show you an example testing with StepVerifier.

    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-test</artifactId>
        <scope>test</scope>
        <version>3.7.0</version>
    </dependency>
    

    You can decomment the flatMap to see how it works as well.

    class ReactiveTest {
    
        @Test
        @DisplayName("expecting lowerCase names when filter(__ -> names.size() > 5)")
        void expectingLowerCase() {
            List<String> names = Arrays.asList("google", "abc", "fb", "stackoverflow");
    
            StepVerifier.create(Flux.fromIterable(names)
                            .filter(name -> name.length() > 5)
                            .collectList()
                            .flatMap(commonAppliedFilters ->
                                    Mono.just(commonAppliedFilters)
                                            .filter(__ -> names.size() > 5)
                                            .flatMapIterable(list -> list)
                                            .map(String::toUpperCase)
                                            .collectList()
                                            //.flatMap(list -> !list.isEmpty() ? Mono.just(list) : Mono.empty())
                                            .filter(list -> !list.isEmpty())
                                            // If the list is empty, a Mono.empty() is returned which will force switchIfEmpty
                                            .switchIfEmpty(Mono.defer(() -> Mono.just(commonAppliedFilters)))
                            ))
                    .expectNext(List.of("google", "stackoverflow"))
                    .verifyComplete();
        }
    
        @Test
        @DisplayName("expecting upperCase names when filter(__ -> names.size() > 3) ")
        void expectingUpperCase() {
            List<String> names = Arrays.asList("google", "abc", "fb", "stackoverflow");
    
            StepVerifier.create(Flux.fromIterable(names)
                            .filter(name -> name.length() > 5)
                            .collectList()
                            .flatMap(commonAppliedFilters ->
                                    Mono.just(commonAppliedFilters)
                                            .filter(__ -> names.size() > 3)
                                            .flatMapIterable(list -> list)
                                            .map(String::toUpperCase)
                                            .collectList()
    //                                        .flatMap(list -> !list.isEmpty() ? Mono.just(list) : Mono.empty())
                                            .filter(list -> !list.isEmpty())
                                            .switchIfEmpty(Mono.defer(() -> Mono.just(commonAppliedFilters)))
                            ))
                    .expectNext(List.of("GOOGLE", "STACKOVERFLOW"))
                    .verifyComplete();
        }
    
    }
    

    enter image description here