diff --git a/bundles/sirix-query/src/main/java/io/sirix/query/json/JsonDBItem.java b/bundles/sirix-query/src/main/java/io/sirix/query/json/JsonDBItem.java index dfa360f1b..fa8ce5ae0 100644 --- a/bundles/sirix-query/src/main/java/io/sirix/query/json/JsonDBItem.java +++ b/bundles/sirix-query/src/main/java/io/sirix/query/json/JsonDBItem.java @@ -21,14 +21,21 @@ public interface JsonDBItem extends UpdatableJsonItem { */ @Override default void replaceValue(Sequence newValue) { - final JsonNodeReadOnlyTrx rtx = getTrx(); - rtx.moveTo(getNodeKey()); - final JsonResourceSession resourceSession = getResourceSession(); final JsonNodeTrx wtx = resourceSession.getNodeTrx().orElseGet(resourceSession::beginNodeTrx); wtx.moveTo(getNodeKey()); - - // Use the JsonItemSequence utility to perform the replacement JsonItemSequence.replaceValue(wtx, newValue, getCollection()); } + + /** + * Default implementation of delete that removes this item from its parent. + * This enables the use of sdb:select-item with delete expressions. + */ + @Override + default void delete() { + final JsonResourceSession resourceSession = getResourceSession(); + final JsonNodeTrx wtx = resourceSession.getNodeTrx().orElseGet(resourceSession::beginNodeTrx); + wtx.moveTo(getNodeKey()); + wtx.remove(); + } } diff --git a/bundles/sirix-query/src/test/java/io/sirix/query/JsonIntegrationTest.java b/bundles/sirix-query/src/test/java/io/sirix/query/JsonIntegrationTest.java index 98bc6247c..0a4582abb 100644 --- a/bundles/sirix-query/src/test/java/io/sirix/query/JsonIntegrationTest.java +++ b/bundles/sirix-query/src/test/java/io/sirix/query/JsonIntegrationTest.java @@ -713,6 +713,39 @@ public void testRemoveFromObject() throws IOException { test(storeQuery, updateQuery, openQuery, "{\"baz\":true}"); } + @Test + public void testRemoveFromObjectViaSdbSelectItem() throws IOException { + final String storeQuery = """ + jn:store('json-path1','mydoc.jn','{"foo": "bar", "baz": true}') + """; + // Use sdb:select-item to get the 'foo' object-record (nodeKey 2) and delete it directly + final String updateQuery = """ + delete json sdb:select-item(jn:doc('json-path1','mydoc.jn'), 2) + """; + final String openQuery = "jn:doc('json-path1','mydoc.jn')"; + test(storeQuery, updateQuery, openQuery, "{\"baz\":true}"); + } + + @Test + public void testUpdateThenDeleteSameNode() throws IOException { + // Per XQuery Update Facility 1.0 section 3.2.1: + // "If a node is marked for deletion, updates to its properties have no effect." + // Brackit should skip the update and only apply the delete. + // + // In this test, we use sdb:select-item for both update and delete to target + // the exact same item (the "name" object record at nodeKey 2). + final String storeQuery = """ + jn:store('json-path1','mydoc.jn','{"name": "original", "other": 123}') + """; + // Update and delete the same node (nodeKey 2 = "name" object record) - update should be skipped + final String updateQuery = """ + let $item := sdb:select-item(jn:doc('json-path1','mydoc.jn'), 2) + return (replace json value of $item with "updated", delete json $item) + """; + final String openQuery = "jn:doc('json-path1','mydoc.jn')"; + test(storeQuery, updateQuery, openQuery, "{\"other\":123}"); + } + @Test public void testRenameFieldInObject() throws IOException { final String storeQuery = """