Author: chetanm
Date: Wed Aug 17 06:33:33 2016
New Revision: 1756580

URL: http://svn.apache.org/viewvc?rev=1756580&view=rev
Log:
OAK-4676 - Index definition on nt:base with analyzed property incorrectly 
matches query with different property

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/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=1756580&r1=1756579&r2=1756580&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
 Wed Aug 17 06:33:33 2016
@@ -391,10 +391,6 @@ public final class IndexDefinition imple
         return evaluatePathRestrictions;
     }
 
-    public boolean indexesAllTypes() {
-        return indexesAllTypes;
-    }
-
     public Analyzer getAnalyzer(){
         return analyzer;
     }

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=1756580&r1=1756579&r2=1756580&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 Aug 17 06:33:33 2016
@@ -50,6 +50,7 @@ import static com.google.common.collect.
 import static com.google.common.collect.Lists.newArrayListWithCapacity;
 import static com.google.common.collect.Maps.newHashMap;
 import static org.apache.jackrabbit.JcrConstants.JCR_SCORE;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getAncestorPath;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getDepth;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
@@ -352,9 +353,15 @@ class IndexPlanner {
                 if (nodePath != null
                         && !indexingRule.isAggregated(nodePath)){
                     nonIndexedPaths.add(p);
-                } else if (propertyPath != null
-                        && !indexingRule.isIndexed(propertyPath)){
-                    nonIndexedPaths.add(p);
+                } else if (propertyPath != null) {
+                    PropertyDefinition pd = 
indexingRule.getConfig(propertyPath);
+                    //If given prop is not analyzed then its
+                    //not indexed
+                    if (pd == null){
+                        nonIndexedPaths.add(p);
+                    } else if (!pd.analyzed){
+                        nonIndexedPaths.add(p);
+                    }
                 }
 
                 if (nodeScopedTerm(propertyName)){
@@ -372,15 +379,44 @@ class IndexPlanner {
             return false;
         }
 
+        //where contains('jcr:content/bar', 'mountain OR valley') and 
contains('jcr:content/foo', 'mountain OR valley')
+        //above query can be evaluated by index which indexes foo and bar with 
restriction that both belong to same node
+        //by displacing the query path to evaluate on contains('bar', ...) and 
filter out those parents which do not
+        //have jcr:content as parent. So ensure that relPaths size is 1 or 0
         if (!nonIndexedPaths.isEmpty()){
             if (relPaths.size() > 1){
                 log.debug("Following relative  property paths are not index", 
relPaths);
                 return false;
             }
             result.setParentPath(Iterables.getOnlyElement(relPaths, ""));
-            //Such path translation would only work if index contains
-            //all the nodes
-            return definition.indexesAllTypes();
+
+            //Such non indexed path can possibly be evaluated via any rule on 
nt:base
+            //which can possibly index everything
+            IndexingRule rule = definition.getApplicableIndexingRule(NT_BASE);
+            if (rule == null){
+                return false;
+            }
+
+            for (String p : nonIndexedPaths){
+                //Index can only evaluate a node search jcr:content/*
+                //if it indexes node scope indexing is enabled
+                if (LucenePropertyIndex.isNodePath(p)){
+                    if (!rule.isNodeFullTextIndexed()) {
+                        return false;
+                    }
+                } else {
+                    //Index can only evaluate a property like jcr:content/type
+                    //if it indexes 'type' and that too analyzed
+                    String propertyName = PathUtils.getName(p);
+                    PropertyDefinition pd = rule.getConfig(propertyName);
+                    if (pd == null){
+                        return false;
+                    }
+                    if (!pd.analyzed){
+                        return false;
+                    }
+                }
+            }
         } else {
             result.setParentPath("");
         }

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=1756580&r1=1756579&r2=1756580&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 Aug 17 06:33:33 2016
@@ -45,6 +45,7 @@ import static org.junit.Assert.assertTru
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -62,6 +63,9 @@ import org.apache.jackrabbit.oak.query.a
 import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextContains;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextParser;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -561,6 +565,178 @@ public class IndexPlannerTest {
         assertNull(plan);
     }
 
+    @Test
+    public void fullTextQuery_RelativePath1() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");
+
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+        foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(), 
FullTextParser.parse("bar", "mountain"));
+
+        //No plan for unindex property
+        assertNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_IndexAllProps() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("allProps"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, 
"indexRules/nt:base/properties/allProps");
+        foob.setProperty(LuceneIndexConstants.PROP_NAME, 
LuceneIndexConstants.REGEX_ALL_PROPS);
+        foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+        foob.setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
+
+        FullTextExpression exp = FullTextParser.parse("bar", "mountain OR 
valley");
+        exp = new FullTextContains("bar", "mountain OR valley", exp);
+        IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(), 
exp);
+
+        //No plan for unindex property
+        assertNotNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_IndexAllProps_NodePathQuery() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("allProps"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, 
"indexRules/nt:base/properties/allProps");
+        foob.setProperty(LuceneIndexConstants.PROP_NAME, 
LuceneIndexConstants.REGEX_ALL_PROPS);
+        foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+        foob.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+        foob.setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
+
+        //where contains('jcr:content/*', 'mountain OR valley') can be 
evaluated by index
+        //on nt:base by evaluating on '.' and then checking if node name is 
'jcr:content'
+        IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+                FullTextParser.parse("jcr:content/*", "mountain OR valley"));
+
+        //No plan for unindex property
+        assertNotNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_IndexAllProps_AggregatedNodePathQuery() throws 
Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("allProps"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder agg = 
defn.child(LuceneIndexConstants.AGGREGATES).child("nt:base").child("include0");
+        agg.setProperty(LuceneIndexConstants.AGG_PATH, "jcr:content");
+        agg.setProperty(LuceneIndexConstants.AGG_RELATIVE_NODE, true);
+
+        //where contains('jcr:content/*', 'mountain OR valley') can be 
evaluated by index
+        //on nt:base by evaluating on '.' and then checking if node name is 
'jcr:content'
+        IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+                FullTextParser.parse("jcr:content/*", "mountain OR valley"));
+
+        //No plan for unindex property
+        assertNotNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_IndexAllProps_NodePathQuery_NoPlan() throws 
Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+        foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+        foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        //where contains('jcr:content/*', 'mountain OR valley') can be 
evaluated by index
+        //on nt:base by evaluating on '.' and then checking if node name is 
'jcr:content'
+        IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+                FullTextParser.parse("jcr:content/*", "mountain OR valley"));
+
+        //No plan for unindex property
+        assertNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_NonAnalyzedProp_NoPlan() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo", "bar"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+        foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+
+        NodeBuilder barb = getNode(defn, "indexRules/nt:base/properties/bar");
+        barb.setProperty(LuceneIndexConstants.PROP_NAME, "bar");
+        barb.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        //where contains('jcr:content/*', 'mountain OR valley') can be 
evaluated by index
+        //on nt:base by evaluating on '.' and then checking if node name is 
'jcr:content'
+        IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+                FullTextParser.parse("foo", "mountain OR valley"));
+
+        //No plan for unindex property
+        assertNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_RelativePropertyPaths() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo", "bar"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+        foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+        foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        NodeBuilder barb = getNode(defn, "indexRules/nt:base/properties/bar");
+        barb.setProperty(LuceneIndexConstants.PROP_NAME, "bar");
+        barb.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+
+        //where contains('jcr:content/bar', 'mountain OR valley') and 
contains('jcr:content/foo', 'mountain OR valley')
+        //above query can be evaluated by index which indexes foo and bar with 
restriction that both belong to same node
+        //by displacing the query path to evaluate on contains('bar', ...) and 
filter out those parents which do not
+        //have jcr:content as parent
+        FullTextExpression fooExp = FullTextParser.parse("jcr:content/bar", 
"mountain OR valley");
+        FullTextExpression barExp = FullTextParser.parse("jcr:content/foo", 
"mountain OR valley");
+        FullTextExpression exp = new FullTextAnd(Arrays.asList(fooExp, 
barExp));
+        IndexPlanner planner = 
createPlannerForFulltext(defn.getNodeState(),exp);
+
+        //No plan for unindex property
+        assertNotNull(planner.getPlan());
+    }
+
+    @Test
+    public void fullTextQuery_DisjointPropertyPaths() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo", "bar"), "async");
+
+        //Index all props and then perform fulltext
+        defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+        NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+        foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+        foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        NodeBuilder barb = getNode(defn, "indexRules/nt:base/properties/bar");
+        barb.setProperty(LuceneIndexConstants.PROP_NAME, "bar");
+        barb.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        FullTextExpression fooExp = FullTextParser.parse("metadata/bar", 
"mountain OR valley");
+        FullTextExpression barExp = FullTextParser.parse("jcr:content/foo", 
"mountain OR valley");
+        FullTextExpression exp = new FullTextAnd(Arrays.asList(fooExp, 
barExp));
+        IndexPlanner planner = 
createPlannerForFulltext(defn.getNodeState(),exp);
+
+        //No plan for unindex property
+        assertNull(planner.getPlan());
+    }
+
+    private IndexPlanner createPlannerForFulltext(NodeState defn, 
FullTextExpression exp) throws IOException {
+        IndexNode node = createIndexNode(new IndexDefinition(root, defn));
+        FilterImpl filter = createFilter("nt:base");
+        filter.setFullTextConstraint(exp);
+        return new IndexPlanner(node, "/foo", filter, 
Collections.<OrderEntry>emptyList());
+    }
+
     private IndexNode createSuggestionOrSpellcheckIndex(String nodeType,
                                                         boolean 
enableSuggestion,
                                                         boolean 
enableSpellcheck) throws Exception {

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=1756580&r1=1756579&r2=1756580&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
 Wed Aug 17 06:33:33 2016
@@ -2327,7 +2327,6 @@ public class LucenePropertyIndexTest ext
         assertPlanAndQuery(query, "lucene:test1(/oak:index/test1)", 
Collections.<String>emptyList());
     }
 
-    @Ignore("OAK-4676")
     @Test
     public void relativePropertyWithIndexOnNtBase() throws Exception {
         Tree idx = createIndex("test1", of("propa"));


Reply via email to