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 th...@apache.org on 2014/10/29 13:16:40 UTC

svn commit: r1635108 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/ oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/

Author: thomasm
Date: Wed Oct 29 12:16:40 2014
New Revision: 1635108

URL: http://svn.apache.org/r1635108
Log:
OAK-2226 Aggregate Lucene no results for multiple jcr:contains

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregationCursor.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest2.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java?rev=1635108&r1=1635107&r2=1635108&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java Wed Oct 29 12:16:40 2014
@@ -16,20 +16,11 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.aggregate;
 
-
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
-import com.google.common.base.Function;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextOr;
@@ -38,10 +29,13 @@ import org.apache.jackrabbit.oak.query.f
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Cursors;
-import org.apache.jackrabbit.oak.spi.query.Cursors.AbstractCursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
-import org.apache.jackrabbit.oak.spi.query.IndexRow;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
 
 import static org.apache.jackrabbit.oak.spi.query.QueryIndex.AdvanceFulltextQueryIndex;
 
@@ -51,6 +45,8 @@ import static org.apache.jackrabbit.oak.
  */
 public class AggregateIndex implements AdvanceFulltextQueryIndex {
 
+    private static final Logger LOG = LoggerFactory.getLogger(AggregateIndex.class);    
+
     private final AdvanceFulltextQueryIndex baseIndex;
 
     public AggregateIndex(AdvanceFulltextQueryIndex baseIndex) {
@@ -72,32 +68,40 @@ public class AggregateIndex implements A
         if (baseIndex == null) {
             return Collections.emptyList();
         }
-        return baseIndex.getPlans(filter, sortOrder, rootState);
+        FullTextExpression e = filter.getFullTextConstraint();
+        if (getNodeAggregator() == null || e == null) {
+            // no aggregation: path-though
+            return baseIndex.getPlans(filter, sortOrder, rootState);
+        }
+        if (!hasCompositeExpression(e)) {
+            // path-though, but without node type restriction
+            return baseIndex.getPlans(newAggregationFilter(filter, null), sortOrder, rootState);
+        }
+        AggregateIndexPlan plan = new AggregateIndexPlan(filter);
+        collectCombinedPlan(e, filter, sortOrder, rootState, plan, "");
+        if (plan.containsPathWithoutPlan()) {
+            // this is not expected (a full-text index that 
+            // can't deal with a full-text restriction)
+            LOG.warn("Full-text index without plan: " + e);
+            return Collections.emptyList();
+        }
+        return Collections.singletonList((IndexPlan) plan);
     }
 
     @Override
     public Cursor query(IndexPlan plan, NodeState rootState) {
-
-        if (baseIndex.getNodeAggregator() == null) {
+        if (getNodeAggregator() == null) {
+            // path-though
             return baseIndex.query(plan, rootState);
-        }
-        return newCursor(plan, baseIndex, rootState);
-    }
-
-    private static Cursor newCursor(IndexPlan plan, AdvanceFulltextQueryIndex index,
-                                    NodeState state) {
-        Filter f = plan.getFilter();
-        FullTextExpression e = f.getFullTextConstraint();
-        if (hasCompositeExpression(e)) {
-            Cursor c = flatten(e, plan, index, state);
-            if (c != null) {
-                return c;
-            }
-        }
-
-        IndexPlan newPlan = newPlanWithAggregationFilter(plan, null);
-        return new AggregationCursor(index.query(newPlan,
-                state), index.getNodeAggregator(), state);
+        } else if (!(plan instanceof AggregateIndexPlan)) {
+            // remove the node type restriction
+            plan.setFilter(newAggregationFilter(plan.getFilter(), null));
+            return newAggregationCursor(plan, rootState);
+        }
+        Filter filter = plan.getFilter();
+        AggregateIndexPlan aggPlan = (AggregateIndexPlan) plan;
+        FullTextExpression constraint = filter.getFullTextConstraint();
+        return flatten(constraint, aggPlan, filter, rootState, "");
     }
 
     private static boolean hasCompositeExpression(FullTextExpression ft) {
@@ -150,30 +154,34 @@ public class AggregateIndex implements A
         return hasNegative.get();
     }
 
-    private static Cursor flatten(FullTextExpression constraint,
-            final IndexPlan plan, final AdvanceFulltextQueryIndex index,
-            final NodeState state) {
+    private Cursor flatten(FullTextExpression constraint,
+            final AggregateIndexPlan plan,
+            final Filter filter, final NodeState state,
+            final String path) {
         if (constraint == null) {
             return null;
         }
-        final Filter filter = plan.getFilter();
         final AtomicReference<Cursor> result = new AtomicReference<Cursor>();
         constraint.accept(new FullTextVisitor() {
 
             @Override
             public boolean visit(FullTextTerm term) {
-                result.set(filterToCursor(newPlanWithAggregationFilter(plan, term),
-                        index, state));
+                IndexPlan p = plan.getPlan(path);
+                result.set(newAggregationCursor(p, state));
                 return true;
             }
 
             @Override
             public boolean visit(FullTextAnd and) {
                 Iterator<FullTextExpression> iterator = and.list.iterator();
-                Cursor c = flatten(iterator.next(), plan, index, state);
+                int index = 0;
+                Cursor c = flatten(iterator.next(), plan, filter, state,
+                        path + " and(" + index + ")");
                 while (iterator.hasNext()) {
+                    index++;
                     FullTextExpression input = iterator.next();
-                    Cursor newC = flatten(input, plan, index, state);
+                    Cursor newC = flatten(input, plan, filter, state,
+                            path + " and(" + index + ")");
                     c = Cursors.newIntersectionCursor(c, newC,
                             filter.getQueryEngineSettings());
                 }
@@ -183,11 +191,13 @@ public class AggregateIndex implements A
 
             @Override
             public boolean visit(FullTextOr or) {
+                final int[] index = new int[1];
                 List<Cursor> cursors = Lists.transform(or.list,
                         new Function<FullTextExpression, Cursor>() {
                             @Override
                             public Cursor apply(FullTextExpression input) {
-                                return flatten(input, plan, index, state);
+                                return flatten(input, plan, filter, state,
+                                        path + " or(" + index[0]++ + ")");
                             }
                         });
                 result.set(Cursors.newConcatCursor(cursors,
@@ -198,22 +208,63 @@ public class AggregateIndex implements A
         return result.get();
     }
 
-    private static Cursor filterToCursor(IndexPlan plan, AdvanceFulltextQueryIndex index,
-            NodeState state) {
-        return new AggregationCursor(index.query(plan, state),
-                index.getNodeAggregator(), state);
+    private void collectCombinedPlan(FullTextExpression constraint,
+            final Filter filter,
+            final List<OrderEntry> sortOrder, 
+            final NodeState state,
+            final AggregateIndexPlan target,
+            final String path) {
+        
+        constraint.accept(new FullTextVisitor() {
+
+            @Override
+            public boolean visit(FullTextTerm term) {
+                List<IndexPlan> list = baseIndex.getPlans(
+                        newAggregationFilter(filter, term), sortOrder, state);
+                target.setPlan(path, list);
+                return true;
+            }
+
+            @Override
+            public boolean visit(FullTextAnd and) {
+                int index = 0;
+                for (FullTextExpression input : and.list) {
+                    collectCombinedPlan(input, filter, sortOrder, state, target, 
+                            path + " and(" + index + ")");
+                    index++;
+                }
+                return true;
+            }
+
+            @Override
+            public boolean visit(FullTextOr or) {
+                int index = 0;
+                for (FullTextExpression input : or.list) {
+                    collectCombinedPlan(input, filter, sortOrder, state, target, 
+                            path + " or(" + index + ")");
+                    index++;
+                }
+                return true;
+            }
+        });
+    }
+
+    private Cursor newAggregationCursor(IndexPlan plan, NodeState rootState) {
+        Cursor c = baseIndex.query(plan, rootState);
+        // we possibly get results from a child,
+        // so we need to wrap the cursor to do aggregation
+        return new AggregationCursor(c,
+                getNodeAggregator(), rootState);         
     }
 
-    private static IndexPlan newPlanWithAggregationFilter(IndexPlan plan, FullTextExpression exp) {
-        FilterImpl f = new FilterImpl(plan.getFilter());
+    private static Filter newAggregationFilter(Filter filter, FullTextExpression exp) {
+        FilterImpl f = new FilterImpl(filter);
         // disables node type checks for now
         f.setMatchesAllTypes(true);
         if (exp != null) {
             f.setFullTextConstraint(exp);
         }
-        IndexPlan copy = plan.copy();
-        copy.setFilter(f);
-        return copy;
+        return f;
     }
 
     @Override
@@ -226,7 +277,23 @@ public class AggregateIndex implements A
         if (baseIndex == null) {
             return "aggregate no-index";
         }
-        return "aggregate " + baseIndex.getPlanDescription(plan, root);
+        StringBuilder buff = new StringBuilder("aggregate ");
+        if (getNodeAggregator() == null) {
+            // path-though
+            buff.append(baseIndex.getPlanDescription(plan, root));
+            return buff.toString();
+        } else if (!(plan instanceof AggregateIndexPlan)) {
+            buff.append(baseIndex.getPlanDescription(plan, root));
+            return buff.toString();
+        }
+        AggregateIndexPlan aggPlan = (AggregateIndexPlan) plan;
+        for (IndexPlan p : aggPlan.getPlans()) {
+            if (p != null) {
+                buff.append(baseIndex.getPlanDescription(p, root));
+                buff.append(" ");
+            }
+        }
+        return buff.toString();
     }
 
     @Override
@@ -242,101 +309,4 @@ public class AggregateIndex implements A
         return baseIndex.getNodeAggregator();
     }
 
-    /**
-     * An aggregation aware cursor.
-     */
-    private static class AggregationCursor extends AbstractCursor {
-
-        private final Cursor cursor;
-        private final NodeAggregator aggregator;
-        private final NodeState rootState;
-
-        private boolean init;
-        private boolean closed;
-
-        /**
-         * the current row
-         */
-        private IndexRow currentRow;
-
-        /**
-         * the path of the current item of the cursor
-         */
-        private String currentPath;
-
-        /**
-         * all of the item's known aggregates
-         */
-        private Iterator<String> aggregates;
-
-        /**
-         * should enforce uniqueness of the aggregated paths
-         */
-        private Set<String> seenPaths = new HashSet<String>();
-
-        public AggregationCursor(Cursor cursor, NodeAggregator aggregator,
-                NodeState rootState) {
-            this.cursor = cursor;
-            this.aggregator = aggregator;
-            this.rootState = rootState;
-        }
-
-        @Override
-        public boolean hasNext() {
-            if (!closed && !init) {
-                fetchNext();
-                init = true;
-            }
-            return !closed;
-        }
-
-        private void fetchNext() {
-            if (aggregates != null && aggregates.hasNext()) {
-                currentPath = aggregates.next();
-                init = true;
-                return;
-            }
-            aggregates = null;
-            if (cursor.hasNext()) {
-                currentRow = cursor.next();
-                String path = currentRow.getPath();
-                aggregates = Iterators.filter(Iterators.concat(
-                        Iterators.singletonIterator(path),
-                        aggregator.getParents(rootState, path)), Predicates
-                        .not(Predicates.in(seenPaths)));
-                fetchNext();
-                return;
-            }
-            closed = true;
-        }
-
-        @Override
-        public IndexRow next() {
-            if (!hasNext()) {
-                throw new NoSuchElementException();
-            }
-            seenPaths.add(currentPath);
-            init = false;
-            if (currentRow.getPath().equals(currentPath)) {
-                return currentRow;
-            }
-            // create a new overlayed index row,
-            // where the path is different but all other
-            // properties are kept
-            return new IndexRow() {
-
-                @Override
-                public String getPath() {
-                    return currentPath;
-                }
-
-                @Override
-                public PropertyValue getValue(String columnName) {
-                    return currentRow.getValue(columnName);
-                }
-                
-            };
-        }
-    }
-
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java?rev=1635108&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndexPlan.java Wed Oct 29 12:16:40 2014
@@ -0,0 +1,205 @@
+/*
+ * 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.aggregate;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.OrderEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * An index plan for multiple query indexes.
+ */
+public class AggregateIndexPlan implements IndexPlan {
+    
+    private Filter filter;
+    private boolean pathWithoutPlan;
+    
+    /**
+     * The map of terms to plans.
+     */
+    private final HashMap<String, IndexPlan> basePlans = new HashMap<String, IndexPlan>();
+    
+    AggregateIndexPlan(Filter filter) {
+        this.filter = filter;
+    }
+    
+    void setPlan(String path, List<IndexPlan> plans) {
+        if (plans.size() == 0) {
+            // no index
+            basePlans.put(path, null);
+            pathWithoutPlan = true;
+        } else {
+            // we always pick the first plan
+            basePlans.put(path, plans.get(0));
+        }
+    }
+    
+    boolean containsPathWithoutPlan() {
+        return pathWithoutPlan;
+    }
+    
+    IndexPlan getPlan(String path) {
+        return basePlans.get(path);
+    }
+    
+    Collection<IndexPlan> getPlans() {
+        return basePlans.values();
+    }
+
+    @Override
+    public double getCostPerExecution() {
+        double cost = 0;
+        for (IndexPlan p : basePlans.values()) {
+            if (p != null) {
+                cost += p.getCostPerExecution();
+            }
+        }
+        return cost;
+    }
+
+    @Override
+    public double getCostPerEntry() {
+        // calculate the weigted average
+        double costPerEntry = 0;
+        long totalEntries = getEstimatedEntryCount();
+        if (totalEntries == 0) {
+            return 0;
+        }
+        for (IndexPlan p : basePlans.values()) {
+            if (p != null) {
+                costPerEntry += p.getCostPerEntry() * p.getEstimatedEntryCount() / totalEntries;
+            }
+        }
+        return costPerEntry;
+    }
+
+    @Override
+    public long getEstimatedEntryCount() {
+        long totalEntries = 0;
+        for (IndexPlan p : basePlans.values()) {
+            if (p != null) {
+                totalEntries += p.getEstimatedEntryCount();
+            }
+        }
+        return totalEntries;
+    }
+
+    @Override
+    public Filter getFilter() {
+        return filter;
+    }
+
+    @Override
+    public void setFilter(Filter filter) {
+        this.filter = filter;
+    }
+
+    /**
+     * Whether any base plan is delayed.
+     * 
+     * @return true if yes
+     */
+    @Override
+    public boolean isDelayed() {
+        for (IndexPlan p : basePlans.values()) {
+            if (p != null && p.isDelayed()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Whether any base plan is a full text index.
+     * 
+     * @return true if yes
+     */
+    @Override
+    public boolean isFulltextIndex() {
+        for (IndexPlan p : basePlans.values()) {
+            if (p != null && p.isFulltextIndex()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Whether all base plan include node data.
+     * 
+     * @return true if yes
+     */
+    @Override
+    public boolean includesNodeData() {
+        for (IndexPlan p : basePlans.values()) {
+            if (p != null && !p.includesNodeData()) {
+                return false;
+            }
+        }
+        return true;   
+    }
+
+    /**
+     * An aggregated query can not sort, as it gets results from a number of
+     * indexes.
+     * 
+     * @return null
+     */
+    @Override
+    public List<OrderEntry> getSortOrder() {
+        return null;
+    }
+
+    // the following methods probably shouldn't be in the IndexPlan interface
+    // as they are only used locally (in the ordered index, or in the lucene index)
+    
+    @Override
+    @CheckForNull
+    public PropertyRestriction getPropertyRestriction() {
+        return null;
+    }
+
+    @Override
+    public IndexPlan copy() {
+        return null;
+    }
+    
+    @Override
+    public NodeState getDefinition() {
+        return null;
+    }
+
+    @Override
+    public String getPathPrefix() {
+        return null;
+    }
+
+    @Override
+    @CheckForNull
+    public Object getAttribute(String name) {
+        return null;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregationCursor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregationCursor.java?rev=1635108&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregationCursor.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregationCursor.java Wed Oct 29 12:16:40 2014
@@ -0,0 +1,128 @@
+/*
+ * 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.aggregate;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.IndexRow;
+import org.apache.jackrabbit.oak.spi.query.Cursors.AbstractCursor;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterators;
+
+/**
+ * An aggregation aware cursor.
+ */
+class AggregationCursor extends AbstractCursor {
+
+    private final Cursor cursor;
+    private final NodeAggregator aggregator;
+    private final NodeState rootState;
+
+    private boolean init;
+    private boolean closed;
+
+    /**
+     * the current row
+     */
+    private IndexRow currentRow;
+
+    /**
+     * the path of the current item of the cursor
+     */
+    private String currentPath;
+
+    /**
+     * all of the item's known aggregates
+     */
+    private Iterator<String> aggregates;
+
+    /**
+     * should enforce uniqueness of the aggregated paths
+     */
+    private Set<String> seenPaths = new HashSet<String>();
+
+    public AggregationCursor(Cursor cursor, NodeAggregator aggregator,
+            NodeState rootState) {
+        this.cursor = cursor;
+        this.aggregator = aggregator;
+        this.rootState = rootState;
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (!closed && !init) {
+            fetchNext();
+            init = true;
+        }
+        return !closed;
+    }
+
+    private void fetchNext() {
+        if (aggregates != null && aggregates.hasNext()) {
+            currentPath = aggregates.next();
+            init = true;
+            return;
+        }
+        aggregates = null;
+        if (cursor.hasNext()) {
+            currentRow = cursor.next();
+            String path = currentRow.getPath();
+            aggregates = Iterators.filter(Iterators.concat(
+                    Iterators.singletonIterator(path),
+                    aggregator.getParents(rootState, path)), Predicates
+                    .not(Predicates.in(seenPaths)));
+            fetchNext();
+            return;
+        }
+        closed = true;
+    }
+
+    @Override
+    public IndexRow next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+        seenPaths.add(currentPath);
+        init = false;
+        if (currentRow.getPath().equals(currentPath)) {
+            return currentRow;
+        }
+        // create a new overlayed index row,
+        // where the path is different but all other
+        // properties are kept
+        return new IndexRow() {
+
+            @Override
+            public String getPath() {
+                return currentPath;
+            }
+
+            @Override
+            public PropertyValue getValue(String columnName) {
+                return currentRow.getValue(columnName);
+            }
+            
+        };
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest2.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest2.java?rev=1635108&r1=1635107&r2=1635108&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest2.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest2.java Wed Oct 29 12:16:40 2014
@@ -154,7 +154,6 @@ public class LuceneIndexAggregationTest2
             .newRuleWithName("rep:User", newArrayList("profile"));
     }
         
-    @Ignore
     @Test
     public void oak2226() throws Exception {
         setTraversalEnabled(false);