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/10/13 07:09:58 UTC

svn commit: r1631284 - 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: Mon Oct 13 05:09:57 2014
New Revision: 1631284

URL: http://svn.apache.org/r1631284
Log:
OAK-2005 - Use separate Lucene index for performing property related queries

- Modifying logic to support multiple Lucene index definitions. For compatibility purpose the first full text enabled Lucene index would be used
- Introduced IndexDefinition class to encapsulate index definitions node

Added:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java   (with props)
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookup.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.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/util/LuceneIndexHelper.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java

Added: 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=1631284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java Mon Oct 13 05:09:57 2014
@@ -0,0 +1,115 @@
+/*
+ * 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 java.util.Collections;
+import java.util.Set;
+
+import javax.jcr.PropertyType;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.EXCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.FULL_TEXT_ENABLED;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_TYPES;
+
+public class IndexDefinition {
+    private static final Logger log = LoggerFactory.getLogger(IndexDefinition.class);
+    private final int propertyTypes;
+
+    private final Set<String> excludes;
+
+    private final Set<String> includes;
+
+    private final boolean fullTextEnabled;
+
+    private final NodeBuilder definition;
+
+    public IndexDefinition(NodeBuilder defn) {
+        this.definition = defn;
+        PropertyState pst = defn.getProperty(INCLUDE_PROPERTY_TYPES);
+        if (pst != null) {
+            int types = 0;
+            for (String inc : pst.getValue(Type.STRINGS)) {
+                try {
+                    types |= 1 << PropertyType.valueFromName(inc);
+                } catch (IllegalArgumentException e) {
+                    log.warn("Unknown property type: " + inc);
+                }
+            }
+            this.propertyTypes = types;
+        } else {
+            this.propertyTypes = -1;
+        }
+
+        this.excludes = toLowerCase(getMultiProperty(defn, EXCLUDE_PROPERTY_NAMES));
+        this.includes = getMultiProperty(defn, INCLUDE_PROPERTY_NAMES);
+        this.fullTextEnabled = getOptionalValue(defn, FULL_TEXT_ENABLED, true);
+    }
+
+    boolean includeProperty(String name) {
+        if(!includes.isEmpty()){
+            return includes.contains(name);
+        }
+        return !excludes.contains(name.toLowerCase());
+    }
+
+    boolean includePropertyType(int type){
+        if(propertyTypes < 0){
+            return false;
+        }
+        return (propertyTypes & (1 << type)) != 0;
+    }
+
+    public NodeBuilder getDefinition() {
+        return definition;
+    }
+
+    public boolean isFullTextEnabled() {
+        return fullTextEnabled;
+    }
+
+    //~------------------------------------------< Internal >
+
+    private static boolean getOptionalValue(NodeBuilder definition, String propName, boolean defaultVal){
+        PropertyState ps = definition.getProperty(propName);
+        return ps == null ? defaultVal : ps.getValue(Type.BOOLEAN);
+    }
+
+    private static Set<String> getMultiProperty(NodeBuilder definition, String propName){
+        PropertyState pse = definition.getProperty(propName);
+        return pse != null ? ImmutableSet.copyOf(pse.getValue(Type.STRINGS)) : Collections.<String>emptySet();
+    }
+
+    private static Set<String> toLowerCase(Set<String> values){
+        Set<String> result = Sets.newHashSet();
+        for(String val : values){
+            result.add(val.toLowerCase());
+        }
+        return Collections.unmodifiableSet(result);
+    }
+}

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

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java?rev=1631284&r1=1631283&r2=1631284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java Mon Oct 13 05:09:57 2014
@@ -68,7 +68,7 @@ class IndexNode {
 
     private final String name;
 
-    private final NodeState definition;
+    private final IndexDefinition definition;
 
     private final Directory directory;
 
@@ -83,7 +83,7 @@ class IndexNode {
     IndexNode(String name, NodeState definition, Directory directory)
             throws IOException {
         this.name = name;
-        this.definition = definition;
+        this.definition = new IndexDefinition(new ReadOnlyBuilder(definition));
         this.directory = directory;
         this.reader = DirectoryReader.open(directory);
         this.searcher = new IndexSearcher(reader);
@@ -93,7 +93,7 @@ class IndexNode {
         return name;
     }
 
-    NodeState getDefinition() {
+    IndexDefinition getDefinition() {
         return definition;
     }
 

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java?rev=1631284&r1=1631283&r2=1631284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java Mon Oct 13 05:09:57 2014
@@ -20,15 +20,13 @@ import static com.google.common.base.Pre
 import static com.google.common.base.Predicates.in;
 import static com.google.common.base.Predicates.not;
 import static com.google.common.base.Predicates.notNull;
-import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Lists.newArrayListWithCapacity;
 import static com.google.common.collect.Maps.filterKeys;
 import static com.google.common.collect.Maps.filterValues;
 import static com.google.common.collect.Maps.newHashMap;
 import static java.util.Collections.emptyMap;
-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 static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TYPE_LUCENE;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.isLuceneIndexNode;
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 
 import java.io.IOException;
@@ -42,7 +40,6 @@ import org.apache.jackrabbit.oak.spi.com
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.commit.EditorDiff;
 import org.apache.jackrabbit.oak.spi.commit.SubtreeEditor;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -82,10 +79,6 @@ class IndexTracker {
             final String path = entry.getKey();
             final String name = entry.getValue().getName();
 
-            List<String> elements = newArrayList();
-            Iterables.addAll(elements, PathUtils.elements(path));
-            elements.add(INDEX_DEFINITIONS_NAME);
-            elements.add(name);
             editors.add(new SubtreeEditor(new DefaultEditor() {
                 @Override
                 public void leave(NodeState before, NodeState after) {
@@ -97,7 +90,7 @@ class IndexTracker {
                         log.error("Failed to open Lucene index at " + path, e);
                     }
                 }
-            }, elements.toArray(new String[elements.size()])));
+            }, Iterables.toArray(PathUtils.elements(path), String.class)));
         }
 
         EditorDiff.process(CompositeEditor.compose(editors), this.root, root);
@@ -145,24 +138,23 @@ class IndexTracker {
 
         NodeState node = root;
         for (String name : PathUtils.elements(path)) {
-            node = root.getChildNode(name);
+            node = node.getChildNode(name);
         }
-        node = node.getChildNode(INDEX_DEFINITIONS_NAME);
 
+        final String indexName = PathUtils.getName(path);
         try {
-            for (ChildNodeEntry child : node.getChildNodeEntries()) {
-                node = child.getNodeState();
-                if (TYPE_LUCENE.equals(node.getString(TYPE_PROPERTY_NAME))) {
-                    index = IndexNode.open(child.getName(), node);
-                    if (index != null) {
-                        checkState(index.acquire());
-                        indices = ImmutableMap.<String, IndexNode>builder()
-                                .putAll(indices)
-                                .put(path, index)
-                                .build();
-                        return index;
-                    }
+            if (isLuceneIndexNode(node)) {
+                index = IndexNode.open(indexName, node);
+                if (index != null) {
+                    checkState(index.acquire());
+                    indices = ImmutableMap.<String, IndexNode>builder()
+                            .putAll(indices)
+                            .put(path, index)
+                            .build();
+                    return index;
                 }
+            } else if (node.exists()) {
+                log.warn("Cannot open Lucene Index at path {} as the index is not of type {}", path, TYPE_LUCENE);
             }
         } catch (IOException e) {
             log.error("Could not access the Lucene index at " + path, e);

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java?rev=1631284&r1=1631283&r2=1631284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java Mon Oct 13 05:09:57 2014
@@ -26,8 +26,6 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
 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.EXCLUDE_PROPERTY_NAMES;
-import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.INCLUDE_PROPERTY_TYPES;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newFulltextTerm;
 import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
@@ -50,7 +48,6 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.jcr.PropertyType;
 
 import com.google.common.collect.AbstractIterator;
 import com.google.common.collect.Queues;
@@ -152,13 +149,19 @@ public class LuceneIndex implements Adva
     public static final String NATIVE_QUERY_FUNCTION = "native*lucene";
 
     /**
+     * IndexPaln Attribute name which refers to the path of Lucene index to be used
+     * to perform query
+     */
+    static final String ATTR_INDEX_PATH = "oak.lucene.indexPath";
+
+    /**
      * Batch size for fetching results from Lucene queries.
      */
     static final int LUCENE_QUERY_BATCH_SIZE = 50;
-    
+
     static final boolean USE_PATH_RESTRICTION = Boolean.getBoolean("oak.luceneUsePath");
 
-    private final IndexTracker tracker;
+    protected final IndexTracker tracker;
 
     private final Analyzer analyzer;
 
@@ -186,37 +189,35 @@ public class LuceneIndex implements Adva
             return Collections.emptyList();
         }
 
-        IndexNode index = tracker.acquireIndexNode("/");
-        if (index == null) { // unusable index
+        String indexPath = new LuceneIndexLookup(rootState).getFullTextIndexPath(filter, tracker);
+        if (indexPath == null) { // unusable index
             return Collections.emptyList();
         }
-        try {
-            Set<String> relPaths = getRelativePaths(ft);
-            if (relPaths.size() > 1) {
-                LOG.warn("More than one relative parent for query " + filter.getQueryStatement());
-                // there are multiple "parents", as in
-                // "contains(a/x, 'hello') and contains(b/x, 'world')"
-                return Collections.emptyList();
-            }
-            String parent = relPaths.iterator().next();
-
-            // no relative properties
-            double cost = 10;
-            if (!parent.isEmpty()) {
-                // all relative properties have the same "parent", as in
-                // "contains(a/x, 'hello') and contains(a/y, 'world')" or
-                // "contains(a/x, 'hello') or contains(a/*, 'world')"
-                // TODO: proper cost calculation
-                // we assume this will cause more read operations,
-                // as we need to read the node and then the parent
-                cost = 15;
-            }
-            return Collections.singletonList(planBuilder(filter)
-                    .setCostPerExecution(cost)
-                    .build());
-        } finally {
-            index.release();
+        Set<String> relPaths = getRelativePaths(ft);
+        if (relPaths.size() > 1) {
+            LOG.warn("More than one relative parent for query " + filter.getQueryStatement());
+            // there are multiple "parents", as in
+            // "contains(a/x, 'hello') and contains(b/x, 'world')"
+            return Collections.emptyList();
         }
+        String parent = relPaths.iterator().next();
+
+        // no relative properties
+        double cost = 10;
+        if (!parent.isEmpty()) {
+            // all relative properties have the same "parent", as in
+            // "contains(a/x, 'hello') and contains(a/y, 'world')" or
+            // "contains(a/x, 'hello') or contains(a/*, 'world')"
+            // TODO: proper cost calculation
+            // we assume this will cause more read operations,
+            // as we need to read the node and then the parent
+            cost = 15;
+        }
+        return Collections.singletonList(planBuilder(filter)
+                .setCostPerExecution(cost)
+                .setAttribute(ATTR_INDEX_PATH, indexPath)
+                .build());
+
     }
 
     @Override
@@ -232,7 +233,7 @@ public class LuceneIndex implements Adva
     @Override
     public String getPlanDescription(IndexPlan plan, NodeState root) {
         Filter filter = plan.getFilter();
-        IndexNode index = tracker.acquireIndexNode("/");
+        IndexNode index = tracker.acquireIndexNode((String) plan.getAttribute(ATTR_INDEX_PATH));
         checkState(index != null, "The Lucene index is not available");
         try {
             FullTextExpression ft = filter.getFullTextConstraint();
@@ -260,7 +261,7 @@ public class LuceneIndex implements Adva
     }
 
     @Override
-    public Cursor query(IndexPlan plan, NodeState rootState) {
+    public Cursor query(final IndexPlan plan, NodeState rootState) {
         final Filter filter = plan.getFilter();
         FullTextExpression ft = filter.getFullTextConstraint();
         Set<String> relPaths = getRelativePaths(ft);
@@ -325,7 +326,7 @@ public class LuceneIndex implements Adva
             private boolean loadDocs() {
                 ScoreDoc lastDocToRecord = null;
 
-                IndexNode indexNode = tracker.acquireIndexNode("/");
+                IndexNode indexNode = tracker.acquireIndexNode((String) plan.getAttribute(ATTR_INDEX_PATH));
                 checkState(indexNode != null);
                 try {
                     IndexSearcher searcher = indexNode.getSearcher();
@@ -437,7 +438,7 @@ public class LuceneIndex implements Adva
      * @return the Lucene query
      */
     private static Query getQuery(Filter filter, IndexReader reader,
-            boolean nonFullTextConstraints, Analyzer analyzer, NodeState indexDefinition) {
+            boolean nonFullTextConstraints, Analyzer analyzer, IndexDefinition indexDefinition) {
         List<Query> qs = new ArrayList<Query>();
         FullTextExpression ft = filter.getFullTextConstraint();
         if (ft == null) {
@@ -485,7 +486,7 @@ public class LuceneIndex implements Adva
     }
 
     private static void addNonFullTextConstraints(List<Query> qs,
-            Filter filter, IndexReader reader, Analyzer analyzer, NodeState indexDefinition) {
+            Filter filter, IndexReader reader, Analyzer analyzer, IndexDefinition indexDefinition) {
         if (!filter.matchesAllTypes()) {
             addNodeTypeConstraints(qs, filter);
         }
@@ -629,7 +630,7 @@ public class LuceneIndex implements Adva
     }
 
     private static boolean isExcludedProperty(PropertyRestriction pr,
-            NodeState definition) {
+            IndexDefinition definition) {
         String name = pr.propertyName;
         if (name.contains("/")) {
             // lucene cannot handle child-level property restrictions
@@ -637,10 +638,8 @@ public class LuceneIndex implements Adva
         }
 
         // check name
-        for (String e : definition.getStrings(EXCLUDE_PROPERTY_NAMES)) {
-            if (e.equalsIgnoreCase(name)) {
-                return true;
-            }
+        if(!definition.includeProperty(name)){
+            return true;
         }
 
         // check type
@@ -653,13 +652,7 @@ public class LuceneIndex implements Adva
             type = pr.list.get(0).getType().tag();
         }
         if (type != null) {
-            boolean isIn = false;
-            for (String e : definition.getStrings(INCLUDE_PROPERTY_TYPES)) {
-                if (PropertyType.valueFromName(e) == type) {
-                    isIn = true;
-                }
-            }
-            if (!isIn) {
+            if (!definition.includePropertyType(type)) {
                 return true;
             }
         }

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=1631284&r1=1631283&r2=1631284&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 Mon Oct 13 05:09:57 2014
@@ -54,4 +54,16 @@ public interface LuceneIndexConstants {
      */
     String EXPERIMENTAL_STORAGE = "oak.experimental.storage";
 
+    /**
+     * Determines if full text indexing is enabled for this index definition.
+     * Default is true
+     */
+    String FULL_TEXT_ENABLED = "fulltextEnabled";
+
+    /**
+     * Only include properties with name in this set. If this property is defined
+     * then {@code excludePropertyNames} would be ignored
+     */
+    String INCLUDE_PROPERTY_NAMES = "includePropertyNames";
+
 }

Added: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookup.java?rev=1631284&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookup.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexLookup.java Mon Oct 13 05:09:57 2014
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.List;
+
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.isLuceneIndexNode;
+
+public class LuceneIndexLookup {
+    private final NodeState root;
+
+    public LuceneIndexLookup(NodeState root) {
+        this.root = root;
+    }
+
+    /**
+     * Returns the path of the first Lucene index node which supports
+     * fulltext search
+     */
+    public String getFullTextIndexPath(Filter filter, IndexTracker tracker) {
+        List<String> indexPaths = collectIndexNodePaths(filter);
+        IndexNode indexNode = null;
+        for (String path : indexPaths) {
+            try {
+                indexNode = tracker.acquireIndexNode(path);
+                if (indexNode != null
+                        && indexNode.getDefinition().isFullTextEnabled()) {
+                    return path;
+                }
+            } finally {
+                if (indexNode != null) {
+                    indexNode.release();
+                }
+            }
+        }
+        return null;
+    }
+
+    private List<String> collectIndexNodePaths(Filter filter){
+        List<String> paths = Lists.newArrayList();
+        collectIndexNodePaths(filter.getPath(), paths);
+        return paths;
+    }
+
+    private void collectIndexNodePaths(String filterPath, List<String> paths) {
+        //TODO Add support for looking index nodes from non root paths
+        NodeState state = root.getChildNode(INDEX_DEFINITIONS_NAME);
+        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+            if (isLuceneIndexNode(entry.getNodeState())) {
+                paths.add(createIndexNodePath(entry.getName()));
+            }
+        }
+    }
+
+    private String createIndexNodePath(String name){
+        //TODO getPath would lead to duplicate path constru
+        return PathUtils.concat("/", INDEX_DEFINITIONS_NAME, name);
+    }
+}

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

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/LuceneIndexHelper.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/LuceneIndexHelper.java?rev=1631284&r1=1631283&r2=1631284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/LuceneIndexHelper.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/LuceneIndexHelper.java Mon Oct 13 05:09:57 2014
@@ -45,6 +45,7 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 public class LuceneIndexHelper {
 
@@ -151,4 +152,8 @@ public class LuceneIndexHelper {
     public static boolean skipTokenization(String name) {
         return NOT_TOKENIZED.contains(name);
     }
+
+    public static boolean isLuceneIndexNode(NodeState node){
+        return TYPE_LUCENE.equals(node.getString(TYPE_PROPERTY_NAME));
+    }
 }

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java?rev=1631284&r1=1631283&r2=1631284&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LowCostLuceneIndexProvider.java Mon Oct 13 05:09:57 2014
@@ -44,8 +44,10 @@ public class LowCostLuceneIndexProvider 
 
         @Override
         public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> sortOrder, NodeState rootState) {
+            String indexPath = new LuceneIndexLookup(rootState).getFullTextIndexPath(filter, tracker);
             return Collections.singletonList(planBuilder(filter)
                     .setCostPerExecution(1e-3)
+                    .setAttribute(ATTR_INDEX_PATH, indexPath)
                     .build());
         }
     }

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=1631284&r1=1631283&r2=1631284&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 Mon Oct 13 05:09:57 2014
@@ -289,7 +289,10 @@ public class LuceneIndexTest {
     }
 
     private IndexPlan createPlan(Filter filter){
-        return new IndexPlan.Builder().setFilter(filter).build();
+        return new IndexPlan.Builder()
+                .setFilter(filter)
+                .setAttribute(LuceneIndex.ATTR_INDEX_PATH, "/oak:index/lucene")
+                .build();
     }
 
 }