Realm-java: OrderedRealmCollectionChangeListener issue when a change causes insertion or removal

Created on 11 Apr 2017  ·  18Comments  ·  Source: realm/realm-java

Goal

Have RealmResults filtered by a field (isBoomarked, in my case), change value of the field to insert or remove objects.

Expected Results

Change isBookmarked value on a single object, and get a changeSet with a single deletionRange or insertionRange.

Actual Results

Sometimes a "random" changeRange is included in the changeSet.

Example

When isBookmarked is changed to false on single object, the changeSet usually comes with:

  • a deletionRange with the startIndex of the deleted object and length = 1 -> this is correct;
  • a changeRange with startIndex = 0 and length = 1 -> this seems like a bug, and it happens very often (not always). The removed object doesn't have startIndex = 0.

Code (Kotlin)

where(Item::class.java).equalTo("isBookmarked", true).findAllSortedAsync("id", Sort.DESCENDING)
realm.executeTransaction { realm ->
    item.isBookmarked = !item.isBookmarked
}

Version of Realm and tooling

Realm version(s): 3.1.1

T-Bug

All 18 comments

Is it possible you changed the first item in some other places?
Can you please help to debug this a bit like below:

  1. Register a RealmObjectChangeListener on the first object (since the startIndex = 0 in the buggy case)
results = where(Item::class.java).equalTo("isBookmarked", true).findAllSortedAsync("id", Sort.DESCENDING);
// The firstItem needs to have a strong ref (member var?) otherwise after it gets GCed, the listener will not work
firstItem = results.get(0);
firstItem.addChangeListener(myListener);
  1. Check if the RealmObjectChangeListener gets triggered when the error case happens
  2. if it gets triggered, please help to check the ObjectChangeSet to see if any fields actually gets changed by some other places.

Thanks!

@beeender I assume he would have to retain the reference to firstItem as field ref

Just tested it.

The RealmObjectChangeListener is not triggered even when the OrderedCollectionChangeSet comes with a changeRange with startIndex = 0 (it's usually the case).

However, when I specifically change the first item, the RealmObjectChangeListener triggers normally and the only changeField is isBookmarked.

Could the Item have duplicated id?

No, and id is @PrimaryKey.

Can you share the definition of class Item? Does it contain any other RealmObject or RealmList?

Is it possible that the lateinit open var snooze_item: Item for the index=0 in your query results it the one you changed isBookmarked?

No.
The issue happens very consistently with almost any item I change.
The only object in common is parentFeed, but that's common to all Items.

OK ... if you can share the source code with us, that would be the best. Otherwise can you share your apk with us? to [email protected]
and let us know how to reproduce it. thanks!

The issue is indeed related to parentFeed.
To reproduce the issue:

@jpmcosta Thanks a lot for the simple and clear demo project. I think it is very much like a bug.

First, to clarify the behaviour:

Yeah ... we should probably doc them both in both places...

So the right behaviour would be if you change one Item, the change range should include all other elements. It actually works if you click on the first item at the first time, it will log:

04-13 08:26:30.296 16325-16325/com.jpmcosta.test.realmtestproject I/ItemListAdapter: deletion: 0, 1
04-13 08:26:30.296 16325-16325/com.jpmcosta.test.realmtestproject I/ItemListAdapter: change: 0, 9

But if click on others, the change range will only include only one element.

To reproduce this in a realm test case:

    @Test
    @RunTestInLooperThread
    public void allParentObjectShouldBeInChangeSet() {
        Realm realm = looperThread.realm;

        realm.beginTransaction();
        Owner owner = realm.createObject(Owner.class);
        Dog dog1 = realm.createObject(Dog.class);
        dog1.setHasTail(true);
        dog1.setOwner(owner);
        owner.getDogs().add(dog1);
        Dog dog2 = realm.createObject(Dog.class);
        dog2.setOwner(owner);
        dog2.setHasTail(true);
        owner.getDogs().add(dog2);
        Dog dog3 = realm.createObject(Dog.class);
        dog3.setOwner(owner);
        dog3.setHasTail(true);
        owner.getDogs().add(dog3);

        realm.commitTransaction();

        RealmResults<Dog> dogs = realm.where(Dog.class).equalTo(Dog.FIELD_HAS_TAIL, true).findAll();
        looperThread.keepStrongReference.add(dogs);
        dogs.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Dog>>() {
            @Override
            public void onChange(RealmResults<Dog> collection, OrderedCollectionChangeSet changeSet) {
                assertEquals(1, changeSet.getDeletions().length);
                assertEquals(0, changeSet.getInsertions().length);

                //int[] changes = changeSet.getChanges();
                assertEquals(1, changeSet.getChangeRanges().length);
                assertEquals(0, changeSet.getChangeRanges()[0].startIndex);
                assertEquals(2, changeSet.getChangeRanges()[0].length);

                looperThread.testComplete();
            }
        });

        realm.beginTransaction();
        //dog1.setHasTail(false); This would pass
        dog3.setHasTail(false);
        realm.commitTransaction();
    }

Is this an object store bug? 😮

@Zhuinden very much like a bug in the object store :P

@beeender I have a follow-up question (I hope it's OK to ask here).
If an Item isBookmarked value is changed, should the Feed RealmChangeListener be triggered?
I would assume so, since the Item is included in the Feed RealmList<Item> items. However, that stopped working in 3.1.1.
I'm not sure if that's by design, and if I should be listening for changes on RealmList<Item> items instead. If you think it might be a bug, I can try to reproduce it in my test project and open a new issue.

@beeender also, thanks for the attention and explanation of the bug.

If an Item isBookmarked value is changed, should the Feed RealmChangeListener be triggered?

Do you mean the RealmChangeListener on the feed? In this case no, it won't be triggered. If the RealmObject has a RealmList field, the listener on the RealmObject will be triggered when you remove/add element to the RealmList, but it will NOT be triggered if any list's element's field changes.

I'm not sure if that's by design, and if I should be listening for changes on RealmList items instead.

But if the listener is on the field RealmList<Item> items, the listener should be triggered if any item in the items list isBookMarked change.

Let me know if it is till confusing you :)

That's perfect. Thanks for the explanation! 😄

Was this page helpful?
0 / 5 - 0 ratings