From 5bbc1af53d3c17ce02ddbf98cb76e487f0bb6b71 Mon Sep 17 00:00:00 2001
From: Vikas Saurabh <vsaurabh@adobe.com>
Date: Wed, 8 Jul 2015 18:55:08 +0530
Subject: [PATCH] OAK-3087 Add a method to remove hidden structure under
 disabled indices. In dryMode, it can also be used to generate a list of
 currently disabled indices in the system.

---
 oak-run/src/main/js/oak-mongo.js | 124 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/oak-run/src/main/js/oak-mongo.js b/oak-run/src/main/js/oak-mongo.js
index 31fb0c1..e1f6d8c 100644
--- a/oak-run/src/main/js/oak-mongo.js
+++ b/oak-run/src/main/js/oak-mongo.js
@@ -638,6 +638,130 @@ var oak = (function(global){
         return mongoExportCommand;
     };
 
+    /**
+     * This method is used to remove hidden structure under disabled indices.
+     * It can be invoked in dry and non-dry mode. While running in dryMode,
+     * it can generate a list of currently disabled index names.<br/><br/> 
+     * <i>Note: This method WON'T delete index definition nodes - those still
+     * need to be deleted externally. But, after clean up is done, deleting
+     * those won't take much time. Please follow "Post cleanup operations"
+     * section below to get an idea of what care needs to be taken while
+     * removing index defintion after index structure cleanup.</i>
+     * <br/><br/>
+     * Post cleanup operations:
+     * <ol>
+     * <li>Shut down Oak instance(s) connected to the repository</li>
+     * <li>Delete persistent cache files if persistent cache is enabled</li>
+     * <li>Start Oak instance(s)</li>
+     * <li>Remove index definition nodes using some external tool</li>
+     * </ol>
+     *
+     * @example
+     * //Generate and print list of currently disabled index names.
+     * //Then use that list to continue in dryMode.
+     * > oak.cleanupDisabledIndices()
+     * Generated index names: ["disabledIndex1", "notToBeCleanedDisabledIndex1", "disabledIndex2"]
+     *
+     * //validate index names to be cleaned up
+     * > oak.cleanupDisabledIndices(["disabledIndex1", "enabledIndex", "disabledIndex2"])
+     * ...
+     * Index enabledIndex is NOT DISABLED
+     * ...
+     * 
+     * //carry out actual cleanup
+     * > oak.cleanupDisabledIndices(["disabledIndex1", "disabledIndex2"], false)
+     * Deleting tree at path /oak:index/disabledIndex1/:index
+     * Deleted 12 documents in 2ms
+     * ...
+     * 
+     * @memberof oak
+     * @method cleanupDisabledIndices
+     * @param {Array} indexNames list of index names to be considered for clean up.
+     *                           Optional if script is run in dryRun mode
+     * @param {boolean} dryRun (Optional) flag to mark if actual clean up needs to be carried out or not
+     */
+    api.cleanupDisabledIndices = function(indexNames, dryRun) {
+        var dryRun = (!(dryRun==false));
+
+        var isIndexDisabled = function(indexName) {
+            var indexDoc = api.findOne("/oak:index/" + indexName);
+            if (indexDoc == null) {
+                return -1;
+            }
+            var type = indexDoc.type;
+
+            if (type == null) {
+                return -2;
+            }
+
+            var maxRev = new Revision("r0-0-0");
+            for (var revStr in type) {
+                var rev = new Revision(revStr);
+                if (rev.isNewerThan(maxRev)) {
+                    maxRev = rev;
+                }
+            }
+
+            var latestTypeValue = type[maxRev.toString()];
+            return ("\"disabled\"" == latestTypeValue);
+        };
+
+        var deleteIndex = function(indexName) {
+            var isDisabled = isIndexDisabled(indexName);
+
+            if (isDisabled == -1) {
+                print ("Index " + indexName + " does not exist");
+                return;
+            } else if (isDisabled == -2) {
+                print ("Index" + indexName + " does not have 'type' property");
+                return;
+            } else if (!isDisabled) {
+                print ("Index " + indexName + " is NOT DISABLED");
+                return;
+            }
+
+            var path = "/oak:index/" + indexName + "/:index";
+            print("Deleting tree at " + path);
+            if (dryRun === false) {
+                var startTime = new Date().getTime();
+                var removed = api.removeDescendantsAndSelf(path);
+                var endTime = new Date().getTime();
+                print ("Deleted " + removed["nRemoved"] + " documents in " + (endTime - startTime) + "ms");
+            }
+        };
+
+        var findDisabledIndexNodes = function() {
+            var indicesDocs = db.nodes.find({_id: {$regex: "^2:/oak:index/"}});
+
+            var len = "/oak:index/".length;
+            var disabledIndices = [];
+            indicesDocs.forEach(function(indexDoc) {
+                var indexPath = api.pathFromId(indexDoc._id);
+                var indexName = indexPath.substr(len);
+
+                var isDisabled = isIndexDisabled(indexName);
+                if (isDisabled == -1) {
+                    //weird... we just looked up the doc
+                } else if (isDisabled == -2) {
+                    //the child isn't probably an index type (rep:policy is an example)
+                } else if (isDisabled) {
+                    disabledIndices.push(indexName);
+                }
+            });
+
+            return disabledIndices;
+        };
+
+        if (dryRun && indexNames == undefined) {
+            indexNames = findDisabledIndexNodes();
+            print ("Generated index names: " + JSON.stringify(indexNames));
+        }
+
+        indexNames.forEach(function(index) {
+            deleteIndex(index);
+        });
+    };
+
     //~--------------------------------------------------< internal >
 
     var checkOrFixDeepHistory = function(path, fix, prepare, verbose) {
-- 
2.1.4

