1

I have two models -

class Direction : RealmObject {
    var distance : Int
    var polyline : String
}

class Route  : RealmObject {
    var id : String
    var directionList : RealmList<Direction>
}

I have been using insertOrUpdate() to update Route class with the assumption that when I call it, the existing direction objects in directionList were removed and replaced with the new list I provided. However, I recently discovered that that is not happening. Even cascade delete is not supported when I call route.deleteFromRealm(). So now I've ended up with hundreds of objects in Direction table with no objects referring to them.

How can I remove all those objects from Direction class which have no Route objects referring to them in Realm migration?

vepzfe
  • 4,217
  • 5
  • 26
  • 46

3 Answers3

2

Two possible ways I can think of.

The first may not help you at this time, but it can in future perhaps. By adding a LinkingObjects property to the Direction class, you can let the model determine which Direction objects have no related Route objects. LinkingObjects is described here (https://realm.io/docs/java/5.8.0/api/io/realm/annotations/LinkingObjects.html). With a property on Direction: E.g.:

\@LinkingObjects("direction")
final RealmResults<Route> routes = null;

Then you can delete any objects where:

RealmResults<Direction> unusedDirections = realm.where(Direction.class).isEmpty("routes").findAll();

You may need to do this for your next release though.

A second way is more long winded, but essentially:

  1. Find all Direction objects: RealmResults<Direction> redundantDirections = realm.where(Direction.class).findAll();
  2. Find all Route objects (similar to above).
  3. Iterate through all Route objects.
  4. Filter the redundantDirections query to exclude any Direction objects referenced by each Route object.
  5. Delete the final redundantDirections.

I'd hope there's a third way I'm unaware of.....

Chris Shaw
  • 1,610
  • 2
  • 10
  • 14
  • Hey @chris-shaw, Thanks for your answer. I have a follow up doubt. Is there a way to add these linking objects in the migration itself? If there is, maybe I can add that first and then remove the ones which are not linked. However, apart from this usecase, I don't have any other need to the linkingObjects and seems redundant for my use case. I solved my issue in a someway crude but working manner, a little similar to how you've mentioned in your 2nd way. – vepzfe Mar 19 '19 at 09:56
  • @abhiank Not that I know of. You can try asking another question specifically about this in case someone knows. – Chris Shaw Mar 19 '19 at 22:51
  • `DynamicRealmObject` actually has a `linkingObjects` method, which was a surprise to me, too. – EpicPandaForce Mar 28 '19 at 14:21
1

This is the way I solved it -

override fun migrate(realm: DynamicRealm, oldVersion1: Long, newVersion: Long) {

    if (oldVersion == 2L) {

        val routeSchema = schema.get("Route")
        val directionSchema = schema.get("Direction")

        /*
        Creating a new temp field called isLinked which is set to true for those which are
        references by Route objects. Rest of them are set to false. Then removing all
        those which are false and hence duplicate and unnecessary. Then removing the temp field
        isLinked
         */
        directionSchema!!
                .addField("isLinked", Boolean::class.java)
                .transform { obj ->
                    //Setting to false for all by default
                    obj.set("isLinked", false)
                }

        routeSchema!!.transform { obj ->
            obj.getList("directionList").forEach {
                //Setting to true for those which are referenced
                it.set("isLinked", true)
            }
        }

        //Removing all those which are set as false
        realm.where("Direction")
                .equalTo("isLinked", false)
                .findAll()?.deleteAllFromRealm()

        directionSchema.removeField("isLinked")

        //Rest of the migration
    }
}

There are some more things I discovered. According to this very informative talk on Realm - https://academy.realm.io/posts/jp-simard-realm-core-database-engine/ (skip to 28:45), there is way to remove all those unreferenced nodes from the B-Tree. However, I could not find a way to do that. Realm.compactRealm() seemed like a way to do that but it did not work.

vepzfe
  • 4,217
  • 5
  • 26
  • 46
0

How can I remove all those objects from Direction class which have no Route objects referring to them in Realm migration?

Should be easy using DynamicRealmObject.linkingObjects.

val routes = realm.where("Destination").findAll()
routes.createSnapshot().forEach { route ->
    val linkingObjects = route.linkingObjects("Route", "directionList")
    if(linkingObjects.isEmpty()) {
        route.deleteFromRealm()
    }
}

This should theoretically work

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428