Skip to content

Commit a2ffbfc

Browse files
committed
Fix: Point2D serialization and deserialization
1 parent 6473d27 commit a2ffbfc

File tree

2 files changed

+96
-21
lines changed

2 files changed

+96
-21
lines changed

common/src/main/kotlin/streams/utils/JSONUtils.kt

+17-17
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ import com.fasterxml.jackson.module.kotlin.convertValue
1717
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
1818
import com.fasterxml.jackson.module.kotlin.readValue
1919
import org.neo4j.driver.internal.value.PointValue
20-
import org.neo4j.function.ThrowingBiConsumer
2120
import org.neo4j.graphdb.spatial.Point
22-
import org.neo4j.values.AnyValue
2321
import org.neo4j.values.storable.CoordinateReferenceSystem
2422
import org.neo4j.values.storable.Values
2523
import org.neo4j.values.virtual.MapValue
@@ -40,17 +38,19 @@ import java.time.temporal.TemporalAccessor
4038
import kotlin.reflect.full.isSubclassOf
4139

4240
abstract class StreamsPoint { abstract val crs: String }
43-
data class StreamsPointCartesian(override val crs: String, val x: Double, val y: Double, val z: Double? = null): StreamsPoint()
44-
data class StreamsPointWgs(override val crs: String, val latitude: Double, val longitude: Double, val height: Double? = null): StreamsPoint()
41+
data class StreamsPointCartesian2D(override val crs: String, val x: Double, val y: Double): StreamsPoint()
42+
data class StreamsPointCartesian3D(override val crs: String, val x: Double, val y: Double, val z: Double): StreamsPoint()
43+
data class StreamsPointWgs2D(override val crs: String, val latitude: Double, val longitude: Double): StreamsPoint()
44+
data class StreamsPointWgs3D(override val crs: String, val latitude: Double, val longitude: Double, val height: Double): StreamsPoint()
4545

4646
fun Point.toStreamsPoint(): StreamsPoint {
4747
val crsType = this.crs.type
4848
val coordinate = this.coordinates[0].coordinate
4949
return when (this.crs) {
50-
CoordinateReferenceSystem.Cartesian -> StreamsPointCartesian(crsType, coordinate[0], coordinate[1])
51-
CoordinateReferenceSystem.Cartesian_3D -> StreamsPointCartesian(crsType, coordinate[0], coordinate[1], coordinate[2])
52-
CoordinateReferenceSystem.WGS84 -> StreamsPointWgs(crsType, coordinate[0], coordinate[1])
53-
CoordinateReferenceSystem.WGS84_3D -> StreamsPointWgs(crsType, coordinate[0], coordinate[1], coordinate[2])
50+
CoordinateReferenceSystem.Cartesian -> StreamsPointCartesian2D(crsType, coordinate[0], coordinate[1])
51+
CoordinateReferenceSystem.Cartesian_3D -> StreamsPointCartesian3D(crsType, coordinate[0], coordinate[1], coordinate[2])
52+
CoordinateReferenceSystem.WGS84 -> StreamsPointWgs2D(crsType, coordinate[0], coordinate[1])
53+
CoordinateReferenceSystem.WGS84_3D -> StreamsPointWgs3D(crsType, coordinate[0], coordinate[1], coordinate[2])
5454
else -> throw IllegalArgumentException("Point type $crsType not supported")
5555
}
5656
}
@@ -65,21 +65,21 @@ fun Map<String, Any>.toMapValue(): MapValue {
6565
fun PointValue.toStreamsPoint(): StreamsPoint {
6666
val point = this.asPoint()
6767
return when (val crsType = point.srid()) {
68-
CoordinateReferenceSystem.Cartesian.code -> StreamsPointCartesian(CoordinateReferenceSystem.Cartesian.name, point.x(), point.y())
69-
CoordinateReferenceSystem.Cartesian_3D.code -> StreamsPointCartesian(CoordinateReferenceSystem.Cartesian_3D.name, point.x(), point.y(), point.z())
70-
CoordinateReferenceSystem.WGS84.code -> StreamsPointWgs(CoordinateReferenceSystem.WGS84.name, point.x(), point.y())
71-
CoordinateReferenceSystem.WGS84_3D.code -> StreamsPointWgs(CoordinateReferenceSystem.WGS84_3D.name, point.x(), point.y(), point.z())
68+
CoordinateReferenceSystem.Cartesian.code -> StreamsPointCartesian2D(CoordinateReferenceSystem.Cartesian.name, point.x(), point.y())
69+
CoordinateReferenceSystem.Cartesian_3D.code -> StreamsPointCartesian3D(CoordinateReferenceSystem.Cartesian_3D.name, point.x(), point.y(), point.z())
70+
CoordinateReferenceSystem.WGS84.code -> StreamsPointWgs2D(CoordinateReferenceSystem.WGS84.name, point.x(), point.y())
71+
CoordinateReferenceSystem.WGS84_3D.code -> StreamsPointWgs3D(CoordinateReferenceSystem.WGS84_3D.name, point.x(), point.y(), point.z())
7272
else -> throw IllegalArgumentException("Point type $crsType not supported")
7373
}
7474
}
7575

7676
fun org.neo4j.driver.types.Point.toStreamsPoint(): StreamsPoint {
7777
val point = this
7878
return when (val crsType = point.srid()) {
79-
CoordinateReferenceSystem.Cartesian.code -> StreamsPointCartesian(CoordinateReferenceSystem.Cartesian.name, point.x(), point.y())
80-
CoordinateReferenceSystem.Cartesian_3D.code -> StreamsPointCartesian(CoordinateReferenceSystem.Cartesian_3D.name, point.x(), point.y(), point.z())
81-
CoordinateReferenceSystem.WGS84.code -> StreamsPointWgs(CoordinateReferenceSystem.WGS84.name, point.x(), point.y())
82-
CoordinateReferenceSystem.WGS84_3D.code -> StreamsPointWgs(CoordinateReferenceSystem.WGS84_3D.name, point.x(), point.y(), point.z())
79+
CoordinateReferenceSystem.Cartesian.code -> StreamsPointCartesian2D(CoordinateReferenceSystem.Cartesian.name, point.x(), point.y())
80+
CoordinateReferenceSystem.Cartesian_3D.code -> StreamsPointCartesian3D(CoordinateReferenceSystem.Cartesian_3D.name, point.x(), point.y(), point.z())
81+
CoordinateReferenceSystem.WGS84.code -> StreamsPointWgs2D(CoordinateReferenceSystem.WGS84.name, point.x(), point.y())
82+
CoordinateReferenceSystem.WGS84_3D.code -> StreamsPointWgs3D(CoordinateReferenceSystem.WGS84_3D.name, point.x(), point.y(), point.z())
8383
else -> throw IllegalArgumentException("Point type $crsType not supported")
8484
}
8585
}
@@ -236,7 +236,7 @@ abstract class StreamsTransactionEventDeserializer<EVENT, PAYLOAD: Payload> : Js
236236
?.properties
237237
?.mapValues {
238238
if (points.contains(it.key)) {
239-
org.neo4j.values.storable.PointValue.fromMap((it.value as Map<String, Any>).toMapValue())
239+
org.neo4j.values.storable.PointValue.fromMap((it.value as Map<String, Any>).toMapValue().filter { _, u -> u != Values.NO_VALUE })
240240
} else {
241241
it.value
242242
}

common/src/test/kotlin/streams/utils/JSONUtilsTest.kt

+79-4
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ class JSONUtilsTest {
2626
@Test
2727
fun `should serialize Geometry and Temporal Data Types`() {
2828
// Given
29-
val expected = "{\"point2dCartesian\":{\"crs\":\"cartesian\",\"x\":1.0,\"y\":2.0,\"z\":null}," +
29+
val expected = "{\"point2dCartesian\":{\"crs\":\"cartesian\",\"x\":1.0,\"y\":2.0}," +
3030
"\"point3dCartesian\":{\"crs\":\"cartesian-3d\",\"x\":1.0,\"y\":2.0,\"z\":3.0}," +
31-
"\"point2dWgs84\":{\"crs\":\"wgs-84\",\"latitude\":1.0,\"longitude\":2.0,\"height\":null}," +
31+
"\"point2dWgs84\":{\"crs\":\"wgs-84\",\"latitude\":1.0,\"longitude\":2.0}," +
3232
"\"point3dWgs84\":{\"crs\":\"wgs-84-3d\",\"latitude\":1.0,\"longitude\":2.0,\"height\":3.0}," +
3333
"\"time\":\"14:00:00Z\",\"dateTime\":\"2017-12-17T17:14:35.123456789Z\"}"
3434
val map = linkedMapOf<String, Any>("point2dCartesian" to pointValue(Cartesian, 1.0, 2.0),
@@ -48,9 +48,9 @@ class JSONUtilsTest {
4848
@Test
4949
fun `should serialize driver Point Data Types`() {
5050
// Given
51-
val expected = "{\"point2dCartesian\":{\"crs\":\"cartesian\",\"x\":1.0,\"y\":2.0,\"z\":null}," +
51+
val expected = "{\"point2dCartesian\":{\"crs\":\"cartesian\",\"x\":1.0,\"y\":2.0}," +
5252
"\"point3dCartesian\":{\"crs\":\"cartesian-3d\",\"x\":1.0,\"y\":2.0,\"z\":3.0}," +
53-
"\"point2dWgs84\":{\"crs\":\"wgs-84\",\"latitude\":1.0,\"longitude\":2.0,\"height\":null}," +
53+
"\"point2dWgs84\":{\"crs\":\"wgs-84\",\"latitude\":1.0,\"longitude\":2.0}," +
5454
"\"point3dWgs84\":{\"crs\":\"wgs-84-3d\",\"latitude\":1.0,\"longitude\":2.0,\"height\":3.0}," +
5555
"\"time\":\"14:00:00Z\",\"dateTime\":\"2017-12-17T17:14:35.123456789Z\"}"
5656
val map = linkedMapOf<String, Any>("point2dCartesian" to pointValue(Cartesian, 1.0, 2.0),
@@ -111,6 +111,81 @@ class JSONUtilsTest {
111111
assertEquals(cdcData, fromString)
112112
}
113113

114+
@Test
115+
fun `should convert wgs2D with height null to PointValue`() {
116+
// given
117+
val timestamp = System.currentTimeMillis()
118+
119+
val expected = org.neo4j.values.storable.PointValue.fromMap(mapOf("crs" to "wgs-84", "latitude" to 12.78, "longitude" to 56.7).toMapValue())
120+
121+
val cdcMap = mapOf<String, Any>(
122+
"meta" to mapOf("timestamp" to timestamp,
123+
"username" to "user",
124+
"txId" to 1,
125+
"txEventId" to 0,
126+
"txEventsCount" to 1,
127+
"operation" to OperationType.created),
128+
"payload" to mapOf("id" to "0",
129+
"before" to null,
130+
"after" to NodeChange(properties = mapOf("location" to mapOf("crs" to "wgs-84", "latitude" to 12.78, "longitude" to 56.7, "height" to null)),
131+
labels = listOf("LabelCDC")),
132+
"type" to EntityType.node),
133+
"schema" to mapOf("properties" to mapOf("location" to "PointValue"))
134+
)
135+
136+
val cdcString = """{
137+
|"meta":{"timestamp":$timestamp,"username":"user","txId":1,"txEventId":0,"txEventsCount":1,"operation":"created"},
138+
|"payload":{"id":"0","before":null,"after":{"properties":{"location":{"crs":"wgs-84","latitude":12.78,"longitude":56.7,"height":null}},
139+
|"labels":["LabelCDC"]},"type":"node"},
140+
|"schema":{"properties":{"location":"PointValue"}}
141+
|}""".trimMargin()
142+
143+
val fromMap = JSONUtils.asStreamsTransactionEvent(cdcMap)
144+
val fromMapPointValue = fromMap.payload.after?.properties?.get("location") as org.neo4j.values.storable.PointValue
145+
assertEquals(expected, fromMapPointValue)
146+
147+
val fromString = JSONUtils.asStreamsTransactionEvent(cdcString)
148+
val fromStringPointValue = fromString.payload.after?.properties?.get("location") as org.neo4j.values.storable.PointValue
149+
assertEquals(expected, fromStringPointValue)
150+
}
151+
152+
@Test
153+
fun `should convert cartesian2D with z null to PointValue`() {
154+
// given
155+
val timestamp = System.currentTimeMillis()
156+
val expected = org.neo4j.values.storable.PointValue.fromMap(mapOf("crs" to "cartesian", "x" to 12.78, "y" to 56.7).toMapValue())
157+
158+
val cdcMap = mapOf<String, Any>(
159+
"meta" to mapOf("timestamp" to timestamp,
160+
"username" to "user",
161+
"txId" to 1,
162+
"txEventId" to 0,
163+
"txEventsCount" to 1,
164+
"operation" to OperationType.created),
165+
"payload" to mapOf("id" to "0",
166+
"before" to null,
167+
"after" to NodeChange(properties = mapOf("location" to mapOf("crs" to "cartesian", "x" to 12.78, "y" to 56.7, "z" to null)),
168+
labels = listOf("LabelCDC")),
169+
"type" to EntityType.node),
170+
"schema" to mapOf("properties" to mapOf("location" to "PointValue"))
171+
)
172+
173+
val cdcString = """{
174+
|"meta":{"timestamp":$timestamp,"username":"user","txId":1,"txEventId":0,"txEventsCount":1,"operation":"created"},
175+
|"payload":{"id":"0","before":null,"after":{"properties":{"location":{"crs":"cartesian","x":12.78,"y":56.7,"z":null}},
176+
|"labels":["LabelCDC"]},"type":"node"},
177+
|"schema":{"properties":{"location":"PointValue"}}
178+
|}""".trimMargin()
179+
180+
val fromMap = JSONUtils.asStreamsTransactionEvent(cdcMap)
181+
val fromMapPointValue = fromMap.payload.after?.properties?.get("location") as org.neo4j.values.storable.PointValue
182+
assertEquals(expected, fromMapPointValue)
183+
184+
val fromString = JSONUtils.asStreamsTransactionEvent(cdcString)
185+
val fromStringPointValue = fromString.payload.after?.properties?.get("location") as org.neo4j.values.storable.PointValue
186+
assertEquals(expected, fromStringPointValue)
187+
}
188+
114189
@Test
115190
fun `should deserialize plain values`() {
116191
assertEquals("a", JSONUtils.readValue("a", stringWhenFailure = true))

0 commit comments

Comments
 (0)