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) {


Reply via email to