FrangSierra/RxFirebase

RxFirestore getDocument() exists

Junderwood93 opened this issue · 5 comments

The RXFirestore getDocument() method currently returns a Maybe, but onSuccess is only called if the document exists.

I need this document to be returned regardless of whether it exists or not, as if it doesn't I need to create the document, but if it does I need to update it.

Hope this makes sense?

Hello @Junderwood93 .

if you want to do that. I recommend you to use a transaction. In that way you can check if the document exist and then do an operation based on that decision in an atomic way.
In other hand, you could also create the document if the Maybe returns an onComplete call and update it if the onSuccess is called.

Both ways should resolve your problem. But I think that you should go for a transaction.

Thanks for getting back to me!

I can't use transactions because they fail when offline.

On your other suggestion, I did consider this but couldn't figure out how to check if onComplete was called without onSuccess being called in the middle of an RX chain.

I guess what I'm trying to build is something that works like this, but without having to use transactions:

getDoc(ref)
.onEmptyReturn( () -> setDoc(ref, docData)
.andThen(getDoc(ref))
.map( doc -> {
..
}

But as far as I can tell, onEmptyReturn isn't a thing.

Just onSuccess or onComplete will be called. Never both of them. the library follows the MaybeObserver contract:

After a MaybeObserver calls a Maybe's subscribe method, first the Maybe calls onSubscribe(Disposable) with a Disposable that allows cancelling the sequence at any time, then the Maybe calls only one of the MaybeObserver's onSuccess(T), onError(java.lang.Throwable) or onComplete() methods to provide notifications.

You can read more about it here:
http://reactivex.io/RxJava/javadoc/io/reactivex/MaybeObserver.html
If you want to use it on a RxChain. I suggest you to wrap it using a toSingle(defaultValue)
For example:

getDoc(ref)
.map( snapshot -> snapshot.exists())
.toSingle(false) //By default it will be false because the map will just happend if the snapshot exists
.subscribe()

Anyway, seeing your implementation. You don't need to do a set and later a get. If you can do a set it means that you have the data that you want to retrieve from the database already. So there is no need to go for that andThen. You are doing an extra call without no reason.

I would do something like:

  RxFirestore.getDocument(ref)
            .map { it.toObject(MyClass::class.java) } 
            .subscribeOn(Schedulers.io())
            .subscribe(
                { myData -> doSomethingWithMyData() },
                { error -> manageError(error) }, {
                {
                    updateDataAsync(defaultDocValue) //this will do the setDoc
                    doSomethingWithMyData(defaultDocValue)
                }
            })

You can also use doOnComplete to set the document and doOnSuccess to update it.

   RxFirestore.getDocument(ref, MyClass::class.java)
            .doOnComplete { setDoc() }
            .doOnSuccess { updateDoc() }
            .subscribe({ data ->
                doSomethingWithMyData(data)
            }, { error ->
                manageError(error)
            }, {
                doSomethingWithMyData(defaultDocValue)
            })

This is great, thanks so much for taking the time to explain it! RX is still pretty new to me.

You welcome! Feel free to ask whatever you need !