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.
*