Author: chetanm
Date: Fri Feb 20 11:51:59 2015
New Revision: 1661099

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

Merging revisions 1660676

Modified:
    jackrabbit/oak/branches/1.0/   (props changed)
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
    
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java

Propchange: jackrabbit/oak/branches/1.0/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Feb 20 11:51:59 2015
@@ -1,2 +1,2 @@
-/jackrabbit/oak/trunk:1584578,1584602,1584614,1584616,1584709,1584781,1584937,1585297,1585304-1585305,1585420,1585424,1585427,1585448,1585465,1585468,1585486,1585497,1585509,1585647,1585655-1585656,1585661,1585665-1585666,1585669-1585670,1585673,1585680,1585719,1585763,1585770,1585896,1585904,1585907,1585940,1585949,1585951,1585956,1585962-1585963,1586287,1586320,1586364,1586372,1586655,1586836,1587130,1587224,1587399,1587408,1587472,1587485,1587488,1587538,1587580,1587807,1588033,1588042,1588046,1588066,1588201,1589025,1589101,1589137,1589141,1589263,1589440,1589442,1589484,1589488,1589661,1589664,1589682,1589708,1589741,1589748,1589789,1589794,1589850,1589864,1590628,1590660,1590684,1590697,1590701,1590980,1590988,1591101,1591226,1591229,1591293,1591314,1591317,1591362,1591374,1591381,1591438,1591467,1591552,1591704,1591713,1591715,1591723,1591874,1592487,1592512,1592658,1592665,1592677,1592742,1592744,1592787,1592809,1592955,1593036,1593048,1593061,1593133,1593210-1593211,1593231
 
,1593245,1593250,1593294,1593304,1593317,1593342,1593554,1594158-1594164,1594166-1594167,1594169,1594237,1594800,1594808,1594835,1594888,1595147,1595457,1595856,1596241,1596474,1596534,1596844,1597569,1597795,1597854,1597860,1598292,1598302,1598352,1598369,1598595,1598631,1598696,1598732,1598797-1598798,1599299,1599332,1599416,1599434,1599671,1600088,1600935,1601309,1601388,1601578,1601649,1601676,1601757,1601768,1601814,1601833,1601838,1601853,1601878,1601888,1601922,1602156,1602170,1602174,1602179,1602183,1602201,1602207,1602227,1602256,1602261,1602342,1602796-1602797,1602800,1602809,1602853,1602872,1602914,1603155,1603307,1603401,1603441,1603748,1604166,1605030,1605036,1605038,1605292,1605447,1605526,1605670,1605725,1605831,1605852,1606077,1606079,1606087,1606638,1606641,1606644,1606708,1606711,1607031-1607032,1607077,1607127,1607141,1607152,1607185,1607196,1607331,1607362,1607366,1607392,1607526,1607557,1607664,1607737,1608560,1608731,1608783,1609064,1609081,1609165,1609214,1609
 
488,1610489,1610592,1610603,1610634,1610658,1610664,1611021,1611041,1611270,1611275,1611277,1611313,1611332,1611584,1612560,1612825,1612993,1613018,1613041,1614265,1614272,1614344-1614345,1614384-1614385,1614397,1614405-1614406,1614574,1614591,1614593,1614596,1614604,1614689,1614807,1614835,1614891,1615417-1615418,1616182,1616236,1616463,1616719,1617417,1617451,1617463,1617711,1618158,1618613,1618624,1618709,1619222,1619411,1619695,1619800,1619808,1619815,1619823-1619824,1620512,1620581,1620585,1620634,1620898,1620905,1621115,1621123-1621124,1621168,1621192,1621201,1621706,1621962,1622197,1622201,1622207,1622250,1622479,1623364,1623766,1623827,1623949,1623969,1623973,1624216,1624317,1624551,1624559,1624973,1624993-1624994,1625025,1625036,1625158,1625224,1625237,1625299,1625348,1625620,1625916,1625962-1625963,1626021,1626053,1626163,1626168,1626175,1626191,1626265,1626770,1627047,1627052,1627228,1627346,1627470,1627473,1627479,1627503,1627586,1627590,1627715,1627731,1628180,1628198,1
 
628262,1628447,1628608,1629688,1629840,1629917,1630055-1630057,1630156,1630299,1630338,1630773,1631283-1631284,1631333-1631334,1631617-1631619,1631630,1631699,1631704,1631711,1631967-1631969,1631986,1631990,1631999,1632002-1632003,1632017,1632258,1632264,1632270,1632293,1632303,1632592,1632605,1633315,1633559-1633560,1633562,1633567,1633571,1633598,1633608,1633641,1633687,1633697,1633768,1633783,1634505,1634513,1634774,1634779,1634781,1634792,1634803,1634814,1634816,1634838,1634841,1634852,1634864,1634896,1634898,1635044-1635045,1635060,1635077,1635089,1635102,1635108,1635218,1635387,1635435,1635518,1635563,1635586,1636336,1636348,1636505,1636585,1636799,1637368,1637382,1637413,1637651,1637815,1638779-1638783,1639260,1639577,1639622,1639963,1639966,1639973,1640134,1640143,1640555-1640556,1640694-1640695,1640715,1640722-1640723,1640728,1640863-1640872,1641340,1641350,1641352,1641541,1641596-1641599,1641601,1641662,1641671,1641695,1641771,1641802,1641811,1641950,1642031,1642056,164211
 
9,1642285,1642648,1642667,1642954,1642959,1643111,1643178,1643186,1643204,1643287,1643767,1643774,1643982,1644016,1644106,1644366,1644383,1644397-1644398,1644407,1644479,1644547,1644552,1644554,1644588,1644645,1644650,1644654,1644689,1644750,1645421,1645424,1645459,1645585,1645611,1645637,1645646,1645660-1645663,1645888,1645901,1645948,1645966,1645970-1645971,1646014,1646164,1646174,1646684,1649743,1649803,1650015,1650239,1650529,1650797,1651323,1651382,1651643,1651652,1651730,1651988-1651989,1651996,1652024,1652035,1652058-1652059,1652075,1652127,1652158,1652467,1652965,1652971,1653207,1653446,1653463,1653484,1653572,1653579,1653591,1653804,1653809,1653848-1653850,1653882,1654116,1654743,1654756,1654778,1655049,1655054-1655055,1655086,1655237,1655248,1655996,1656027,1656394,1656400,1656425,1656427,1656453,1656628,1657804,1658470,1659483,1659527,1659550,1659578,1659765,1660154-1660155,1660383,1660409,1660426,1660870,1660872,1660897,1661069
+/jackrabbit/oak/trunk:1584578,1584602,1584614,1584616,1584709,1584781,1584937,1585297,1585304-1585305,1585420,1585424,1585427,1585448,1585465,1585468,1585486,1585497,1585509,1585647,1585655-1585656,1585661,1585665-1585666,1585669-1585670,1585673,1585680,1585719,1585763,1585770,1585896,1585904,1585907,1585940,1585949,1585951,1585956,1585962-1585963,1586287,1586320,1586364,1586372,1586655,1586836,1587130,1587224,1587399,1587408,1587472,1587485,1587488,1587538,1587580,1587807,1588033,1588042,1588046,1588066,1588201,1589025,1589101,1589137,1589141,1589263,1589440,1589442,1589484,1589488,1589661,1589664,1589682,1589708,1589741,1589748,1589789,1589794,1589850,1589864,1590628,1590660,1590684,1590697,1590701,1590980,1590988,1591101,1591226,1591229,1591293,1591314,1591317,1591362,1591374,1591381,1591438,1591467,1591552,1591704,1591713,1591715,1591723,1591874,1592487,1592512,1592658,1592665,1592677,1592742,1592744,1592787,1592809,1592955,1593036,1593048,1593061,1593133,1593210-1593211,1593231
 
,1593245,1593250,1593294,1593304,1593317,1593342,1593554,1594158-1594164,1594166-1594167,1594169,1594237,1594800,1594808,1594835,1594888,1595147,1595457,1595856,1596241,1596474,1596534,1596844,1597569,1597795,1597854,1597860,1598292,1598302,1598352,1598369,1598595,1598631,1598696,1598732,1598797-1598798,1599299,1599332,1599416,1599434,1599671,1600088,1600935,1601309,1601388,1601578,1601649,1601676,1601757,1601768,1601814,1601833,1601838,1601853,1601878,1601888,1601922,1602156,1602170,1602174,1602179,1602183,1602201,1602207,1602227,1602256,1602261,1602342,1602796-1602797,1602800,1602809,1602853,1602872,1602914,1603155,1603307,1603401,1603441,1603748,1604166,1605030,1605036,1605038,1605292,1605447,1605526,1605670,1605725,1605831,1605852,1606077,1606079,1606087,1606638,1606641,1606644,1606708,1606711,1607031-1607032,1607077,1607127,1607141,1607152,1607185,1607196,1607331,1607362,1607366,1607392,1607526,1607557,1607664,1607737,1608560,1608731,1608783,1609064,1609081,1609165,1609214,1609
 
488,1610489,1610592,1610603,1610634,1610658,1610664,1611021,1611041,1611270,1611275,1611277,1611313,1611332,1611584,1612560,1612825,1612993,1613018,1613041,1614265,1614272,1614344-1614345,1614384-1614385,1614397,1614405-1614406,1614574,1614591,1614593,1614596,1614604,1614689,1614807,1614835,1614891,1615417-1615418,1616182,1616236,1616463,1616719,1617417,1617451,1617463,1617711,1618158,1618613,1618624,1618709,1619222,1619411,1619695,1619800,1619808,1619815,1619823-1619824,1620512,1620581,1620585,1620634,1620898,1620905,1621115,1621123-1621124,1621168,1621192,1621201,1621706,1621962,1622197,1622201,1622207,1622250,1622479,1623364,1623766,1623827,1623949,1623969,1623973,1624216,1624317,1624551,1624559,1624973,1624993-1624994,1625025,1625036,1625158,1625224,1625237,1625299,1625348,1625620,1625916,1625962-1625963,1626021,1626053,1626163,1626168,1626175,1626191,1626265,1626770,1627047,1627052,1627228,1627346,1627470,1627473,1627479,1627503,1627586,1627590,1627715,1627731,1628180,1628198,1
 
628262,1628447,1628608,1629688,1629840,1629917,1630055-1630057,1630156,1630299,1630338,1630773,1631283-1631284,1631333-1631334,1631617-1631619,1631630,1631699,1631704,1631711,1631967-1631969,1631986,1631990,1631999,1632002-1632003,1632017,1632258,1632264,1632270,1632293,1632303,1632592,1632605,1633315,1633559-1633560,1633562,1633567,1633571,1633598,1633608,1633641,1633687,1633697,1633768,1633783,1634505,1634513,1634774,1634779,1634781,1634792,1634803,1634814,1634816,1634838,1634841,1634852,1634864,1634896,1634898,1635044-1635045,1635060,1635077,1635089,1635102,1635108,1635218,1635387,1635435,1635518,1635563,1635586,1636336,1636348,1636505,1636585,1636799,1637368,1637382,1637413,1637651,1637815,1638779-1638783,1639260,1639577,1639622,1639963,1639966,1639973,1640134,1640143,1640555-1640556,1640694-1640695,1640715,1640722-1640723,1640728,1640863-1640872,1641340,1641350,1641352,1641541,1641596-1641599,1641601,1641662,1641671,1641695,1641771,1641802,1641811,1641950,1642031,1642056,164211
 
9,1642285,1642648,1642667,1642954,1642959,1643111,1643178,1643186,1643204,1643287,1643767,1643774,1643982,1644016,1644106,1644366,1644383,1644397-1644398,1644407,1644479,1644547,1644552,1644554,1644588,1644645,1644650,1644654,1644689,1644750,1645421,1645424,1645459,1645585,1645611,1645637,1645646,1645660-1645663,1645888,1645901,1645948,1645966,1645970-1645971,1646014,1646164,1646174,1646684,1649743,1649803,1650015,1650239,1650529,1650797,1651323,1651382,1651643,1651652,1651730,1651988-1651989,1651996,1652024,1652035,1652058-1652059,1652075,1652127,1652158,1652467,1652965,1652971,1653207,1653446,1653463,1653484,1653572,1653579,1653591,1653804,1653809,1653848-1653850,1653882,1654116,1654743,1654756,1654778,1655049,1655054-1655055,1655086,1655237,1655248,1655996,1656027,1656394,1656400,1656425,1656427,1656453,1656628,1657804,1658470,1659483,1659527,1659550,1659578,1659765,1660154-1660155,1660383,1660409,1660426,1660676,1660870,1660872,1660897,1661069
 /jackrabbit/trunk:1345480

Modified: 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
 Fri Feb 20 11:51:59 2015
@@ -64,6 +64,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/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
 Fri Feb 20 11:51:59 2015
@@ -71,7 +71,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;
@@ -507,6 +506,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;
@@ -527,15 +527,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();
         }
 
         /**
@@ -555,6 +558,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();
@@ -582,6 +586,10 @@ class IndexDefinition implements Aggrega
             return nodeTypeName;
         }
 
+        public List<PropertyDefinition> getNullCheckEnabledProperties() {
+            return nullCheckEnabledProperties;
+        }
+
         @Override
         public String toString() {
             String str = "IndexRule: "+ nodeTypeName;
@@ -674,7 +682,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);
 
@@ -700,9 +709,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);
@@ -745,6 +758,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
@@ -764,6 +786,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");
+            }
+        }
     }
 
     /**
@@ -893,7 +922,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++;
                 }
@@ -955,7 +984,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);
@@ -1175,7 +1204,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/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
 Fri Feb 20 11:51:59 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/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
 Fri Feb 20 11:51:59 2015
@@ -192,4 +192,10 @@ public interface LuceneIndexConstants {
      * nt:file node under 'tika' node which refers to the config xml file
      */
     String TIKA_CONFIG = "config.xml";
+
+    /**
+     * Property definition config indicating that null check support should be
+     * enabled for this property
+     */
+    String PROP_NULL_CHECK_ENABLED = "nullCheckEnabled";
 }

Modified: 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
 Fri Feb 20 11:51:59 2015
@@ -298,6 +298,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
@@ -488,6 +489,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/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
 Fri Feb 20 11:51:59 2015
@@ -657,6 +657,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;
@@ -711,7 +716,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);
                 }
@@ -740,7 +745,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);
                 }
@@ -774,7 +779,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/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/PropertyDefinition.java
 Fri Feb 20 11:51:59 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,11 +74,25 @@ class PropertyDefinition {
 
     final boolean ordered;
 
+    final boolean nullCheckEnabled;
+
     final int includedPropertyTypes;
 
-    public PropertyDefinition(IndexingRule idxDefn, String name, NodeState 
defn) {
+    final boolean relative;
+
+    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 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
@@ -90,7 +110,11 @@ class PropertyDefinition {
         //TODO Add test case for above cases
 
         this.propertyType = getPropertyType(idxDefn, name, defn);
-    }
+        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
@@ -149,11 +173,40 @@ class PropertyDefinition {
                 ", propertyIndex=" + propertyIndex +
                 ", analyzed=" + analyzed +
                 ", ordered=" + ordered +
+                ", 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/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
 Fri Feb 20 11:51:59 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.tree.ImmutableTree;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -33,7 +32,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;
@@ -47,12 +45,14 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INDEX_RULES;
 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.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.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;
@@ -150,13 +150,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);
 
@@ -239,9 +241,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);
@@ -263,10 +265,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);
@@ -289,11 +291,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);
@@ -324,7 +326,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);
@@ -486,6 +488,37 @@ public class IndexDefinitionTest {
         assertTrue(defn.hasCustomTikaConfig());
     }
 
+    @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)));
     }
@@ -500,10 +533,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/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
 Fri Feb 20 11:51:59 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/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
 Fri Feb 20 11:51:59 2015
@@ -41,6 +41,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.newLucenePropertyIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
@@ -217,6 +219,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);
@@ -438,7 +515,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/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
 Fri Feb 20 11:51:59 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;
@@ -279,8 +281,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
@@ -313,6 +316,35 @@ public class LucenePropertyIndexTest ext
     }
 
     @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 rangeQueriesWithLong() throws Exception {
         Tree idx = createIndex("test1", of("propa", "propb"));
         Tree propIdx = idx.addChild(PROP_NODE).addChild("propa");

Modified: 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java?rev=1661099&r1=1661098&r2=1661099&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
 (original)
+++ 
jackrabbit/oak/branches/1.0/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
 Fri Feb 20 11:51:59 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.core.SystemRoot;
 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.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());
     }
@@ -87,6 +112,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;
 
@@ -107,4 +139,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 = new SystemRoot(
+                store, new EditorHook(new CompositeEditorProvider(
+                new NamespaceEditorProvider(),
+                new TypeEditorProvider())));
+        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