-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support partial updates of a RealmObject #3500
Comments
Technically the object with the same primary key is updated to be the new object. |
Naming is one of the hardest tasks in implementing any API. As @Zhuinden points out, objects with primary key benefit from this method. Maybe you can take another look at your model classes, and see if they have fields which can be used as primary key. |
@kneth You misunderstand. My model classes have a primary key. I was simply surprised that To give some more context. Imagine the following REST API
The first one returns an array of Projects that contain an array of Places who are just specified by an ID Now, If you use Retrofit and deserialize this into Realm Models / POJOs and call copyToRealmOrUpdate(..) on the different results you get from the above endpoints, you get into trouble. You have no idea what state your Places are in. Some could be complete, some could be partial, because every time you store the results from Retrofit into Realm you overwrite whatever other version is there. I agree that naming is hard. You have two similar methods, one called |
The problem is that in a POJO you cannot tell the difference between a field not being set and the field being set to the default value. E.g
By looking at that you don't know if nobody set the field yet, or if it was actually set to With JSON it is possible to express that distinction by just removing the field completely. Note that when we support polymorphism ( #761 ) it would be possible to express it this way:
|
@cmelchior You could tell if it's the default value (using for example https://google.github.io/guava/releases/19.0/api/docs/com/google/common/base/Defaults.html) and allow us to skip the update for those fields as a behavior? GSON has a default rule about not serializing null fields. At the moment I'm considering serializing my POJOs back to JSON and calling createOrUpdateObjectFromJson(..) on them. However, that requires me to write a TypeAdapter for all my models (which is quite a few) because of this shortcoming https://realm.io/docs/java/latest/#serialization |
As a design guideline we try to stay as close to the Java language as possible, but I did not know about the Guava Defaults. I will take a look, thanks. Having a specific override or annotation that makes Realm ignore default field might be an idea as well, although just having a But for solutions right now, it is probably easier to just ask Retrofit to return the JSON from your server and use that directly: http://stackoverflow.com/a/31112405/1389357 |
@cmelchior Was that the correct reddit link? I get a comment that doesn't mention JSON nor Retrofit |
Ups, fixed |
@cmelchior I would also be very interested in a feature that doesn't overwrite the already existing values. Generally the following options come to mind (for the specific use case of my app):
|
@cmelchior Thanks for the updated link. Now, if I were to do as you suggest, and just ask Retrofit to return the JSON from the server and use that directly in a call to createOrUpdateObjectFromJson(..) then GSON is ignored right? You have your own internal parser for converting JSON into Realm Models I assume? So |
Yes, that is correct. |
After looking at Guava I don't think that is a solution, so to summarize: Use case It should be possible to easily update RealmObjects using both end points Current situation
Solutions on the way or with issues
Right now I'm leaning towards Inheritence probably being the simplest way of solving this problem as all the other solutions will add a lot of overhead to our API. Even if we add |
@cmelchior In the inheritance solution, we would have to implement two models: MyPartialFooModel and MyFullFooModel. And I assume the full one extends the partial one. Question 1: How would I query Realm to get any available FooModel, and how would it prefer the full one if it exists? I can't picture the query nor the return type. Is there an abstract Model as well? Question 2: Would this solve the use case (mentioned elsewhere) where local fields (not coming from REST API JSON) are overwritten when updating? I guess we could have a PartialRestFooModel, FullRestFooModel, and FullRestFooModelWithLocalValues. |
I agree with @snowpong, while inheritance is quite a powerful feature, the model classes would quickly get unwieldy if we need to adjust them for such use cases. Not to mention that it's not exactly intuitive (at least for me). Since I'm using JSON for the data sync, having a public class TestObject extends RealmObject {
@SerializedName("testObjectId") @PrimaryKey
long mId;
@SerializedRealmObjectId("relatedObjectId")
RelatedObject mObject;
// other variables
}
public RelatedObject extends RealmObject {
@SerializedName("relatedObjectId") @PrimaryKey
long mId;
// other variables
} This would allow me to properly process server output using the
Might still be worth though looking more into ignoring fields as an additional pattern that developers can use to cover other cases. Thanks for all the consideration and work 👍 |
Can default values (see #777) help? |
@kneth My current solution when using GSON / RetroFit and handling partial/full updates to Realm is:
You can see parts of that implementation here https://github.com/Turistforeningen/SjekkUT/blob/master/android/app/src/main/java/no/dnt/sjekkut/network/StorePlaceCallback.java#L20 and https://github.com/Turistforeningen/SjekkUT/blob/master/android/app/src/main/java/no/dnt/sjekkut/network/StoreProjectCallback.java#L20 It's not perfect. But at least I ensure that full versions will overwrite partial versions and that we don't constantly write to Realm unless it's new data. Regarding defaults in Realm: Since GSON already "supports" default values (GSON uses the default values set by you unless the JSON overwrites it) I'm not helped too much by it also working in Realm. My Realm Models are always created by GSON and then copied into Realm. |
@snowpong Thanks for the details. Please consider to document it in a broader forum (medium.com, etc.). |
I think this should be re-opened - the support for partial updates is still pretty essential for mobile apps. In our case, we have exactly the situation @cmelchior describes, where some of our endpoints return partial data about our objects. It seems like the right way has to offer some sort of merge. This is too common a use case to not have native support from Realm. |
@tmtrademarked there is still no way to reliably tell the difference between when an element is specifically set to |
Sure, that's fair, but there are possible solutions to this. Here's a strawman proposal:
This certainly doesn't solve every case - but it handles many, and would still be useful. If you want to be able to clear fields, you can use the existing insertOrUpdate. So merge would only be used for this kind of limited case. I understand the concerns around expanding the API - but this is a serious pain point when using Realm. It differs from the semantics expected from other database systems (partial updates are super common in SQLite systems, for example). Partial data models are increasingly popular in mobile development, especially in places that use json-api or GraphQL. So I really think Realm needs to support this kind of functionality. |
Since However, partial update is very useful (and almost necessary) and I think we should seek for the good API to achieve that. |
I am having the same issue.
|
@mhd-adeeb-masoud You could take a look at https://realm.io/docs/java/latest/#the-realm-mobile-platform :-) |
@mhd-adeeb-masoud with partial JSONs, that's pretty much the only way to do it. Otherwise you cannot differentiate between |
@Zhuinden I think so. But wouldn't it be more convenient if we have something like RealmObject.update(colomns,values) or maybe RealmObject.set(column,value) Rather than having to write the java setter for each individual property. |
@mhd-adeeb-masoud nobody stops you from using reflection to invoke your setter by property name. |
@mhd-adeeb-masoud I don't think you need Means if your object has a primary key, and the JSON object has the same primary key, when you call |
@beeender assuming the incoming JSON perfectly matches the json parser code that Realm's schema mediator has. |
@Zhuinden Uh ... yes ... non-matching field name is another topic :) |
@beeender thank you, that is exactly what I am trying to do... |
Goal
I want to update an object touching only the values that are actually set.
More specifically, we have a REST API that follows the expand fields
"design pattern", similar to Confluence here https://developer.atlassian.com/confdev/confluence-server-rest-api/expansions-in-the-rest-api . Basically it means that you can receive the same JSON object as a very terse object (maybe only an ID) or a fully expanded object (fields, like title, url , child objects etc.).
The returned objects are deserialized by Retrofit2 and GSON into POJOs that extends RealmObject, and we pass these to copyToRealmOrUpdate(..).
Expected Results
Existing objects are updated, preserving fields that are not set.
Actual Results
Existing objects are overwritten, fields are overwritten with null.
Comments
Now, I understand why after reading the fine-print in the documentation https://realm.io/docs/java/1.2.0/api/io/realm/Realm.html#copyToRealmOrUpdate-E-
but your method name suggest otherwise. This is not an update, that is a set(..) or overwrite(..).
Is there any scenario where you actually update using this method?
It's almost as if Retrofit (or any converting from JSON to POJO) is discouraged with the current behavior/method since the complementing method https://realm.io/docs/java/1.2.0/api/io/realm/Realm.html#createOrUpdateObjectFromJson-java.lang.Class-org.json.JSONObject- has the behavior I'm after. Would it not make sense to provide a similar functionality when "updating" with POJOs to?
The text was updated successfully, but these errors were encountered: