Author: chetanm
Date: Wed Feb 18 16:58:00 2015
New Revision: 1660676

URL: http://svn.apache.org/r1660676
Log:
OAK-2517 - Support IS NULL based property restrictions in LucenePropertyIndex

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/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/LuceneIndexConstants.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/LucenePropertyIndex.java
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.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
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
    
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.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=1660676&r1=1660675&r2=1660676&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 Feb 18 16:58:00 2015
@@ -75,6 +75,12 @@ public final class FieldNames {
     public static final String FULLTEXT_RELATIVE_NODE = "fullnode:";
 
     /**
+     * Name of the field that contains those property names which are not found
+     * (or were null) for the given
+     */
+    public static final String NULL_PROPS = ":nullProps";
+
+    /**
      * Used to select only the PATH field from the lucene documents
      */
     public static final Set<String> PATH_SELECTOR = new HashSet<String>(

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=1660676&r1=1660675&r2=1660676&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 Feb 18 16:58:00 2015
@@ -72,7 +72,6 @@ import static org.apache.jackrabbit.JcrC
 import static org.apache.jackrabbit.JcrConstants.NT_BASE;
 import static org.apache.jackrabbit.oak.api.Type.NAMES;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
-import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
 import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
 import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
 import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_COUNT;
@@ -562,6 +561,7 @@ class IndexDefinition implements Aggrega
         private final String nodeTypeName;
         private final Map<String, PropertyDefinition> propConfigs;
         private final List<NamePattern> namePatterns;
+        private final List<PropertyDefinition> nullCheckEnabledProperties;
         private final boolean indexesAllNodesOfMatchingType;
 
         final float boost;
@@ -582,15 +582,18 @@ class IndexDefinition implements Aggrega
             this.propertyTypes = getSupportedTypes(config, 
INCLUDE_PROPERTY_TYPES, TYPES_ALLOW_ALL);
 
             List<NamePattern> namePatterns = newArrayList();
+            List<PropertyDefinition> nonExistentProperties = newArrayList();
             List<Aggregate.Include> propIncludes = newArrayList();
-            this.propConfigs = collectPropConfigs(config, namePatterns, 
propIncludes);
+            this.propConfigs = collectPropConfigs(config, namePatterns, 
propIncludes, nonExistentProperties);
             this.propAggregate = new Aggregate(nodeTypeName, propIncludes);
             this.aggregate = combine(propAggregate, nodeTypeName);
 
             this.namePatterns = ImmutableList.copyOf(namePatterns);
+            this.nullCheckEnabledProperties = 
ImmutableList.copyOf(nonExistentProperties);
             this.fulltextEnabled = aggregate.hasNodeAggregates() || 
hasAnyFullTextEnabledProperty();
             this.propertyIndexEnabled = hasAnyPropertyIndexConfigured();
             this.indexesAllNodesOfMatchingType = 
allMatchingNodeByTypeIndexed();
+            validateRuleDefinition();
         }
 
         /**
@@ -610,6 +613,7 @@ class IndexDefinition implements Aggrega
             this.propertyTypes = original.propertyTypes;
             this.propertyIndexEnabled = original.propertyIndexEnabled;
             this.propAggregate = original.propAggregate;
+            this.nullCheckEnabledProperties = 
original.nullCheckEnabledProperties;
             this.aggregate = combine(propAggregate, nodeTypeName);
             this.fulltextEnabled = aggregate.hasNodeAggregates() || 
original.fulltextEnabled;
             this.indexesAllNodesOfMatchingType = 
allMatchingNodeByTypeIndexed();
@@ -637,6 +641,10 @@ class IndexDefinition implements Aggrega
             return nodeTypeName;
         }
 
+        public List<PropertyDefinition> getNullCheckEnabledProperties() {
+            return nullCheckEnabledProperties;
+        }
+
         @Override
         public String toString() {
             String str = "IndexRule: "+ nodeTypeName;
@@ -729,7 +737,8 @@ class IndexDefinition implements Aggrega
         }
 
         private Map<String, PropertyDefinition> collectPropConfigs(NodeState 
config, List<NamePattern> patterns,
-                                                                   
List<Aggregate.Include> propAggregate) {
+                                                                   
List<Aggregate.Include> propAggregate,
+                                                                   
List<PropertyDefinition> nonExistentProperties) {
             Map<String, PropertyDefinition> propDefns = newHashMap();
             NodeState propNode = 
config.getChildNode(LuceneIndexConstants.PROP_NODE);
 
@@ -755,9 +764,13 @@ class IndexDefinition implements Aggrega
                         propDefns.put(pd.name, pd);
                     }
 
-                    if (isRelativeProperty(pd.name)){
+                    if (pd.relative){
                         propAggregate.add(new Aggregate.PropertyInclude(pd));
                     }
+
+                    if (pd.nullCheckEnabled){
+                        nonExistentProperties.add(pd);
+                    }
                 }
             }
             return ImmutableMap.copyOf(propDefns);
@@ -800,6 +813,15 @@ class IndexDefinition implements Aggrega
                 return true;
             }
 
+            //If there is nullCheckEnabled property which is not relative then
+            //all nodes would be indexed. relativeProperty with 
nullCheckEnabled might
+            //not ensure that (OAK-1085)
+            for (PropertyDefinition pd : nullCheckEnabledProperties){
+                if (!pd.relative) {
+                    return true;
+                }
+            }
+
             //jcr:primaryType is present on all node. So if such a property
             //is indexed then it would mean all nodes covered by this index 
rule
             //are indexed
@@ -819,6 +841,13 @@ class IndexDefinition implements Aggrega
             }
             return new Aggregate(nodeTypeName, includes);
         }
+
+        private void validateRuleDefinition() {
+            if (!nullCheckEnabledProperties.isEmpty() && isBasedOnNtBase()){
+                throw new IllegalStateException("nt:base based rule cannot 
have a " +
+                        "PropertyDefinition with nullCheckEnabled");
+            }
+        }
     }
 
     /**
@@ -948,7 +977,7 @@ class IndexDefinition implements Aggrega
                 String propNodeName = propName;
 
                 //For proper propName use the propName as childNode name
-                if(isRelativeProperty(propName)
+                if(PropertyDefinition.isRelativeProperty(propName)
                         || propName.equals(includeAllProp)){
                     propNodeName = "prop" + i++;
                 }
@@ -1010,7 +1039,7 @@ class IndexDefinition implements Aggrega
     private static NodeState getPropDefnNode(NodeState defn, String propName){
         NodeState propNode = defn.getChildNode(LuceneIndexConstants.PROP_NODE);
         NodeState propDefNode;
-        if (isRelativeProperty(propName)) {
+        if (PropertyDefinition.isRelativeProperty(propName)) {
             NodeState result = propNode;
             for (String name : PathUtils.elements(propName)) {
                 result = result.getChildNode(name);
@@ -1239,7 +1268,4 @@ class IndexDefinition implements Aggrega
         return defn.getChildNode(LuceneIndexConstants.INDEX_RULES).exists();
     }
 
-    private static boolean isRelativeProperty(String propertyName){
-        return !isAbsolute(propertyName) && 
PathUtils.getNextSlash(propertyName, 0) > 0;
-    }
 }

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=1660676&r1=1660675&r2=1660676&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 Feb 18 16:58:00 2015
@@ -139,12 +139,11 @@ class IndexPlanner {
         //for property index
         if (indexingRule.propertyIndexEnabled) {
             for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
-                if (pr.isNullRestriction()) {
-                    // ignore for planning
-                    continue;
-                }
                 PropertyDefinition pd = 
indexingRule.getConfig(pr.propertyName);
                 if (pd != null && pd.propertyIndexEnabled()) {
+                    if (pr.isNullRestriction() && !pd.nullCheckEnabled){
+                        continue;
+                    }
                     indexedProps.add(pr.propertyName);
                     result.propDefns.put(pr.propertyName, pd);
                 }

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1660676&r1=1660675&r2=1660676&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
 Wed Feb 18 16:58:00 2015
@@ -241,4 +241,10 @@ public interface LuceneIndexConstants {
      * whether use this property values for spellchecking
      */
     String PROP_USE_IN_SPELLCHECK = "useInSpellcheck";
+
+    /**
+     * Property definition config indicating that null check support should be
+     * enabled for this property
+     */
+    String PROP_NULL_CHECK_ENABLED = "nullCheckEnabled";
 }

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=1660676&r1=1660675&r2=1660676&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
 Wed Feb 18 16:58:00 2015
@@ -303,6 +303,7 @@ public class LuceneIndexEditor implement
         }
 
         dirty |= indexAggregates(path, fields, state);
+        dirty |= indexNullCheckEnabledProps(path, fields, state);
 
         if (isUpdate && !dirty) {
             // updated the state but had no relevant changes
@@ -514,6 +515,46 @@ public class LuceneIndexEditor implement
         return fields;
     }
 
+    //~-------------------------------------------------------< NullCheck 
Support >
+
+    private boolean indexNullCheckEnabledProps(String path, List<Field> 
fields, NodeState state) {
+        boolean fieldAdded = false;
+        for (PropertyDefinition pd : 
indexingRule.getNullCheckEnabledProperties()) {
+            if (isPropertyNull(state, pd)) {
+                fields.add(new StringField(FieldNames.NULL_PROPS, pd.name, 
Field.Store.NO));
+                fieldAdded = true;
+            }
+        }
+        return fieldAdded;
+    }
+
+    /**
+     * Determine if the property as defined by PropertyDefinition exists or 
not.
+     *
+     * <p>For relative property if the intermediate nodes do not exist then 
property is
+     * <bold>not</bold> considered to be null</p>
+     *
+     * @return true if the property does not exist
+     */
+    private boolean isPropertyNull(NodeState state, PropertyDefinition pd){
+        NodeState propertyNode = getPropertyNode(state, pd);
+        if (!propertyNode.exists()){
+            return false;
+        }
+        return !propertyNode.hasProperty(pd.nonRelativeName);
+    }
+
+    private static NodeState getPropertyNode(NodeState nodeState, 
PropertyDefinition pd) {
+        if (!pd.relative){
+            return nodeState;
+        }
+        NodeState node = nodeState;
+        for (String name : pd.ancestors) {
+            node = node.getChildNode(name);
+        }
+        return node;
+    }
+
     //~-------------------------------------------------------< Aggregate >
 
     @Override

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=1660676&r1=1660675&r2=1660676&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
 Wed Feb 18 16:58:00 2015
@@ -718,6 +718,11 @@ public class LucenePropertyIndex impleme
     private static Query createQuery(PropertyRestriction pr,
                                      PropertyDefinition defn) {
         int propType = determinePropertyType(defn, pr);
+
+        if (pr.isNullRestriction()){
+            return new TermQuery(new Term(FieldNames.NULL_PROPS, defn.name));
+        }
+
         switch (propType) {
             case PropertyType.DATE: {
                 Long first = pr.first != null ? 
FieldFactory.dateToLong(pr.first.getValue(Type.DATE)) : null;
@@ -772,7 +777,7 @@ public class LucenePropertyIndex impleme
                         
in.add(NumericRangeQuery.newDoubleRange(pr.propertyName, doubleVal, doubleVal, 
true, true), BooleanClause.Occur.SHOULD);
                     }
                     return in;
-                } else if (pr.first == null && pr.last == null ) {
+                } else if (pr.isNotNullRestriction()) {
                     // not null.
                     return NumericRangeQuery.newDoubleRange(pr.propertyName, 
Double.MIN_VALUE, Double.MAX_VALUE, true, true);
                 }
@@ -801,7 +806,7 @@ public class LucenePropertyIndex impleme
                         in.add(NumericRangeQuery.newLongRange(pr.propertyName, 
longVal, longVal, true, true), BooleanClause.Occur.SHOULD);
                     }
                     return in;
-                } else if (pr.first == null && pr.last == null ) {
+                } else if (pr.isNotNullRestriction()) {
                     // not null.
                     return NumericRangeQuery.newLongRange(pr.propertyName, 
Long.MIN_VALUE, Long.MAX_VALUE, true, true);
                 }
@@ -835,7 +840,7 @@ public class LucenePropertyIndex impleme
                         in.add(new TermQuery(new Term(pr.propertyName, 
strVal)), BooleanClause.Occur.SHOULD);
                     }
                     return in;
-                } else if (pr.first == null && pr.last == null ) {
+                } else if (pr.isNotNullRestriction()) {
                     return new TermRangeQuery(pr.propertyName, null, null, 
true, true);
                 }
             }

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=1660676&r1=1660675&r2=1660676&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
 Wed Feb 18 16:58:00 2015
@@ -19,16 +19,22 @@
 
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import javax.annotation.CheckForNull;
 import javax.jcr.PropertyType;
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.IndexingRule;
 import org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static com.google.common.collect.ImmutableList.copyOf;
+import static com.google.common.collect.Iterables.toArray;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.FIELD_BOOST;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_IS_REGEX;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.ConfigUtil.getOptionalValue;
@@ -68,15 +74,29 @@ class PropertyDefinition {
 
     final boolean ordered;
 
+    final boolean nullCheckEnabled;
+
     final int includedPropertyTypes;
 
-    boolean useInSuggest;
+    final boolean relative;
+
+    final boolean useInSuggest;
+
+    final boolean useInSpellcheck;
 
-    boolean useInSpellcheck;
+    final String[] ancestors;
+
+    /**
+     * Property name excluding the relativePath. For regular expression based 
definition
+     * its set to null
+     */
+    @CheckForNull
+    final String nonRelativeName;
 
-    public PropertyDefinition(IndexingRule idxDefn, String name, NodeState 
defn) {
+    public PropertyDefinition(IndexingRule idxDefn, String nodeName, NodeState 
defn) {
         this.isRegexp = getOptionalValue(defn, PROP_IS_REGEX, false);
-        this.name = getName(defn, name);
+        this.name = getName(defn, nodeName);
+        this.relative = isRelativeProperty(name);
         this.boost = getOptionalValue(defn, FIELD_BOOST, DEFAULT_BOOST);
 
         //By default if a property is defined it is indexed
@@ -93,11 +113,16 @@ class PropertyDefinition {
 
         //TODO Add test case for above cases
 
-        this.propertyType = getPropertyType(idxDefn, name, defn);
+        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.nonRelativeName = determineNonRelativeName();
+        this.ancestors = computeAncestors(name);
+        validate();
     }
 
+
     /**
      * If 'analyzed' is enabled then property value would be used to evaluate 
the
      * contains clause related to those properties. In such mode also some 
properties
@@ -156,12 +181,40 @@ class PropertyDefinition {
                 ", analyzed=" + analyzed +
                 ", ordered=" + ordered +
                 ", useInSuggest=" + useInSuggest+
-                ", useInSpellcheck=" + useInSpellcheck+
+                ", nullCheckEnabled=" + nullCheckEnabled+
                 '}';
     }
 
+    static boolean isRelativeProperty(String propertyName){
+        return !isAbsolute(propertyName) && 
PathUtils.getNextSlash(propertyName, 0) > 0;
+    }
+
     //~---------------------------------------------< internal >
 
+    private void validate() {
+        if (nullCheckEnabled && isRegexp){
+            throw new IllegalStateException(String.format("%s can be set to 
true for property definition using " +
+                    "regular expression", 
LuceneIndexConstants.PROP_NULL_CHECK_ENABLED));
+        }
+    }
+
+    private String determineNonRelativeName() {
+        if (isRegexp){
+            return null;
+        }
+
+        if (!relative){
+            return name;
+        }
+
+        return PathUtils.getName(name);
+    }
+
+    private static String[] computeAncestors(String parentPath) {
+        return toArray(copyOf(elements(PathUtils.getParentPath(parentPath))), 
String.class);
+    }
+
+
     private static String getName(NodeState definition, String defaultName){
         PropertyState ps = 
definition.getProperty(LuceneIndexConstants.PROP_NAME);
         return ps == null ? defaultName : ps.getValue(Type.STRING);

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=1660676&r1=1660675&r2=1660676&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
 Wed Feb 18 16:58:00 2015
@@ -24,7 +24,6 @@ import javax.jcr.PropertyType;
 import com.google.common.collect.ImmutableList;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.lucene.util.TokenizerChain;
 import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
@@ -34,7 +33,6 @@ import org.apache.lucene.codecs.Codec;
 import 
org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.IndexingRule;
 import org.junit.Test;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.ImmutableSet.of;
 import static javax.jcr.PropertyType.TYPENAME_LONG;
 import static javax.jcr.PropertyType.TYPENAME_STRING;
@@ -51,12 +49,14 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_NAME;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PROP_NODE;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TIKA;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.registerTestNodeType;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLuceneIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 import static 
org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
 import static 
org.apache.jackrabbit.oak.plugins.tree.impl.TreeConstants.OAK_CHILD_ORDER;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -154,13 +154,15 @@ public class IndexDefinitionTest {
         assertNotNull(rule.getConfig("foo1/bar"));
         assertEquals(PropertyType.DATE, rule.getConfig("foo1/bar").getType());
         assertEquals(PropertyType.LONG, 
rule.getConfig("foo2/bar2/baz").getType());
+        assertTrue(rule.getConfig("foo1/bar").relative);
+        assertArrayEquals(new String[]{"foo2", "bar2"}, 
rule.getConfig("foo2/bar2/baz").ancestors);
     }
 
     @Test
     public void indexRuleSanity() throws Exception{
         NodeBuilder rules = builder.child(INDEX_RULES);
         rules.child("nt:folder").setProperty(LuceneIndexConstants.FIELD_BOOST, 
2.0);
-        child(rules, "nt:folder/properties/prop1")
+        TestUtil.child(rules, "nt:folder/properties/prop1")
                 .setProperty(LuceneIndexConstants.FIELD_BOOST, 3.0)
                 .setProperty(LuceneIndexConstants.PROP_TYPE, 
PropertyType.TYPENAME_BOOLEAN);
 
@@ -243,9 +245,9 @@ public class IndexDefinitionTest {
     public void indexRuleWithPropertyRegEx() throws Exception{
         NodeBuilder rules = builder.child(INDEX_RULES);
         rules.child("nt:folder");
-        child(rules, "nt:folder/properties/prop1")
+        TestUtil.child(rules, "nt:folder/properties/prop1")
                 .setProperty(LuceneIndexConstants.FIELD_BOOST, 3.0);
-        child(rules, "nt:folder/properties/prop2")
+        TestUtil.child(rules, "nt:folder/properties/prop2")
                 .setProperty(LuceneIndexConstants.PROP_NAME, "foo.*")
                 .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
                 .setProperty(LuceneIndexConstants.FIELD_BOOST, 4.0);
@@ -267,10 +269,10 @@ public class IndexDefinitionTest {
     public void indexRuleWithPropertyRegEx2() throws Exception{
         NodeBuilder rules = builder.child(INDEX_RULES);
         rules.child("nt:folder");
-        child(rules, "nt:folder/properties/prop1")
+        TestUtil.child(rules, "nt:folder/properties/prop1")
                 .setProperty(LuceneIndexConstants.PROP_NAME, ".*")
                 .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
-        child(rules, "nt:folder/properties/prop2")
+        TestUtil.child(rules, "nt:folder/properties/prop2")
                 .setProperty(LuceneIndexConstants.PROP_NAME, "metadata/.*")
                 .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
                 .setProperty(LuceneIndexConstants.FIELD_BOOST, 4.0);
@@ -293,11 +295,11 @@ public class IndexDefinitionTest {
     public void indexRuleWithPropertyOrdering() throws Exception{
         NodeBuilder rules = builder.child(INDEX_RULES);
         rules.child("nt:folder");
-        child(rules, "nt:folder/properties/prop1")
+        TestUtil.child(rules, "nt:folder/properties/prop1")
                 .setProperty(LuceneIndexConstants.PROP_NAME, "foo.*")
                 .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
                 .setProperty(LuceneIndexConstants.FIELD_BOOST, 3.0);
-        child(rules, "nt:folder/properties/prop2")
+        TestUtil.child(rules, "nt:folder/properties/prop2")
                 .setProperty(LuceneIndexConstants.PROP_NAME, ".*")
                 .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
                 .setProperty(LuceneIndexConstants.FIELD_BOOST, 4.0);
@@ -328,7 +330,7 @@ public class IndexDefinitionTest {
     public void skipTokenization() throws Exception{
         NodeBuilder rules = builder.child(INDEX_RULES);
         rules.child("nt:folder");
-        child(rules, "nt:folder/properties/prop2")
+        TestUtil.child(rules, "nt:folder/properties/prop2")
                 .setProperty(LuceneIndexConstants.PROP_NAME, ".*")
                 .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
                 .setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
@@ -519,6 +521,37 @@ public class IndexDefinitionTest {
         assertEquals(1000, defn.getMaxExtractLength());
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void nullCheckEnabledWithNtBase() throws Exception{
+        
builder.child(PROP_NODE).child("foo").setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED,
 true);
+        IndexDefinition idxDefn = new IndexDefinition(root, 
builder.getNodeState());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void nullCheckEnabledWithRegex() throws Exception{
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        rules.child(TestUtil.NT_TEST);
+        TestUtil.child(rules, "oak:TestNode/properties/prop2")
+                .setProperty(LuceneIndexConstants.PROP_NAME, ".*")
+                .setProperty(LuceneIndexConstants.PROP_IS_REGEX, true)
+                .setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, 
true);
+        root = registerTestNodeType(builder).getNodeState();
+        IndexDefinition idxDefn = new IndexDefinition(root, 
builder.getNodeState());
+    }
+
+    @Test
+    public void nullCheckEnabledWithTestNode() throws Exception{
+        NodeBuilder rules = builder.child(INDEX_RULES);
+        TestUtil.child(rules, "oak:TestNode/properties/prop2")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "foo")
+                .setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, 
true);
+        root = registerTestNodeType(builder).getNodeState();
+        IndexDefinition idxDefn = new IndexDefinition(root, 
builder.getNodeState());
+        
assertTrue(!idxDefn.getApplicableIndexingRule(TestUtil.NT_TEST).getNullCheckEnabledProperties().isEmpty());
+    }
+
+    //TODO indexesAllNodesOfMatchingType - with nullCheckEnabled
+
     private static IndexingRule getRule(IndexDefinition defn, String typeName){
         return defn.getApplicableIndexingRule(newTree(newNode(typeName)));
     }
@@ -533,10 +566,4 @@ public class IndexDefinitionTest {
         return builder;
     }
 
-    private static NodeBuilder child(NodeBuilder nb, String path) {
-        for (String name : PathUtils.elements(checkNotNull(path))) {
-            nb = nb.child(name);
-        }
-        return nb;
-    }
 }

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=1660676&r1=1660675&r2=1660676&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 Feb 18 16:58:00 2015
@@ -55,8 +55,11 @@ import static org.apache.jackrabbit.oak.
 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;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_RULES;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.ORDERED_PROP_NAMES;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.NT_TEST;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.registerTestNodeType;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLuceneIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
@@ -324,6 +327,42 @@ public class IndexPlannerTest {
         assertEquals(numofDocs, plan.getEstimatedEntryCount());
     }
 
+    @Test
+    public void nullPropertyCheck() throws Exception{
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");
+
+        IndexNode node = createIndexNode(new IndexDefinition(root, 
defn.getNodeState()));
+        FilterImpl filter = createFilter("nt:base");
+        filter.restrictProperty("foo", Operator.EQUAL, null);
+        IndexPlanner planner = new IndexPlanner(node, "/foo", filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+        assertNull("For null checks no plan should be returned",plan);
+    }
+
+    @Test
+    public void nullPropertyCheck2() throws Exception{
+        root = registerTestNodeType(builder).getNodeState();
+        NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test", 
of("foo"), "async");
+        NodeBuilder rules = defn.child(INDEX_RULES);
+        TestUtil.child(rules, "oak:TestNode/properties/prop2")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "foo")
+                .setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, 
true)
+                .setProperty(LuceneIndexConstants.PROP_PROPERTY_INDEX, true);
+
+        IndexDefinition idxDefn = new IndexDefinition(root, 
builder.getNodeState().getChildNode("test"));
+        IndexNode node = createIndexNode(idxDefn);
+
+        FilterImpl filter = createFilter(NT_TEST);
+        filter.restrictProperty("foo", Operator.EQUAL, null);
+
+        IndexPlanner planner = new IndexPlanner(node, "/foo", filter, 
Collections.<OrderEntry>emptyList());
+        QueryIndex.IndexPlan plan = planner.getPlan();
+        assertNotNull("For null checks plan should be returned with 
nullCheckEnabled", plan);
+        IndexPlanner.PlanResult pr =
+                (IndexPlanner.PlanResult) 
plan.getAttribute(LucenePropertyIndex.ATTR_PLAN_RESULT);
+        assertNotNull(pr.getPropDefn(filter.getPropertyRestriction("foo")));
+    }
+
     private IndexNode createIndexNode(IndexDefinition defn, long numOfDocs) 
throws IOException {
         return new IndexNode("foo", defn, createSampleDirectory(numOfDocs));
     }

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java?rev=1660676&r1=1660675&r2=1660676&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
 Wed Feb 18 16:58:00 2015
@@ -48,6 +48,8 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PERSISTENCE_FILE;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PERSISTENCE_NAME;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.PERSISTENCE_PATH;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.NT_TEST;
+import static 
org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.createNodeWithType;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLuceneIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLucenePropertyIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
@@ -227,6 +229,81 @@ public class LuceneIndexTest {
     }
 
     @Test
+    public void testPropertyNonExistence() throws Exception {
+        root = TestUtil.registerTestNodeType(builder).getNodeState();
+
+        NodeBuilder index = 
newLucenePropertyIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
+                "lucene", ImmutableSet.of("foo"), null);
+        NodeBuilder rules = index.child(INDEX_RULES);
+        NodeBuilder propNode = 
rules.child(NT_TEST).child(LuceneIndexConstants.PROP_NODE);
+
+        NodeBuilder fooProp = propNode.child("foo");
+        fooProp.setProperty(LuceneIndexConstants.PROP_PROPERTY_INDEX, true);
+        fooProp.setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, 
true);
+
+        NodeState before = builder.getNodeState();
+        createNodeWithType(builder, "a", NT_TEST).setProperty("foo", "bar");
+        createNodeWithType(builder, "b", NT_TEST).setProperty("foo", "bar");
+        createNodeWithType(builder, "c", NT_TEST);
+
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after,CommitInfo.EMPTY);
+
+        IndexTracker tracker = new IndexTracker();
+        tracker.update(indexed);
+        AdvancedQueryIndex queryIndex = new LucenePropertyIndex(tracker);
+
+        FilterImpl filter = createFilter(NT_TEST);
+        filter.restrictProperty("foo", Operator.EQUAL, null);
+        assertFilter(filter, queryIndex, indexed, ImmutableList.of("/c"));
+    }
+
+    @Test
+    public void testRelativePropertyNonExistence() throws Exception {
+        root = TestUtil.registerTestNodeType(builder).getNodeState();
+
+        NodeBuilder index = 
newLucenePropertyIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
+                "lucene", ImmutableSet.of("foo"), null);
+        NodeBuilder rules = index.child(INDEX_RULES);
+        NodeBuilder propNode = 
rules.child(NT_TEST).child(LuceneIndexConstants.PROP_NODE);
+
+        propNode.child("bar")
+                .setProperty(LuceneIndexConstants.PROP_NAME, "jcr:content/bar")
+                .setProperty(LuceneIndexConstants.PROP_PROPERTY_INDEX, true)
+                .setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, 
true);
+
+        NodeState before = builder.getNodeState();
+
+        NodeBuilder a1 = createNodeWithType(builder, "a1", NT_TEST);
+        a1.child("jcr:content").setProperty("bar", "foo");
+
+        NodeBuilder b1 = createNodeWithType(builder, "b1", NT_TEST);
+        b1.child("jcr:content");
+
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after,CommitInfo.EMPTY);
+
+        IndexTracker tracker = new IndexTracker();
+        tracker.update(indexed);
+        AdvancedQueryIndex queryIndex = new LucenePropertyIndex(tracker);
+
+        FilterImpl filter = createFilter(NT_TEST);
+        filter.restrictProperty("jcr:content/bar", Operator.EQUAL, null);
+        assertFilter(filter, queryIndex, indexed, ImmutableList.of("/b1"));
+
+        builder.child("b1").child("jcr:content").setProperty("bar", "foo");
+        after = builder.getNodeState();
+        indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+        tracker.update(indexed);
+
+        filter = createFilter(NT_TEST);
+        filter.restrictProperty("jcr:content/bar", Operator.EQUAL, null);
+        assertFilter(filter, queryIndex, indexed, 
Collections.<String>emptyList());
+    }
+
+    @Test
     public void testPathRestrictions() throws Exception {
         NodeBuilder idx = 
newLucenePropertyIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
                 "lucene", ImmutableSet.of("foo"), null);
@@ -494,7 +571,8 @@ public class LuceneIndexTest {
         for (String p : expected) {
             assertTrue("Expected path " + p + " not found", paths.contains(p));
         }
-        assertEquals("Result set size is different", expected.size(), 
paths.size());
+        assertEquals("Result set size is different \nExpected: " +
+                expected + "\nActual: " + paths, expected.size(), 
paths.size());
         return paths;
     }
 

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=1660676&r1=1660675&r2=1660676&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 Feb 18 16:58:00 2015
@@ -33,6 +33,7 @@ import com.google.common.collect.Immutab
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
@@ -46,6 +47,7 @@ import org.apache.jackrabbit.oak.plugins
 import 
org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.NodeTypeRegistry;
 import org.apache.jackrabbit.oak.query.AbstractQueryTest;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
@@ -296,8 +298,9 @@ public class LucenePropertyIndexTest ext
         assertQuery(propabQuery, asList("/test/a", "/test/b", "/test"));
     }
 
-    private static void setNodeType(Tree t, String typeName){
+    private static Tree setNodeType(Tree t, String typeName){
         t.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
+        return t;
     }
 
     @Test
@@ -328,6 +331,35 @@ public class LucenePropertyIndexTest ext
 
         assertQuery("select [jcr:path] from [nt:base] where propa is not 
null", asList("/test/a", "/test/b"));
     }
+
+    @Test
+    public void propertyNonExistenceQuery() throws Exception {
+        NodeTypeRegistry.register(root, 
IOUtils.toInputStream(TestUtil.TEST_NODE_TYPE), "test nodeType");
+
+        Tree idx = createIndex("test1", of("propa", "propb"));
+        Tree props = TestUtil.newRulePropTree(idx, TestUtil.NT_TEST);
+        Tree prop = props.addChild(TestUtil.unique("prop"));
+        prop.setProperty(LuceneIndexConstants.PROP_NAME, "propa");
+        prop.setProperty(LuceneIndexConstants.PROP_PROPERTY_INDEX, true);
+        prop.setProperty(LuceneIndexConstants.PROP_NULL_CHECK_ENABLED, true);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        createNodeWithType(test, "a", "oak:TestNode").setProperty("propa", 
"a");
+        createNodeWithType(test, "b", "oak:TestNode").setProperty("propa", 
"c");
+        createNodeWithType(test, "c", "oak:TestNode").setProperty("propb", 
"e");
+        root.commit();
+
+        String propabQuery = "select [jcr:path] from [oak:TestNode] where 
[propa] is null";
+        assertThat(explain(propabQuery), 
containsString("lucene:test1(/oak:index/test1) :nullProps:propa"));
+        assertQuery(propabQuery, asList("/test/c"));
+    }
+
+    private static Tree createNodeWithType(Tree t, String nodeName, String 
typeName){
+        t = t.addChild(nodeName);
+        t.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
+        return t;
+    }
     
     @Test
     public void orderByScore() throws Exception {
@@ -781,9 +813,9 @@ public class LucenePropertyIndexTest ext
                 getSortedPaths(tuples, OrderDirection.ASC))));
         // Append the path of property added as timestamp string to the sorted 
list
         assertOrderedQuery(
-            "select [jcr:path] from [nt:base] where [bar] = 'baz' order by 
[foo] DESC", Lists
-            .newArrayList(Iterables.concat(getSortedPaths(tuples, 
OrderDirection.DESC),
-                    Lists.newArrayList("/test/n0"))));
+                "select [jcr:path] from [nt:base] where [bar] = 'baz' order by 
[foo] DESC", Lists
+                        .newArrayList(Iterables.concat(getSortedPaths(tuples, 
OrderDirection.DESC),
+                                Lists.newArrayList("/test/n0"))));
     }
 
     @Test

Modified: 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java?rev=1660676&r1=1660675&r2=1660676&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
 Wed Feb 18 16:58:00 2015
@@ -26,13 +26,38 @@ import java.util.concurrent.atomic.Atomi
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState;
+import org.apache.jackrabbit.oak.plugins.name.NamespaceEditorProvider;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypeEditorProvider;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.NodeTypeRegistry;
+import org.apache.jackrabbit.oak.plugins.tree.RootFactory;
+import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.state.ApplyDiff;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import static com.google.common.base.Preconditions.checkNotNull;
 
 public class TestUtil {
     private static final AtomicInteger COUNTER = new AtomicInteger();
 
+    public static final String NT_TEST = "oak:TestNode";
+
+    public static final String TEST_NODE_TYPE = "[oak:TestNode]\n" +
+            " - * (UNDEFINED) multiple\n" +
+            " - * (UNDEFINED)\n" +
+            " + * (nt:base) = oak:TestNode VERSION";
+
     static void useV2(NodeBuilder idxNb) {
         idxNb.setProperty(LuceneIndexConstants.COMPAT_MODE, 
IndexFormatVersion.V2.getVersion());
     }
@@ -88,6 +113,13 @@ public class TestUtil {
         return props;
     }
 
+    public static NodeBuilder child(NodeBuilder nb, String path) {
+        for (String name : PathUtils.elements(checkNotNull(path))) {
+            nb = nb.child(name);
+        }
+        return nb;
+    }
+
     static class AggregatorBuilder {
         private final Tree aggs;
 
@@ -108,4 +140,34 @@ public class TestUtil {
     static String unique(String name){
         return name + COUNTER.getAndIncrement();
     }
+
+    public static NodeBuilder registerTestNodeType(NodeBuilder builder){
+        registerNodeType(builder, TEST_NODE_TYPE);
+        return builder;
+    }
+
+    public static void registerNodeType(NodeBuilder builder, String 
nodeTypeDefn){
+        //Taken from 
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent
+        NodeState base = ModifiedNodeState.squeeze(builder.getNodeState());
+        NodeStore store = new MemoryNodeStore(base);
+        Root root = RootFactory.createSystemRoot(
+                store, new EditorHook(new CompositeEditorProvider(
+                        new NamespaceEditorProvider(),
+                        new TypeEditorProvider())), null, null, null, null);
+        NodeTypeRegistry.register(root, IOUtils.toInputStream(nodeTypeDefn), 
"test node types");
+        NodeState target = store.getRoot();
+        target.compareAgainstBaseState(base, new ApplyDiff(builder));
+    }
+
+    public static Tree createNodeWithType(Tree t, String nodeName, String 
typeName){
+        t = t.addChild(nodeName);
+        t.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
+        return t;
+    }
+
+    public static NodeBuilder createNodeWithType(NodeBuilder builder, String 
nodeName, String typeName){
+        builder = builder.child(nodeName);
+        builder.setProperty(JcrConstants.JCR_PRIMARYTYPE, typeName, Type.NAME);
+        return builder;
+    }
 }


Reply via email to