You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2014/07/17 16:16:48 UTC

svn commit: r1611358 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/ oak-core/src/test/resources/org/apache/jackrabbit/oak/query/ oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/

Author: jukka
Date: Thu Jul 17 14:16:48 2014
New Revision: 1611358

URL: http://svn.apache.org/r1611358
Log:
OAK-1965: Support for constraints like: foo = 'X' OR bar = 'Y'

Refactor PropertyIndex to make it easier to extend.

Make the property index query plans more informative by including
the name of the index being used and the encoded values being looked up.

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
    jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java?rev=1611358&r1=1611357&r2=1611358&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java Thu Jul 17 14:16:48 2014
@@ -16,6 +16,11 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.property;
 
+import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.HashSet;
@@ -23,18 +28,15 @@ import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
-import org.apache.jackrabbit.oak.spi.query.Cursors;
 import org.apache.jackrabbit.oak.spi.query.Filter;
-import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Charsets;
-import com.google.common.collect.Iterables;
 
 /**
  * Provides a QueryIndex that does lookups against a property index
@@ -84,6 +86,8 @@ import com.google.common.collect.Iterabl
  */
 class PropertyIndex implements QueryIndex {
 
+    private static final String PROPERTY = "property";
+
     // TODO the max string length should be removed, or made configurable
     private static final int MAX_STRING_LENGTH = 100;
 
@@ -117,52 +121,36 @@ class PropertyIndex implements QueryInde
         return values;
     }
 
-    private Cheapest findCheapestProperty(Filter filter, PropertyIndexLookup lookup) {
-        Cheapest cost = new Cheapest();
-        for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
-            String propertyName = PathUtils.getName(pr.propertyName);
-            double propertyCost = Double.POSITIVE_INFINITY;
-            // TODO support indexes on a path
-            // currently, only indexes on the root node are supported
-            if (lookup.isIndexed(propertyName, "/", filter)) {
-                if (pr.firstIncluding && pr.lastIncluding
-                    && pr.first != null && pr.first.equals(pr.last)) {
-                    // "[property] = $value"
-                    propertyCost = lookup.getCost(filter, propertyName, pr.first);
-                } else if (pr.list != null) {
-                    propertyCost = 0;
-                    for (PropertyValue p : pr.list) {
-                        propertyCost += lookup.getCost(filter, propertyName, p);
+    private PropertyIndexPlan plan(NodeState root, Filter filter) {
+        PropertyIndexPlan bestPlan = null;
+
+        // TODO support indexes on a path
+        // currently, only indexes on the root node are supported
+        NodeState state = root.getChildNode(INDEX_DEFINITIONS_NAME);
+        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+            NodeState definition = entry.getNodeState();
+            if (PROPERTY.equals(definition.getString(TYPE_PROPERTY_NAME))
+                    && definition.hasChildNode(INDEX_CONTENT_NODE_NAME)) {
+                PropertyIndexPlan plan = new PropertyIndexPlan(
+                        entry.getName(), definition, filter);
+                if (plan.getCost() != Double.POSITIVE_INFINITY) {
+                    LOG.debug("property cost for {} is {}",
+                            plan.getName(), plan.getCost());
+                    if (bestPlan == null || plan.getCost() < bestPlan.getCost()) {
+                        bestPlan = plan;
                     }
-                } else {
-                    // processed as "[property] is not null"
-                    propertyCost = lookup.getCost(filter, propertyName, null);
                 }
             }
-            LOG.debug("property cost for {} is {}", propertyName, propertyCost);
-            if (propertyCost < cost.cost) {
-                cost.cost = propertyCost;
-                cost.propertyRestriction = pr;
-            }
         }
-        return cost;
+
+        return bestPlan;
     }
 
     //--------------------------------------------------------< QueryIndex >--
 
     @Override
     public String getIndexName() {
-        return "property";
-    }
-
-    /**
-     * return the proper implementation of the Lookup
-     * 
-     * @param root
-     * @return the lookup
-     */
-    PropertyIndexLookup getLookup(NodeState root) {
-        return new PropertyIndexLookup(root);
+        return PROPERTY;
     }
 
     @Override
@@ -176,103 +164,31 @@ class PropertyIndex implements QueryInde
             return Double.POSITIVE_INFINITY;
         }
 
-        PropertyIndexLookup lookup = getLookup(root);
-        Cheapest cheapest = findCheapestProperty(filter, lookup);
-        LOG.debug("Cheapest property cost is {} for property {}", cheapest.cost, cheapest.propertyRestriction != null ? cheapest.propertyRestriction.propertyName : null);
-        return cheapest.cost;
+        PropertyIndexPlan plan = plan(root, filter);
+        if (plan != null) {
+            return plan.getCost();
+        } else {
+            return Double.POSITIVE_INFINITY;
+        }
     }
 
     @Override
     public Cursor query(Filter filter, NodeState root) {
-        Iterable<String> paths = null;
-
-        PropertyIndexLookup lookup = getLookup(root);
-        int depth = 1;
-
-        Cheapest cheapest = findCheapestProperty(filter, lookup);
-        PropertyRestriction pr = cheapest.propertyRestriction;
-
-        if (pr != null) {
-            String propertyName = PathUtils.getName(pr.propertyName);
-            depth = PathUtils.getDepth(pr.propertyName);
-            // TODO support indexes on a path
-            // currently, only indexes on the root node are supported
-            if (lookup.isIndexed(propertyName, "/", filter)) {
-                // equality
-                if (pr.firstIncluding && pr.lastIncluding
-                    && pr.first != null && pr.first.equals(pr.last)) {
-                    // "[property] = $value"
-                    paths = lookup.query(filter, propertyName, pr.first);
-                } else if (pr.list != null) {
-                    for (PropertyValue pv : pr.list) {
-                        Iterable<String> p = lookup.query(filter, propertyName, pv);
-                        if (paths == null) {
-                            paths = p;
-                        } else {
-                            paths = Iterables.concat(paths, p);
-                        }
-                    }
-                } else {
-                    // processed as "[property] is not null"
-                    paths = lookup.query(filter, propertyName, null);
-                }
-            }
-        }
-        if (paths == null) {
-            throw new IllegalStateException("Property index is used even when no index is available for filter " + filter);
-        }
-        Cursor c = Cursors.newPathCursor(paths, filter.getQueryEngineSettings());
-        if (depth > 1) {
-            c = Cursors.newAncestorCursor(c, depth - 1, filter.getQueryEngineSettings());
-        }
-        return c;
+        PropertyIndexPlan plan = plan(root, filter);
+        checkState(plan != null,
+                "Property index is used even when no index"
+                + " is available for filter " + filter);
+        return plan.execute();
     }
-    
+
     @Override
     public String getPlan(Filter filter, NodeState root) {
-        StringBuilder buff = new StringBuilder("property");
-        StringBuilder notIndexed = new StringBuilder();
-        PropertyIndexLookup lookup = getLookup(root);
-        Cheapest cheapest = findCheapestProperty(filter, lookup);
-        PropertyRestriction pr = cheapest.propertyRestriction;
-
-        if (pr != null) {
-            String propertyName = PathUtils.getName(pr.propertyName);
-            // TODO support indexes on a path
-            // currently, only indexes on the root node are supported
-            if (lookup.isIndexed(propertyName, "/", filter)) {
-                if (pr.firstIncluding && pr.lastIncluding
-                    && pr.first != null && pr.first.equals(pr.last)) {
-                    buff.append(' ').append(propertyName).append('=').append(pr.first);
-                } else {
-                    buff.append(' ').append(propertyName);
-                }
-            } else if (pr.list != null) {
-                buff.append(' ').append(propertyName).append(" IN(");
-                int i = 0;
-                for (PropertyValue pv : pr.list) {
-                    if (i++ > 0) {
-                        buff.append(", ");
-                    }
-                    buff.append(pv);
-                }
-                buff.append(')');
-            } else {
-                notIndexed.append(' ').append(propertyName);
-                if (!pr.toString().isEmpty()) {
-                    notIndexed.append(':').append(pr);
-                }
-            }
-        }
-        if (notIndexed.length() > 0) {
-            buff.append(" (").append(notIndexed.toString().trim()).append(")");
+        PropertyIndexPlan plan = plan(root, filter);
+        if (plan != null) {
+            return plan.toString();
+        } else {
+            return "property index not applicable";
         }
-        return buff.toString();
-    }
-
-    private static class Cheapest {
-        private double cost = Double.POSITIVE_INFINITY;
-        private PropertyRestriction propertyRestriction;
     }
 
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java?rev=1611358&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java Thu Jul 17 14:16:48 2014
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.property;
+
+import static com.google.common.base.Predicates.in;
+import static com.google.common.collect.Iterables.any;
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static java.util.Collections.emptySet;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.UNIQUE_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode;
+
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Cursors;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Plan for querying a given property index using a given filter.
+ */
+public class PropertyIndexPlan {
+
+    /**
+     * The cost overhead to use the index in number of read operations.
+     */
+    private static final double COST_OVERHEAD = 2;
+
+    /**
+     * The maximum cost when the index can be used.
+     */
+    static final int MAX_COST = 100;
+
+    /** Index storage strategy */
+    private static final IndexStoreStrategy MIRROR =
+            new ContentMirrorStoreStrategy();
+
+    /** Index storage strategy */
+    private static final IndexStoreStrategy UNIQUE =
+            new UniqueEntryStoreStrategy();
+
+    private final NodeState definition;
+
+    private final String name;
+
+    private final IndexStoreStrategy strategy;
+
+    private final Filter filter;
+
+    private boolean matchesAllTypes;
+
+    private boolean matchesNodeTypes;
+
+    private final double cost;
+
+    private final Set<String> values;
+
+    private final int depth;
+
+    PropertyIndexPlan(String name, NodeState definition, Filter filter) {
+        this.name = name;
+        this.definition = definition;
+
+        if (definition.getBoolean(UNIQUE_PROPERTY_NAME)) {
+            this.strategy = UNIQUE;
+        } else {
+            this.strategy = MIRROR;
+        }
+
+        this.filter = filter;
+
+        Iterable<String> types = definition.getNames(DECLARING_NODE_TYPES);
+        this.matchesAllTypes = isEmpty(types);
+        this.matchesNodeTypes =
+                matchesAllTypes || any(types, in(filter.getSupertypes()));
+
+        double bestCost = Double.POSITIVE_INFINITY;
+        Set<String> bestValues = emptySet();
+        int bestDepth = 1;
+
+        if (matchesNodeTypes) {
+            for (String property : definition.getNames(PROPERTY_NAMES)) {
+                int depth = 1;
+
+                PropertyRestriction restriction =
+                        filter.getPropertyRestriction(property);
+                if (restriction == null) {
+                    // no direct restriction, try one with a relative path
+                    String suffix = "/" + property;
+                    for (PropertyRestriction relative
+                            : filter.getPropertyRestrictions()) {
+                        if (relative.propertyName.endsWith(suffix)) {
+                            restriction = relative;
+                            depth = PathUtils.getDepth(relative.propertyName);
+                        }
+                    }
+                }
+                if (restriction == null) {
+                    continue; // no matching restrictions
+                }
+
+                Set<String> values = getValues(restriction);
+                double cost = strategy.count(definition, values, MAX_COST);
+                if (cost < bestCost) {
+                    bestDepth = depth;
+                    bestValues = values;
+                    bestCost = cost;
+                }
+            }
+        }
+
+        this.depth = bestDepth;
+        this.values = bestValues;
+        this.cost = COST_OVERHEAD + bestCost;
+    }
+
+    private Set<String> getValues(PropertyRestriction restriction) {
+        if (restriction.firstIncluding
+                && restriction.lastIncluding
+                && restriction.first != null
+                && restriction.first.equals(restriction.last)) {
+            // "[property] = $value"
+            return encode(restriction.first);
+        } else if (restriction.list != null) {
+            // "[property] IN (...)
+            Set<String> values = newLinkedHashSet(); // keep order for testing
+            for (PropertyValue value : restriction.list) {
+                values.addAll(encode(value));
+            }
+            return values;
+        } else {
+            // processed as "[property] is not null"
+            return null;
+        }
+    }
+
+    String getName() {
+        return name;
+    }
+
+    double getCost() {
+        return cost;
+    }
+
+    Cursor execute() {
+        QueryEngineSettings settings = filter.getQueryEngineSettings();
+        Cursor cursor = Cursors.newPathCursor(
+                strategy.query(filter, name, definition, values),
+                settings);
+        if (depth > 1) {
+            cursor = Cursors.newAncestorCursor(cursor, depth - 1, settings);
+        }
+        return cursor;
+    }
+
+    //------------------------------------------------------------< Object >--
+
+    public String toString() {
+        StringBuilder buffer = new StringBuilder("property ");
+        buffer.append(name);
+        if (values == null) {
+            buffer.append(" IS NOT NULL");
+        } else if (values.isEmpty()) {
+            buffer.append(" NOT APPLICABLE");
+        } else if (values.size() == 1) {
+            buffer.append(" = ");
+            buffer.append(values.iterator().next());
+        } else {
+            buffer.append(" IN (");
+            boolean comma = false;
+            for (String value : values) {
+                if (comma) {
+                    buffer.append(", ");
+                }
+                buffer.append(value);
+                comma = true;
+            }
+            buffer.append(")");
+        }
+        return buffer.toString();
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt?rev=1611358&r1=1611357&r2=1611358&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2_index.txt Thu Jul 17 14:16:48 2014
@@ -27,7 +27,7 @@
 explain select *
   from [nt:base]
   where [jcr:uuid] like '%'
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IS NOT NULL
   where [nt:base].[jcr:uuid] like '%' */
 
 explain select e.[jcr:path]
@@ -42,7 +42,7 @@ explain select e.[jcr:path]
   and name(c) = 'd'
   and name(d) = 'e'
   and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4')
-[nt:base] as [e] /* property jcr:uuid
+[nt:base] as [e] /* property uuid IN (1, 2, 3, 4)
   where [e].[jcr:uuid] in('1', '2', '3', '4') */
   inner join [nt:base] as [d] /* traverse "* && //parent/of/join"
   where name([d]) = 'e' */
@@ -70,7 +70,7 @@ explain select e.[jcr:path]
   and name(c) = 'd'
   and name(d) = 'e'
   and (e.[jcr:uuid] = '1' or e.[jcr:uuid] = '2' or e.[jcr:uuid] = '3' or e.[jcr:uuid] = '4')
-[nt:base] as [e] /* property jcr:uuid
+[nt:base] as [e] /* property uuid IN (1, 2, 3, 4)
   where [e].[jcr:uuid] in('1', '2', '3', '4') */
   inner join [nt:base] as [d] /* traverse "* && //parent/of/join"
   where name([d]) = 'e' */
@@ -115,7 +115,7 @@ explain select b.[jcr:uuid]
   from [nt:base] as a
   inner join [nt:base] as b on isdescendantnode(b, a)
   where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2')
-[nt:base] as [a] /* property jcr:uuid
+[nt:base] as [a] /* property uuid IN (1, 2)
   where [a].[jcr:uuid] in('1', '2') */
   inner join [nt:base] as [b] /* traverse "* && //path/from/join//*" */
   on isdescendantnode([b], [a])
@@ -125,48 +125,48 @@ explain select b.[jcr:uuid]
   inner join [nt:base] as b on isdescendantnode(b, a)
   where (a.[jcr:uuid] = '1' or a.[jcr:uuid] = '2')
   and b.[jcr:uuid] is not null
-[nt:base] as [a] /* property jcr:uuid
+[nt:base] as [a] /* property uuid IN (1, 2)
   where [a].[jcr:uuid] in('1', '2') */
-  inner join [nt:base] as [b] /* property jcr:uuid
+  inner join [nt:base] as [b] /* property uuid IS NOT NULL
   where [b].[jcr:uuid] is not null */
   on isdescendantnode([b], [a])
 
 explain select *
   from [nt:base]
   where length([jcr:uuid])=1 or upper([jcr:uuid])='1' or lower([jcr:uuid])='3'
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IS NOT NULL
   where [nt:base].[jcr:uuid] is not null */
 
 explain select *
   from [nt:base]
   where [jcr:uuid] = '1' or ([jcr:uuid] = '2'
   and [b] = '3')
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IN (1, 2)
   where ([nt:base].[jcr:uuid] is not null)
   and ([nt:base].[jcr:uuid] in('1', '2')) */
 
 explain select *
   from [nt:base]
   where [jcr:uuid] in('1', '2')
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IN (1, 2)
   where [nt:base].[jcr:uuid] in('1', '2') */
 
 explain select *
   from [nt:base]
   where [jcr:uuid] = '1' or [jcr:uuid] = '2'
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IN (1, 2)
   where [nt:base].[jcr:uuid] in('1', '2') */
 
 explain select *
   from [nt:base]
   where [jcr:uuid] = '123'
-[nt:base] as [nt:base] /* property jcr:uuid=123
+[nt:base] as [nt:base] /* property uuid = 123
   where [nt:base].[jcr:uuid] = '123' */
 
 explain select *
   from [nt:base]
   where [jcr:uuid] is not null
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IS NOT NULL
   where [nt:base].[jcr:uuid] is not null */
 
 explain select *
@@ -174,9 +174,9 @@ explain select *
   inner join [nt:base] as b on isdescendantnode(b, a)
   where a.[jcr:uuid] is not null
   and b.[jcr:uuid] is not null
-[nt:base] as [a] /* property jcr:uuid
+[nt:base] as [a] /* property uuid IS NOT NULL
   where [a].[jcr:uuid] is not null */
-  inner join [nt:base] as [b] /* property jcr:uuid
+  inner join [nt:base] as [b] /* property uuid IS NOT NULL
   where [b].[jcr:uuid] is not null */
   on isdescendantnode([b], [a])
 
@@ -185,9 +185,9 @@ explain select *
   inner join [nt:base] as b on isdescendantnode(b, a)
   where (a.[jcr:uuid]=1 or a.[jcr:uuid]=2)
   and (b.[jcr:uuid]=3 or b.[jcr:uuid]=4)
-[nt:base] as [a] /* property jcr:uuid
+[nt:base] as [a] /* property uuid IN (1, 2)
   where [a].[jcr:uuid] in(1, 2) */
-  inner join [nt:base] as [b] /* property jcr:uuid
+  inner join [nt:base] as [b] /* property uuid IN (3, 4)
   where [b].[jcr:uuid] in(3, 4) */
   on isdescendantnode([b], [a])
 
@@ -196,7 +196,7 @@ explain select *
   inner join [nt:base] as b on isdescendantnode(b, a)
   where a.[jcr:uuid] is not null
   and b.[x] is not null
-[nt:base] as [a] /* property jcr:uuid
+[nt:base] as [a] /* property uuid IS NOT NULL
   where [a].[jcr:uuid] is not null */
   inner join [nt:base] as [b] /* traverse "* && //path/from/join//*"
   where [b].[x] is not null */
@@ -205,7 +205,7 @@ explain select *
 explain select [rep:excerpt]
   from [nt:base]
   where [jcr:uuid] is not null
-[nt:base] as [nt:base] /* property jcr:uuid
+[nt:base] as [nt:base] /* property uuid IS NOT NULL
   where [nt:base].[jcr:uuid] is not null */
 
 commit / + "test": { "jcr:uuid": "xyz", "a": { "jcr:uuid": "123" } }

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java?rev=1611358&r1=1611357&r2=1611358&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryPlanTest.java Thu Jul 17 14:16:48 2014
@@ -88,9 +88,9 @@ public class QueryPlanTest extends Abstr
         plan = it.nextRow().getValue("plan").getString();
         // System.out.println("plan: " + plan);
         // should use the index on "jcr:uuid"
-        assertEquals("[oak:Unstructured] as [a] " + 
-                "/* property jcr:uuid where ([a].[jcr:uuid] is not null) " + 
-                "and (ischildnode([a], [/a/b/c/d/e/f/g/h/i/j/k])) */", 
+        assertEquals("[oak:Unstructured] as [a] " +
+                "/* property uuid IS NOT NULL where ([a].[jcr:uuid] is not null) " +
+                "and (ischildnode([a], [/a/b/c/d/e/f/g/h/i/j/k])) */",
                 plan);
     }     
     
@@ -124,7 +124,7 @@ public class QueryPlanTest extends Abstr
         String plan = it.nextRow().getValue("plan").getString();
         // System.out.println("plan: " + plan);
         // should not use the index on "jcr:uuid"
-        assertEquals("[nt:base] as [a] /* property notNull " +
+        assertEquals("[nt:base] as [a] /* property notNull IS NOT NULL " +
                 "where ([a].[notNull] is not null) " +
                 "and ([a].[equals] = 1) " +
                 "and (isdescendantnode([a], [/])) */",
@@ -165,7 +165,7 @@ public class QueryPlanTest extends Abstr
         String plan = it.nextRow().getValue("plan").getString();
         // System.out.println("plan: " + plan);
         // should not use the index on "jcr:uuid"
-        assertEquals("[nt:base] as [a] /* property tenPercent " +
+        assertEquals("[nt:base] as [a] /* property tenPercent IS NOT NULL " +
                 "where ([a].[tenPercent] is not null) " +
                 "and ([a].[fiftyPercent] is not null) " +
                 "and ([a].[hundredPercent] is not null) " +
@@ -200,7 +200,7 @@ public class QueryPlanTest extends Abstr
         String plan = it.nextRow().getValue("plan").getString();
         // System.out.println("plan: " + plan);
         // should not use the index on "jcr:uuid"
-        assertEquals("[nt:base] as [a] /* property jcr:uuid " + 
+        assertEquals("[nt:base] as [a] /* property uuid IS NOT NULL " +
                 "where ([a].[jcr:uuid] is not null) and " + 
                 "(isdescendantnode([a], [/testroot/n/n/n/n/n/n/n])) */", 
                 plan);
@@ -287,7 +287,7 @@ public class QueryPlanTest extends Abstr
         it = result.getRows();
         assertTrue(it.hasNext());
         plan = it.nextRow().getValue("plan").getString();
-        assertEquals("[nt:base] as [a] /* property jcr:uuid " + 
+        assertEquals("[nt:base] as [a] /* property uuid IS NOT NULL " +
                 "where ([a].[jcr:uuid] is not null) " + 
                 "and (ischildnode([a], [/testroot])) */", plan);