rxjava2 - if else on Maybe

Viewed 5368

I am looking for what is the recommended practice in rxjava2 to handle a case where one flowable leads to conditional behaviors.

More concretely, I have a Maybe<String> for which I want to Update the String on the database if the String exists or, if it doesn't exists I want to create a new String and save it on the database.

I thought of the below but obviously it is not what I am looking for:

Maybe<String> source = Maybe.just(new String("foo")); //oversimplified source
source.switchIfEmpty(Maybe.just(new String("bar"))).subscribe(result -> 
System.out.println("save to database "+result));
source.subscribe(result -> System.out.println("update result "+result));

The above obviously produces

save to database foo
update result foo

I tried also the below which gives the expected result but still feel it's... weird.

Maybe<String> source = Maybe.just(new String("foo")); //oversimplified source
source.switchIfEmpty(Maybe.just(new String("bar")).doOnSuccess(result -> 
System.out.println("save to database "+result))).subscribe();
source.doOnSuccess(result -> System.out.println("update result "+result)).subscribe();

How can I have an action for when the result exists and when it doesn't exists? How is that use case supposed to be handled in rxjava2?

Update 01

I tried the below and it looks cleaner than what I came up with above. Note sure it is recommended rxjava2 practice however...

Maybe.just(new String("foo"))
     .map(value -> Optional.of(value))
     .defaultIfEmpty(Optional.empty())
     .subscribe(result -> {
         if(result.isPresent()) {
             System.out.println("update result "+result);
         }
         else {
             System.out.println("save to database "+"bar");
         }
     });
4 Answers

This is a common pattern in our code as well, though in our case the choices are themselves async. You can't get quite the right semantic by simply composing flatMapX and switchIfEmpty (in either order), so I am curious why this isn't part of the API.

Here's what we're doing for now (this example for when the 2 options are both Completables, we have similar things for the other types as well):

public static <T> Completable flatMapCompletable(Maybe<T> target, 
                                                 @ClosureParams(FirstParam.FirstGenericType.class) 
                                                         Closure<? extends CompletableSource> completableSupplier, 
                                                 Supplier<CompletableSource> emptySupplier) {
    Maybe<T> result = target.cache();
    return result.isEmpty().flatMapCompletable(empty -> {
       if (empty) {
           return emptySupplier.get();
       } else {
           return result.flatMapCompletable(completableSupplier::call);
       }
    });
}

We're using Groovy, so we package these up as extension methods. I'm not thrilled with the need to use cache() so I'm wondering if there is a better alternative. From looking at the code, an operator which basically combines flatMapX and switch looks like it wouldn't be too hard (but I feel like I'm missing something).

Try something like this. checkDB can return a Maybe or Single or whatever which emits either an optional or a wrapper Object.

checkDB(String)
            .flatMap(s -> {
                if (s.isPresent()) {
                    return updateDB(s.get());
                } else {
                    return insertDB("new String");
                }
            })

There is an solution using the flatMap call with 3 params

fun addOrUpdate(message: LocalMessage): Single<LocalMessage> {
        return getById(message.id) // returns Maybe
            .flatMap(
                Function {
                    update(message) // onSuccess update call returns Single
                },
                Function {
                    Single.error(it) // onError
                },
                Callable {
                    add(message) // onComplete add call returns Single
                }
            )
    }
}

Or shorter version

fun addOrUpdate(message: LocalMessage): Single<LocalMessage> {
        return getById(message.id) // returns Maybe
            .flatMap(
                {
                    update(message) // onSuccess update call returns Single
                },
                {
                    Single.error(it) // onError
                },
                {
                    add(message) // onComplete add call returns Single
                }
            )
    }
}
Related