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/>
+ * reader threw an
exception while reading fields, OR <br/>
+ * exception thrown while
reading count for the field, OR <br/>
+ * 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 {