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 ca...@apache.org on 2019/06/25 22:49:38 UTC

svn commit: r1862093 - in /jackrabbit/oak/trunk: oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/ oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ oak-search/src/main/java/org/apache/jackrabbit/oak/plugin...

Author: catholicon
Date: Tue Jun 25 22:49:38 2019
New Revision: 1862093

URL: http://svn.apache.org/viewvc?rev=1862093&view=rev
Log:
OAK-8437: direct children, exact, and parent path restrictions don't work when path transformation takes place

Added:
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexPathRestrictionTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java

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=1862093&r1=1862092&r2=1862093&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 Tue Jun 25 22:49:38 2019
@@ -1054,12 +1054,21 @@ public class LucenePropertyIndex extends
                 if (defn.evaluatePathRestrictions()) {
                     BooleanQuery bq = new BooleanQuery();
                     bq.add(new BooleanClause(new TermQuery(newAncestorTerm(path)), BooleanClause.Occur.MUST));
-                    bq.add(new BooleanClause(newDepthQuery(path), BooleanClause.Occur.MUST));
+                    bq.add(new BooleanClause(newDepthQuery(path, planResult), BooleanClause.Occur.MUST));
                     qs.add(bq);
                 }
                 break;
             case EXACT:
-                qs.add(new TermQuery(newPathTerm(path)));
+                // For transformed paths, we can only add path restriction if absolute path to property can be
+                // deduced
+                if (planResult.isPathTransformed()) {
+                    String parentPathSegment = planResult.getParentPathSegment();
+                    if ( ! Iterables.any(PathUtils.elements(parentPathSegment), "*"::equals)) {
+                        qs.add(new TermQuery(newPathTerm(path + parentPathSegment)));
+                    }
+                } else {
+                    qs.add(new TermQuery(newPathTerm(path)));
+                }
                 break;
             case PARENT:
                 if (denotesRoot(path)) {
@@ -1068,7 +1077,16 @@ public class LucenePropertyIndex extends
                     // is no way to say "match no documents" in Lucene
                     qs.add(new TermQuery(new Term(FieldNames.PATH, "///")));
                 } else {
-                    qs.add(new TermQuery(newPathTerm(getParentPath(path))));
+                    // For transformed paths, we can only add path restriction if absolute path to property can be
+                    // deduced
+                    if (planResult.isPathTransformed()) {
+                        String parentPathSegment = planResult.getParentPathSegment();
+                        if ( ! Iterables.any(PathUtils.elements(parentPathSegment), "*"::equals)) {
+                            qs.add(new TermQuery(newPathTerm(getParentPath(path) + parentPathSegment)));
+                        }
+                    } else {
+                        qs.add(new TermQuery(newPathTerm(getParentPath(path))));
+                    }
                 }
                 break;
             case NO_RESTRICTION:
@@ -1505,8 +1523,8 @@ public class LucenePropertyIndex extends
         }
     }
 
-    private static Query newDepthQuery(String path) {
-        int depth = PathUtils.getDepth(path) + 1;
+    private static Query newDepthQuery(String path, PlanResult planResult) {
+        int depth = PathUtils.getDepth(path) + planResult.getParentDepth() + 1;
         return NumericRangeQuery.newIntRange(FieldNames.PATH_DEPTH, depth, depth, true, true);
     }
 

Added: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexPathRestrictionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexPathRestrictionTest.java?rev=1862093&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexPathRestrictionTest.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexPathRestrictionTest.java Tue Jun 25 22:49:38 2019
@@ -0,0 +1,280 @@
+/*
+ * 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.lucene;
+
+import com.google.common.collect.Sets;
+import org.apache.jackrabbit.oak.InitialContentHelper;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.util.IndexDefinitionBuilder;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyValues;
+import org.apache.jackrabbit.oak.query.NodeStateNodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.query.ast.NodeTypeInfo;
+import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.ast.Operator;
+import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.ImmutableSet.of;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.oak.spi.commit.CommitInfo.EMPTY;
+import static org.junit.Assert.assertEquals;
+
+public class LuceneIndexPathRestrictionTest {
+    private static final EditorHook HOOK = new EditorHook(
+            new IndexUpdateProvider(new LuceneIndexEditorProvider()));
+
+    private NodeState root;
+    private NodeBuilder rootBuilder;
+    private IndexTracker tracker;
+    private LucenePropertyIndex index;
+
+    @Before
+    public void setup() throws Exception {
+        root = InitialContentHelper.INITIAL_CONTENT;
+        rootBuilder = root.builder();
+
+        tracker = new IndexTracker();
+        tracker.update(root);
+        index = new LucenePropertyIndex(tracker);
+
+        // remove any indexes (that cause commits to fail due to missing provider)
+        rootBuilder.child(IndexConstants.INDEX_DEFINITIONS_NAME).remove();
+
+        commit();
+    }
+
+    @Test
+    public void pathTranformationWithNoPathRestriction() throws Exception {
+        IndexDefinitionBuilder idxBuilder =
+                new IndexDefinitionBuilder(rootBuilder.child(IndexConstants.INDEX_DEFINITIONS_NAME).child("fooIndex"))
+                        .noAsync().evaluatePathRestrictions();
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        idxBuilder.build();
+        commit();
+
+        NodeBuilder testRootBuilder = rootBuilder.child("test");
+        testRootBuilder.child("a").child("j:c").setProperty("foo", "bar");
+        testRootBuilder.child("b").setProperty("foo", "bar");
+        testRootBuilder.child("c").child("d").child("j:c").setProperty("foo", "bar");
+        commit();
+
+        FilterImpl f;
+
+        // //*[j:c/foo = 'bar'] -> foo:bar -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("j:c/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        validateResult(f, of("/test", "/test/a", "/test/c/d"));
+
+        // //*[*/foo = 'bar'] -> foo:bar -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        validateResult(f, of("/test/a", "/test", "/test/c/d"));
+
+        // //*[d/*/foo = 'bar'] -> foo:bar -> transform 2 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("d/*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        validateResult(f, of("/", "/test", "/test/c"));
+    }
+
+    @Test
+    public void pathTranformationWithAllChildrenPathRestriction() throws Exception {
+        IndexDefinitionBuilder idxBuilder =
+                new IndexDefinitionBuilder(rootBuilder.child(IndexConstants.INDEX_DEFINITIONS_NAME).child("fooIndex"))
+                        .noAsync().evaluatePathRestrictions();
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        idxBuilder.build();
+        commit();
+
+        NodeBuilder testRootBuilder = rootBuilder.child("test");
+        testRootBuilder.child("a").child("j:c").setProperty("foo", "bar");
+        testRootBuilder.child("b").setProperty("foo", "bar");
+        testRootBuilder.child("c").child("d").child("j:c").setProperty("foo", "bar");
+        commit();
+
+        FilterImpl f;
+
+        // /jcr:root/test//*[j:c/foo = 'bar'] -> foo:bar :ancestors:/test -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("j:c/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test", Filter.PathRestriction.ALL_CHILDREN);
+        validateResult(f, of("/test/a", "/test/c/d"));
+
+        // /jcr:root/test//*[*/foo = 'bar'] -> foo:bar :ancestors:/test -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test", Filter.PathRestriction.ALL_CHILDREN);
+        validateResult(f, of("/test/a", "/test/c/d"));
+
+        // /jcr:root/test//*[d/*/foo = 'bar'] -> foo:bar :ancestors:/test -> transform 2 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("d/*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test", Filter.PathRestriction.ALL_CHILDREN);
+        validateResult(f, of("/test/c"));
+    }
+
+    @Test
+    public void pathTranformationWithDirectChildrenPathRestriction() throws Exception {
+        IndexDefinitionBuilder idxBuilder =
+                new IndexDefinitionBuilder(rootBuilder.child(IndexConstants.INDEX_DEFINITIONS_NAME).child("fooIndex"))
+                        .noAsync().evaluatePathRestrictions();
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        idxBuilder.build();
+        commit();
+
+        NodeBuilder testRootBuilder = rootBuilder.child("test");
+        testRootBuilder.child("a").child("j:c").setProperty("foo", "bar");
+        testRootBuilder.child("b").setProperty("foo", "bar");
+        testRootBuilder.child("c").child("d").child("j:c").setProperty("foo", "bar");
+        commit();
+
+        FilterImpl f;
+
+        // /jcr:root/test/*[j:c/foo = 'bar'] -> foo:bar :ancestors:/test :depth:[3 TO 3] -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("j:c/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test", Filter.PathRestriction.DIRECT_CHILDREN);
+        validateResult(f, of("/test/a"));
+
+        // /jcr:root/test/*[*/foo = 'bar'] -> foo:bar :ancestors:/test :depth:[3 TO 3] -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test", Filter.PathRestriction.DIRECT_CHILDREN);
+        validateResult(f, of("/test/a"));
+
+        // /jcr:root/test/*[d/*/foo = 'bar'] -> foo:bar :ancestors:/test :depth:[4 TO 4] -> transform 2 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("d/*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test", Filter.PathRestriction.DIRECT_CHILDREN);
+        validateResult(f, of("/test/c"));
+    }
+
+    @Test
+    public void pathTranformationWithExactPathRestriction() throws Exception {
+        IndexDefinitionBuilder idxBuilder =
+                new IndexDefinitionBuilder(rootBuilder.child(IndexConstants.INDEX_DEFINITIONS_NAME).child("fooIndex"))
+                        .noAsync().evaluatePathRestrictions();
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        idxBuilder.build();
+        commit();
+
+        NodeBuilder testRootBuilder = rootBuilder.child("test");
+        testRootBuilder.child("a").child("j:c").setProperty("foo", "bar");
+        testRootBuilder.child("b").setProperty("foo", "bar");
+        testRootBuilder.child("c").child("d").child("j:c").setProperty("foo", "bar");
+        commit();
+
+        FilterImpl f;
+
+        // /jcr:root/test/a[j:c/foo = 'bar'] -> foo:bar :path:/test/a/j:c -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("j:c/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test/a", Filter.PathRestriction.EXACT);
+        validateResult(f, of("/test/a"));
+
+        // /jcr:root/test/a[*/foo = 'bar'] -> foo:bar -> transform 1 level up + filter path restriction
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test/a", Filter.PathRestriction.EXACT);
+        validateResult(f, of("/test/a"));
+
+        // /jcr:root/test/c[d/*/foo = 'bar'] -> foo:bar -> transform 2 level up + filter path restriction
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("d/*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test/c", Filter.PathRestriction.EXACT);
+        validateResult(f, of("/test/c"));
+    }
+
+    @Test
+    public void pathTranformationWithParentFilter() throws Exception {
+        IndexDefinitionBuilder idxBuilder =
+                new IndexDefinitionBuilder(rootBuilder.child(IndexConstants.INDEX_DEFINITIONS_NAME).child("fooIndex"))
+                        .noAsync().evaluatePathRestrictions();
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        idxBuilder.build();
+        commit();
+
+        NodeBuilder testRootBuilder = rootBuilder.child("test");
+        testRootBuilder.child("a").child("j:c").setProperty("foo", "bar");
+        testRootBuilder.child("b").setProperty("foo", "bar");
+        testRootBuilder.child("c").child("d").child("j:c").setProperty("foo", "bar");
+        commit();
+
+        FilterImpl f;
+
+        // /jcr:root/test/a/b/j:c/..[j:c/foo = 'bar'] -> foo:bar :path:/test/a/b/j:c -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("j:c/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test/c/d/j:c", Filter.PathRestriction.PARENT);
+        validateResult(f, of("/test/c/d"));
+
+        // /jcr:root/test/a/b/j:c/..[*/foo = 'bar'] -> foo:bar -> transform 1 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test/a/b/j:c", Filter.PathRestriction.PARENT);
+        validateResult(f, of("/test", "/test/a", "/test/c/d"));
+
+        // /jcr:root/test/c/d/..[d/*/foo = 'bar'] -> foo:bar -> transform 2 level up
+        f = createFilter(root, NT_BASE);
+        f.restrictProperty("d/*/foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        f.restrictPath("/test/c/d", Filter.PathRestriction.PARENT);
+        validateResult(f, of("/", "/test", "/test/c"));
+    }
+
+    private void validateResult(Filter f, Set<String> expected) {
+        List<QueryIndex.IndexPlan> plans = index.getPlans(f, null, root);
+
+        assertEquals("Only one plan must show up", 1, plans.size());
+
+        QueryIndex.IndexPlan plan = plans.get(0);
+
+        Cursor cursor = index.query(plan, root);
+        Set<String> paths = Sets.newHashSet();
+
+        while (cursor.hasNext()) {
+            paths.add(cursor.next().getPath());
+        }
+
+        assertEquals(f.toString(), expected, paths);
+    }
+
+    private void commit() throws Exception {
+        root = HOOK.processCommit(rootBuilder.getBaseState(), rootBuilder.getNodeState(), EMPTY);
+        rootBuilder = root.builder();
+
+        tracker.update(root);
+    }
+
+    private static FilterImpl createFilter(NodeState root, String nodeTypeName) {
+        NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(root);
+        NodeTypeInfo type = nodeTypes.getNodeTypeInfo(nodeTypeName);
+        SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
+        return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings());
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexPathRestrictionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=1862093&r1=1862092&r2=1862093&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 Tue Jun 25 22:49:38 2019
@@ -3330,6 +3330,59 @@ public class LucenePropertyIndexTest ext
                 "lucene:fooIndex(/oak:index/fooIndex)", asList("/d"));
     }
 
+    @Test
+    public void pathTransformationWithEvaluatePathRestriction() throws Exception {
+        // This test might feel redundant in presence of LuceneIndexPathRestrictionTest, BUT, this test
+        // gets result via query engine so the results is exactly correct. While in LuceneIndexPathRestrictionTest
+        // we're interested in how many constraint could the index resolve.. so those results would be super-set of
+        // what we see here
+
+        IndexDefinitionBuilder idxBuilder = new IndexDefinitionBuilder()
+                .noAsync().evaluatePathRestrictions();
+        idxBuilder.indexRule("nt:base").property("foo").propertyIndex();
+        Tree idx = root.getTree("/").getChild("oak:index").addChild("fooIndex");
+        idxBuilder.build(idx);
+        root.commit();
+
+        Tree rootTree = root.getTree("/").addChild("test");
+        rootTree.addChild("a").addChild("j:c").setProperty("foo", "bar");
+        rootTree.addChild("b").setProperty("foo", "bar");
+        rootTree.addChild("c").addChild("d").addChild("j:c").setProperty("foo", "bar");
+        root.commit();
+
+        // no path restriction
+        assertPlanAndQueryXPath("//*[j:c/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a", "/test/c/d"));
+        assertPlanAndQueryXPath("//*[*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a", "/test", "/test/c/d"));
+        assertPlanAndQueryXPath("//*[d/*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/c"));
+
+        // any descendant
+        assertPlanAndQueryXPath("/jcr:root/test//*[j:c/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a", "/test/c/d"));
+        assertPlanAndQueryXPath("/jcr:root/test//*[*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a", "/test/c/d"));
+        assertPlanAndQueryXPath("/jcr:root/test//*[d/*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/c"));
+
+        // direct children
+        assertPlanAndQueryXPath("/jcr:root/test/*[j:c/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a"));
+        assertPlanAndQueryXPath("/jcr:root/test/*[*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a"));
+        assertPlanAndQueryXPath("/jcr:root/test/*[d/*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/c"));
+
+        // exact path
+        assertPlanAndQueryXPath("/jcr:root/test/a[j:c/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a"));
+        assertPlanAndQueryXPath("/jcr:root/test/a[*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/a"));
+        assertPlanAndQueryXPath("/jcr:root/test/c[d/*/@foo = 'bar']",
+                "lucene:fooIndex(/oak:index/fooIndex)", asList("/test/c"));
+    }
+
     private void assertPlanAndQueryXPath(String query, String planExpectation, List<String> paths) throws ParseException {
         assertXpathPlan(query, planExpectation);
         assertQuery(query, XPATH, paths, false);

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java?rev=1862093&r1=1862092&r2=1862093&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java Tue Jun 25 22:49:38 2019
@@ -1012,6 +1012,14 @@ public class FulltextIndexPlanner {
             return relativize;
         }
 
+        public int getParentDepth() {
+            return parentDepth;
+        }
+
+        public String getParentPathSegment() {
+            return parentPathSegment;
+        }
+
         public boolean isUniquePathsRequired() {
             return uniquePathsRequired;
         }