This is an automated email from the ASF dual-hosted git repository.

daim pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 43682faecb OAK-12027 : added api to remove node and its descendants 
starting fro… (#2636)
43682faecb is described below

commit 43682faecba41ebc3addc8754aa5548ce89b54cd
Author: Rishabh Kumar <[email protected]>
AuthorDate: Tue Dec 2 21:47:38 2025 +0530

    OAK-12027 : added api to remove node and its descendants starting fro… 
(#2636)
    
    * OAK-12027 : added api to remove node and its descendants starting from 
leaf nodes upto parents
    
    * OAK-12027 : improved comments
---
 oak-run/src/main/js/oak-mongo.js | 97 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/oak-run/src/main/js/oak-mongo.js b/oak-run/src/main/js/oak-mongo.js
index 36a3c9195b..0daada1d8c 100644
--- a/oak-run/src/main/js/oak-mongo.js
+++ b/oak-run/src/main/js/oak-mongo.js
@@ -351,6 +351,103 @@ var oak = (function(global){
         return {deletedCount : count};
     };
 
+    /**
+     * Removes the complete subtree rooted at the given path, deleting leaf 
nodes first and then parents.
+     * This ensures that if the operation is interrupted, parent nodes remain 
available for resuming.
+     *
+     * @memberof oak
+     * @method removeDescendantsAndSelfWithLeavesFirst
+     * @param {string} path the path of the subtree to remove.
+     * @returns {Object} Object with deletedCount property
+     */
+    api.removeDescendantsAndSelfWithLeavesFirst = function(path) {
+        var count = 0;
+        var depth = pathDepth(path);
+        var prefix = path + "/";
+        var escapedPrefix = escapeForRegExp(prefix);
+
+        // check if root exists
+        var rootExists = db.nodes.findOne({_id: depth + ":" + path}) !== null;
+        print("Checked root existence at depth " + depth + ": " + (rootExists 
? "found" : "not found"));
+        if (!rootExists) {
+            return {deletedCount: 0};
+        }
+
+        // Check each depth level one at a time, until no more nodes are found.
+        // The process stops automatically when there are no nodes at the next 
depth
+        var maxDepth = depth;
+        var currentDepth = depth + 1;
+
+        while (true) {
+            // Check regular nodes at this depth
+            var hasRegularNodes = db.nodes.findOne({
+                _id: new RegExp("^" + currentDepth + ":" + escapedPrefix)
+            }, {_id: 1}) !== null;
+            print("Checked regular nodes at depth " + currentDepth + ": " + 
(hasRegularNodes ? "found" : "not found"));
+
+            // Check long path nodes at this depth
+            var hasLongPathNodes = db.nodes.findOne({
+                _id: currentDepth + ":h",
+                _path: new RegExp("^" + escapedPrefix)
+            }, {_id: 1}) !== null;
+            print("Checked long path nodes at depth " + currentDepth + ": " + 
(hasLongPathNodes ? "found" : "not found"));
+
+            if (!hasRegularNodes && !hasLongPathNodes) {
+                // No nodes at this depth, previous depth was max
+                break;
+            }
+
+            maxDepth = currentDepth;
+            currentDepth++;
+        }
+
+        print("Max depth found: " + maxDepth + " (root depth: " + depth + ")");
+
+        // If maxDepth equals depth, only root exists
+        if (maxDepth === depth) {
+            var rootResult = db.nodes.deleteMany({_id: depth + ":" + path});
+            print("Deleted root regular nodes at depth " + depth + ": " + 
rootResult.deletedCount + " nodes");
+            var longPathResult = db.nodes.deleteMany(longPathQuery(path));
+            print("Deleted root long path nodes at depth " + depth + ": " + 
longPathResult.deletedCount + " nodes");
+            return {deletedCount: rootResult.deletedCount + 
longPathResult.deletedCount};
+        }
+
+        // Delete from deepest level to root (children only, excludes root)
+        for (var d = maxDepth; d > depth; d--) {
+            var bulkOps = [];
+
+            // Add bulk operations for this depth level
+            bulkOps.push({ deleteMany: { filter: longPathFilter(d, prefix)}});
+            bulkOps.push({ deleteMany: { filter: {_id: pathFilter(d, 
prefix)}}});
+
+            // Execute bulk operations for this depth level
+            if (bulkOps.length > 0) {
+                var bulkResult = db.nodes.bulkWrite(bulkOps, {
+                    ordered: false,           // Allow parallel execution
+                    writeConcern: {w: 1}       // Adjust based on consistency 
needs
+                });
+                count += bulkResult.deletedCount;
+                print("Deleted nodes at depth " + d + ": " + 
bulkResult.deletedCount + " nodes");
+            }
+        }
+
+        // now remove root
+        var rootBulkOps = [];
+        rootBulkOps.push({ deleteMany: { filter: longPathQuery(path)}});
+        rootBulkOps.push({ deleteMany: { filter: {_id: depth + ":" + path}}});
+
+        if (rootBulkOps.length > 0) {
+            var rootBulkResult = db.nodes.bulkWrite(rootBulkOps, {
+                ordered: false,           // Allow parallel execution
+                writeConcern: {w: 1}       // Adjust based on consistency needs
+            });
+            count += rootBulkResult.deletedCount;
+            print("Deleted root nodes at depth " + depth + ": " + 
rootBulkResult.deletedCount + " nodes");
+        }
+
+        return {deletedCount: count};
+    };
+
     /**
      * Helper method to find nodes based on Regular Expression.
      *

Reply via email to