From 24941cc4a2ac5d65488fd4474f3924db28809729 Mon Sep 17 00:00:00 2001
From: Vikas Saurabh <vsaurabh@adobe.com>
Date: Wed, 10 Dec 2014 03:32:36 +0530
Subject: [PATCH 2/4] OAK-2341: Refactor ContentMirrorStoreStategy::count a bit
 to have a single point of return

---
 .../plugins/index/counter/NodeCounterEditor.java   |   2 +-
 .../strategy/ContentMirrorStoreStrategy.java       | 180 +++++++++++----------
 2 files changed, 93 insertions(+), 89 deletions(-)
 mode change 100644 => 100755 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
 mode change 100644 => 100755 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java

diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
old mode 100644
new mode 100755
index 3282e29..d5a4590
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
@@ -36,7 +36,7 @@ public class NodeCounterEditor implements Editor {
 
     public static final String DATA_NODE_NAME = ":index";
     public static final String COUNT_PROPERTY_NAME = ":count";
-    private static final int DEFAULT_RESOLUTION = 1000;
+    public static final int DEFAULT_RESOLUTION = 1000;
     
     private final NodeCounterRoot root;
     private final NodeCounterEditor parent;
diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
old mode 100644
new mode 100755
index 106e55d..193aca2
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
@@ -30,6 +30,7 @@ import javax.annotation.Nonnull;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.counter.NodeCounterEditor;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
 import org.apache.jackrabbit.oak.query.FilterIterators;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
@@ -181,110 +182,113 @@ public class ContentMirrorStoreStrategy implements IndexStoreStrategy {
     public long count(Filter filter, NodeState root, NodeState indexMeta, final String indexStorageNodeName,
             Set<String> values, int max) {
         NodeState index = indexMeta.getChildNode(indexStorageNodeName);
-        long count = 0;
+        long count = -1;
         if (values == null) {
             PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);
             if (ec != null) {
-                count = ec.getValue(Type.LONG);
-                if (count >= 0) {
-                    return count;
-                }
+                count = ec.getValue(Type.LONG);//negative value implies fall-back to counting
+            } else {
+                count = ApproximateCounter.getCountSync(index);//negative value means that approximation isn't available
             }
-            if (count == 0) {
-                long approxCount = ApproximateCounter.getCountSync(index);
-                if (approxCount != -1) {
-                    return approxCount;
+            if (count < 0) {
+                CountingNodeVisitor v = new CountingNodeVisitor(max);
+                v.visit(index);
+                count = v.getEstimatedCount();
+                if (count >= max) {
+                    // "is not null" queries typically read more data
+                    count *= 10;
                 }
             }
-            CountingNodeVisitor v = new CountingNodeVisitor(max);
-            v.visit(index);
-            count = v.getEstimatedCount();
-            if (count >= max) {
-                // "is not null" queries typically read more data
-                count *= 10;
-            }
         } else {
             int size = values.size();
-            if (size == 0) {
-                return 0;
-            }
-            PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);       
-            if (ec != null) {
-                count = ec.getValue(Type.LONG);
-                if (count >= 0) {
-                    // assume 10000 entries per key, so that this index is used
-                    // instead of traversal, but not instead of a regular property index
-                    long keyCount = count / 10000;
-                    ec = indexMeta.getProperty(KEY_COUNT_PROPERTY_NAME);
-                    if (ec != null) {
-                        keyCount = ec.getValue(Type.LONG);
+            if (size != 0) {
+                long approxMax = 0;
+
+                PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);
+                if (ec != null) {
+                    count = ec.getValue(Type.LONG);
+                    if (count >= 0) {
+                        // assume 10*NodeCounterEditor.DEFAULT_RESOLUTION entries per key, so that this index is used
+                        // instead of traversal, but not instead of a regular property index
+                        long keyCount = count / (10 * NodeCounterEditor.DEFAULT_RESOLUTION);
+                        ec = indexMeta.getProperty(KEY_COUNT_PROPERTY_NAME);
+                        if (ec != null) {
+                            keyCount = ec.getValue(Type.LONG);
+                        }
+                        // cast to double to avoid overflow
+                        // (entryCount could be Long.MAX_VALUE)
+                        // the cost is not multiplied by the size,
+                        // otherwise the traversing index might be used
+                        keyCount = Math.max(1, keyCount);
+                        count = (long) ((double) count / keyCount) + size;
                     }
-                    // cast to double to avoid overflow 
-                    // (entryCount could be Long.MAX_VALUE)
-                    // the cost is not multiplied by the size, 
-                    // otherwise the traversing index might be used              
-                    keyCount = Math.max(1, keyCount);
-                    return (long) ((double) count / keyCount) + size;
-                }
-            }
-            long approxMax = 0;
-            if (count == 0) {
-                long approxCount = ApproximateCounter.getCountSync(index);
-                if (approxCount != -1) {
-                    for (String p : values) {
-                        NodeState s = index.getChildNode(p);
-                        if (s.exists()) {
-                            long a = ApproximateCounter.getCountSync(s);
-                            if (a != -1) {
-                                approxMax += a;
+                } else {
+                    long approxCount = ApproximateCounter.getCountSync(index);
+                    if (approxCount != -1) {
+                        for (String p : values) {
+                            NodeState s = index.getChildNode(p);
+                            if (s.exists()) {
+                                long a = ApproximateCounter.getCountSync(s);
+                                if (a != -1) {
+                                    approxMax += a;
+                                } else if (approxMax > 0) {
+                                    approxMax += (10 * NodeCounterEditor.DEFAULT_RESOLUTION); //in absence of approx count for a key we should be conservative
+                                }
                             }
                         }
                     }
                 }
-            }
-            count = 0;
-            max = Math.max(10, max / size);
-            int i = 0;
-            String filterRootPath = null;
-            if (filter != null &&
-                    filter.getPathRestriction().equals(Filter.PathRestriction.ALL_CHILDREN)) {
-                filterRootPath = filter.getPath();
-            }
-            if (filterRootPath == null && approxMax > 0) {
-                // we do have an approximation, and
-                // there is no path filter
-                return approxMax;
-            }
-            for (String p : values) {
-                if (count > max && i > 3) {
-                    // the total count is extrapolated from the the number 
-                    // of values counted so far to the total number of values
-                    count = count * size / i;
-                    break;
-                }
-                NodeState s = index.getChildNode(p);
-                if (filterRootPath != null && s.exists()) {
-                    // Descend directly to path restriction inside index tree
-                    for (String pathFragment : PathUtils
-                            .elements(filterRootPath)) {
-                        s = s.getChildNode(pathFragment);
-                        if (!s.exists()) {
-                            break;
+
+                if (count < 0) {
+                    String filterRootPath = null;
+                    if (filter != null &&
+                            filter.getPathRestriction().equals(Filter.PathRestriction.ALL_CHILDREN)) {
+                        filterRootPath = filter.getPath();
+                    }
+                    if (filterRootPath == null && approxMax > 0) {
+                        // we do have an approximation, and
+                        // there is no path filter
+                        count = approxMax;
+                    } else {
+                        count = 0;
+                        max = Math.max(10, max / size);
+                        int i = 0;
+
+                        for (String p : values) {
+                            if (count > max && i > 3) {
+                                // the total count is extrapolated from the the number
+                                // of values counted so far to the total number of values
+                                count = count * size / i;
+                                break;
+                            }
+                            NodeState s = index.getChildNode(p);
+                            if (filterRootPath != null && s.exists()) {
+                                // Descend directly to path restriction inside index tree
+                                for (String pathFragment : PathUtils
+                                        .elements(filterRootPath)) {
+                                    s = s.getChildNode(pathFragment);
+                                    if (!s.exists()) {
+                                        break;
+                                    }
+                                }
+                            }
+                            if (s.exists()) {
+                                CountingNodeVisitor v = new CountingNodeVisitor(max);
+                                v.visit(s);
+                                count += v.getEstimatedCount();
+                            }
+                            i++;
+                        }
+                        if (approxMax > 0 && approxMax > count) {
+                            // we do have an approximation, and
+                            // it is higher than what we counted
+                            // (we don't count that far)
+                            count = approxMax;
                         }
                     }
                 }
-                if (s.exists()) {
-                    CountingNodeVisitor v = new CountingNodeVisitor(max);
-                    v.visit(s);
-                    count += v.getEstimatedCount();
-                }
-                i++;
-            }
-            if (approxMax > 0 && approxMax > count) {
-                // we do have an approximation, and
-                // it is higher than what we counted
-                // (we don't count that far)
-                count = approxMax;
+            } else {
+                count = 0;
             }
         }
         return count;
-- 
2.1.1

