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
Load encoded values from disk #2542
Conversation
core/src/test/java/com/graphhopper/routing/ev/EnumEncodedValueTest.java
Outdated
Show resolved
Hide resolved
// serialize | ||
List<String> serializedEVs = new ArrayList<>(); | ||
for (EncodedValue e : encodedValues) { | ||
String s = mapper.writeValueAsString(e); | ||
serializedEVs.add(s); | ||
} | ||
|
||
// deserialize | ||
List<EncodedValue> deserializedEVs = new ArrayList<>(); | ||
for (String s : serializedEVs) { | ||
JsonNode jsonNode = mapper.readTree(s); | ||
deserializedEVs.add(mapper.treeToValue(jsonNode, EncodedValue.class)); | ||
} |
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.
This is pretty nice and seems to work for all our EncodedValue
subclasses automatically, due to the @JsonTypeInfo
annotation that I added to the EncodedValue
interface.
…er because of subclass constructors
int storedVersion = jsonNode.get("version").asInt(); | ||
((ObjectNode) jsonNode).remove("version"); | ||
EncodedValue encodedValue = MAPPER.treeToValue(jsonNode, EncodedValue.class); | ||
if (storedVersion != encodedValue.getVersion()) | ||
throw new IllegalStateException("Version does not match. Cannot properly read encoded value: " + encodedValue.getName() + ". " + | ||
"You need to use the same version of GraphHopper you used to import the data"); |
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.
This is a bit self-made and probably can also be done using Jackson Annotations, but since we aren't really planning to de/serialize encoded values with something other than these two methods I think this is fine. I'm not so experienced with Jackson and open for improvements of course.
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'm also no expert. I tried to get it working and embedded your work into a common Deserializer (and using @JsonProperty(access = JsonProperty.Access.READ_ONLY)
for getVersion to avoid a custom Serializer).
But the problem was that we don't want to manually handle the className and I wasn't able to find a way to use both:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
and
@JsonDeserialize(using = EncodedValueDeserializer.class)
.
The only solution was with some wrapper class but this here looks simpler IMO.
@@ -25,14 +28,30 @@ | |||
* This class allows to store distinct values via an enum. I.e. it stores just the indices | |||
*/ | |||
public final class EnumEncodedValue<E extends Enum> extends IntEncodedValueImpl { | |||
private final E[] arr; | |||
public final Class<E> enumType; |
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.
Can this be reverted back to private
?
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.
Ah yes, thanks I forgot.
@@ -30,7 +27,7 @@ public StringEncodedValue(String name, int expectedValueCount, boolean storeTwoD | |||
|
|||
this.maxValues = roundUp(expectedValueCount); | |||
this.values = new ArrayList<>(maxValues); | |||
this.indexMap = new ObjectIntHashMap<>(maxValues); | |||
this.indexMap = new HashMap<>(maxValues); |
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.
Could we avoid this with a dedicated getter instead of using field access? I'm not sure if we want to deal with auto(un)boxing in a potentially hot code path.
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.
Does this really not work because we use field access? I thought we need a custom serializer for hppc, but I might be wrong. I thought about the auto unboxing for a moment but then thought it's probably not as bad bc the maps will be very small. But you are more than welcome to make this work with the hppc map again. I just did not feel like spending a lot more time on this, also because currently the default GraphHopper server does not use StringEncodedValue currently.
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 tried changing indexMap
to the concrete type ObjectIntHashMap
and got another error which is due to the fact that HashOrderMixingStrategy
is an abstract type and Jackson does not know which type to use upon de-serialization.
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.
It works when we add jackson-datatype-hppc
: https://github.com/FasterXML/jackson-datatypes-collections
I tried this here including a little speed test: fa80be9
I could not measure a notable difference, though so I would go with java.util for now. You are invited to prove the opposite, of course.
Yes this would also solve #2243 😉 Does Jackson require those constructors to be public? Maybe we could reduce the visibility if they're only intended to be used by Jackson. |
Yes, package private also works and is better thanks. We could also make them protected, but not sure if realistically users want to create their own subclasses of |
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.
👍
int storedVersion = jsonNode.get("version").asInt(); | ||
((ObjectNode) jsonNode).remove("version"); | ||
EncodedValue encodedValue = MAPPER.treeToValue(jsonNode, EncodedValue.class); | ||
if (storedVersion != encodedValue.getVersion()) | ||
throw new IllegalStateException("Version does not match. Cannot properly read encoded value: " + encodedValue.getName() + ". " + | ||
"You need to use the same version of GraphHopper you used to import the data"); |
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'm also no expert. I tried to get it working and embedded your work into a common Deserializer (and using @JsonProperty(access = JsonProperty.Access.READ_ONLY)
for getVersion to avoid a custom Serializer).
But the problem was that we don't want to manually handle the className and I wasn't able to find a way to use both:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
and
@JsonDeserialize(using = EncodedValueDeserializer.class)
.
The only solution was with some wrapper class but this here looks simpler IMO.
|
||
static { | ||
MAPPER.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); | ||
MAPPER.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); |
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.
for consistency shall we add
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
here?
I tried using Jackson to serialize/deserialize an
EnumEncodedValue
.For example a
RoadClass
EV is serialized to:For EnumEncodedValue we do not store the enum values explicitly, but rather the name of the enum class. For StringEncodedValue we simply store all the values.