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 al...@apache.org on 2013/08/23 10:30:56 UTC

svn commit: r1516744 - in /jackrabbit/oak/trunk: oak-core/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ oak-core/src/main/java/org/apache/jackrabbit/oak/query/inde...

Author: alexparvulescu
Date: Fri Aug 23 08:30:55 2013
New Revision: 1516744

URL: http://svn.apache.org/r1516744
Log:
OAK-828 Full-text support for index aggregates

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/NodeAggregator.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregator.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregatorTest.java   (with props)
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java   (with props)
Removed:
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/aggregation/
Modified:
    jackrabbit/oak/trunk/oak-core/pom.xml
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/AggregateIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
    jackrabbit/oak/trunk/oak-lucene/pom.xml
    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/LuceneIndexEditor.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
    jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/LuceneOakRepositoryStub.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryFulltextTest.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryJcrTest.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-core/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/pom.xml?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-core/pom.xml Fri Aug 23 08:30:55 2013
@@ -51,6 +51,7 @@
               org.apache.jackrabbit.oak.plugins.value,
               org.apache.jackrabbit.oak.plugins.commit,
               org.apache.jackrabbit.oak.plugins.index,
+              org.apache.jackrabbit.oak.plugins.index.aggregate,
               org.apache.jackrabbit.oak.plugins.index.nodetype,
               org.apache.jackrabbit.oak.plugins.index.property,
               org.apache.jackrabbit.oak.plugins.memory,

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=1516744&r1=1516743&r2=1516744&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 Fri Aug 23 08:30:55 2013
@@ -16,26 +16,37 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.aggregate;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
 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.query.QueryIndex.FulltextQueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+
 /**
  * A virtual full-text that can aggregate nodes based on aggregate definitions.
  * Internally, it uses another full-text index.
  */
 public class AggregateIndex implements FulltextQueryIndex {
-    
+
     private final FulltextQueryIndex baseIndex;
-    
+
     public AggregateIndex(FulltextQueryIndex baseIndex) {
         this.baseIndex = baseIndex;
     }
 
     @Override
     public double getCost(Filter filter, NodeState rootState) {
-        // TODO dummy implementation
         if (baseIndex == null) {
             return Double.POSITIVE_INFINITY;
         }
@@ -44,26 +55,99 @@ public class AggregateIndex implements F
 
     @Override
     public Cursor query(Filter filter, NodeState rootState) {
-        // TODO dummy implementation
-        return baseIndex.query(filter, rootState);
+        // pass-through impl
+        if (baseIndex.getNodeAggregator() == null) {
+            return baseIndex.query(filter, rootState);
+        }
+        return new AggregationCursor(baseIndex.query(
+                newAggregationFilter(filter), rootState),
+                baseIndex.getNodeAggregator(), rootState);
+    }
+
+    private static Filter newAggregationFilter(Filter filter) {
+        FilterImpl f = new FilterImpl(filter);
+        // disables node type checks for now
+        f.setMatchesAllTypes(true);
+        return f;
     }
 
     @Override
     public String getPlan(Filter filter, NodeState rootState) {
-        // TODO dummy implementation
         if (baseIndex == null) {
-            return "no plan";
+            return "aggregate no-index";
         }
         return "aggregate " + baseIndex.getPlan(filter, rootState);
     }
 
     @Override
     public String getIndexName() {
-        // TODO dummy implementation
         if (baseIndex == null) {
-            return "aggregat";
+            return "aggregate no-index";
+        }
+        return "aggregate " + baseIndex.getIndexName();
+    }
+
+    @Override
+    public NodeAggregator getNodeAggregator() {
+        return baseIndex.getNodeAggregator();
+    }
+
+    // ----- 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;
+        private Iterator<String> aggregates = null;
+
+        private String item = null;
+
+        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()) {
+                item = aggregates.next();
+                init = true;
+                return;
+            }
+            aggregates = null;
+            if (cursor.hasNext()) {
+                IndexRow row = cursor.next();
+                String path = row.getPath();
+                aggregates = Iterators.concat(ImmutableSet.of(path).iterator(),
+                        aggregator.getParents(rootState, path));
+                fetchNext();
+                return;
+            }
+            closed = true;
+        }
+
+        @Override
+        public IndexRow next() {
+            if (hasNext() == false) {
+                throw new NoSuchElementException();
+            }
+            init = false;
+            return new IndexRowImpl(item);
         }
-        return "aggregate." + baseIndex.getIndexName();
     }
 
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/NodeAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/NodeAggregator.java?rev=1516744&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/NodeAggregator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/NodeAggregator.java Fri Aug 23 08:30:55 2013
@@ -0,0 +1,29 @@
+/*
+ * 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.Iterator;
+
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+public interface NodeAggregator {
+
+    Iterator<String> getParents(NodeState rootState, String path);
+
+}

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

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/NodeAggregator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregator.java?rev=1516744&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregator.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregator.java Fri Aug 23 08:30:55 2013
@@ -0,0 +1,146 @@
+/*
+ * 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 static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getDepth;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.Iterators;
+
+/**
+ * List based NodeAggregator
+ * 
+ */
+public class SimpleNodeAggregator implements NodeAggregator {
+
+    public static final String INCLUDE_ALL = "*";
+
+    private final List<ChildNameRule> aggregates = new ArrayList<ChildNameRule>();
+
+    @Override
+    public Iterator<String> getParents(NodeState root, String path) {
+        return getParents(root, path, true);
+    }
+
+    private Iterator<String> getParents(NodeState root, String path,
+            boolean acceptStarIncludes) {
+
+        int levelsUp = 0;
+        Set<String> primaryType = new HashSet<String>();
+        for (ChildNameRule r : aggregates) {
+            String name = getName(path);
+            for (String inc : r.includes) {
+                // check node name match
+                if (name.equals(getName(inc))) {
+                    levelsUp = 1;
+                    primaryType.add(r.primaryType);
+                    if (acceptStarIncludes) {
+                        break;
+                    }
+                }
+                if (acceptStarIncludes) {
+                    // check '*' rule, which could span over more than one level
+                    if (INCLUDE_ALL.equals(getName(inc))) {
+                        // basic approach to dealing with a '*' include
+                        levelsUp = Math.max(getDepth(inc), levelsUp);
+                        primaryType.add(r.primaryType);
+                    }
+                }
+            }
+        }
+        if (levelsUp > 0 && !primaryType.isEmpty()) {
+            List<String> parents = new ArrayList<String>();
+            levelsUp = Math.min(levelsUp, getDepth(path));
+            String parentPath = path;
+            for (int i = 0; i < levelsUp; i++) {
+                parentPath = getParentPath(parentPath);
+                if (isNodeType(root, parentPath, primaryType)) {
+                    parents.add(parentPath);
+                    parents.addAll(newArrayList(getParents(root, parentPath,
+                            false)));
+                    return parents.iterator();
+                }
+            }
+        }
+
+        return Iterators.emptyIterator();
+    }
+
+    private static boolean isNodeType(NodeState root, String path, Set<String> types) {
+        NodeState state = root;
+        for (String p : elements(path)) {
+            if (state.hasChildNode(p)) {
+                state = state.getChildNode(p);
+            } else {
+                return false;
+            }
+        }
+        PropertyState ps = state.getProperty(JCR_PRIMARYTYPE);
+        if (ps == null) {
+            return false;
+        }
+        return types.contains(ps.getValue(STRING));
+    }
+
+    // ----- builder methods
+
+    /**
+     * Include children with the provided name. '*' means include all children
+     * 
+     * Note: there is no support for property names yet
+     * 
+     */
+    public SimpleNodeAggregator newRuleWithName(String primaryType,
+            List<String> includes) {
+        aggregates.add(new ChildNameRule(primaryType, includes));
+        return this;
+    }
+
+    // ----- aggregation rules
+
+    private static interface Rule {
+
+    }
+
+    private static class ChildNameRule implements Rule {
+
+        private final String primaryType;
+        private final List<String> includes;
+
+        ChildNameRule(String primaryType, List<String> includes) {
+            this.primaryType = primaryType;
+            this.includes = includes;
+        }
+    }
+
+}

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

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java Fri Aug 23 08:30:55 2013
@@ -331,6 +331,9 @@ public class SelectorImpl extends Source
             // We store the search token (the full-text condition text) 
             // in this column (which is also weird), as this is needed for highlighting
             String searchToken = SimpleExcerptProvider.extractFulltext(query.getConstraint());
+            if (searchToken == null) {
+                return PropertyValues.newString(path);
+            }
             return PropertyValues.newString(searchToken);
         }
         return PropertyValues.create(t.getProperty(propertyName));

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java Fri Aug 23 08:30:55 2013
@@ -56,6 +56,12 @@ public class FilterImpl implements Filte
     private boolean alwaysFalse;
 
     /**
+     * inherited from the selector, duplicated here so it can be over-written by
+     * other filters
+     */
+    private boolean matchesAllTypes;
+
+    /**
      *  The path, or "/" (the root node, meaning no filter) if not set.
      */
     private String path = "/";
@@ -98,8 +104,26 @@ public class FilterImpl implements Filte
         this.selector = selector;
         this.queryStatement = queryStatement;
         this.rootTree = rootTree;
+        this.matchesAllTypes = selector != null ? selector.matchesAllTypes()
+                : false;
     }
-    
+
+    public FilterImpl(Filter filter) {
+        FilterImpl impl = (FilterImpl) filter;
+        this.alwaysFalse = impl.alwaysFalse;
+        this.distinct = impl.distinct;
+        this.fullTextConstraint = impl.fullTextConstraint;
+        this.matchesAllTypes = impl.matchesAllTypes;
+        this.path = impl.path;
+        this.pathRestriction = impl.pathRestriction;
+        this.propertyRestrictions.putAll(impl.propertyRestrictions);
+        this.queryStatement = impl.queryStatement;
+        this.rootTree = impl.rootTree;
+        this.selector = impl.selector;
+        this.matchesAllTypes = selector != null ? selector.matchesAllTypes()
+                : false;
+    }
+
     public void setPreparing(boolean preparing) {
         this.preparing = preparing;
     }
@@ -158,7 +182,7 @@ public class FilterImpl implements Filte
 
     @Override
     public boolean matchesAllTypes() {
-        return selector.matchesAllTypes();
+        return matchesAllTypes;
     }
 
     @Override @Nonnull
@@ -481,4 +505,8 @@ public class FilterImpl implements Filte
         return queryStatement;
     }
 
+    public void setMatchesAllTypes(boolean matchesAllTypes) {
+        this.matchesAllTypes = matchesAllTypes;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/QueryIndex.java Fri Aug 23 08:30:55 2013
@@ -18,6 +18,9 @@
  */
 package org.apache.jackrabbit.oak.spi.query;
 
+import javax.annotation.CheckForNull;
+
+import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
@@ -111,6 +114,14 @@ public interface QueryIndex {
      */
     public interface FulltextQueryIndex extends QueryIndex {
         // marker interface
+
+        /**
+         * Returns the NodeAggregator responsible for providing the aggregation
+         * settings or null if aggregation is not available/desired
+         * 
+         */
+        @CheckForNull
+        NodeAggregator getNodeAggregator();
     }
 
 //    /**

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregatorTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregatorTest.java?rev=1516744&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregatorTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregatorTest.java Fri Aug 23 08:30:55 2013
@@ -0,0 +1,194 @@
+/*
+ * 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 static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_FILE;
+import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.apache.jackrabbit.oak.plugins.index.aggregate.SimpleNodeAggregator.INCLUDE_ALL;
+
+import java.util.List;
+
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Test;
+
+public class SimpleNodeAggregatorTest {
+
+    @Test
+    public void testNodeName() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder file = builder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE);
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FILE, newArrayList(JCR_CONTENT));
+
+        String path = "/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertEquals(newArrayList("/file"), actual);
+
+    }
+
+    @Test
+    public void testNodeNameWrongParentType() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder file = builder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE + "_");
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FILE, newArrayList(JCR_CONTENT));
+
+        String path = "/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertTrue(actual.isEmpty());
+
+    }
+
+    @Test
+    public void testStarName() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder file = builder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE);
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FILE, newArrayList(INCLUDE_ALL));
+
+        String path = "/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertEquals(newArrayList("/file"), actual);
+
+    }
+
+    @Test
+    public void testStarNameMoreLevels() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder file = builder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE);
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FILE, newArrayList("*", "*/*", "*/*/*", "*/*/*/*"));
+
+        String path = "/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertEquals(newArrayList("/file"), actual);
+
+    }
+
+    @Test
+    public void testStarNameWrongParentType() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder file = builder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE + "_");
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FILE, newArrayList(INCLUDE_ALL));
+
+        String path = "/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertTrue(actual.isEmpty());
+
+    }
+
+    @Test
+    public void testCascadingStarName() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder folder = builder.child("folder");
+        folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER);
+
+        NodeBuilder file = folder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE);
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FOLDER, newArrayList("file")).newRuleWithName(NT_FILE,
+                newArrayList(INCLUDE_ALL));
+
+        String path = "/folder/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertEquals(newArrayList("/folder/file", "/folder"), actual);
+
+    }
+
+    @Test
+    public void testCascadingNodeName() {
+
+        NodeState root = new MemoryNodeStore().getRoot();
+        NodeBuilder builder = root.builder();
+
+        NodeBuilder folder = builder.child("folder");
+        folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER);
+
+        NodeBuilder file = folder.child("file");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE);
+        file.child(JCR_CONTENT);
+
+        SimpleNodeAggregator agg = new SimpleNodeAggregator().newRuleWithName(
+                NT_FOLDER, newArrayList("file")).newRuleWithName(NT_FILE,
+                newArrayList(JCR_CONTENT));
+
+        String path = "/folder/file/jcr:content";
+        List<String> actual = newArrayList(agg.getParents(
+                builder.getNodeState(), path));
+
+        assertEquals(newArrayList("/folder/file", "/folder"), actual);
+
+    }
+
+}

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

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/aggregate/SimpleNodeAggregatorTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-lucene/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/pom.xml?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-lucene/pom.xml Fri Aug 23 08:30:55 2013
@@ -36,13 +36,61 @@
     <tika.version>1.3</tika.version>
     <lucene.version>4.3.0</lucene.version>
     <known.issues>
-      org.apache.jackrabbit.core.query.FulltextQueryTest#testContainsPropScopeSQL    <!-- OAK-819 -->
-      org.apache.jackrabbit.core.query.FulltextQueryTest#testContainsPropScopeXPath  <!-- OAK-819 -->
-      org.apache.jackrabbit.core.query.FulltextQueryTest#testMultiByte               <!-- OAK-954 -->
-      org.apache.jackrabbit.core.query.JoinTest#testJoinWithOR4 <!-- OAK-955 -->
-      org.apache.jackrabbit.core.query.JoinTest#testJoinWithOR5 <!-- OAK-955 -->
-      org.apache.jackrabbit.core.query.SQL2NodeLocalNameTest#testLowerLocalNameOrContains <!-- OAK-957 -->
-      org.apache.jackrabbit.core.query.SQL2NodeLocalNameTest#testUpperLocalNameOrContains <!-- OAK-957 -->
+      <!-- Jackrabbit query tests -->
+      org.apache.jackrabbit.core.query.ExcerptTest#testMoreTextDotsAtEnd                             <!-- OAK-318 -->
+      org.apache.jackrabbit.core.query.ExcerptTest#testMoreTextDotsAtStart                           <!-- OAK-318 -->
+      org.apache.jackrabbit.core.query.ExcerptTest#testMoreTextDotsAtStartAndEnd                     <!-- OAK-318 -->
+      org.apache.jackrabbit.core.query.ExcerptTest#testPunctuationStartsFragment                     <!-- OAK-318 -->
+      org.apache.jackrabbit.core.query.ExcerptTest#testPunctuationStartsFragmentEndsWithDots         <!-- OAK-318 -->
+      org.apache.jackrabbit.core.query.ExcerptTest#testPreferPhrase                                  <!-- OAK-318 -->
+      org.apache.jackrabbit.core.query.QueryResultTest#testSkip                                      <!-- OAK-484 -->
+      org.apache.jackrabbit.core.query.SimilarQueryTest#testSimilar                                  <!-- OAK-319 -->
+      org.apache.jackrabbit.core.query.DerefTest#testDeref                                           <!-- OAK-321 -->
+      org.apache.jackrabbit.core.query.DerefTest#testDerefInPredicate                                <!-- OAK-321 -->
+      org.apache.jackrabbit.core.query.DerefTest#testRewrite                                         <!-- OAK-321 -->
+      org.apache.jackrabbit.core.query.DerefTest#testDerefToVersionNode                              <!-- OAK-321 -->
+      org.apache.jackrabbit.core.query.DerefTest#testMultipleDeref                                   <!-- OAK-321 -->
+      org.apache.jackrabbit.core.query.FulltextQueryTest#testContainsPropScopeSQL                    <!-- OAK-902 -->
+      org.apache.jackrabbit.core.query.FulltextQueryTest#testContainsPropScopeXPath                  <!-- OAK-902 -->
+      org.apache.jackrabbit.core.query.XPathAxisTest#testIndex0Descendant                            <!-- OAK-322 -->
+      org.apache.jackrabbit.core.query.XPathAxisTest#testIndex1Descendant                            <!-- OAK-322 -->
+      org.apache.jackrabbit.core.query.XPathAxisTest#testIndex2Descendant                            <!-- OAK-322 -->
+      org.apache.jackrabbit.core.query.XPathAxisTest#testIndex3Descendant                            <!-- OAK-322 -->
+      org.apache.jackrabbit.core.query.SQL2QueryResultTest#testSQL2SelectColums                      <!-- OAK-874 -->
+      org.apache.jackrabbit.core.query.SimpleQueryTest#testGeneralComparison                         <!-- OAK-327 -->
+      org.apache.jackrabbit.core.query.FnNameQueryTest#testLikeWithPrefix                            <!-- OAK-328 -->
+      org.apache.jackrabbit.core.query.UpperLowerCaseQueryTest#testInvalidQuery                      <!-- OAK-329 -->
+      org.apache.jackrabbit.core.query.SQL2PathEscapingTest#testGetChildrenNoEscaping                <!-- OAK-481 -->
+      org.apache.jackrabbit.core.query.SQL2PathEscapingTest#testGetChildrenEscapedFull               <!-- OAK-481 -->
+      org.apache.jackrabbit.core.query.SQL2PathEscapingTest#testGetChildrenEscapedNode               <!-- OAK-481 -->
+      org.apache.jackrabbit.core.query.ChildAxisQueryTest#testRelationQuery                          <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.ChildAxisQueryTest#testRelationQueryDeep                      <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.ChildAxisQueryTest#testMultiRelation                          <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.ChildAxisQueryTest#testLike                                   <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.ChildAxisQueryTest#testContains                               <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.ChildAxisQueryTest#testStarNameTest                           <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.SelectClauseTest#testSameNameSiblingSQL                       <!-- OAK-203 -->
+      org.apache.jackrabbit.core.query.ShareableNodeTest#testName                                    <!-- OAK-118 -->
+      org.apache.jackrabbit.core.query.ShareableNodeTest#testPathConstraint                          <!-- OAK-118 -->
+      org.apache.jackrabbit.core.observation.ShareableNodesTest#testAddShare                         <!-- OAK-118 workspace support needed -->
+      org.apache.jackrabbit.core.observation.ShareableNodesTest#testRemoveShare                      <!-- OAK-118 workspace support needed -->
+      org.apache.jackrabbit.core.query.FulltextQueryTest#testMultiByte                               <!-- OAK-954 -->
+      org.apache.jackrabbit.core.query.JoinTest#testJoinWithOR4                                      <!-- OAK-955 -->
+      org.apache.jackrabbit.core.query.JoinTest#testJoinWithOR5                                      <!-- OAK-955 -->
+      org.apache.jackrabbit.core.query.SQL2NodeLocalNameTest#testLowerLocalNameOrContains            <!-- OAK-957 -->
+      org.apache.jackrabbit.core.query.SQL2NodeLocalNameTest#testUpperLocalNameOrContains            <!-- OAK-957 -->
+      org.apache.jackrabbit.core.query.QueryResultTest#testGetSizeOrderByScore                       <!-- OAK-484 -->
+      org.apache.jackrabbit.core.query.QueryResultTest#testIteratorNext                              <!-- OAK-484 -->
+      org.apache.jackrabbit.core.query.QueryResultTest#testIteratorNextOrderByScore                  <!-- OAK-484 -->
+      org.apache.jackrabbit.core.query.QueryResultTest#testSkipOrderByProperty                       <!-- OAK-484 -->
+      org.apache.jackrabbit.core.query.QueryResultTest#testGetSize                                   <!-- OAK-484 -->
+      org.apache.jackrabbit.core.query.SimpleQueryTest#testDoubleField                               <!-- OAK-327 -->
+      org.apache.jackrabbit.core.query.SimpleQueryTest#testBigDecimalField                           <!-- OAK-327 -->
+      org.apache.jackrabbit.core.query.SimpleQueryTest#testLongField                                 <!-- OAK-327 -->
+      org.apache.jackrabbit.core.query.SimpleQueryTest#testLikePatternEscaped                        <!-- OAK-327 -->
+      org.apache.jackrabbit.core.query.SimpleQueryTest#testNegativeNumber                            <!-- OAK-327 -->
+      org.apache.jackrabbit.core.query.SQL2OrderByTest#testOrderByScore
+      org.apache.jackrabbit.oak.jcr.query.QueryFulltextTest#testFulltextRelativeProperty             <!-- OAK-828 -->
     </known.issues>
   </properties>
 

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=1516744&r1=1516743&r2=1516744&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 Fri Aug 23 08:30:55 2013
@@ -49,6 +49,7 @@ import java.util.concurrent.atomic.Atomi
 
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
 import org.apache.jackrabbit.oak.query.fulltext.FullTextOr;
@@ -130,8 +131,11 @@ public class LuceneIndex implements Full
 
     private final Analyzer analyzer;
 
-    public LuceneIndex(Analyzer analyzer) {
+    private final NodeAggregator aggregator;
+
+    public LuceneIndex(Analyzer analyzer, NodeAggregator aggregator) {
         this.analyzer = analyzer;
+        this.aggregator = aggregator;
     }
 
     @Override
@@ -331,10 +335,8 @@ public class LuceneIndex implements Full
                 try {
                     IndexSearcher searcher = new IndexSearcher(reader);
                     Collection<String> paths = new ArrayList<String>();
-                    HashSet<String> seenPaths = new HashSet<String>();
                     Query query = getQuery(filter, reader,
                             nonFullTextConstraints, analyzer);
-                    int parentDepth = PathUtils.getDepth(parent);
                     if (query != null) {
                         // OAK-925
                         // TODO how to best avoid loading all entries in memory?
@@ -348,20 +350,20 @@ public class LuceneIndex implements Full
                                 if ("".equals(path)) {
                                     path = "/";
                                 }
-                                if (!parent.isEmpty()) {
-                                    // ensure the path ends with the given
-                                    // relative path
-                                    if (!path.endsWith("/" + parent)) {
-                                        continue;
-                                    }
-                                    // get the base path
-                                    path = PathUtils.getAncestorPath(path, parentDepth);
-                                    // avoid duplicate entries
-                                    if (seenPaths.contains(path)) {
-                                        continue;
-                                    }
-                                    seenPaths.add(path);
-                                }
+//                                if (!parent.isEmpty()) {
+//                                    // ensure the path ends with the given
+//                                    // relative path
+//                                    if (!path.endsWith("/" + parent)) {
+//                                        continue;
+//                                    }
+//                                    // get the base path
+//                                    path = PathUtils.getAncestorPath(path, parentDepth);
+//                                    // avoid duplicate entries
+//                                    if (seenPaths.contains(path)) {
+//                                        continue;
+//                                    }
+//                                    seenPaths.add(path);
+//                                }
                                 paths.add(path);
                             }
                         }
@@ -565,7 +567,6 @@ public class LuceneIndex implements Full
             @Override
             public boolean visit(FullTextOr or) {
                 BooleanQuery q = new BooleanQuery();
-                q.setMinimumNumberShouldMatch(1);
                 for (FullTextExpression e : or.list) {
                     Query x = getFullTextQuery(e, analyzer);
                     q.add(x, SHOULD);
@@ -597,7 +598,8 @@ public class LuceneIndex implements Full
             public boolean visit(FullTextTerm term) {
                 String p = term.getPropertyName();
                 if (p != null && p.indexOf('/') >= 0) {
-                    p = PathUtils.getName(p);
+                    //do not add constraints on child nodes properties
+                    p = "*";
                 }
                 Query q = tokenToQuery(term.getText(), analyzer);
                 if (q == null) {
@@ -789,4 +791,9 @@ public class LuceneIndex implements Full
         return out;
     }
 
+    @Override
+    public NodeAggregator getNodeAggregator() {
+        return aggregator;
+    }
+
 }

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=1516744&r1=1516743&r2=1516744&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 Aug 23 08:30:55 2013
@@ -34,7 +34,6 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
-import org.apache.jackrabbit.oak.plugins.index.lucene.aggregation.AggregatedState;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -193,22 +192,6 @@ public class LuceneIndexEditor implement
             }
         }
 
-        for (AggregatedState agg : context.getAggregator().getAggregates(state)) {
-            for (PropertyState property : agg.getProperties()) {
-                String pname = property.getName();
-                if (isVisible(pname)
-                        && (context.getPropertyTypes() & (1 << property.getType().tag())) != 0) {
-                    if (Type.BINARY.tag() == property.getType().tag()) {
-                        addBinaryValue(document, property, agg.get());
-                    } else {
-                        for (String v : property.getValue(Type.STRINGS)) {
-                            document.add(newFulltextField(v));
-                        }
-                    }
-                }
-            }
-        }
-
         return document;
     }
 

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorContext.java Fri Aug 23 08:30:55 2013
@@ -30,7 +30,6 @@ import javax.jcr.PropertyType;
 
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.index.lucene.aggregation.NodeAggregator;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.index.IndexWriter;
@@ -86,8 +85,6 @@ public class LuceneIndexEditorContext {
         }
     }
 
-    private static final NodeAggregator aggregator = new NodeAggregator();
-
     private final IndexWriterConfig config;
 
     private static final Parser parser = new AutoDetectParser();
@@ -125,10 +122,6 @@ public class LuceneIndexEditorContext {
         return propertyTypes;
     }
 
-    NodeAggregator getAggregator() {
-        return aggregator;
-    }
-
     Parser getParser() {
         return parser;
     }

Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java Fri Aug 23 08:30:55 2013
@@ -22,6 +22,7 @@ import javax.annotation.Nonnull;
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -45,6 +46,8 @@ public class LuceneIndexProvider impleme
      */
     protected Analyzer analyzer = ANALYZER;
 
+    protected NodeAggregator aggregator = null;
+
     @Override
     @Nonnull
     public List<QueryIndex> getQueryIndexes(NodeState nodeState) {
@@ -52,7 +55,7 @@ public class LuceneIndexProvider impleme
     }
 
     protected LuceneIndex newLuceneIndex() {
-        return new LuceneIndex(analyzer);
+        return new LuceneIndex(analyzer, aggregator);
     }
 
     /**
@@ -62,6 +65,13 @@ public class LuceneIndexProvider impleme
         this.analyzer = analyzer;
     }
 
+    /**
+     * sets the default node aggregator that will be used at query time
+     */
+    public void setAggregator(NodeAggregator aggregator) {
+        this.aggregator = aggregator;
+    }
+
     // ----- helper builder method
 
     public LuceneIndexProvider with(Analyzer analyzer) {
@@ -69,4 +79,9 @@ public class LuceneIndexProvider impleme
         return this;
     }
 
+    public LuceneIndexProvider with(NodeAggregator analyzer) {
+        this.setAggregator(analyzer);
+        return this;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/LuceneOakRepositoryStub.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/LuceneOakRepositoryStub.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/LuceneOakRepositoryStub.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/LuceneOakRepositoryStub.java Fri Aug 23 08:30:55 2013
@@ -16,10 +16,17 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
+import static org.apache.jackrabbit.JcrConstants.NT_FILE;
+
 import java.util.Properties;
 
 import javax.jcr.RepositoryException;
 
+import org.apache.jackrabbit.oak.plugins.index.aggregate.AggregateIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.SimpleNodeAggregator;
 import org.apache.jackrabbit.oak.plugins.index.lucene.LowCostLuceneIndexProvider;
 import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.lucene.util.LuceneInitializerHelper;
@@ -34,7 +41,14 @@ public class LuceneOakRepositoryStub ext
     @Override
     protected void preCreateRepository(Jcr jcr) {
         jcr.with(new LuceneInitializerHelper("luceneGlobal", null))
-                .with(new LowCostLuceneIndexProvider())
+                .with(AggregateIndexProvider
+                        .wrap(new LowCostLuceneIndexProvider()
+                                .with(getNodeAggregator())))
                 .with(new LuceneIndexEditorProvider());
     }
+
+    private static NodeAggregator getNodeAggregator() {
+        return new SimpleNodeAggregator()
+            .newRuleWithName(NT_FILE, newArrayList(JCR_CONTENT, JCR_CONTENT + "/*"));
+    }
 }

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryFulltextTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryFulltextTest.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryFulltextTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryFulltextTest.java Fri Aug 23 08:30:55 2013
@@ -59,7 +59,7 @@ public class QueryFulltextTest extends A
 //                    "where contains([nt:base].[text], cast('hello OR hallo' as string)) */", 
 //                    getResult(q.execute(), "plan"));
             assertEquals("[nt:base] as [nt:base] /* " + 
-                    "+((:fulltext:hallo* :fulltext:hello*)~1) +text:{* TO *} " + 
+                    "aggregate +(:fulltext:hallo* :fulltext:hello*) +text:{* TO *} " + 
                     "ft:(text:\"hallo\" OR text:\"hello\") " +
                     "where contains([nt:base].[text], cast('hello OR hallo' as string)) */", 
                     getResult(q.execute(), "plan"));
@@ -114,7 +114,7 @@ public class QueryFulltextTest extends A
 //                  "and (contains([nt:base].[text], cast('hallo' as string))) */", 
 //                  getResult(q.execute(), "plan"));
             assertEquals("[nt:base] as [nt:base] /* " + 
-                    "+:fulltext:hallo* +:path:/testroot/* +text:{* TO *} " + 
+                    "aggregate +:fulltext:hallo* +:path:/testroot/* +text:{* TO *} " + 
                     "ft:(text:\"hallo\") " + 
                     "where (ischildnode([nt:base], [/testroot])) " + 
                     "and (contains([nt:base].[text], cast('hallo' as string))) */", 
@@ -144,7 +144,7 @@ public class QueryFulltextTest extends A
 //                    "where contains([nt:base].[node2/text], cast('hello OR hallo' as string)) */", 
 //                    getResult(q.execute(), "plan"));
             assertEquals("[nt:base] as [nt:base] /* " + 
-                    "(:fulltext:hallo* :fulltext:hello*)~1 " + 
+                    ":fulltext:hallo* :fulltext:hello* " + 
                     "ft:(node2/text:\"hallo\" OR node2/text:\"hello\") " + 
                     "parent:node2 " + 
                     "where contains([nt:base].[node2/text], cast('hello OR hallo' as string)) */", 

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryJcrTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryJcrTest.java?rev=1516744&r1=1516743&r2=1516744&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryJcrTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryJcrTest.java Fri Aug 23 08:30:55 2013
@@ -19,6 +19,11 @@ package org.apache.jackrabbit.oak.jcr.qu
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+
+import org.apache.jackrabbit.core.query.ChildAxisQueryTest;
+import org.apache.jackrabbit.core.query.DerefTest;
+import org.apache.jackrabbit.core.query.ExcerptTest;
+import org.apache.jackrabbit.core.query.FnNameQueryTest;
 import org.apache.jackrabbit.core.query.FulltextQueryTest;
 import org.apache.jackrabbit.core.query.FulltextSQL2QueryTest;
 import org.apache.jackrabbit.core.query.JoinTest;
@@ -27,11 +32,22 @@ import org.apache.jackrabbit.core.query.
 import org.apache.jackrabbit.core.query.OrderByTest;
 import org.apache.jackrabbit.core.query.ParentNodeTest;
 import org.apache.jackrabbit.core.query.PathQueryNodeTest;
+import org.apache.jackrabbit.core.query.QueryResultTest;
 import org.apache.jackrabbit.core.query.SQL2NodeLocalNameTest;
 import org.apache.jackrabbit.core.query.SQL2OffsetLimitTest;
+import org.apache.jackrabbit.core.query.SQL2OrderByTest;
 import org.apache.jackrabbit.core.query.SQL2OuterJoinTest;
+import org.apache.jackrabbit.core.query.SQL2PathEscapingTest;
+import org.apache.jackrabbit.core.query.SQL2QueryResultTest;
 import org.apache.jackrabbit.core.query.SQLTest;
+import org.apache.jackrabbit.core.query.SelectClauseTest;
+import org.apache.jackrabbit.core.query.ShareableNodeTest;
+import org.apache.jackrabbit.core.query.SimilarQueryTest;
+import org.apache.jackrabbit.core.query.SimpleQueryTest;
 import org.apache.jackrabbit.core.query.SkipDeletedNodesTest;
+import org.apache.jackrabbit.core.query.UpperLowerCaseQueryTest;
+import org.apache.jackrabbit.core.query.VersionStoreQueryTest;
+import org.apache.jackrabbit.core.query.XPathAxisTest;
 import org.apache.jackrabbit.test.ConcurrentTestSuite;
 
 public class QueryJcrTest extends TestCase {
@@ -46,39 +62,27 @@ public class QueryJcrTest extends TestCa
         suite.addTestSuite(PathQueryNodeTest.class);
         suite.addTestSuite(FulltextSQL2QueryTest.class);
         suite.addTestSuite(SQL2NodeLocalNameTest.class);
+        suite.addTestSuite(SQL2OrderByTest.class);
         suite.addTestSuite(MixinTest.class);
         suite.addTestSuite(SQL2OuterJoinTest.class);
         suite.addTestSuite(SQL2OffsetLimitTest.class);
         suite.addTestSuite(LimitAndOffsetTest.class);
         suite.addTestSuite(OrderByTest.class);
+        suite.addTestSuite(ExcerptTest.class);
+        suite.addTestSuite(QueryResultTest.class);
         suite.addTestSuite(ParentNodeTest.class);
-
-        // FAILURES
-        //
-        // suite.addTestSuite(SQL2OrderByTest.class); // order by score is not stable
-        // suite.addTestSuite(QueryResultTest.class); // OAK-484
-        // suite.addTestSuite(ExcerptTest.class); // OAK-318
-        // suite.addTestSuite(SimilarQueryTest.class); // OAK-319
-        // suite.addTestSuite(DerefTest.class); // OAK-321
-        // suite.addTestSuite(XPathAxisTest.class); // OAK-322
-        // suite.addTestSuite(SQL2QueryResultTest.class); // OAK-323
-        // suite.addTestSuite(SimpleQueryTest.class); // OAK-327
-        // suite.addTestSuite(FnNameQueryTest.class); // OAK-328
-        // suite.addTestSuite(UpperLowerCaseQueryTest.class); // OAK-329
-        // suite.addTestSuite(SQL2PathEscapingTest.class); // OAK-481
-
-        // NOT IMPLEMENTED
-        //
-        // suite.addTestSuite(ChildAxisQueryTest.class); // sns
-        // suite.addTestSuite(SelectClauseTest.class); // sns
-        // suite.addTestSuite(ShareableNodeTest.class); // ws#clone
-        // suite.addTestSuite(VersionStoreQueryTest.class); // versioning
-
-        // TOO JR SPECIFIC
-        //
-        // suite.addTestSuite(LimitedAccessQueryTest.class); // acls
-        // suite.addTestSuite(SkipDeniedNodesTest.class); // acls
-
+        suite.addTestSuite(SimilarQueryTest.class);
+        suite.addTestSuite(DerefTest.class);
+        suite.addTestSuite(XPathAxisTest.class);
+        suite.addTestSuite(SQL2QueryResultTest.class);
+        suite.addTestSuite(SimpleQueryTest.class);
+        suite.addTestSuite(FnNameQueryTest.class);
+        suite.addTestSuite(UpperLowerCaseQueryTest.class);
+        suite.addTestSuite(SQL2PathEscapingTest.class);
+        suite.addTestSuite(ChildAxisQueryTest.class);
+        suite.addTestSuite(SelectClauseTest.class);
+        suite.addTestSuite(ShareableNodeTest.class);
+        suite.addTestSuite(VersionStoreQueryTest.class);
         return suite;
     }
 }

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=1516744&r1=1516743&r2=1516744&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 Fri Aug 23 08:30:55 2013
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+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;
 import org.apache.lucene.analysis.Analyzer;
@@ -29,20 +30,13 @@ public class LowCostLuceneIndexProvider 
 
     @Override
     protected LuceneIndex newLuceneIndex() {
-        return new LowCostLuceneIndex(analyzer);
-    }
-
-    // ----- helper builder method
-
-    public LowCostLuceneIndexProvider with(Analyzer analyzer) {
-        this.setAnalyzer(analyzer);
-        return this;
+        return new LowCostLuceneIndex(analyzer, aggregator);
     }
 
     private static class LowCostLuceneIndex extends LuceneIndex {
 
-        public LowCostLuceneIndex(Analyzer analyzer) {
-            super(analyzer);
+        public LowCostLuceneIndex(Analyzer analyzer, NodeAggregator aggregator) {
+            super(analyzer, aggregator);
         }
 
         @Override

Added: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java?rev=1516744&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java Fri Aug 23 08:30:55 2013
@@ -0,0 +1,226 @@
+/*
+ * 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 static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_FILE;
+import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
+import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.AggregateIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.SimpleNodeAggregator;
+import static org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState.binaryProperty;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class LuceneIndexAggregationTest extends AbstractQueryTest {
+
+    @Override
+    protected void createTestIndexNode() throws Exception {
+        Tree index = root.getTree("/");
+        createTestIndexNode(index, LuceneIndexConstants.TYPE_LUCENE);
+        root.commit();
+    }
+
+    @Override
+    protected ContentRepository createRepository() {
+        return new Oak()
+                .with(new InitialContent())
+                .with(new OpenSecurityProvider())
+                .with(AggregateIndexProvider
+                        .wrap(new LowCostLuceneIndexProvider()
+                                .with(getNodeAggregator())))
+                .with(new LuceneIndexEditorProvider())
+                .createContentRepository();
+    }
+
+    /**
+     * <code>
+     * <aggregate primaryType="nt:file"> 
+     *   <include>jcr:content</include>
+     *   <include>jcr:content/*</include>
+     *   <include-property>jcr:content/jcr:lastModified</include-property>
+     * </aggregate>
+     * <code>
+     * 
+     */
+    private static NodeAggregator getNodeAggregator() {
+        return new SimpleNodeAggregator()
+            .newRuleWithName(NT_FILE, newArrayList(JCR_CONTENT, JCR_CONTENT + "/*"))
+            .newRuleWithName(NT_FOLDER, newArrayList("myFile"));
+    }
+
+    /**
+     * simple index aggregation from jcr:content to nt:file
+     * 
+     */
+    @Test
+    public void testNtFileAggregate() throws Exception {
+
+        String sqlBase = "SELECT * FROM [nt:file] as f WHERE";
+        String sqlCat = sqlBase + " CONTAINS (f.*, 'cat')";
+        String sqlDog = sqlBase + " CONTAINS (f.*, 'dog')";
+
+        Tree file = root.getTree("/").addChild("myFile");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+        Tree resource = file.addChild(JCR_CONTENT);
+        resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+        resource.setProperty("jcr:lastModified", Calendar.getInstance());
+        resource.setProperty("jcr:encoding", "UTF-8");
+        resource.setProperty("jcr:mimeType", "text/plain");
+        resource.setProperty(binaryProperty(JCR_DATA,
+                "the quick brown fox jumps over the lazy dog."));
+        root.commit();
+        assertQuery(sqlDog, ImmutableList.of("/myFile"));
+
+        // update jcr:data
+        root.getTree("/")
+                .getChild("myFile")
+                .getChild(JCR_CONTENT)
+                .setProperty(
+                        binaryProperty(JCR_DATA,
+                                "the quick brown fox jumps over the lazy cat."));
+        root.commit();
+        assertQuery(sqlDog, new ArrayList<String>());
+        assertQuery(sqlCat, ImmutableList.of("/myFile"));
+
+        // replace jcr:content with unstructured
+        root.getTree("/").getChild("myFile").getChild(JCR_CONTENT).remove();
+
+        Tree unstrContent = root.getTree("/").getChild("myFile")
+                .addChild(JCR_CONTENT);
+        unstrContent.setProperty(JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED,
+                Type.NAME);
+
+        Tree foo = unstrContent.addChild("foo");
+        foo.setProperty(JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED,
+                Type.NAME);
+        foo.setProperty("text", "the quick brown fox jumps over the lazy dog.");
+        root.commit();
+
+        assertQuery(sqlDog, ImmutableList.of("/myFile"));
+        assertQuery(sqlCat, new ArrayList<String>());
+
+        // remove foo
+        root.getTree("/").getChild("myFile").getChild(JCR_CONTENT)
+                .getChild("foo").remove();
+
+        root.commit();
+        assertQuery(sqlDog, new ArrayList<String>());
+        assertQuery(sqlCat, new ArrayList<String>());
+
+        // replace jcr:content again with resource
+        root.getTree("/").getChild("myFile").getChild(JCR_CONTENT).remove();
+
+        resource = root.getTree("/").getChild("myFile").addChild(JCR_CONTENT);
+        resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+        resource.setProperty("jcr:lastModified", Calendar.getInstance());
+        resource.setProperty("jcr:encoding", "UTF-8");
+        resource.setProperty("jcr:mimeType", "text/plain");
+        resource.setProperty(binaryProperty(JCR_DATA,
+                "the quick brown fox jumps over the lazy cat."));
+        root.commit();
+        assertQuery(sqlDog, new ArrayList<String>());
+        assertQuery(sqlCat, ImmutableList.of("/myFile"));
+
+    }
+
+    @Test
+    public void testChildNodeWithOr() throws Exception {
+        Tree file = root.getTree("/").addChild("myFile");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+        Tree resource = file.addChild(JCR_CONTENT);
+        resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+        resource.setProperty("jcr:lastModified", Calendar.getInstance());
+        resource.setProperty("jcr:encoding", "UTF-8");
+        resource.setProperty("jcr:mimeType", "text/plain");
+        resource.setProperty(binaryProperty(JCR_DATA,
+                "the quick brown fox jumps over the lazy dog."));
+
+        resource.setProperty("jcr:title", "title");
+        resource.setProperty("jcr:description", "description");
+
+        root.commit();
+
+        String matchContentSimple = "//element(*, nt:file)[(jcr:contains(jcr:content, 'dog'))]";
+        assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFile"));
+
+        String matchContent = " //element(*, nt:file)[(jcr:contains(jcr:content, 'dog') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
+        assertQuery(matchContent, "xpath", ImmutableList.of("/myFile"));
+
+        String matchTitle = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'title') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
+        assertQuery(matchTitle, "xpath", ImmutableList.of("/myFile"));
+
+        String matchDesc = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'description'))]";
+        assertQuery(matchDesc, "xpath", ImmutableList.of("/myFile"));
+
+        String matchNone = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
+        assertQuery(matchNone, "xpath", new ArrayList<String>());
+    }
+
+    @Test
+    public void testChildNodeWithOrComposite() throws Exception {
+
+        Tree folder = root.getTree("/").addChild("myFolder");
+        folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+        Tree file = folder.addChild("myFile");
+        file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+        file.setProperty("jcr:title", "title");
+        file.setProperty("jcr:description", "description");
+
+        Tree resource = file.addChild(JCR_CONTENT);
+        resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+        resource.setProperty("jcr:lastModified", Calendar.getInstance());
+        resource.setProperty("jcr:encoding", "UTF-8");
+        resource.setProperty("jcr:mimeType", "text/plain");
+        resource.setProperty(binaryProperty(JCR_DATA,
+                "the quick brown fox jumps over the lazy dog."));
+
+        root.commit();
+
+        String matchContentSimple = "//element(*, nt:folder)[(jcr:contains(myFile, 'dog'))]";
+        assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFolder"));
+
+        String matchContent = " //element(*, nt:folder)[(jcr:contains(myFile, 'dog') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
+        assertQuery(matchContent, "xpath", ImmutableList.of("/myFolder"));
+
+        String matchTitle = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'title') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
+        assertQuery(matchTitle, "xpath", ImmutableList.of("/myFolder"));
+
+        String matchDesc = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'description'))]";
+        assertQuery(matchDesc, "xpath", ImmutableList.of("/myFolder"));
+
+        String matchNone = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
+        assertQuery(matchNone, "xpath", new ArrayList<String>());
+    }
+
+}

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

Propchange: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

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=1516744&r1=1516743&r2=1516744&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 Aug 23 08:30:55 2013
@@ -68,7 +68,7 @@ public class LuceneIndexTest {
 
         NodeState indexed = HOOK.processCommit(before, after);
 
-        QueryIndex queryIndex = new LuceneIndex(analyzer);
+        QueryIndex queryIndex = new LuceneIndex(analyzer, null);
         FilterImpl filter = createFilter(NT_BASE);
         filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
@@ -94,7 +94,7 @@ public class LuceneIndexTest {
 
         NodeState indexed = HOOK.processCommit(before, after);
 
-        QueryIndex queryIndex = new LuceneIndex(analyzer);
+        QueryIndex queryIndex = new LuceneIndex(analyzer, null);
         FilterImpl filter = createFilter(NT_BASE);
         // filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
@@ -125,7 +125,7 @@ public class LuceneIndexTest {
 
         NodeState indexed = HOOK.processCommit(before, after);
 
-        QueryIndex queryIndex = new LuceneIndex(analyzer);
+        QueryIndex queryIndex = new LuceneIndex(analyzer, null);
         FilterImpl filter = createFilter(NT_BASE);
         // filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,