realm-auto-migration icon indicating copy to clipboard operation
realm-auto-migration copied to clipboard

Cannot migrate when the field type of a field is changed

Open sabid-habib opened this issue 6 years ago • 2 comments

I have a CatRealm with a boolean field isPet.

Class CatRealm extends RealmObject{
    boolean isPet;
}

Now, I have updated the type of isPet to Long type.

Class CatRealm extends RealmObject{
    Long isPet;
}

but the AutoMigration class is not detecting the type change and app crashes due to migration exception. How can I detect the type change on AutoMigration and execute the migration perfectly?

sabid-habib avatar Sep 11 '18 07:09 sabid-habib

You're right, I don't seem to handle that, just adding/removing fields and classes, and changing attributes (index, nullable, pk).

I think the reasoning was that if you change the type of the field, the only thing AutoMigration could do automatically is remove the field and re-add the field, but you'd lose the previous values of the data.

Imagine that you have a schema with boolean isPet and you want to transform these values to 0 and 1 for whatever reason. I was trying to figure out a good way for that with AutoMigration but it just doesn't fit in there anywhere.

So I assumed the reasonable solution is to do the

    schema.addField("isPet_temp", Long.class)
                 .transform((obj) -> { obj.setLong("isPet_temp", obj.getBoolean("isPet") ? 1 : 0) })
                 .removeField("isPet")
                 .renameField("isPet_temp", "isPet");

dance before running auto-migration.

(on the other hand, I think I also didn't consider making this kind of auto-behavior even added as optional, because I forgot about it. whoops)

Zhuinden avatar Sep 11 '18 08:09 Zhuinden

To make this more easy, i created this method:

private void changeFieldDataType(DynamicRealm realm, Class<?> newDataType, String entity, final String field, final Command changeFieldDataType) {
        final String tempField = TEMP_PREFIX+field;
        RealmObjectSchema schema = realm.getSchema().get(entity);
        if (schema != null) {
            schema.addField(tempField, newDataType);
            schema.transform(new RealmObjectSchema.Function() {
                @Override
                public void apply(@NonNull DynamicRealmObject obj) {
                    changeFieldDataType.execute(obj, tempField, field);
                }
            })
            .removeField(field)
            .renameField(tempField, field);
        }
    }

Command is an interface that help us to implement Command Pattern, this way we could create little data type conversions to pass to changeFieldDataType() , like this:

public class DoubleStringCommand implements Command {
    @Override
    public void execute(DynamicRealmObject object, String tempField, String field) {
        object.setString(tempField, String.valueOf((object.getDouble(field))));
    }
}

I think this approach could be improved a little bit to be more friendly if "AutoMigration" turns into a library and have a bunch of commands inside.

robsonbbs avatar Feb 05 '19 12:02 robsonbbs