realm-swift
realm-swift copied to clipboard
Add API for creating embedded objects during migration
Problem
When migrating from Object to Embedded object you need to take care of handling that each embedded object can have one and exactly one backlinks. To do this you must
1: Ensure that all orphaned objects are deleted. 2: Ensure that if an object has multiple parents. This can be done by either removing the relations to all backlinks but one. But you should also be able to copy the data to a new embedded object during migration to prevent data loss.
In the Java SDK this can be achieved by calling DynamicRealm.createEmbeddedObject, but In the Swift SDK there doesn't seem to be an API for creating embedded objects during migration.
I have tried the following approaches to create embedded objects during a swift migration. 1: Create an embedded object using the DynamicObject(value: objectWithPropertiesIWantToCopy) then adding it to an array. This doesn't work since you can't add unmanaged objects to a list during migration and it will throw an error.
2: Use the migration.createObject to create an embedded object. This will throw an error that the new object is embedded and cannot be created directly.
In my case I don't want any data loss, which is why my initial thought when an object has multiple backlinks would be to create a copy of the object and replace the original object, but this doesn't seem to be possible in the Swift API. Or is there a way?
Solution
To match the functionality of the Java SDK it would make sense to expose a Migration.createEmbeddedObject function
How important is this improvement for you?
Dealbreaker
The intended API here is that you just assign an unmanaged object of the actual object type (not DynamicObject). This works correctly for object properties, but not for collections due to the Realm being in an odd state during the migration. The schema says that the target type is embedded but the underlying table isn't actually converted to embedded until after the migration block. One step of the code for adding an object to a collection is looking at the table while another is looking at the schema, and this mismatch results in it taking the wrong path.
@tgoyne So if I understand you correctly, this is currently not possible to do? When you say that the intended API is to just assign an unmanagedObject of the actual object type, how is this supposed to work? The migration only exposes dynamic objects and dynamic lists, so how am I supposed to be able to append an unmanaged actual object?
I assume that the API is to use setValue(myRealmObjectToEmbed, forKey:"theProperty"), but this won't work for lists as the append and replace expects DynamicObject instances.
An unmanaged object is an object created via the normal initializer. If you have @Persisted var parent: Person? you can do newObject["parent"] = Person(value: ...) for both embedded and top-level objects. The bug is that unsafeBitcast(newObject["children"], to: List<Person>.self).append(Person(value: ...)) only works for top-level objects and not embedded objects, but it should work for both.
Ok, the API isn't super clear to me. Is this mentioned anywhere in the docs? I had to read it several times until I figured out how it was supposed to work. The unsafeBitcast bit does make me feel.... a bit unsafe
EDIT: No I still don't get it. Why would I want to create a new parent when it is the child that has multiple parents? I want to make a clone of the child so one of the parents can get the clone instead of them sharing the same child.
That is correct, you would want to duplicate the child, since this is the EmbeddedObject with multiple backlinks. And as Thomas said, the API should work for both top-level and embedded objects. The latter does not work though which is why this issue is flagged as a bug and needs fixing. 👍
Thanks for the confirmation @DominicFrei and the info @tgoyne. I am done with the migration on the Android side but will put it on hold on iOS until this is resolved.
Hi @sipersso I'm working on this, trying to solve this bug, I'll link any pull request to this issue when I'll do the fix.
Any updates on this @dianaafanador3? Did you find anything out?
Hi @sipersso I found the source of the issue, and I'll have a fix after I finish some other task on my list. I'll add any update to this issue.
@dianaafanador3 awesome news! Thank you so much for looking into this :)
@dianaafanador3 any updates on this issue?
@tgoyne do you know if the bug something that is being worked on or will be worked on in the near future? Is it tracked elsewhere as well? If this isn't something that will be fixed in the near future I would have to prioritize other work (and delay getting the app on MongoDb Realm Sync)
Hey @sipersso I have a couple of weeks of Stabilization from monday, so I'll try to get back to this. Sorry is taking so mucho time to get to this.
@dianaafanador3 thank you so much for getting back to me! Super happy to hear that this is still in the pipeline.
@dianaafanador3 is this still in the pipeline?
Hi @sipersso I have a branch for this which needs clean up, I'll push it next week and tag this issue.
Good news! Thank you @dianaafanador3
I just ran into the same issue migrating from Object to EmbeddedObject. I'm glad to hear about the upcoming improvement. Thanks!
Any updates on this issue?
Also, it would be great to clarify what the API actually will look like. I know that there is a bug preventing this from being possible, but it is also very unclear how to create embedded objects during migration. @tgoyne wrote something about the intended API, but I am afraid I did not understand how this is supposed to work.
@dianaafanador3 when you fix this, would it be possible to provide a sample for how you are supposed to be able to create embedded objects during migration?
Hey @sipersso, this branch will include the bug fix for this. I'm sorry I haven't been able to upload this but I'm only able to work on this during support weeks and I haven't been able to add tests and clean up this branch. Also I'll take a closer look to the Swift API while working on this.
I reported this issue 6 months ago. Before then I had struggled with migrating my database list structure from using relations to embedded for a long time. I commented on the issue of creating a helper function for embedded objects in February 2021 https://github.com/realm/realm-swift/issues/7145. I reported a bug in May 2021 that prevented the migration to embedded https://github.com/realm/realm-swift/issues/7248. So I have essentially been blocked by this for over a year now.
When you are saying that you can only work on this during support weeks, it sounds like this is a very low priority issue and I am starting to lose hope that it will ever get fixed. I have already done the work on Android and the same migration works without issues there. On iOS, there is this bug, but there is also no clear way of achieving it with the current API.
Is there a timeframe for when you think this might be fixed or do I need to spend time figuring out if there is an alternative solution I have to go for?
Any updates on this @dianaafanador3?
This will be addressed by https://github.com/realm/realm-core/pull/5737
Hi @sipersso we recently added support for automatic handlings for backlinks on migration https://github.com/realm/realm-core/pull/5737, which solves the initial issue on this issue. Please let us know if this helps you or if something else is blocking you.
Thanks for letting me know! As far as the migration, this is all I need :)