Author: chetanm
Date: Mon Nov 16 07:14:28 2015
New Revision: 1714520

URL: http://svn.apache.org/viewvc?rev=1714520&view=rev
Log:
OAK-3591 - Lucene index with 'analyzed=true' sometimes used by mistake

PropertyDefinition
If index=false then all other config are assumed to be false ignoring whatever 
is configured. As if property is not be indexed then it is not considered for 
all other cases also

IndexDefinition
Introduced a new `nodeFullTextIndexed` field to treat cases where node is 
indexed from cases where only certain property are index with analyzed=true. 
This ensures that `areAlMatchingNodeByTypeIndexed` would only return true if 
there is a nodeScope indexed field. So far it was returning true even when 
there was only a analyzed field

IndexPlanner
For a fulltext query now if the fulltext term is based on node then it only 
answers true if and only if node is indexed for fulltext i.e. there at least 
one property config with nodeScopeIndex=true

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/LuceneIndexEditor.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/package-info.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.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=1714520&r1=1714519&r2=1714520&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
 Mon Nov 16 07:14:28 2015
@@ -643,6 +643,7 @@ class IndexDefinition implements Aggrega
         final int propertyTypes;
         final boolean fulltextEnabled;
         final boolean propertyIndexEnabled;
+        final boolean nodeFullTextIndexed;
 
         final Aggregate aggregate;
         final Aggregate propAggregate;
@@ -670,8 +671,9 @@ class IndexDefinition implements Aggrega
             this.nullCheckEnabledProperties = 
ImmutableList.copyOf(nonExistentProperties);
             this.notNullCheckEnabledProperties = 
ImmutableList.copyOf(existentProperties);
             this.fulltextEnabled = aggregate.hasNodeAggregates() || 
hasAnyFullTextEnabledProperty();
+            this.nodeFullTextIndexed = aggregate.hasNodeAggregates() || 
anyNodeScopeIndexedProperty();
             this.propertyIndexEnabled = hasAnyPropertyIndexConfigured();
-            this.indexesAllNodesOfMatchingType = 
allMatchingNodeByTypeIndexed();
+            this.indexesAllNodesOfMatchingType = 
areAlMatchingNodeByTypeIndexed();
             this.nodeNameIndexed = getOptionalValue(config, 
LuceneIndexConstants.INDEX_NODE_NAME, false);
             validateRuleDefinition();
         }
@@ -698,7 +700,8 @@ class IndexDefinition implements Aggrega
             this.nodeScopeAnalyzedProps = original.nodeScopeAnalyzedProps;
             this.aggregate = combine(propAggregate, nodeTypeName);
             this.fulltextEnabled = aggregate.hasNodeAggregates() || 
original.fulltextEnabled;
-            this.indexesAllNodesOfMatchingType = 
allMatchingNodeByTypeIndexed();
+            this.nodeFullTextIndexed = aggregate.hasNodeAggregates() || 
original.nodeFullTextIndexed;
+            this.indexesAllNodesOfMatchingType = 
areAlMatchingNodeByTypeIndexed();
             this.nodeNameIndexed = original.nodeNameIndexed;
         }
 
@@ -787,9 +790,22 @@ class IndexDefinition implements Aggrega
             return nodeNameIndexed;
         }
 
+        /**
+         * Returns true if the rule has any property definition which enables
+         * evaluation of fulltext related clauses
+         */
         public boolean isFulltextEnabled() {
             return fulltextEnabled;
         }
+
+        /**
+         * Returns true if a fulltext field for the node would be created
+         * for any node matching the current rule.
+         */
+        public boolean isNodeFullTextIndexed() {
+            return nodeFullTextIndexed;
+        }
+
         /**
          * @param propertyName name of a property.
          * @return the property configuration or <code>null</code> if this
@@ -920,10 +936,26 @@ class IndexDefinition implements Aggrega
             return false;
         }
 
-        private boolean allMatchingNodeByTypeIndexed(){
-            //Incase of fulltext all nodes matching this rule type would be 
indexed
-            //and would have entry in the index
-            if (fulltextEnabled){
+        private boolean anyNodeScopeIndexedProperty(){
+            //Check if there is any nodeScope indexed property as
+            //for such case a node would always be indexed
+            for (PropertyDefinition pd : propConfigs.values()){
+                if (pd.nodeScopeIndex){
+                    return true;
+                }
+            }
+
+            for (NamePattern np : namePatterns){
+                if (np.getConfig().nodeScopeIndex){
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private boolean areAlMatchingNodeByTypeIndexed(){
+            if (nodeFullTextIndexed){
                 return true;
             }
 
@@ -1300,7 +1332,7 @@ class IndexDefinition implements Aggrega
 
     private static boolean hasFulltextEnabledIndexRule(List<IndexingRule> 
rules) {
         for (IndexingRule rule : rules){
-            if (rule.fulltextEnabled){
+            if (rule.isFulltextEnabled()){
                 return true;
             }
         }

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=1714520&r1=1714519&r2=1714520&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
 Mon Nov 16 07:14:28 2015
@@ -266,6 +266,7 @@ class IndexPlanner {
         final HashSet<String> relPaths = new HashSet<String>();
         final HashSet<String> nonIndexedPaths = new HashSet<String>();
         final AtomicBoolean relativeParentsFound = new AtomicBoolean();
+        final AtomicBoolean nodeScopedCondition = new AtomicBoolean();
         ft.accept(new FullTextVisitor.FullTextVisitorBase() {
             @Override
             public boolean visit(FullTextContains contains) {
@@ -308,9 +309,17 @@ class IndexPlanner {
                         && !indexingRule.isIndexed(propertyPath)){
                     nonIndexedPaths.add(p);
                 }
+
+                if (nodeScopedTerm(propertyName)){
+                    nodeScopedCondition.set(true);
+                }
             }
         });
 
+        if (nodeScopedCondition.get() && 
!indexingRule.isNodeFullTextIndexed()){
+            return false;
+        }
+
         if (relativeParentsFound.get()){
             log.debug("Relative parents found {} which are not supported", 
relPaths);
             return false;
@@ -460,6 +469,14 @@ class IndexPlanner {
         return false;
     }
 
+    /**
+     * Determine if the propertyName of a fulltext term indicates current node
+     * @param propertyName property name in the full text term clause
+     */
+    private static boolean nodeScopedTerm(String propertyName) {
+        return propertyName == null || ".".equals(propertyName) || 
"*".equals(propertyName);
+    }
+
     //~--------------------------------------------------------< PlanResult >
 
     public static class PlanResult {

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java?rev=1714520&r1=1714519&r2=1714520&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
 Mon Nov 16 07:14:28 2015
@@ -360,7 +360,7 @@ public class LuceneIndexEditor implement
 
         //For property index no use making an empty document if
         //none of the properties are indexed
-        if(!indexingRule.isFulltextEnabled() && !dirty){
+        if(!indexingRule.indexesAllNodesOfMatchingType() && !dirty){
             return null;
         }
 

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java?rev=1714520&r1=1714519&r2=1714520&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
 Mon Nov 16 07:14:28 2015
@@ -103,29 +103,29 @@ class PropertyDefinition {
 
         //By default if a property is defined it is indexed
         this.index = getOptionalValue(defn, LuceneIndexConstants.PROP_INDEX, 
true);
-        this.stored = getOptionalValue(defn, 
LuceneIndexConstants.PROP_USE_IN_EXCERPT, false);
-        this.nodeScopeIndex = getOptionalValue(defn, 
LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, false);
+        this.stored = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_USE_IN_EXCERPT, false);
+        this.nodeScopeIndex = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, false);
 
         //If boost is specified then that field MUST be analyzed
         if (defn.hasProperty(FIELD_BOOST)){
             this.analyzed = true;
         } else {
-            this.analyzed = getOptionalValue(defn, 
LuceneIndexConstants.PROP_ANALYZED, false);
+            this.analyzed = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_ANALYZED, false);
         }
 
         //If node is not set for full text then a property definition 
indicates that definition is for property index
-        this.propertyIndex = getOptionalValue(defn, 
LuceneIndexConstants.PROP_PROPERTY_INDEX, false);
-        this.ordered = getOptionalValue(defn, 
LuceneIndexConstants.PROP_ORDERED, false);
+        this.propertyIndex = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_PROPERTY_INDEX, false);
+        this.ordered = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_ORDERED, false);
         this.includedPropertyTypes = IndexDefinition.getSupportedTypes(defn, 
LuceneIndexConstants.PROP_INCLUDED_TYPE,
                 IndexDefinition.TYPES_ALLOW_ALL);
 
         //TODO Add test case for above cases
 
         this.propertyType = getPropertyType(idxDefn, nodeName, defn);
-        this.useInSuggest = getOptionalValue(defn, 
LuceneIndexConstants.PROP_USE_IN_SUGGEST, false);
-        this.useInSpellcheck = getOptionalValue(defn, 
LuceneIndexConstants.PROP_USE_IN_SPELLCHECK, false);
-        this.nullCheckEnabled = getOptionalValue(defn, 
LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, false);
-        this.notNullCheckEnabled = getOptionalValue(defn, 
LuceneIndexConstants.PROP_NOT_NULL_CHECK_ENABLED, false);
+        this.useInSuggest = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_USE_IN_SUGGEST, false);
+        this.useInSpellcheck = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_USE_IN_SPELLCHECK, false);
+        this.nullCheckEnabled = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, false);
+        this.notNullCheckEnabled = getOptionalValueIfIndexed(defn, 
LuceneIndexConstants.PROP_NOT_NULL_CHECK_ENABLED, false);
         this.nonRelativeName = determineNonRelativeName();
         this.ancestors = computeAncestors(name);
         validate();
@@ -201,6 +201,15 @@ class PropertyDefinition {
 
     //~---------------------------------------------< internal >
 
+    private boolean getOptionalValueIfIndexed(NodeState definition, String 
propName, boolean defaultVal){
+        //If property is not to be indexed then all other config would be
+        //set to false ignoring whatever is defined in config for them
+        if (!index){
+            return false;
+        }
+        return getOptionalValue(definition, propName, defaultVal);
+    }
+
     private void validate() {
         if (nullCheckEnabled && isRegexp){
             throw new IllegalStateException(String.format("%s can be set to 
true for property definition using " +

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/package-info.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/package-info.java?rev=1714520&r1=1714519&r2=1714520&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/package-info.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/package-info.java
 Mon Nov 16 07:14:28 2015
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("2.6.0")
+@Version("2.7.0")
 @Export(optional = "provide:=true")
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java?rev=1714520&r1=1714519&r2=1714520&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
 Mon Nov 16 07:14:28 2015
@@ -682,6 +682,101 @@ public class IndexDefinitionTest {
         assertEquals(2, rule1.getNodeScopeAnalyzedProps().size());
     }
 
+    @Test
+    public void nodeFullTextIndexed_Regex() throws Exception {
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:folder");
+        TestUtil.child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.PROP_NAME, ".*")
+                .setProperty(LuceneIndexConstants.PROP_ANALYZED, true)
+                .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
+
+        IndexDefinition defn = new IndexDefinition(root, 
builder.getNodeState());
+        IndexingRule rule = 
defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertNotNull(rule);
+        assertFalse(rule.isNodeFullTextIndexed());
+
+        TestUtil.child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+        defn = new IndexDefinition(root, builder.getNodeState());
+        rule = defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertTrue(rule.isNodeFullTextIndexed());
+        assertTrue(rule.indexesAllNodesOfMatchingType());
+    }
+
+    @Test
+    public void nodeFullTextIndexed_Simple() throws Exception {
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:folder");
+        TestUtil.child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "foo")
+                .setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        IndexDefinition defn = new IndexDefinition(root, 
builder.getNodeState());
+        IndexingRule rule = 
defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertNotNull(rule);
+        assertFalse(rule.isNodeFullTextIndexed());
+
+        TestUtil.child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+        defn = new IndexDefinition(root, builder.getNodeState());
+        rule = defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertTrue(rule.isNodeFullTextIndexed());
+        assertTrue(rule.indexesAllNodesOfMatchingType());
+    }
+
+    @Test
+    public void nodeFullTextIndexed_Aggregates() throws Exception {
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:folder");
+        TestUtil.child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "foo")
+                .setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+        NodeBuilder aggregates = 
builder.child(LuceneIndexConstants.AGGREGATES);
+        NodeBuilder aggFolder = aggregates.child("nt:folder");
+        aggFolder.child("i1").setProperty(LuceneIndexConstants.AGG_PATH, "*");
+
+        IndexDefinition defn = new IndexDefinition(root, 
builder.getNodeState());
+        IndexingRule rule = 
defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertNotNull(rule);
+        assertTrue(rule.isNodeFullTextIndexed());
+        assertTrue(rule.indexesAllNodesOfMatchingType());
+    }
+
+    @Test
+    public void nonIndexPropShouldHaveAllOtherConfigDisabled() throws 
Exception{
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child("nt:folder");
+        TestUtil.child(rules, "nt:folder/properties/prop1")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "foo")
+                .setProperty(LuceneIndexConstants.PROP_INDEX, false)
+                .setProperty(LuceneIndexConstants.PROP_USE_IN_SUGGEST, true)
+                .setProperty(LuceneIndexConstants.PROP_USE_IN_SPELLCHECK, true)
+                .setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, 
true)
+                .setProperty(LuceneIndexConstants.PROP_NOT_NULL_CHECK_ENABLED, 
true)
+                .setProperty(LuceneIndexConstants.PROP_USE_IN_EXCERPT, true)
+                .setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true)
+                .setProperty(LuceneIndexConstants.PROP_ORDERED, true)
+                .setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+        IndexDefinition defn = new IndexDefinition(root, 
builder.getNodeState());
+        IndexingRule rule = 
defn.getApplicableIndexingRule(newTree(newNode("nt:folder")));
+        assertNotNull(rule);
+
+        PropertyDefinition pd = rule.getConfig("foo");
+        //Assert that all other config is false if the index=false for any 
property
+        assertFalse(pd.index);
+        assertFalse(pd.nodeScopeIndex);
+        assertFalse(pd.useInSuggest);
+        assertFalse(pd.useInSpellcheck);
+        assertFalse(pd.nullCheckEnabled);
+        assertFalse(pd.notNullCheckEnabled);
+        assertFalse(pd.stored);
+        assertFalse(pd.ordered);
+        assertFalse(pd.analyzed);
+
+    }
+
     //TODO indexesAllNodesOfMatchingType - with nullCheckEnabled
 
     private static IndexingRule getRule(IndexDefinition defn, String typeName){

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=1714520&r1=1714519&r2=1714520&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
 Mon Nov 16 07:14:28 2015
@@ -422,7 +422,6 @@ public class IndexPlannerTest {
         assertNotNull(planner.getPlan());
     }
 
-    @Ignore("OAK-3591")
     @Test
     public void noPlanForFulltextQueryAndOnlyAnalyzedProperties() throws 
Exception{
         NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");
@@ -441,7 +440,6 @@ public class IndexPlannerTest {
         assertNull(plan);
     }
 
-    @Ignore("OAK-3591")
     @Test
     public void noPlanForNodeTypeQueryAndOnlyAnalyzedProperties() throws 
Exception{
         NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");


Reply via email to