Author: catholicon
Date: Wed Nov  1 13:38:54 2017
New Revision: 1813973

URL: http://svn.apache.org/viewvc?rev=1813973&view=rev
Log:
OAK-6735: Lucene Index: improved cost estimation by using document count per 
field

Review comments from Chetan:
1) field to cost map can be null
2) isPropertyField makes more sense in FieldNames

Also, added a few more tests with minor refactoring

Added:
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNamesTest.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java?rev=1813973&r1=1813972&r2=1813973&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
 Wed Nov  1 13:38:54 2017
@@ -128,4 +128,14 @@ public final class FieldNames {
     public static String createFacetFieldName(String pname) {
         return pname + "_facet";
     }
+
+    /**
+     * @return if {@code field} represents a field property indexed data
+     */
+    static boolean isPropertyField(String field) {
+        return !field.startsWith(ANALYZED_FIELD_PREFIX)
+                && !field.startsWith(FULLTEXT_RELATIVE_NODE)
+                && !field.startsWith(":")
+                && !field.endsWith("_facet");
+    }
 }

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1813973&r1=1813972&r2=1813973&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 Wed Nov  1 13:38:54 2017
@@ -280,7 +280,10 @@ class IndexPlanner {
 
             // Set a index based guess here. Unique would set its own value 
below
             if (useActualEntryCount && !definition.isEntryCountDefined()) {
-                
plan.setEstimatedEntryCount(getMaxPossibleNumDocs(result.propDefns));
+                int maxPossibleNumDocs = 
getMaxPossibleNumDocs(result.propDefns);
+                if (maxPossibleNumDocs >= 0) {
+                    plan.setEstimatedEntryCount(maxPossibleNumDocs);
+                }
             }
 
             if (sortOrder.isEmpty() && ft == null) {

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java?rev=1813973&r1=1813972&r2=1813973&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatistics.java
 Wed Nov  1 13:38:54 2017
@@ -27,40 +27,55 @@ import java.io.IOException;
 import java.util.Collections;
 import java.util.Map;
 
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.isPropertyField;
+
 /**
  * This class would populate some statistics from a reader. We want to be 
careful here such that
  * we only collect statistics which don't incur reads from the index i.e. we 
would only collect
  * stats that lucene would already have read into memory when the reader was 
opened.
  */
 public class IndexStatistics {
-    static final Logger LOG = LoggerFactory.getLogger(IndexStatistics.class);
+    private static final Logger LOG = 
LoggerFactory.getLogger(IndexStatistics.class);
     private final int numDocs;
     private final Map<String, Integer> numDocsForField;
+    private final boolean safelyInitialized;
+
+    // For ease of tests as there didn't seem an easy way to make an 
IndexReader delegator
+    // that would fail calls to reader on-demand.
+    static boolean failReadingFields = false;
+    static boolean failReadingFieldJcrTitle = false;
 
     /**
      * @param reader {@link IndexReader} for which statistics need to be 
collected.
      */
-    public IndexStatistics(IndexReader reader) {
+    IndexStatistics(IndexReader reader) {
         numDocs = reader.numDocs();
 
         Map<String, Integer> numDocsForField = Maps.newHashMap();
 
         Fields fields = null;
         try {
+            if (failReadingFields) {
+                throw new IOException("Synthetically fail to read fields");
+            }
             fields = MultiFields.getFields(reader);
         } catch (IOException e) {
             LOG.warn("Couldn't open fields for reader ({}). Won't extract doc 
count per field", reader);
             numDocsForField = null;
         }
 
+
         if (fields != null) {
             for(String f : fields) {
                 if (isPropertyField(f)) {
-                    int docCntForField = numDocs;
+                    int docCntForField = -1;
                     try {
+                        if (failReadingFieldJcrTitle && "jcr:title".equals(f)) 
{
+                            throw new IOException("Synthetically fail to read 
count for field jcr:title");
+                        }
                         docCntForField = reader.getDocCount(f);
                     } catch (IOException e) {
-                        LOG.warn("Couldn't read doc count for field {} via 
reader ({}). Would use numDocs for this field");
+                        LOG.warn("Couldn't read doc count for field {} via 
reader ({}).");
                     }
                     numDocsForField.put(f, docCntForField);
                 }
@@ -69,8 +84,10 @@ public class IndexStatistics {
 
         if (numDocsForField != null) {
             this.numDocsForField = 
Collections.unmodifiableMap(numDocsForField);
+            this.safelyInitialized = true;
         } else {
-            this.numDocsForField = null;
+            this.numDocsForField = Collections.emptyMap();
+            this.safelyInitialized = false;
         }
     }
 
@@ -84,9 +101,17 @@ public class IndexStatistics {
     /**
      * @param field Index field for which number of indexed documents are to 
be return
      * @return number of indexed documents (without subtracting potentially 
deleted ones)
-     *         for the given {@code field}.
+     *         for the given {@code field}.<br/>
+     *         -1: if index codec doesn't store doc-count-for-field 
statistics, OR <br/>
+     *             &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reader threw an 
exception while reading fields, OR <br/>
+     *             &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exception thrown while 
reading count for the field, OR <br/>
+     *             &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doc-count is asked for 
a non-property field.
      */
     public int getDocCountFor(String field) {
+        if (!safelyInitialized) {
+            return -1;
+        }
+
         int docCntForField = isPropertyField(field) ? 0 : -1;
         if (numDocsForField.containsKey(field)) {
             docCntForField = numDocsForField.get(field);
@@ -95,10 +120,4 @@ public class IndexStatistics {
         return docCntForField;
     }
 
-    private boolean isPropertyField(String field) {
-        return !field.startsWith(FieldNames.ANALYZED_FIELD_PREFIX)
-                && !field.startsWith(FieldNames.FULLTEXT_RELATIVE_NODE)
-                && !field.startsWith(":")
-                && !field.endsWith("_facet");
-    }
 }

Added: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNamesTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNamesTest.java?rev=1813973&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNamesTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNamesTest.java
 Wed Nov  1 13:38:54 2017
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.lucene;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.ANALYZED_FIELD_PREFIX;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.FULLTEXT;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.FULLTEXT_RELATIVE_NODE;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.isPropertyField;
+
+public class FieldNamesTest {
+    @Test
+    public void checkPropertyFieldNames() {
+        assertPropertyFieldName("abc", true);
+        assertPropertyFieldName("a:b", true);
+        assertPropertyFieldName("a/b", true);
+        assertPropertyFieldName("a/b:c", true);
+        assertPropertyFieldName("a:b/c", true);
+
+        assertPropertyFieldName(FULLTEXT_RELATIVE_NODE + "a", false);
+        assertPropertyFieldName(ANALYZED_FIELD_PREFIX + "a", false);
+        assertPropertyFieldName(FULLTEXT, false);
+        assertPropertyFieldName(":abc", false);
+        assertPropertyFieldName("abc_facet", false);
+        assertPropertyFieldName("a:b_facet", false);
+        assertPropertyFieldName("a/b_facet", false);
+        assertPropertyFieldName("a/b:c_facet", false);
+        assertPropertyFieldName("a:b/c_facet", false);
+    }
+
+    private void assertPropertyFieldName(String name, boolean expected) {
+        Assert.assertTrue("Check for field name " + name + " doesn't meet 
expectation - " + expected,
+                isPropertyField(name) == expected);
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNamesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java?rev=1813973&r1=1813972&r2=1813973&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 Wed Nov  1 13:38:54 2017
@@ -1292,28 +1292,28 @@ public class IndexPlannerTest {
     @Test
     public void useNumDocsOnFieldForCost() throws Exception {
         NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo", "foo1", "foo2"), "async");
-        long numofDocs = IndexDefinition.DEFAULT_ENTRY_COUNT + 1000;
+        long numOfDocs = IndexDefinition.DEFAULT_ENTRY_COUNT + 1000;
 
         IndexDefinition idxDefn = new IndexDefinition(root, 
defn.getNodeState(), "/test");
         Document doc = new Document();
         doc.add(new StringField("foo1", "bar1", Field.Store.NO));
-        Directory sampleDirectory = createSampleDirectory(numofDocs, doc);
+        Directory sampleDirectory = createSampleDirectory(numOfDocs, doc);
         IndexNode node = createIndexNode(idxDefn, sampleDirectory);
 
         // Query on "foo"
         FilterImpl filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        IndexPlanner planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node, "/test", filter, 
Collections.emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
-        assertEquals(numofDocs, plan.getEstimatedEntryCount());
+        assertEquals(numOfDocs, plan.getEstimatedEntryCount());
         assertEquals(1.0, plan.getCostPerExecution(), 0);
         assertEquals(1.0, plan.getCostPerEntry(), 0);
 
         // Query on "foo1"
         filter = createFilter("nt:base");
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
-        planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, "/test", filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         assertEquals(1, plan.getEstimatedEntryCount());
@@ -1324,7 +1324,7 @@ public class IndexPlannerTest {
         filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
-        planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, "/test", filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         assertEquals(1, plan.getEstimatedEntryCount());
@@ -1334,7 +1334,7 @@ public class IndexPlannerTest {
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
         filter.restrictProperty("foo2", Operator.EQUAL, 
PropertyValues.newString("bar2"));
-        planner = new IndexPlanner(node, "/test", filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, "/test", filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         assertEquals(0, plan.getEstimatedEntryCount());
@@ -1368,7 +1368,7 @@ public class IndexPlannerTest {
         // Query on "foo"
         FilterImpl filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
         //scale down 1000 by 500 = 2
@@ -1377,7 +1377,7 @@ public class IndexPlannerTest {
         // Query on "foo1"
         filter = createFilter("nt:base");
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         //scale down 60 by 20 = 2
@@ -1387,7 +1387,7 @@ public class IndexPlannerTest {
         filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         //min(2, 3)
@@ -1397,7 +1397,7 @@ public class IndexPlannerTest {
         filter = createFilter("nt:base");
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.restrictProperty("foo2", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         //don't scale down 1 by 0 (foo1 would estimate 3)
@@ -1407,7 +1407,7 @@ public class IndexPlannerTest {
         filter = createFilter("nt:base");
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.restrictProperty("foo3", Operator.EQUAL, 
PropertyValues.newString("bar"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         //min(0, 3)
@@ -1442,7 +1442,7 @@ public class IndexPlannerTest {
         // Query on and "bar1"
         FilterImpl filter = createFilter("nt:base");
         filter.restrictProperty("bar1", Operator.EQUAL, 
PropertyValues.newString("foo1"));
-        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
         //scale down 60 by 20 = 3
@@ -1452,7 +1452,7 @@ public class IndexPlannerTest {
         filter = createFilter("nt:base");
         filter.restrictProperty("bar1", Operator.EQUAL, 
PropertyValues.newString("foo1"));
         filter.restrictProperty("bar2", Operator.EQUAL, 
PropertyValues.newString("foo2"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         //min(3, 2)
@@ -1482,7 +1482,7 @@ public class IndexPlannerTest {
         // Query on and "foo"
         FilterImpl filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("foo1"));
-        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
         //scale down 1000 by INT_MAX/2 and ceil ~= 1.
@@ -1492,7 +1492,7 @@ public class IndexPlannerTest {
         filter = createFilter("nt:base");
         filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar1"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         //min(1, 60)
@@ -1519,7 +1519,7 @@ public class IndexPlannerTest {
         FilterImpl filter = createFilter("nt:base");
         filter.restrictProperty(convertToPolishNotation("lower([foo])"), 
Operator.EQUAL,
                 PropertyValues.newString("foo1"));
-        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
         assertEquals(1, plan.getEstimatedEntryCount());
@@ -1534,22 +1534,22 @@ public class IndexPlannerTest {
                 .enclosingRule().property("foo2").analyzed();
         NodeState defn = idxBuilder.build();
 
-        long numofDocs = IndexDefinition.DEFAULT_ENTRY_COUNT + 1000;
+        long numOfDocs = IndexDefinition.DEFAULT_ENTRY_COUNT + 1000;
 
         IndexDefinition idxDefn = new IndexDefinition(root, defn, indexPath);
         Document doc = new Document();
         doc.add(new StringField("foo1", "bar1", Field.Store.NO));
-        Directory sampleDirectory = createSampleDirectory(numofDocs, doc);
+        Directory sampleDirectory = createSampleDirectory(numOfDocs, doc);
         IndexNode node = createIndexNode(idxDefn, sampleDirectory);
 
         // contains(., 'mountain') AND contains('foo2', 'hill')
         FilterImpl filter = createFilter("nt:base");
         filter.setFullTextConstraint(FullTextParser.parse(".", "mountain"));
         filter.setFullTextConstraint(FullTextParser.parse("foo2", "hill"));
-        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         QueryIndex.IndexPlan plan = planner.getPlan();
 
-        assertEquals(numofDocs + 1, plan.getEstimatedEntryCount());
+        assertEquals(numOfDocs + 1, plan.getEstimatedEntryCount());
         assertEquals(1.0, plan.getCostPerExecution(), 0);
         assertEquals(1.0, plan.getCostPerEntry(), 0);
 
@@ -1558,13 +1558,112 @@ public class IndexPlannerTest {
         filter.setFullTextConstraint(FullTextParser.parse(".", "mountain"));
         filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
         filter.setFullTextConstraint(FullTextParser.parse("foo2", "hill"));
-        planner = new IndexPlanner(node, indexPath, filter, 
Collections.<OrderEntry>emptyList());
+        planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
         plan = planner.getPlan();
 
         assertEquals(1, plan.getEstimatedEntryCount());
         assertEquals(1.0, plan.getCostPerExecution(), 0);
         assertEquals(1.0, plan.getCostPerEntry(), 0);
     }
+
+    @Test
+    public void unableToIterateFields() throws Exception {
+        try {
+            IndexStatistics.failReadingFields = true;
+            String indexPath = "/test";
+            IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+            idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+            idxBuilder.indexRule("nt:base").property("bar").propertyIndex();
+            NodeState defn = idxBuilder.build();
+
+            long numOfDocs = 100;
+
+            IndexDefinition idxDefn = new IndexDefinition(root, defn, 
indexPath);
+            IndexNode node = createIndexNode(idxDefn, numOfDocs);
+
+            FilterImpl filter = createFilter("nt:base");
+            filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            QueryIndex.IndexPlan plan = planner.getPlan();
+
+            assertEquals(numOfDocs, plan.getEstimatedEntryCount());
+
+            filter = createFilter("nt:base");
+            filter.restrictProperty("bar", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            plan = planner.getPlan();
+
+            assertEquals(numOfDocs, plan.getEstimatedEntryCount());
+
+            filter = createFilter("nt:base");
+            filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            filter.restrictProperty("bar", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            plan = planner.getPlan();
+
+            assertEquals(numOfDocs, plan.getEstimatedEntryCount());
+        } finally {
+            IndexStatistics.failReadingFields = false;
+        }
+    }
+
+    @Test
+    public void unableToReadCountForJcrTitle() throws Exception {
+        try {
+            IndexStatistics.failReadingFieldJcrTitle = true;
+            String indexPath = "/test";
+            IndexDefinitionBuilder idxBuilder = new 
IndexDefinitionBuilder(child(builder, indexPath));
+            idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+            idxBuilder.indexRule("nt:base").property("foo1").propertyIndex();
+            
idxBuilder.indexRule("nt:base").property("jcr:title").propertyIndex();
+            idxBuilder.indexRule("nt:base").property("bar").propertyIndex();
+            NodeState defn = idxBuilder.build();
+
+            long numOfDocs = 100;
+
+            IndexDefinition idxDefn = new IndexDefinition(root, defn, 
indexPath);
+            Document doc = new Document();
+            doc.add(new StringField("foo1", "bar1", Field.Store.NO));
+            doc.add(new StringField("jcr:title", "title", Field.Store.NO));
+            Directory sampleDirectory = createSampleDirectory(numOfDocs, doc);
+            IndexNode node = createIndexNode(idxDefn, sampleDirectory);
+
+            FilterImpl filter = createFilter("nt:base");
+            filter.restrictProperty("foo", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            IndexPlanner planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            QueryIndex.IndexPlan plan = planner.getPlan();
+
+            assertEquals(numOfDocs, plan.getEstimatedEntryCount());
+
+            filter = createFilter("nt:base");
+            filter.restrictProperty("jcr:title", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            plan = planner.getPlan();
+
+            // jcr:title's count couldn't be read - so, fallback to numDocs
+            assertEquals(numOfDocs + 1, plan.getEstimatedEntryCount());
+
+            filter = createFilter("nt:base");
+            filter.restrictProperty("foo1", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            filter.restrictProperty("jcr:title", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            plan = planner.getPlan();
+
+            // min() still comes into play even when one field's count 
couldn't be read
+            assertEquals(1, plan.getEstimatedEntryCount());
+
+            filter = createFilter("nt:base");
+            filter.restrictProperty("bar", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            filter.restrictProperty("jcr:title", Operator.EQUAL, 
PropertyValues.newString("bar"));
+            planner = new IndexPlanner(node, indexPath, filter, 
Collections.emptyList());
+            plan = planner.getPlan();
+
+            // min() still comes into play even when one field's count 
couldn't be read
+            assertEquals(0, plan.getEstimatedEntryCount());
+        } finally {
+            IndexStatistics.failReadingFieldJcrTitle = false;
+        }
+    }
     //------ END - Cost via doc count per field plan tests
 
 

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java?rev=1813973&r1=1813972&r2=1813973&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexStatisticsTest.java
 Wed Nov  1 13:38:54 2017
@@ -27,7 +27,7 @@ import org.apache.lucene.index.IndexWrit
 import org.apache.lucene.index.Term;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.RAMDirectory;
-import org.junit.Assert;
+import org.junit.After;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -35,15 +35,21 @@ import java.util.Collections;
 import java.util.List;
 
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
+import static org.junit.Assert.assertEquals;
 
 public class IndexStatisticsTest {
+    @After
+    public void resetFailFlags() {
+        IndexStatistics.failReadingFields = false;
+        IndexStatistics.failReadingFieldJcrTitle = false;
+    }
 
     @Test
     public void numDocs() throws Exception {
         Directory d = createSampleDirectory(2);
         IndexStatistics stats = getStats(d);
 
-        Assert.assertEquals(2, stats.numDocs());
+        assertEquals(2, stats.numDocs());
     }
 
     @Test
@@ -56,7 +62,7 @@ public class IndexStatisticsTest {
         }
 
         IndexStatistics stats = getStats(d);
-        Assert.assertEquals(1, stats.numDocs());
+        assertEquals(1, stats.numDocs());
     }
 
     @Test
@@ -64,7 +70,7 @@ public class IndexStatisticsTest {
         Directory d = createSampleDirectory(2);
         IndexStatistics stats = getStats(d);
 
-        Assert.assertEquals(2, stats.getDocCountFor("foo"));
+        assertEquals(2, stats.getDocCountFor("foo"));
     }
 
     @Test
@@ -77,7 +83,7 @@ public class IndexStatisticsTest {
         }
 
         IndexStatistics stats = getStats(d);
-        Assert.assertEquals("Stats don't need to get accurate result which 
might require reading more",
+        assertEquals("Stats don't need to get accurate result which might 
require reading more",
                 2, stats.getDocCountFor("foo"));
     }
 
@@ -86,12 +92,12 @@ public class IndexStatisticsTest {
         Directory d = createSampleDirectory(1);
         IndexStatistics stats = getStats(d);
 
-        Assert.assertEquals(1, stats.getDocCountFor("foo"));
-        Assert.assertEquals(0, stats.getDocCountFor("absent"));
-        Assert.assertEquals(-1, stats.getDocCountFor(":someHiddenField"));
-        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.ANALYZED_FIELD_PREFIX + "foo"));
-        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.FULLTEXT_RELATIVE_NODE + "foo"));
-        Assert.assertEquals(-1, stats.getDocCountFor("foo_facet"));
+        assertEquals(1, stats.getDocCountFor("foo"));
+        assertEquals(0, stats.getDocCountFor("absent"));
+        assertEquals(-1, stats.getDocCountFor(":someHiddenField"));
+        assertEquals(-1, stats.getDocCountFor(FieldNames.ANALYZED_FIELD_PREFIX 
+ "foo"));
+        assertEquals(-1, 
stats.getDocCountFor(FieldNames.FULLTEXT_RELATIVE_NODE + "foo"));
+        assertEquals(-1, stats.getDocCountFor("foo_facet"));
     }
 
     @Test
@@ -105,12 +111,39 @@ public class IndexStatisticsTest {
         Directory d = createSampleDirectory(document);
         IndexStatistics stats = getStats(d);
 
-        Assert.assertEquals(3, stats.getDocCountFor("foo"));
-        Assert.assertEquals(0, stats.getDocCountFor("absent"));
-        Assert.assertEquals(-1, stats.getDocCountFor(":someHiddenField"));
-        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.ANALYZED_FIELD_PREFIX + "foo"));
-        Assert.assertEquals(-1, 
stats.getDocCountFor(FieldNames.FULLTEXT_RELATIVE_NODE + "foo"));
-        Assert.assertEquals(-1, stats.getDocCountFor("foo_facet"));
+        assertEquals(3, stats.getDocCountFor("foo"));
+        assertEquals(0, stats.getDocCountFor("absent"));
+        assertEquals(-1, stats.getDocCountFor(":someHiddenField"));
+        assertEquals(-1, stats.getDocCountFor(FieldNames.ANALYZED_FIELD_PREFIX 
+ "foo"));
+        assertEquals(-1, 
stats.getDocCountFor(FieldNames.FULLTEXT_RELATIVE_NODE + "foo"));
+        assertEquals(-1, stats.getDocCountFor("foo_facet"));
+    }
+
+    @Test
+    public void unableToIterateFields() throws Exception {
+        IndexStatistics.failReadingFields = true;
+
+        IndexStatistics stats = getStats(createSampleDirectory(100));
+
+        assertEquals(100, stats.numDocs());
+        assertEquals(-1, stats.getDocCountFor("foo"));
+        assertEquals(-1, stats.getDocCountFor("bar"));
+    }
+
+    @Test
+    public void unableToReadCountForJcrTitle() throws Exception {
+        IndexStatistics.failReadingFieldJcrTitle = true;
+
+        Document doc = new Document();
+        doc.add(new StringField("foo1", "bar1", Field.Store.NO));
+        doc.add(new StringField("jcr:title", "title", Field.Store.NO));
+        IndexStatistics stats = getStats(createSampleDirectory(doc));
+
+        assertEquals(3, stats.numDocs());
+        assertEquals(2, stats.getDocCountFor("foo"));
+        assertEquals(1, stats.getDocCountFor("foo1"));
+        assertEquals(-1, stats.getDocCountFor("jcr:title"));
+        assertEquals(0, stats.getDocCountFor("bar"));
     }
 
     private static Directory createSampleDirectory(long numOfDocs) throws 
IOException {


Reply via email to