Realm-java: copyToRealmOrUpdate does not trigger findFirstAsync

Created on 27 Feb 2018  路  2Comments  路  Source: realm/realm-java

Goal

My view model is subscribing KeywordLocalDataSource#readCurrent. I expected that calling Realm#copyToRealmOrUpdate emits new(stored) object.

Expected Results

RealmQuery.findFirstAsync().asFlowable() will emit newly saved object (by Realm#copyToRealmOrUpdate)

Actual Results

No new value emitted.

Steps & Code to Reproduce

class KeywordDataRepository @Inject constructor() : KeywordRepository {

    @Inject
    lateinit var localDataSource: KeywordLocal

    @Inject
    lateinit var remoteDataSource: KeywordRemote

    override fun readCurrent(): Flowable<Keyword> {
        remoteDataSource
            .readCurrent()
            .subscribe(
                localDataSource::save,
                Throwable::printStackTrace
        )
        return localDataSource.readCurrent()
    }
}

class KeywordLocalDataSource @Inject constructor(
  val realmProvider: Provider<Realm>
) : KeywordLocal {

    override fun save(keyword: Keyword) {
        realmProvider.get()
            .executeTransactionAsync {
                val entity = KeywordMapper.toEntity(keyword)
                it.copyToRealmOrUpdate(entity)
            }
    }

    override fun readCurrent(): Flowable<Keyword> {
        val current = Calendar.getInstance().time
        return realmProvider.get()
            .where(KeywordEntity::class.java)
            .lessThanOrEqualTo("activatedAt", current)
            .sort("id", Sort.DESCENDING)
            .findFirstAsync()
            .asFlowable<KeywordEntity>()
            .filter(RealmObject::isValid)
            .map(KeywordMapper::fromEntity)
    }
}

KeywordDataRepository#readCurrent method returns KeywordLocalDataSource#readCurrent. I expected that after KeywordRemote#readCurrent succeeds, it will save new object to Realm database, then Realm emits new object. But no value emitted.

Version of Realm and tooling

Realm version(s): 4.3.3

Realm sync feature enabled: no

Android Studio version: 3.0.1

Which Android version and device: Nexus 5 API 27

Most helpful comment

It won't emit an object that won't exist, and won't continue looking for a new one if it doesn't yet exist either.

This behavior was changed in 3.1.0:

findFirstAsync() now returns an invalid object if there is no object matches the query condition instead of running the query repeatedly until it can find one (https://github.com/realm/realm-java/issues/4352).

The tricky thing is that until findFirstAsync returns some kind of RealmOptional<T>, if you ask me, it'll always be wonky 馃槃 I've never found a good use-case for it. None!

Anyways, you should just use

.where(KeywordEntity::class.java)
            .lessThanOrEqualTo("activatedAt", current)
            .sort("id", Sort.DESCENDING)
            .findAllAsync() // <--- !!
            .asFlowable<KeywordEntity>()
            .filter { results -> results.isValid() && results.isLoaded() && !results.isEmpty() } 
            .map { results -> results.first() }  // <-- !!!
            .map(KeywordMapper::fromEntity)

Or something like that.

All 2 comments

It won't emit an object that won't exist, and won't continue looking for a new one if it doesn't yet exist either.

This behavior was changed in 3.1.0:

findFirstAsync() now returns an invalid object if there is no object matches the query condition instead of running the query repeatedly until it can find one (https://github.com/realm/realm-java/issues/4352).

The tricky thing is that until findFirstAsync returns some kind of RealmOptional<T>, if you ask me, it'll always be wonky 馃槃 I've never found a good use-case for it. None!

Anyways, you should just use

.where(KeywordEntity::class.java)
            .lessThanOrEqualTo("activatedAt", current)
            .sort("id", Sort.DESCENDING)
            .findAllAsync() // <--- !!
            .asFlowable<KeywordEntity>()
            .filter { results -> results.isValid() && results.isLoaded() && !results.isEmpty() } 
            .map { results -> results.first() }  // <-- !!!
            .map(KeywordMapper::fromEntity)

Or something like that.

Thank you for your help!

My first approach was this,

Flowable.create({ emitter: FlowableEmitter<Keyword> ->
    realmProvider.get()
        .where(KeywordEntity::class.java)
        .lessThanOrEqualTo("activatedAt", current)
        .sort("id", Sort.DESCENDING)
        .findAllAsync()
        .addChangeListener { entities ->
            if (entities.size > 0)
                emitter.onNext(KeywordMapper.fromEntity(entities.first()!!))
        }
}, BackpressureStrategy.LATEST)

but seems your solution is more pretty :)

Was this page helpful?
0 / 5 - 0 ratings