Author: chetanm
Date: Wed Oct  8 10:10:16 2014
New Revision: 1630056

URL: http://svn.apache.org/r1630056
Log:
OAK-2122 - Make LuceneIndex implement AdvanceQueryIndex

Implemented AdvanceFulltextQueryIndex. The cost calculation is currently done 
in a naive way where it sets the number of entries to 0 and then provide a cost 
per execution thus effective cost is same as the one returned earlier.

Also moved getRelativePaths with no change in its logic

Modified:
    
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.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

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=1630056&r1=1630055&r2=1630056&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
 Wed Oct  8 10:10:16 2014
@@ -33,6 +33,7 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.skipTokenization;
 import static org.apache.jackrabbit.oak.query.QueryImpl.JCR_PATH;
+import static 
org.apache.jackrabbit.oak.spi.query.QueryIndex.AdvanceFulltextQueryIndex;
 import static org.apache.lucene.search.BooleanClause.Occur.MUST;
 import static org.apache.lucene.search.BooleanClause.Occur.MUST_NOT;
 import static org.apache.lucene.search.BooleanClause.Occur.SHOULD;
@@ -72,7 +73,6 @@ import org.apache.jackrabbit.oak.spi.que
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
-import org.apache.jackrabbit.oak.spi.query.QueryIndex.FulltextQueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.TokenStream;
@@ -145,7 +145,7 @@ import org.slf4j.LoggerFactory;
  * @see QueryIndex
  *
  */
-public class LuceneIndex implements FulltextQueryIndex {
+public class LuceneIndex implements AdvanceFulltextQueryIndex {
 
     private static final Logger LOG = LoggerFactory
             .getLogger(LuceneIndex.class);
@@ -178,17 +178,17 @@ public class LuceneIndex implements Full
     }
 
     @Override
-    public double getCost(Filter filter, NodeState root) {
+    public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> sortOrder, 
NodeState rootState) {
         FullTextExpression ft = filter.getFullTextConstraint();
         if (ft == null) {
             // no full-text condition: don't use this index,
             // as there might be a better one
-            return Double.POSITIVE_INFINITY;
+            return Collections.emptyList();
         }
 
         IndexNode index = tracker.acquireIndexNode("/");
         if (index == null) { // unusable index
-            return Double.POSITIVE_INFINITY;
+            return Collections.emptyList();
         }
         try {
             Set<String> relPaths = getRelativePaths(ft);
@@ -196,72 +196,42 @@ public class LuceneIndex implements Full
                 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 new MultiLuceneIndex(filter, root, relPaths).getCost();
+                return Collections.emptyList();
             }
             String parent = relPaths.iterator().next();
-            if (parent.isEmpty()) {
-                // no relative properties
-                return 10;
-            }
-            // 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
-            return 15;
+
+            // 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();
         }
     }
 
-    /**
-     * Get the set of relative paths of a full-text condition. For example, for
-     * the condition "contains(a/b, 'hello') and contains(c/d, 'world'), the 
set
-     * { "a", "c" } is returned. If there are no relative properties, then one
-     * entry is returned (the empty string). If there is no expression, then an
-     * empty set is returned.
-     *
-     * @param ft the full-text expression
-     * @return the set of relative paths (possibly empty)
-     */
-    private static Set<String> getRelativePaths(FullTextExpression ft) {
-        if (ft == null) {
-            // there might be no full-text constraint when using the
-            // LowCostLuceneIndexProvider which is used for testing
-            // TODO if the LowCostLuceneIndexProvider is removed, we should do
-            // the following instead:
-
-            // throw new
-            // IllegalStateException("Lucene index is used even when no 
full-text conditions are used for filter "
-            // + filter);
-
-            return Collections.emptySet();
-        }
-        final HashSet<String> relPaths = new HashSet<String>();
-        ft.accept(new FullTextVisitor.FullTextVisitorBase() {
-
-            @Override
-            public boolean visit(FullTextTerm term) {
-                String p = term.getPropertyName();
-                if (p == null) {
-                    relPaths.add("");
-                } else if (p.startsWith("../") || p.startsWith("./")) {
-                    throw new IllegalArgumentException("Relative parent is not 
supported:" + p);
-                } else if (getDepth(p) > 1) {
-                    String parent = getParentPath(p);
-                    relPaths.add(parent);
-                } else {
-                    relPaths.add("");
-                }
-                return true;
-            }
-        });
-        return relPaths;
+    @Override
+    public double getCost(Filter filter, NodeState root) {
+        throw new UnsupportedOperationException("Not supported as implementing 
AdvancedQueryIndex");
     }
 
     @Override
     public String getPlan(Filter filter, NodeState root) {
+        throw new UnsupportedOperationException("Not supported as implementing 
AdvancedQueryIndex");
+    }
+
+    @Override
+    public String getPlanDescription(IndexPlan plan, NodeState root) {
+        Filter filter = plan.getFilter();
         IndexNode index = tracker.acquireIndexNode("/");
         checkState(index != null, "The Lucene index is not available");
         try {
@@ -274,11 +244,11 @@ public class LuceneIndex implements Full
             // we only restrict non-full-text conditions if there is
             // no relative property in the full-text constraint
             boolean nonFullTextConstraints = parent.isEmpty();
-            String plan = getQuery(filter, null, nonFullTextConstraints, 
analyzer, index.getDefinition()) + " ft:(" + ft + ")";
+            String planDesc = getQuery(filter, null, nonFullTextConstraints, 
analyzer, index.getDefinition()) + " ft:(" + ft + ")";
             if (!parent.isEmpty()) {
-                plan += " parent:" + parent;
+                planDesc += " parent:" + parent;
             }
-            return plan;
+            return planDesc;
         } finally {
             index.release();
         }
@@ -286,10 +256,16 @@ public class LuceneIndex implements Full
 
     @Override
     public Cursor query(final Filter filter, final NodeState root) {
+        throw new UnsupportedOperationException("Not supported as implementing 
AdvancedQueryIndex");
+    }
+
+    @Override
+    public Cursor query(IndexPlan plan, NodeState rootState) {
+        final Filter filter = plan.getFilter();
         FullTextExpression ft = filter.getFullTextConstraint();
         Set<String> relPaths = getRelativePaths(ft);
         if (relPaths.size() > 1) {
-            return new MultiLuceneIndex(filter, root, relPaths).query();
+            return new MultiLuceneIndex(filter, rootState, relPaths).query();
         }
 
         final String parent = relPaths.size() == 0 ? "" : 
relPaths.iterator().next();
@@ -391,6 +367,63 @@ public class LuceneIndex implements Full
         return new LucenePathCursor(itr, settings);
     }
 
+    protected static IndexPlan.Builder planBuilder(Filter filter){
+        return new IndexPlan.Builder()
+                .setCostPerExecution(0) // we're local. Low-cost
+                .setCostPerEntry(1)
+                .setFilter(filter)
+                .setFulltextIndex(true)
+                .setEstimatedEntryCount(0) //TODO Fake it to provide constant 
cost for now
+                .setIncludesNodeData(false) // we should not include node data
+                .setDelayed(true); //Lucene is always async
+    }
+
+    /**
+     * Get the set of relative paths of a full-text condition. For example, for
+     * the condition "contains(a/b, 'hello') and contains(c/d, 'world'), the 
set
+     * { "a", "c" } is returned. If there are no relative properties, then one
+     * entry is returned (the empty string). If there is no expression, then an
+     * empty set is returned.
+     *
+     * @param ft the full-text expression
+     * @return the set of relative paths (possibly empty)
+     */
+    private static Set<String> getRelativePaths(FullTextExpression ft) {
+        if (ft == null) {
+            // there might be no full-text constraint when using the
+            // LowCostLuceneIndexProvider which is used for testing
+            // TODO if the LowCostLuceneIndexProvider is removed, we should do
+            // the following instead:
+
+            // throw new
+            // IllegalStateException("Lucene index is used even when no 
full-text conditions are used for filter "
+            // + filter);
+
+            return Collections.emptySet();
+        }
+        final HashSet<String> relPaths = new HashSet<String>();
+        ft.accept(new FullTextVisitor.FullTextVisitorBase() {
+
+            @Override
+            public boolean visit(FullTextTerm term) {
+                String p = term.getPropertyName();
+                if (p == null) {
+                    relPaths.add("");
+                } else if (p.startsWith("../") || p.startsWith("./")) {
+                    throw new IllegalArgumentException("Relative parent is not 
supported:" + p);
+                } else if (getDepth(p) > 1) {
+                    String parent = getParentPath(p);
+                    relPaths.add(parent);
+                } else {
+                    relPaths.add("");
+                }
+                return true;
+            }
+        });
+        return relPaths;
+    }
+
+
     /**
      * Get the Lucene query for the given filter.
      *
@@ -894,7 +927,7 @@ public class LuceneIndex implements Full
     public NodeAggregator getNodeAggregator() {
         return aggregator;
     }
-    
+
     static class LuceneResultRow {
         final String path;
         final double score;

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=1630056&r1=1630055&r2=1630056&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
 Wed Oct  8 10:10:16 2014
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -40,8 +43,10 @@ public class LowCostLuceneIndexProvider 
         }
 
         @Override
-        public double getCost(Filter filter, NodeState root) {
-            return 1e-3;
+        public List<IndexPlan> getPlans(Filter filter, List<OrderEntry> 
sortOrder, NodeState rootState) {
+            return Collections.singletonList(planBuilder(filter)
+                    .setCostPerExecution(1e-3)
+                    .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=1630056&r1=1630055&r2=1630056&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
 Wed Oct  8 10:10:16 2014
@@ -37,6 +37,8 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneIndexHelper.newLuceneIndexDefinition;
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
 import static 
org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
+import static 
org.apache.jackrabbit.oak.spi.query.QueryIndex.AdvancedQueryIndex;
+import static org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan;
 
 import com.google.common.base.Function;
 import org.apache.jackrabbit.oak.api.Type;
@@ -55,7 +57,6 @@ import org.apache.jackrabbit.oak.spi.que
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
-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.apache.jackrabbit.oak.spi.state.NodeStore;
@@ -91,12 +92,12 @@ public class LuceneIndexTest {
 
         IndexTracker tracker = new IndexTracker();
         tracker.update(indexed);
-        QueryIndex queryIndex = new LuceneIndex(tracker, analyzer, null);
+        AdvancedQueryIndex queryIndex = new LuceneIndex(tracker, analyzer, 
null);
         FilterImpl filter = createFilter(NT_BASE);
         filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
                 PropertyValues.newString("bar"));
-        Cursor cursor = queryIndex.query(filter, indexed);
+        Cursor cursor = queryIndex.query(createPlan(filter), indexed);
         assertTrue(cursor.hasNext());
         assertEquals("/", cursor.next().getPath());
         assertFalse(cursor.hasNext());
@@ -121,11 +122,11 @@ public class LuceneIndexTest {
 
         IndexTracker tracker = new IndexTracker();
         tracker.update(indexed);
-        QueryIndex queryIndex = new LuceneIndex(tracker, analyzer, null);
+        AdvancedQueryIndex queryIndex = new LuceneIndex(tracker, analyzer, 
null);
         FilterImpl filter = createFilter(NT_BASE);
         filter.restrictProperty("foo", Operator.EQUAL,
                 PropertyValues.newString("bar"));
-        Cursor cursor = queryIndex.query(filter, indexed);
+        Cursor cursor = queryIndex.query(createPlan(filter), indexed);
 
         List<String> paths = copyOf(transform(cursor, new Function<IndexRow, 
String>() {
             public String apply(IndexRow input) {
@@ -154,12 +155,12 @@ public class LuceneIndexTest {
 
         IndexTracker tracker = new IndexTracker();
         tracker.update(indexed);
-        QueryIndex queryIndex = new LuceneIndex(tracker, analyzer, null);
+        AdvancedQueryIndex queryIndex = new LuceneIndex(tracker, analyzer, 
null);
         FilterImpl filter = createFilter(NT_BASE);
         // filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
                 PropertyValues.newString("bar"));
-        Cursor cursor = queryIndex.query(filter, indexed);
+        Cursor cursor = queryIndex.query(createPlan(filter), indexed);
 
         assertTrue(cursor.hasNext());
         assertEquals("/a/b/c", cursor.next().getPath());
@@ -188,12 +189,12 @@ public class LuceneIndexTest {
 
         IndexTracker tracker = new IndexTracker();
         tracker.update(indexed);
-        QueryIndex queryIndex = new LuceneIndex(tracker, analyzer, null);
+        AdvancedQueryIndex queryIndex = new LuceneIndex(tracker, analyzer, 
null);
         FilterImpl filter = createFilter(NT_BASE);
         // filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
                 PropertyValues.newString("bar"));
-        Cursor cursor = queryIndex.query(filter, indexed);
+        Cursor cursor = queryIndex.query(createPlan(filter), indexed);
 
         assertTrue(cursor.hasNext());
         assertEquals("/a", cursor.next().getPath());
@@ -271,12 +272,12 @@ public class LuceneIndexTest {
     }
 
     private void assertQuery(IndexTracker tracker, NodeState indexed, String 
key, String value){
-        QueryIndex queryIndex = new LuceneIndex(tracker, analyzer, null);
+        AdvancedQueryIndex queryIndex = new LuceneIndex(tracker, analyzer, 
null);
         FilterImpl filter = createFilter(NT_BASE);
         filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty(key, Operator.EQUAL,
                 PropertyValues.newString(value));
-        Cursor cursor = queryIndex.query(filter, indexed);
+        Cursor cursor = queryIndex.query(createPlan(filter), indexed);
         assertTrue(cursor.hasNext());
         assertEquals("/", cursor.next().getPath());
         assertFalse(cursor.hasNext());
@@ -287,4 +288,8 @@ public class LuceneIndexTest {
         return dir.getAbsolutePath();
     }
 
+    private IndexPlan createPlan(Filter filter){
+        return new IndexPlan.Builder().setFilter(filter).build();
+    }
+
 }


Reply via email to