objectbox-java icon indicating copy to clipboard operation
objectbox-java copied to clipboard

Only re-deliver query results if results have changed

Open AngryGami opened this issue 4 years ago • 5 comments

Code example probably will be best description here.

@Entity
class KAEntry(
    @Unique val key: String,
    val value: String,
    @Id var id: Long = 0
)

class MyApp : Application() { 
    private lateinit var subs1: DataSubscription
    private lateinit var subs2: DataSubscription
    private lateinit var boxStore: BoxStore
        private set
   
    override fun onCreate() {
        super.onCreate()
        boxStore = MyObjectBox.builder().name("main").androidContext(this).build()
        val box = boxStore.boxFor(KAEntry::class.java)
        box.removeAll() // just for reproducibility, to avoid "unique constraint violation" on second run of app

        subs1 = box.query().equal(KAEntry_.key, "key1")
            .build().subscribe().observer { v ->
                Log.i("MyApp", "triggered for key1" )
            }

        subs2 = box.query().equal(KAEntry_.key, "key2")
            .build().subscribe().observer { v ->
                Log.i("MyApp", "triggered for key2" )
            }

        box.put(KAEntry("key1","some value"))
    }
}

Problem is that even I've only put "key1" object and therefore only first query should trigger it's observer - both actually get triggered:

2021-04-05 14:22:53.263 25926-25962/com.acme I/MyApp: triggered for key1
2021-04-05 14:22:53.263 25926-25963/com.acme I/MyApp: triggered for key2

Either I don't understand something or this observer on query actually bugged. I can see that triggering observer for only those objects that are changed and are in query result may have some performance impact. Though even in this case I would like to have some way of detecting that change was made to objects that query returns and not any object of this type.

AngryGami avatar Apr 05 '21 12:04 AngryGami

Documentation is slightly vague about this aspect of functionality:

query observers will automatically deliver fresh results whenever changes are made to entities in a box

This doesn't explicitly tell that entities which were result of the query were changed. From looking inside source code I see that query subscribes to entity box and will run every time any changes happen with this box. There is no way to figure out if transaction was actually touching entities query returns or not since native code returns only list of entity types that were affected:

int[] entityTypeIdsAffected = nativeCommit(transaction);

So, this is not a bug, but rather a feature request. I don't know how to change tag though.

AngryGami avatar Apr 09 '21 07:04 AngryGami

Yes, this is the current behavior that if anything in a Box is changed any query for that box will re-deliver results.

This is typical database behavior, e.g. also for Room. Not sure if we want or even can change this.

greenrobot-team avatar Apr 12 '21 09:04 greenrobot-team

Well, some databases do that (e.g. Parse DB LiveQuery function promises that). I understand how impactful that can be on the performance, though since I can't see native code implementation it is really a guess. I see several ways to solve this and least problematic one might be to pass around some monotonically increasing transaction ID that observer will see and can compare with every entity of the query result:

subs1 = box.query().equal(KAEntry_.key, "key1")
            .build().subscribe().observer { v, transactionId ->
                v.filter { myentity ->
                     box.getLastTransactionIdForEntity(myentity.id) >= transactionId
                }
            }

This way I have control if I want to pay performance price for only reacting to changes of query results. I can store amount of objects returned in previous attempts if I want address inserts/deletes as well.

AngryGami avatar Apr 12 '21 11:04 AngryGami

Focusing on your use case: what prevents your code from checking if changes were made? E.g. keep the previous value to compare against?

greenrobot-team avatar Apr 12 '21 11:04 greenrobot-team

Nothing :) except that usecase that I've put as an example above is oversimplified :) I usually do not query by unique key (or at least not by single value of unique key) and therefore storing/comparing with previous value become less feasible. Also storing this data is potential race condition issue that I don't want to introduce.

AngryGami avatar Apr 12 '21 11:04 AngryGami