Author: chetanm
Date: Sat Feb 14 09:11:30 2015
New Revision: 1659765
URL: http://svn.apache.org/r1659765
Log:
OAK-2340 - LucenePropertyIndex should support pure nodeType based query
LuceneIndex would opt in for pure nodeType queries if and only if the index
definition ensures that all nodes are index for a give type
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.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/LucenePropertyIndex.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/LucenePropertyIndexTest.java
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1659765&r1=1659764&r2=1659765&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
Sat Feb 14 09:11:30 2015
@@ -724,6 +724,10 @@ class IndexDefinition implements Aggrega
return indexesAllNodesOfMatchingType;
}
+ public boolean isBasedOnNtBase(){
+ return JcrConstants.NT_BASE.equals(baseNodeType);
+ }
+
private Map<String, PropertyDefinition> collectPropConfigs(NodeState
config, List<NamePattern> patterns,
List<Aggregate.Include> propAggregate) {
Map<String, PropertyDefinition> propDefns = newHashMap();
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=1659765&r1=1659764&r2=1659765&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
Sat Feb 14 09:11:30 2015
@@ -147,6 +147,7 @@ class IndexPlanner {
}
}
+ boolean evalNodeTypeRestrictions =
canEvalNodeTypeRestrictions(indexingRule);
boolean evalPathRestrictions = canEvalPathRestrictions(indexingRule);
boolean canEvalAlFullText = canEvalAllFullText(indexingRule, ft);
@@ -158,7 +159,7 @@ class IndexPlanner {
List<OrderEntry> sortOrder = createSortOrder(indexingRule);
boolean canSort = canSortByProperty(sortOrder);
- if (!indexedProps.isEmpty() || canSort || ft != null ||
evalPathRestrictions) {
+ if (!indexedProps.isEmpty() || canSort || ft != null ||
evalPathRestrictions || evalNodeTypeRestrictions) {
//TODO Need a way to have better cost estimate to indicate that
//this index can evaluate more propertyRestrictions natively (if
more props are indexed)
//For now we reduce cost per entry
@@ -180,16 +181,16 @@ class IndexPlanner {
result.enableNonFullTextConstraints();
}
+ if (evalNodeTypeRestrictions){
+ result.enableNodeTypeEvaluation();
+ }
+
return plan.setCostPerEntry(definition.getCostPerEntry() /
costPerEntryFactor);
}
//TODO Support for property existence queries
//TODO support for nodeName queries
- //Above logic would not return any plan for pure nodeType based query
like
- //select * from nt:unstructured. We can do that but this is better
handled
- //by NodeType index
-
return null;
}
@@ -291,6 +292,18 @@ class IndexPlanner {
return definition.evaluatePathRestrictions() &&
rule.indexesAllNodesOfMatchingType();
}
+
+ private boolean canEvalNodeTypeRestrictions(IndexingRule rule) {
+ //No need to handle nt:base
+ if (filter.matchesAllTypes()){
+ return false;
+ }
+
+ //Only opt in if rule is not derived from nt:base otherwise it would
+ //get used when there a full text index on all nodes
+ return rule.indexesAllNodesOfMatchingType() && !rule.isBasedOnNtBase();
+ }
+
private IndexPlan.Builder defaultPlan() {
return new IndexPlan.Builder()
.setCostPerExecution(definition.getCostPerExecution())
@@ -404,6 +417,7 @@ class IndexPlanner {
private int parentDepth;
private String parentPathSegment;
private boolean relativize;
+ private boolean nodeTypeRestrictions;
public PlanResult(String indexPath, IndexDefinition defn, IndexingRule
indexingRule) {
this.indexPath = indexPath;
@@ -449,6 +463,10 @@ class IndexPlanner {
return nonFullTextConstraints;
}
+ public boolean evaluateNodeTypeRestriction() {
+ return nodeTypeRestrictions;
+ }
+
private void setParentPath(String relativePath){
parentPathSegment = "/" + relativePath;
if (relativePath.isEmpty()){
@@ -464,5 +482,9 @@ class IndexPlanner {
private void enableNonFullTextConstraints(){
nonFullTextConstraints = true;
}
+
+ private void enableNodeTypeEvaluation() {
+ nodeTypeRestrictions = true;
+ }
}
}
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java?rev=1659765&r1=1659764&r2=1659765&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
Sat Feb 14 09:11:30 2015
@@ -20,7 +20,6 @@ package org.apache.jackrabbit.oak.plugin
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
@@ -561,10 +560,19 @@ public class LucenePropertyIndex impleme
}
if (qs.size() == 0) {
- if (!defn.isFullTextEnabled()) {
- throw new IllegalStateException("No query created for filter "
+ filter);
+ if (reader == null){
+ //When called in planning mode then some queries like
rep:similar
+ //cannot create query as reader is not provided. In such case
we
+ //just return match all queries
+ return new LuceneRequestFacade<Query>(new MatchAllDocsQuery());
}
- return new LuceneRequestFacade<Query>(new MatchAllDocsQuery());
+ //For purely nodeType based queries all the documents would have to
+ //be returned (if the index definition has a single rule)
+ if (planResult.evaluateNodeTypeRestriction()) {
+ return new LuceneRequestFacade<Query>(new MatchAllDocsQuery());
+ }
+
+ throw new IllegalStateException("No query created for filter " +
filter);
}
if (qs.size() == 1) {
return new LuceneRequestFacade<Query>(qs.get(0));
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=1659765&r1=1659764&r2=1659765&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
Sat Feb 14 09:11:30 2015
@@ -51,6 +51,7 @@ import static com.google.common.base.Pre
import static com.google.common.collect.ImmutableSet.of;
import static javax.jcr.PropertyType.TYPENAME_STRING;
import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
import static org.apache.jackrabbit.oak.api.Type.STRINGS;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_DATA_CHILD_NAME;
@@ -183,6 +184,75 @@ public class IndexPlannerTest {
}
@Test
+ public void fulltextIndexAndNodeTypeRestriction() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo"), "async");
+ defn.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ defn.setProperty(IndexConstants.DECLARING_NODE_TYPES, of("nt:file"),
NAMES);
+
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:file/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+
+ IndexNode node = createIndexNode(new IndexDefinition(root,
defn.getNodeState()));
+ FilterImpl filter = createFilter("nt:file");
+ IndexPlanner planner = new IndexPlanner(node, "/foo", filter,
Collections.<OrderEntry>emptyList());
+
+ //For case when a full text property is present then path restriction
can be
+ //evaluated
+ assertNotNull(planner.getPlan());
+ }
+
+ @Test
+ public void purePropertyIndexAndNodeTypeRestriction() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo"), "async");
+ defn.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ defn.setProperty(IndexConstants.DECLARING_NODE_TYPES, of("nt:file"),
NAMES);
+
+ IndexNode node = createIndexNode(new IndexDefinition(root,
defn.getNodeState()));
+ FilterImpl filter = createFilter("nt:file");
+ IndexPlanner planner = new IndexPlanner(node, "/foo", filter,
Collections.<OrderEntry>emptyList());
+
+ assertNull(planner.getPlan());
+ }
+
+ @Test
+ public void purePropertyIndexAndNodeTypeRestriction2() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo"), "async");
+ defn.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+
+ IndexNode node = createIndexNode(new IndexDefinition(root,
defn.getNodeState()));
+ FilterImpl filter = createFilter("nt:file");
+ IndexPlanner planner = new IndexPlanner(node, "/foo", filter,
Collections.<OrderEntry>emptyList());
+
+ //No plan should be result for a index with just a rule for nt:base
+ assertNull(planner.getPlan());
+ }
+
+ @Test
+ public void purePropertyIndexAndNodeTypeRestriction3() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo"), "async");
+ defn.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ defn.setProperty(IndexConstants.DECLARING_NODE_TYPES, of("nt:file"),
NAMES);
+
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:file/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+
+ IndexNode node = createIndexNode(new IndexDefinition(root,
defn.getNodeState()));
+ FilterImpl filter = createFilter("nt:file");
+ IndexPlanner planner = new IndexPlanner(node, "/foo", filter,
Collections.<OrderEntry>emptyList());
+
+ QueryIndex.IndexPlan plan = planner.getPlan();
+ assertNotNull(plan);
+ assertNotNull(pr(plan));
+ assertTrue(pr(plan).evaluateNodeTypeRestriction());
+ }
+
+ @Test
public void worksWithIndexFormatV2Onwards() throws Exception{
NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
NodeBuilder nb = newLuceneIndexDefinition(index, "lucene",
Modified:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java?rev=1659765&r1=1659764&r2=1659765&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
Sat Feb 14 09:11:30 2015
@@ -58,7 +58,9 @@ import static java.util.Arrays.asList;
import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
import static org.apache.jackrabbit.oak.api.QueryEngine.NO_MAPPINGS;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
@@ -184,7 +186,7 @@ public class LucenePropertyIndexTest ext
Tree indexWithType = createIndex("test2", of("propa"));
indexWithType.setProperty(PropertyStates
- .createProperty(IndexConstants.DECLARING_NODE_TYPES,
of("nt:unstructured"),
+ .createProperty(DECLARING_NODE_TYPES, of("nt:unstructured"),
Type.STRINGS));
Tree test = root.getTree("/").addChild("test");
@@ -216,7 +218,7 @@ public class LucenePropertyIndexTest ext
public void declaringNodeTypeSingleIndex() throws Exception {
Tree indexWithType = createIndex("test2", of("propa", "propb"));
indexWithType.setProperty(PropertyStates
- .createProperty(IndexConstants.DECLARING_NODE_TYPES,
of("nt:unstructured"),
+ .createProperty(DECLARING_NODE_TYPES, of("nt:unstructured"),
Type.STRINGS));
Tree test = root.getTree("/").addChild("test");
@@ -246,6 +248,59 @@ public class LucenePropertyIndexTest ext
}
@Test
+ public void usedAsNodeTypeIndex() throws Exception {
+ Tree nodeTypeIdx = root.getTree("/oak:index/nodetype");
+
nodeTypeIdx.setProperty(PropertyStates.createProperty(DECLARING_NODE_TYPES,
of("nt:resource"), NAMES));
+ nodeTypeIdx.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
+
+ Tree indexWithType = createIndex("test2",
of(JcrConstants.JCR_PRIMARYTYPE, "propb"));
+
indexWithType.setProperty(PropertyStates.createProperty(DECLARING_NODE_TYPES,
of("nt:file"), NAMES));
+
+ Tree test = root.getTree("/").addChild("test");
+ setNodeType(test, "nt:file");
+ root.commit();
+
+ setNodeType(test.addChild("a"), "nt:file");
+ setNodeType(test.addChild("b"), "nt:file");
+ setNodeType(test.addChild("c"), "nt:base");
+ root.commit();
+
+ String propabQuery = "select [jcr:path] from [nt:file]";
+ assertThat(explain(propabQuery), containsString("lucene:test2"));
+ assertQuery(propabQuery, asList("/test/a", "/test/b", "/test"));
+ }
+
+ @Test
+ public void usedAsNodeTypeIndex2() throws Exception {
+ //prevent the default nodeType index from indexing all types
+ Tree nodeTypeIdx = root.getTree("/oak:index/nodetype");
+
nodeTypeIdx.setProperty(PropertyStates.createProperty(DECLARING_NODE_TYPES,
of("nt:resource"), NAMES));
+ nodeTypeIdx.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
+
+ Tree indexWithType = createIndex("test2", of("propb"));
+
indexWithType.setProperty(PropertyStates.createProperty(DECLARING_NODE_TYPES,
of("nt:file"), NAMES));
+ indexWithType.setProperty(LuceneIndexConstants.FULL_TEXT_ENABLED,
true);
+ TestUtil.useV2(indexWithType);
+
+ Tree test = root.getTree("/").addChild("test");
+ setNodeType(test, "nt:file");
+ root.commit();
+
+ setNodeType(test.addChild("a"), "nt:file");
+ setNodeType(test.addChild("b"), "nt:file");
+ setNodeType(test.addChild("c"), "nt:base");
+ root.commit();
+
+ String propabQuery = "select [jcr:path] from [nt:file]";
+ assertThat(explain(propabQuery), containsString("lucene:test2"));
+ assertQuery(propabQuery, asList("/test/a", "/test/b", "/test"));
+ }
+
+ private static void setNodeType(Tree t, String typeName){
+ t.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
+ }
+
+ @Test
public void emptyIndex() throws Exception{
Tree idx = createIndex("test1", of("propa", "propb"));
idx.addChild(PROP_NODE).addChild("propa");
@@ -905,8 +960,8 @@ public class LucenePropertyIndexTest ext
private String measureWithLimit(String query, String lang, int limit)
throws ParseException {
List<? extends ResultRow> result = Lists.newArrayList(
- qe.executeQuery(query, lang, limit, 0, Maps.<String,
PropertyValue>newHashMap(),
- NO_MAPPINGS).getRows());
+ qe.executeQuery(query, lang, limit, 0, Maps.<String,
PropertyValue>newHashMap(),
+ NO_MAPPINGS).getRows());
String measure = "";
if (result.size() > 0) {