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 ch...@apache.org on 2014/12/05 15:06:00 UTC

svn commit: r1643287 - in /jackrabbit/oak/trunk/oak-lucene/src: main/java/org/apache/jackrabbit/oak/plugins/index/lucene/ main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/ test/java/org/apache/jackrabbit/oak/plugins/index/lucene/

Author: chetanm
Date: Fri Dec  5 14:06:00 2014
New Revision: 1643287

URL: http://svn.apache.org/r1643287
Log:
OAK-2306 - Path restriction evaluation support within LucenIndex

Added:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/TokenizerChain.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java Fri Dec  5 14:06:00 2014
@@ -20,9 +20,11 @@ import java.util.Arrays;
 
 import com.google.common.primitives.Ints;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.util.ISO8601;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.FieldType;
+import org.apache.lucene.document.IntField;
 import org.apache.lucene.document.StringField;
 import org.apache.lucene.document.TextField;
 
@@ -108,6 +110,14 @@ public final class FieldFactory {
         return new TextField(FieldNames.createFulltextFieldName(name), value, NO);
     }
 
+    public static Field newAncestorsField(String path){
+        return new TextField(FieldNames.ANCESTORS, path, NO);
+    }
+
+    public static Field newDepthField(String path){
+        return new IntField(FieldNames.PATH_DEPTH, PathUtils.getDepth(path), NO);
+    }
+
     /**
      * Date values are saved with sec resolution
      * @param date jcr data string

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java Fri Dec  5 14:06:00 2014
@@ -38,6 +38,17 @@ public final class FieldNames {
     public static final String PATH = ":path";
 
     /**
+     * Name of the field that contains all the path hierarchy e.g. for /a/b/c
+     * it would contain /a, /a/b, /a/b/c
+     */
+    public static final String ANCESTORS = ":ancestors";
+
+    /**
+     * Name of the field which refers to the depth of path
+     */
+    public static final String PATH_DEPTH = ":depth";
+
+    /**
      * Name of the field that contains the fulltext index.
      */
     public static final String FULLTEXT = ":fulltext";

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java Fri Dec  5 14:06:00 2014
@@ -45,6 +45,7 @@ import org.apache.jackrabbit.oak.api.Tre
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.index.lucene.util.TokenizerChain;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
 import org.apache.jackrabbit.oak.plugins.tree.ImmutableTree;
@@ -53,6 +54,8 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.util.TreeUtil;
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
+import org.apache.lucene.analysis.path.PathHierarchyTokenizerFactory;
 import org.apache.lucene.codecs.Codec;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -154,6 +157,8 @@ class IndexDefinition implements Aggrega
 
     private final boolean indexesAllTypes;
 
+    private final Analyzer analyzer;
+
     public IndexDefinition(NodeState root, NodeState defn) {
         this(root, defn, null);
     }
@@ -207,6 +212,7 @@ class IndexDefinition implements Aggrega
         this.costPerEntry = getOptionalValue(defn, LuceneIndexConstants.COST_PER_ENTRY, 1.0);
         this.costPerExecution = getOptionalValue(defn, LuceneIndexConstants.COST_PER_EXECUTION, 1.0);
         this.indexesAllTypes = areAllTypesIndexed();
+        this.analyzer = createAnalyzer();
     }
 
     public boolean isFullTextEnabled() {
@@ -284,7 +290,7 @@ class IndexDefinition implements Aggrega
     }
 
     public Analyzer getAnalyzer(){
-        return LuceneIndexConstants.ANALYZER;
+        return analyzer;
     }
 
     @Override
@@ -292,6 +298,19 @@ class IndexDefinition implements Aggrega
         return "IndexDefinition : " + indexName;
     }
 
+    //~---------------------------------------------------< Analyzer >
+
+    private Analyzer createAnalyzer() {
+        if (!evaluatePathRestrictions()){
+            return LuceneIndexConstants.ANALYZER;
+        }
+        Map<String, Analyzer> analyzerMap = ImmutableMap.<String, Analyzer>builder()
+                .put(FieldNames.ANCESTORS,
+                        new TokenizerChain(new PathHierarchyTokenizerFactory(Collections.<String, String>emptyMap())))
+                .build();
+        return new PerFieldAnalyzerWrapper(LuceneIndexConstants.ANALYZER, analyzerMap);
+    }
+
     //~---------------------------------------------------< Aggregates >
 
     @CheckForNull

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java Fri Dec  5 14:06:00 2014
@@ -290,6 +290,7 @@ class IndexPlanner {
     }
 
     private String getPathPrefix() {
+        // 2 = /oak:index/<index name>
         String parentPath = PathUtils.getAncestorPath(indexPath, 2);
         return PathUtils.denotesRoot(parentPath) ? "" : parentPath;
     }

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java Fri Dec  5 14:06:00 2014
@@ -141,7 +141,7 @@ public interface LuceneIndexConstants {
      */
     String TEST_MODE = "testMode";
 
-    String EVALUATE_PATH_RESTRICTION = "oak.experimental.evaluatePathRestrictions";
+    String EVALUATE_PATH_RESTRICTION = "evaluatePathRestrictions";
 
     /**
      * Experimental config to restrict which property type gets indexed at

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java Fri Dec  5 14:06:00 2014
@@ -19,7 +19,9 @@ package org.apache.jackrabbit.oak.plugin
 import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
 import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newDepthField;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newFulltextField;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newAncestorsField;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPathField;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPropertyField;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
@@ -313,6 +315,12 @@ public class LuceneIndexEditor implement
         if (indexingRule.isFulltextEnabled()) {
             document.add(newFulltextField(name));
         }
+
+        if (getDefinition().evaluatePathRestrictions()){
+            document.add(newAncestorsField(PathUtils.getParentPath(path)));
+            document.add(newDepthField(path));
+        }
+
         for (Field f : fields) {
             document.add(f);
         }

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=1643287&r1=1643286&r2=1643287&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 Fri Dec  5 14:06:00 2014
@@ -102,6 +102,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.PATH;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newAncestorTerm;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newFulltextTerm;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
 import static org.apache.jackrabbit.oak.query.QueryImpl.JCR_PATH;
@@ -500,26 +501,22 @@ public class LucenePropertyIndex impleme
             addNodeTypeConstraints(planResult.indexingRule, qs, filter);
         }
 
-        String path = filter.getPath();
-        //TODO Readjust path based on pathPrefix of the index
+        String path = getPathRestriction(plan);
         switch (filter.getPathRestriction()) {
         case ALL_CHILDREN:
             if (defn.evaluatePathRestrictions()) {
                 if ("/".equals(path)) {
                     break;
                 }
-                if (!path.endsWith("/")) {
-                    path += "/";
-                }
-                qs.add(new PrefixQuery(newPathTerm(path)));
+                qs.add(new TermQuery(newAncestorTerm(path)));
             }
             break;
         case DIRECT_CHILDREN:
             if (defn.evaluatePathRestrictions()) {
-                if (!path.endsWith("/")) {
-                    path += "/";
-                }
-                qs.add(new PrefixQuery(newPathTerm(path)));
+                BooleanQuery bq = new BooleanQuery();
+                bq.add(new BooleanClause(new TermQuery(newAncestorTerm(path)), BooleanClause.Occur.MUST));
+                bq.add(new BooleanClause(newDepthQuery(path), BooleanClause.Occur.MUST));
+                qs.add(bq);
             }
             break;
         case EXACT:
@@ -1047,6 +1044,21 @@ public class LucenePropertyIndex impleme
         return tokens;
     }
 
+    private static String getPathRestriction(IndexPlan plan) {
+        Filter f = plan.getFilter();
+        String pathPrefix = plan.getPathPrefix();
+        if (pathPrefix.isEmpty()) {
+            return f.getPath();
+        }
+        String relativePath = PathUtils.relativize(pathPrefix, f.getPath());
+        return "/" + relativePath;
+    }
+
+    private static Query newDepthQuery(String path) {
+        int depth = PathUtils.getDepth(path) + 1;
+        return NumericRangeQuery.newIntRange(FieldNames.PATH_DEPTH, depth, depth, true, true);
+    }
+
     static class LuceneResultRow {
         final String path;
         final double score;

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/TermFactory.java Fri Dec  5 14:06:00 2014
@@ -39,10 +39,7 @@ public final class TermFactory {
      * @return the path term.
      */
     public static Term newPathTerm(String path) {
-        if (!"/".equals(path) && !path.startsWith("/")) {
-            path = "/" + path;
-        }
-        return new Term(FieldNames.PATH, path);
+        return new Term(FieldNames.PATH, preparePath(path));
     }
 
     public static Term newFulltextTerm(String ft, String field) {
@@ -52,8 +49,18 @@ public final class TermFactory {
         return new Term(field, ft);
     }
 
+    public static Term newAncestorTerm(String path){
+        return new Term(FieldNames.ANCESTORS, preparePath(path));
+    }
+
     public static Term newFulltextTerm(String ft) {
         return new Term(FieldNames.FULLTEXT, ft);
     }
 
+    private static String preparePath(String path) {
+        if (!"/".equals(path) && !path.startsWith("/")) {
+            path = "/" + path;
+        }
+        return path;
+    }
 }

Added: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/TokenizerChain.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/TokenizerChain.java?rev=1643287&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/TokenizerChain.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/TokenizerChain.java Fri Dec  5 14:06:00 2014
@@ -0,0 +1,92 @@
+/*
+ * 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.util;
+
+import java.io.Reader;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.Tokenizer;
+import org.apache.lucene.analysis.util.CharFilterFactory;
+import org.apache.lucene.analysis.util.TokenFilterFactory;
+import org.apache.lucene.analysis.util.TokenizerFactory;
+
+/**
+ * An analyzer that uses a tokenizer and a list of token filters to
+ * create a TokenStream. Taken from org.apache.solr.analysis.TokenizerChain
+ */
+public final class TokenizerChain extends Analyzer {
+    private final CharFilterFactory[] charFilters;
+    private final TokenizerFactory tokenizer;
+    private final TokenFilterFactory[] filters;
+
+    public TokenizerChain(TokenizerFactory tokenizer) {
+        this(null, tokenizer, null);
+    }
+
+    public TokenizerChain(TokenizerFactory tokenizer, TokenFilterFactory[] filters) {
+        this(null, tokenizer, filters);
+    }
+
+    public TokenizerChain(CharFilterFactory[] charFilters, TokenizerFactory tokenizer, TokenFilterFactory[] filters) {
+        this.charFilters = charFilters;
+        this.tokenizer = tokenizer;
+        this.filters = filters == null ? new TokenFilterFactory[0] : filters;
+    }
+
+    @Override
+    public Reader initReader(String fieldName, Reader reader) {
+        if (charFilters != null && charFilters.length > 0) {
+            Reader cs = reader;
+            for (CharFilterFactory charFilter : charFilters) {
+                cs = charFilter.create(cs);
+            }
+            reader = cs;
+        }
+        return reader;
+    }
+
+    @Override
+    protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
+        Tokenizer tk = tokenizer.create(reader);
+        TokenStream ts = tk;
+        for (TokenFilterFactory filter : filters) {
+            ts = filter.create(ts);
+        }
+        return new TokenStreamComponents(tk, ts);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("TokenizerChain(");
+        for (CharFilterFactory filter : charFilters) {
+            sb.append(filter);
+            sb.append(", ");
+        }
+        sb.append(tokenizer);
+        for (TokenFilterFactory filter : filters) {
+            sb.append(", ");
+            sb.append(filter);
+        }
+        sb.append(')');
+        return sb.toString();
+    }
+
+}

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

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java?rev=1643287&r1=1643286&r2=1643287&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java Fri Dec  5 14:06:00 2014
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
 import java.io.File;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -26,6 +27,7 @@ import javax.jcr.PropertyType;
 
 import static com.google.common.collect.ImmutableList.copyOf;
 import static com.google.common.collect.Iterators.transform;
+import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Sets.newHashSet;
 import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
 import static junit.framework.Assert.assertEquals;
@@ -214,12 +216,48 @@ public class LuceneIndexTest {
         assertFalse(cursor.hasNext());
     }
 
-    private FilterImpl createFilter(String nodeTypeName) {
-        NodeState system = root.getChildNode(JCR_SYSTEM);
-        NodeState types = system.getChildNode(JCR_NODE_TYPES);
-        NodeState type = types.getChildNode(nodeTypeName);
-        SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
-        return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings());
+    @Test
+    public void testPathRestrictions() throws Exception {
+        NodeBuilder idx = newLucenePropertyIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME),
+                "lucene", ImmutableSet.of("foo"), null);
+        idx.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+
+        NodeState before = builder.getNodeState();
+        builder.setProperty("foo", "bar");
+        builder.child("a").setProperty("foo", "bar");
+        builder.child("a1").setProperty("foo", "bar");
+        builder.child("a").child("b").setProperty("foo", "bar");
+        builder.child("a").child("b").child("c").setProperty("foo", "bar");
+
+        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 = createTestFilter();
+        filter.restrictPath("/", Filter.PathRestriction.EXACT);
+        assertFilter(filter, queryIndex, indexed, ImmutableList.of("/"));
+
+        filter = createTestFilter();
+        filter.restrictPath("/", Filter.PathRestriction.DIRECT_CHILDREN);
+        assertFilter(filter, queryIndex, indexed, ImmutableList.of("/a", "/a1"));
+
+        filter = createTestFilter();
+        filter.restrictPath("/a", Filter.PathRestriction.DIRECT_CHILDREN);
+        assertFilter(filter, queryIndex, indexed, ImmutableList.of("/a/b"));
+
+        filter = createTestFilter();
+        filter.restrictPath("/a", Filter.PathRestriction.ALL_CHILDREN);
+        assertFilter(filter, queryIndex, indexed, ImmutableList.of("/a/b", "/a/b/c"));
+    }
+
+    private FilterImpl createTestFilter(){
+        FilterImpl filter = createFilter(NT_BASE);
+        filter.restrictProperty("foo", Operator.EQUAL, PropertyValues.newString("bar"));
+        return filter;
     }
 
     @Test
@@ -366,6 +404,13 @@ public class LuceneIndexTest {
         }
     }
 
+    private FilterImpl createFilter(String nodeTypeName) {
+        NodeState system = root.getChildNode(JCR_SYSTEM);
+        NodeState types = system.getChildNode(JCR_NODE_TYPES);
+        NodeState type = types.getChildNode(nodeTypeName);
+        SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
+        return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings());
+    }
 
     private void assertQuery(IndexTracker tracker, NodeState indexed, String key, String value){
         AdvancedQueryIndex queryIndex = new LucenePropertyIndex(tracker);
@@ -380,6 +425,23 @@ public class LuceneIndexTest {
         assertFalse(cursor.hasNext());
     }
 
+    private static List<String> assertFilter(Filter filter, AdvancedQueryIndex queryIndex,
+                                             NodeState indexed, List<String> expected) {
+        List<IndexPlan> plans = queryIndex.getPlans(filter, null, indexed);
+        Cursor cursor = queryIndex.query(plans.get(0), indexed);
+
+        List<String> paths = newArrayList();
+        while (cursor.hasNext()) {
+            paths.add(cursor.next().getPath());
+        }
+        Collections.sort(paths);
+        for (String p : expected) {
+            assertTrue("Expected path " + p + " not found", paths.contains(p));
+        }
+        assertEquals("Result set size is different", expected.size(), paths.size());
+        return paths;
+    }
+
     private String getIndexDir(){
         File dir = new File("target", "indexdir"+System.nanoTime());
         dirs.add(dir);

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=1643287&r1=1643286&r2=1643287&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 Fri Dec  5 14:06:00 2014
@@ -422,6 +422,7 @@ public class LucenePropertyIndexTest ext
     public void indexDefinitionBelowRoot() throws Exception {
         Tree parent = root.getTree("/").addChild("test");
         Tree idx = createIndex(parent, "test1", of("propa", "propb"));
+        idx.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
         idx.addChild(PROP_NODE).addChild("propa");
         root.commit();
 
@@ -432,7 +433,21 @@ public class LucenePropertyIndexTest ext
         assertQuery("select [jcr:path] from [nt:base] as s where ISDESCENDANTNODE(s, '/test') and propa = 'a'", asList("/test/test2/a"));
     }
 
+    @Test
+    public void indexDefinitionBelowRoot2() throws Exception {
+        Tree parent = root.getTree("/").addChild("test");
+        Tree idx = createIndex(parent, "test1", of("propa", "propb"));
+        idx.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+        idx.addChild(PROP_NODE).addChild("propa");
+        root.commit();
 
+        Tree test = parent.addChild("test2").addChild("test3");
+        test.addChild("a").setProperty("propa", "a");
+        root.commit();
+
+        assertQuery("select [jcr:path] from [nt:base] as s where ISDESCENDANTNODE(s, '/test/test2') and propa = 'a'",
+                asList("/test/test2/test3/a"));
+    }
     @Test
     public void sortQueriesWithLong() throws Exception {
         Tree idx = createIndex("test1", of("foo", "bar"));