diff --git a/dashjoin-core/src/main/java/org/dashjoin/service/JSONDatabase.java b/dashjoin-core/src/main/java/org/dashjoin/service/JSONDatabase.java index d36317d88..67503bed5 100644 --- a/dashjoin-core/src/main/java/org/dashjoin/service/JSONDatabase.java +++ b/dashjoin-core/src/main/java/org/dashjoin/service/JSONDatabase.java @@ -166,8 +166,12 @@ public boolean update(Table s, Map search, Map o for (Entry e : object.entrySet()) { if (e.getValue() == null) r.remove(e.getKey()); - else + else { + // when nested objects are overwritten and the old value contains a pointer, + // make sure to re-use the pointer + copyPointers(r.get(e.getKey()), e.getValue()); r.put(e.getKey(), e.getValue()); + } } return update(s, r); } @@ -304,4 +308,29 @@ public static String toJsonString(Map obj) throws JsonProcessing public static Map fromJsonString(String json) throws JsonProcessingException { return objectMapper.readValue(json, JSONDatabase.tr); } + + /** + * copy -pointer fields from source to dest + */ + @SuppressWarnings("unchecked") + static void copyPointers(Object _from, Object _to) { + if (_from instanceof Map && _to instanceof Map) { + Map from = (Map) _from; + Map to = (Map) _to; + for (Entry e : from.entrySet()) { + Object toValue = to.get(e.getKey()); + if (e.getKey().endsWith("-pointer")) { + String key = e.getKey().substring(0, e.getKey().length() - "-pointer".length()); + if (from.containsKey(key)) + if (to.containsKey(key)) + if (toValue == null) + // from and to contain externalized value + // only from contains pointer + // re-use the from pointer + to.put(e.getKey(), e.getValue()); + } + copyPointers(e.getValue(), toValue); + } + } + } } diff --git a/dashjoin-core/src/test/java/org/dashjoin/service/JSONDatabaseTest.java b/dashjoin-core/src/test/java/org/dashjoin/service/JSONDatabaseTest.java index ec59b11b0..348b809a1 100644 --- a/dashjoin-core/src/test/java/org/dashjoin/service/JSONDatabaseTest.java +++ b/dashjoin-core/src/test/java/org/dashjoin/service/JSONDatabaseTest.java @@ -428,4 +428,45 @@ public void testExtraField() throws Exception { Payload p = JSONDatabase.fromMap(MapUtil.of("s", "hello world", "unknown", 42), Payload.class); Assertions.assertEquals(p.s, "hello world"); } + + @SuppressWarnings("unchecked") + @Test + public void testCopyPointer() { + JSONDatabase.copyPointers(null, null); + JSONDatabase.copyPointers(1, null); + JSONDatabase.copyPointers("test", null); + JSONDatabase.copyPointers(null, 1); + JSONDatabase.copyPointers(null, "test"); + JSONDatabase.copyPointers(null, Map.of("x", 1)); + JSONDatabase.copyPointers(Map.of("x", 1), null); + + Map from = MapUtil.of("x", "code", "x-pointer", "0.txt"); + Map to = MapUtil.of("x", "new code"); + JSONDatabase.copyPointers(from, to); + Assertions.assertEquals("0.txt", to.get("x-pointer")); + + from = Map.of("n", MapUtil.of("x", "code", "x-pointer", "0.txt")); + to = Map.of("n", MapUtil.of("x", "new code")); + JSONDatabase.copyPointers(from, to); + Assertions.assertEquals("0.txt", ((Map) to.get("n")).get("x-pointer")); + + from = Map.of("n", MapUtil.of("x", "code", "x-pointer", "0.txt")); + to = Map.of(); + JSONDatabase.copyPointers(from, to); + + from = MapUtil.of("x", "code", "x-pointer", "0.txt"); + to = MapUtil.of(); + JSONDatabase.copyPointers(from, to); + Assertions.assertNull(to.get("x-pointer")); + + from = MapUtil.of("x-pointer", "0.txt"); + to = MapUtil.of("x", "code"); + JSONDatabase.copyPointers(from, to); + Assertions.assertNull(to.get("x-pointer")); + + from = MapUtil.of("x", "code", "x-pointer", "0.txt"); + to = MapUtil.of("x", "code", "x-pointer", "1.txt"); + JSONDatabase.copyPointers(from, to); + Assertions.assertEquals("1.txt", to.get("x-pointer")); + } }