-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
WIP: Model using jackson #8810
WIP: Model using jackson #8810
Conversation
a proof of concept PR to show how Jackson can be used to parse Model json. Things that are highlited in this PR:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enum like classes (RequirementType, ModelType) that do save unknown values
What do you mean by "unknown values"?
You can read more about requirement on https://github.com/Arthur-Milchior/anki/blob/commented/documentation/templates_generation_rules.md
I think it is not used anymore and will probably disappear with rust
public static RequirementType[] values = new RequirementType[] {NONE, ALL, ANY}; | ||
|
||
@JsonCreator | ||
public static RequirementType createFromValue(@Nullable String value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Nullable RequirementType
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used @Contract("null -> null; !null -> !null")
} | ||
|
||
|
||
private final String mValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NonNull String
} | ||
RequirementType that = (RequirementType) o; | ||
return mUnknown == that.mUnknown && | ||
Objects.equals(mValue, that.mValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can probably uses mValue.equals(that.mValue)
since you know that mValue
can't be null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, that was the auto-generated code from the IDE.
@JsonProperty("ord") | ||
private long mOrd; | ||
private Long mOrd; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What the point of "move to object instead of primitives"
If we decide to go with Object, then this could be directly in the first commit I assume.
How did you test that it has the same performance?
Creating a Long object takes time. On long operation such as searching for empty card or cleaning collection, it's actually noticeable in the profiler.
You may be perfectly right, since all sqlite queries only take objects and no primitive, so we always convert long
to Long
when we need to send a query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a benchmark between one implementation with primitives and the other with objects and they both are pretty much the same.
The benefit that we will have with objects is being able to set them to null, and Jackson will not include any null values to the outputted json.
while primitives can't be null.
If the idea of null is not very okay we can use Optional<T>
for every field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
about the commits I will clean them once I make everything ready
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, last comment was sent too early.
Thanks a lot for attacking this problem. I really appreciate it.
Did you try to use it really by replacing the models from libanki by your models?
I'm of two of mind.
On the one hand, it's extremely verbose. I don't like it. It make it hard to review because a single letter typo would not be easy to catch (that would be one good point of using static String instead of literals as I wanted to, but I digress). It also seems to add to the code complexity, while in fact almost everything is really simple here.
On the other hand, having getter/setter instead of accessing elements by String will be far more efficient, as there is not hashtable involved. I really really love it. Similarly, not having to deal with transforming checked exception into unchecked one will clearly be a great win!
|
||
import androidx.annotation.Nullable; | ||
|
||
public class ModelType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably would want to reuse com/ichi2/libanki/Model.java since it also represents a model
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this is the goal, but replacing the old model with the new one will result in many refactorings. so I'm saving it to the last step
@Arthur-Milchior the goal is to not miss with the data if we don't know how to handle it. for example if have an enum defined in AnkiDroid with tbh my knowledge of how anki handles the low-level details is not good, I will try to read the attached link |
You really don't have to read this link. It's probably deprecated and only used on old version of anki. I understand better what you mean about "unknown", thanks. Nice to see you allow to accept any kind of value later! |
Yes it's very verbose, the idea behind adding annotation to every field is to make the field name independent of the json name. and also becase the nature of java being verbose if we go with kotlin we would only have annotated constructor that's it. we will get setter/getter copy (clone) method equals/hash code all for free data class Model(
@JsonProperty("id")
var id: Long?,
@JsonProperty("name")
var name: String?,
@JsonProperty("type")
var type: ModelType?,
@JsonProperty("mod")
var mod: Long?,
@JsonProperty("usn")
var usn: Long?,
@JsonProperty("sortf")
var sortf: Long?,
@JsonProperty("did")
var did: Long?,
@JsonProperty("tmpls")
var tmpls: List<Template>?,
@JsonProperty("flds")
var flds: List<Field>?,
@JsonProperty("css")
var css: String?,
@JsonProperty("latexPre")
var latexPre: String?,
@JsonProperty("latexPost")
var latexPost: String?,
@JsonProperty("latexsvg")
var latexsvg: Boolean?,
@JsonProperty("req")
var req: List<Requirement>?,
@JsonProperty("tags")
var tags: List<Any>?,
@JsonProperty("vers")
var vers: List<Any>?,
@JsonIgnore
@get:JsonAnyGetter
val mAdditionalProperties: MutableMap<String, Any> = HashMap()
) {
@JsonAnySetter
fun setAdditionalProperty(name: String, value: Any) {
mAdditionalProperties[name] = value
}
} and we can use static constants inside annotations. |
fixup! Create new Model class using jackson
I have made the code less verbose, we don't need to annotate every setter and getter, annotating the field is enough I think. |
The latest Rust code exposes the various Anki objects like notetypes as typed protobuf messages, and notetypes are stored in the database as protobuf internally. While JSON-bound classes are a nice ergonomic improvement, given the changes coming with backend updates, I'm not sure it'd be worth the work involved to use them at the moment. Presumably you can use Jackson with the existing string-key API to get performance gains without requiring so many code changes? |
Thank you, Having the Anki objects saves as binary will be very nice since some JSON fields are more than 30 MBs. hopefully this will speed things I will try to use jackson treemodel to replace the current json setup. I noticed that much of the code in are depended on |
Hello 👋, this PR has been opened for more than 2 months with no activity on it. If you think this is a mistake please comment and ping a maintainer to get this merged ASAP! Thanks for contributing! You have 7 days until this gets closed automatically |
Let us know @TarekkMA if you want us to take over it? |
@Arthur-Milchior Yes, thank you. Sorry I have some issues I'm dealing with 😞. |
Hello 👋, this PR has been opened for more than 2 months with no activity on it. If you think this is a mistake please comment and ping a maintainer to get this merged ASAP! Thanks for contributing! You have 7 days until this gets closed automatically |
Pull Request template
Purpose / Description
Describe the problem or feature and motivation
Fixes
Related to #8668
Approach
How does this change address the problem?
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration (SDK version(s), emulator or physical, etc)
Learning (optional, can help others)
Describe the research stage
Links to blog posts, patterns, libraries or addons used to solve this problem
Checklist
Please, go through these checks before submitting the PR.
if
statements)