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 2016/07/25 12:05:38 UTC

svn commit: r1753981 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/property/ main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ main/java/org/apache/jackrabbit/oak/plugins/index/reference/ ...

Author: alexparvulescu
Date: Mon Jul 25 12:05:37 2016
New Revision: 1753981

URL: http://svn.apache.org/viewvc?rev=1753981&view=rev
Log:
OAK-3403 Multiplexing store support in Property indexes


Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/FilteringIndexStoreStrategy.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/MultiplexersTest.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/reference/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategyTest.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java?rev=1753981&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java Mon Jul 25 12:05:37 2016
@@ -0,0 +1,154 @@
+/*
+ * 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.property;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.FilteringIndexStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+
+import com.google.common.base.Predicate;
+
+public class Multiplexers {
+
+    static boolean RO_PRIVATE_UNIQUE_INDEX;
+
+    static {
+        String ro = System.getProperty(
+                "oak.multiplexing.readOnlyPrivateUniqueIndex", "true");
+        RO_PRIVATE_UNIQUE_INDEX = Boolean.parseBoolean(ro);
+    }
+
+    /** Index storage strategy */
+    private static final IndexStoreStrategy UNIQUE = new UniqueEntryStoreStrategy(
+            INDEX_CONTENT_NODE_NAME);
+
+    /** Index storage strategy */
+    private static final IndexStoreStrategy MIRROR = new ContentMirrorStoreStrategy(
+            INDEX_CONTENT_NODE_NAME);
+
+    public static Set<IndexStoreStrategy> getStrategies(boolean unique,
+            MountInfoProvider mountInfoProvider, NodeBuilder definition,
+            String defaultName) {
+        Iterable<String> children = definition.getChildNodeNames();
+        return getStrategies(unique, mountInfoProvider, children, defaultName);
+    }
+
+    public static Set<IndexStoreStrategy> getStrategies(boolean unique,
+            MountInfoProvider mountInfoProvider, NodeState definition,
+            String defaultName) {
+        Iterable<String> children = definition.getChildNodeNames();
+        return getStrategies(unique, mountInfoProvider, children, defaultName);
+    }
+
+    private static Set<IndexStoreStrategy> getStrategies(boolean unique,
+            MountInfoProvider mountInfoProvider, Iterable<String> children,
+            String defaultName) {
+        if (mountInfoProvider.hasNonDefaultMounts()) {
+            Set<String> names = new HashSet<String>();
+            // TODO should this be collected from the index def?
+            for (String name : children) {
+                if (isIndexStorageNode(name, defaultName)) {
+                    names.add(name);
+                }
+            }
+            names.remove(defaultName);
+            Set<IndexStoreStrategy> strategies = new HashSet<IndexStoreStrategy>();
+            for (Mount m : mountInfoProvider.getNonDefaultMounts()) {
+                String n = getNodeForMount(m, defaultName);
+                names.remove(n);
+                strategies.add(newStrategy(unique, false, n, m));
+            }
+
+            Mount defMount = mountInfoProvider.getDefaultMount();
+            // TODO what to do with non-default names that are not covered by
+            // the mount?
+            for (String n : names) {
+                strategies.add(newStrategy(unique, true, n, defMount));
+            }
+            // default mount
+            strategies.add(newStrategy(unique, true, defaultName, defMount));
+            return strategies;
+        } else {
+            return unique ? newHashSet(UNIQUE) : newHashSet(MIRROR);
+        }
+    }
+
+    private static IndexStoreStrategy newStrategy(boolean unique,
+            boolean defaultMount, String name, Mount m) {
+        Predicate<String> filter = newFilter(m);
+        boolean readOnly = unique && !m.isDefault() && RO_PRIVATE_UNIQUE_INDEX;
+        return unique ? new FilteringIndexStoreStrategy(
+                new UniqueEntryStoreStrategy(name), filter, readOnly)
+                : new FilteringIndexStoreStrategy(
+                        new ContentMirrorStoreStrategy(name), filter);
+    }
+
+    private static Predicate<String> newFilter(final Mount m) {
+        return new Predicate<String>() {
+
+            @Override
+            public boolean apply(String p) {
+                return m.isMounted(p);
+            }
+        };
+    }
+
+    private static boolean isIndexStorageNode(String name, String defaultName) {
+        return NodeStateUtils.isHidden(name)
+                && (name.equals(defaultName) || name
+                        .endsWith(asSuffix(defaultName)));
+    }
+
+    public static String getIndexNodeName(MountInfoProvider mountInfoProvider,
+            String path, String defaultName) {
+        Mount mount = mountInfoProvider.getMountByPath(path);
+        return getNodeForMount(mount, defaultName);
+    }
+
+    public static String getNodeForMount(Mount mount, String defaultName) {
+        if (mount.isDefault()) {
+            return defaultName;
+        }
+        return ":" + mount.getPathFragmentName() + asSuffix(defaultName);
+    }
+
+    private static String asSuffix(String name) {
+        return "-" + stripStartingColon(name);
+    }
+
+    private static String stripStartingColon(String name) {
+        if (name.startsWith(":")) {
+            return name.substring(1);
+        }
+        return name;
+    }
+
+}

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

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java Mon Jul 25 12:05:37 2016
@@ -28,6 +28,7 @@ import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
@@ -98,11 +99,17 @@ class PropertyIndex implements QueryInde
 
     private static final Logger LOG = LoggerFactory.getLogger(PropertyIndex.class);
 
+    private final MountInfoProvider mountInfoProvider;
+
     /**
      * Cached property index plan
      */
     private PropertyIndexPlan plan;
 
+    PropertyIndex(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+    }
+
     static Set<String> encode(PropertyValue value) {
         if (value == null) {
             return null;
@@ -135,13 +142,14 @@ class PropertyIndex implements QueryInde
         if (plan != null && plan.getFilter().toString().equals(filter.toString())) {
             return plan;
         } else {
-            plan = createPlan(root, filter);
+            plan = createPlan(root, filter, mountInfoProvider);
             this.plan = plan;
             return plan;
         }
     }
 
-    private static PropertyIndexPlan createPlan(NodeState root, Filter filter) {
+    private static PropertyIndexPlan createPlan(NodeState root, Filter filter,
+                                                MountInfoProvider mountInfoProvider) {
         PropertyIndexPlan bestPlan = null;
 
         // TODO support indexes on a path
@@ -152,7 +160,7 @@ class PropertyIndex implements QueryInde
             if (PROPERTY.equals(definition.getString(TYPE_PROPERTY_NAME))
                     && definition.hasChildNode(INDEX_CONTENT_NODE_NAME)) {
                 PropertyIndexPlan plan = new PropertyIndexPlan(
-                        entry.getName(), root, definition, filter);
+                        entry.getName(), root, definition, filter, mountInfoProvider);
                 if (plan.getCost() != Double.POSITIVE_INFINITY) {
                     LOG.debug("property cost for {} is {}",
                             plan.getName(), plan.getCost());

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java Mon Jul 25 12:05:37 2016
@@ -41,11 +41,10 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
 import org.apache.jackrabbit.oak.plugins.index.PathFilter;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -60,14 +59,6 @@ import com.google.common.base.Predicate;
  */
 class PropertyIndexEditor implements IndexEditor {
 
-    /** Index storage strategy */
-    private static final IndexStoreStrategy MIRROR =
-            new ContentMirrorStoreStrategy();
-
-    /** Index storage strategy */
-    private static final IndexStoreStrategy UNIQUE =
-            new UniqueEntryStoreStrategy();
-
     /** Parent editor, or {@code null} if this is the root editor. */
     private final PropertyIndexEditor parent;
 
@@ -115,8 +106,10 @@ class PropertyIndexEditor implements Ind
 
     private final PathFilter.Result pathFilterResult;
 
+    private final MountInfoProvider mountInfoProvider;
+
     public PropertyIndexEditor(NodeBuilder definition, NodeState root,
-            IndexUpdateCallback updateCallback) {
+                               IndexUpdateCallback updateCallback, MountInfoProvider mountInfoProvider) {
         this.parent = null;
         this.name = null;
         this.path = "/";
@@ -152,6 +145,7 @@ class PropertyIndexEditor implements Ind
             this.keysToCheckForUniqueness = null;
         }
         this.updateCallback = updateCallback;
+        this.mountInfoProvider = mountInfoProvider;
     }
     
     PropertyIndexEditor(PropertyIndexEditor parent, String name, PathFilter.Result pathFilterResult) {
@@ -166,6 +160,7 @@ class PropertyIndexEditor implements Ind
         this.updateCallback = parent.updateCallback;
         this.pathFilter = parent.pathFilter;
         this.pathFilterResult = pathFilterResult;
+        this.mountInfoProvider = parent.mountInfoProvider;
     }
     
     /**
@@ -221,8 +216,9 @@ class PropertyIndexEditor implements Ind
         return keys;
     }
 
-    IndexStoreStrategy getStrategy(boolean unique) {
-        return unique ? UNIQUE : MIRROR;
+    Set<IndexStoreStrategy> getStrategies(boolean unique) {
+        return Multiplexers.getStrategies(unique, mountInfoProvider,
+                definition, INDEX_CONTENT_NODE_NAME);
     }
 
     @Override
@@ -285,15 +281,18 @@ class PropertyIndexEditor implements Ind
 
             if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()) {
                 updateCallback.indexUpdate();
-                NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME);
                 String properties = definition.getString(PROPERTY_NAMES);
                 boolean uniqueIndex = keysToCheckForUniqueness != null;
-                if (uniqueIndex) {
-                    keysToCheckForUniqueness.addAll(
-                            getExistingKeys(afterKeys, index));
+                for (IndexStoreStrategy strategy : getStrategies(uniqueIndex)) {
+                    NodeBuilder index = definition.child(strategy
+                            .getIndexNodeName());
+                    if (uniqueIndex) {
+                        keysToCheckForUniqueness.addAll(getExistingKeys(
+                                afterKeys, index, strategy));
+                    }
+                    strategy.update(index, getPath(), properties, definition,
+                            beforeKeys, afterKeys);
                 }
-                getStrategy(uniqueIndex).update(
-                        index, getPath(), properties, definition, beforeKeys, afterKeys);
             }
         }
 
@@ -328,11 +327,11 @@ class PropertyIndexEditor implements Ind
      * 
      * @param keys the keys
      * @param index the index
+     * @param s the index store strategy
      * @return the set of keys that already exist in this unique index
      */
-    private Set<String> getExistingKeys(Set<String> keys, NodeBuilder index) {
+    private Set<String> getExistingKeys(Set<String> keys, NodeBuilder index, IndexStoreStrategy s) {
         Set<String> existing = null;
-        IndexStoreStrategy s = getStrategy(true);
         for (String key : keys) {
             if (s.exists(index, key)) {
                 if (existing == null) {
@@ -346,7 +345,7 @@ class PropertyIndexEditor implements Ind
         }
         return existing;
     }
-        
+
     /**
      * From a set of keys, get the first that has multiple entries, if any.
      * 
@@ -355,10 +354,13 @@ class PropertyIndexEditor implements Ind
      * @return the first duplicate, or null if none was found
      */
     private String getFirstDuplicate(Set<String> keys, NodeState indexMeta) {
-        IndexStoreStrategy s = getStrategy(true);
         for (String key : keys) {
-            if (s.count(root, indexMeta, singleton(key), 2) > 1) {
-                return key;
+            long count = 0;
+            for (IndexStoreStrategy s : getStrategies(true)) {
+                count += s.count(root, indexMeta, singleton(key), 2);
+                if (count > 1) {
+                    return key;
+                }
             }
         }
         return null;

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditorProvider.java Mon Jul 25 12:05:37 2016
@@ -20,11 +20,14 @@ import javax.annotation.Nonnull;
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -42,13 +45,20 @@ public class PropertyIndexEditorProvider
 
     public static final String TYPE = "property";
 
+    @Reference
+    private MountInfoProvider mountInfoProvider = Mounts.defaultMountInfoProvider();
+
     @Override
     public Editor getIndexEditor(
             @Nonnull String type, @Nonnull NodeBuilder definition, @Nonnull NodeState root, @Nonnull IndexUpdateCallback callback) {
         if (TYPE.equals(type)) {
-            return new PropertyIndexEditor(definition, root, callback);
+            return new PropertyIndexEditor(definition, root, callback, mountInfoProvider);
         }
         return null;
     }
 
+    public PropertyIndexEditorProvider with(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+        return this;
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java Mon Jul 25 12:05:37 2016
@@ -25,6 +25,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider.TYPE;
 import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode;
 
+import java.util.List;
 import java.util.Set;
 
 import javax.annotation.CheckForNull;
@@ -35,13 +36,16 @@ 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.IndexConstants;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
 /**
  * Is responsible for querying the property index content.
  * <br>
@@ -68,18 +72,17 @@ public class PropertyIndexLookup {
      */
     static final int MAX_COST = 100;
 
-    /** Index storage strategy */
-    private static final IndexStoreStrategy MIRROR =
-            new ContentMirrorStoreStrategy();
-
-    /** Index storage strategy */
-    private static final IndexStoreStrategy UNIQUE =
-            new UniqueEntryStoreStrategy();
-
     private final NodeState root;
 
+    private final MountInfoProvider mountInfoProvider;
+
     public PropertyIndexLookup(NodeState root) {
+        this(root, Mounts.defaultMountInfoProvider());
+    }
+
+    public PropertyIndexLookup(NodeState root, MountInfoProvider mountInfoProvider) {
         this.root = root;
+        this.mountInfoProvider = mountInfoProvider;
     }
 
     /**
@@ -107,19 +110,25 @@ public class PropertyIndexLookup {
         return false;
     }
 
-    public Iterable<String> query(Filter filter, String propertyName, PropertyValue value) {
+    public Iterable<String> query(Filter filter, String propertyName,
+            PropertyValue value) {
         NodeState indexMeta = getIndexNode(root, propertyName, filter);
         if (indexMeta == null) {
             throw new IllegalArgumentException("No index for " + propertyName);
         }
-        return getStrategy(indexMeta).query(filter, propertyName, indexMeta, encode(value));
+        List<Iterable<String>> iterables = Lists.newArrayList();
+        for (IndexStoreStrategy s : getStrategies(indexMeta)) {
+            iterables.add(s.query(filter, propertyName, indexMeta,
+                    encode(value)));
+        }
+        return Iterables.concat(iterables);
     }
 
-    IndexStoreStrategy getStrategy(NodeState indexMeta) {
-        if (indexMeta.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
-            return UNIQUE;
-        }
-        return MIRROR;
+    Set<IndexStoreStrategy> getStrategies(NodeState definition) {
+        boolean unique = definition
+                .getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME);
+        return Multiplexers.getStrategies(unique, mountInfoProvider,
+                definition, INDEX_CONTENT_NODE_NAME);
     }
 
     public double getCost(Filter filter, String propertyName, PropertyValue value) {
@@ -127,8 +136,12 @@ public class PropertyIndexLookup {
         if (indexMeta == null) {
             return Double.POSITIVE_INFINITY;
         }
-        return COST_OVERHEAD +
-                getStrategy(indexMeta).count(filter, root, indexMeta, encode(value), MAX_COST);
+        Set<IndexStoreStrategy> strategies = getStrategies(indexMeta);
+        double cost = strategies.isEmpty() ? MAX_COST : COST_OVERHEAD;
+        for (IndexStoreStrategy s : strategies) {
+            cost += s.count(filter, root, indexMeta, encode(value), MAX_COST);
+        }
+        return cost;
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexPlan.java Mon Jul 25 12:05:37 2016
@@ -22,25 +22,30 @@ import static com.google.common.collect.
 import static com.google.common.collect.Sets.newLinkedHashSet;
 import static java.util.Collections.emptySet;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.PROPERTY_NAMES;
-import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.UNIQUE_PROPERTY_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.property.PropertyIndex.encode;
 
+import java.util.List;
 import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.PathFilter;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Cursors;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
 /**
  * Plan for querying a given property index using a given filter.
  */
@@ -56,21 +61,13 @@ public class PropertyIndexPlan {
      */
     static final int MAX_COST = 100;
 
-    /** Index storage strategy */
-    private static final IndexStoreStrategy MIRROR =
-            new ContentMirrorStoreStrategy();
-
-    /** Index storage strategy */
-    private static final IndexStoreStrategy UNIQUE =
-            new UniqueEntryStoreStrategy();
-
     private final NodeState definition;
 
     private final String name;
 
     private final Set<String> properties;
 
-    private final IndexStoreStrategy strategy;
+    private final Set<IndexStoreStrategy> strategies;
 
     private final Filter filter;
 
@@ -86,18 +83,18 @@ public class PropertyIndexPlan {
 
     private final PathFilter pathFilter;
 
-    PropertyIndexPlan(String name, NodeState root, NodeState definition, Filter filter) {
+    PropertyIndexPlan(String name, NodeState root, NodeState definition,
+                      Filter filter){
+        this(name, root, definition, filter, Mounts.defaultMountInfoProvider());
+    }
+
+    PropertyIndexPlan(String name, NodeState root, NodeState definition,
+                      Filter filter, MountInfoProvider mountInfoProvider) {
         this.name = name;
         this.definition = definition;
         this.properties = newHashSet(definition.getNames(PROPERTY_NAMES));
         pathFilter = PathFilter.from(definition.builder());
-
-        if (definition.getBoolean(UNIQUE_PROPERTY_NAME)) {
-            this.strategy = UNIQUE;
-        } else {
-            this.strategy = MIRROR;
-        }
-
+        this.strategies = getStrategies(definition, mountInfoProvider);
         this.filter = filter;
 
         Iterable<String> types = definition.getNames(DECLARING_NODE_TYPES);
@@ -143,7 +140,11 @@ public class PropertyIndexPlan {
                         continue;
                     }
                     Set<String> values = getValues(restriction);
-                    double cost = strategy.count(filter, root, definition, values, MAX_COST);
+                    double cost = strategies.isEmpty() ? MAX_COST : 0;
+                    for (IndexStoreStrategy strategy : strategies) {
+                        cost += strategy.count(filter, root, definition,
+                                values, MAX_COST);
+                    }
                     if (cost < bestCost) {
                         bestDepth = depth;
                         bestValues = values;
@@ -188,8 +189,11 @@ public class PropertyIndexPlan {
 
     Cursor execute() {
         QueryEngineSettings settings = filter.getQueryEngineSettings();
-        Cursor cursor = Cursors.newPathCursor(
-                strategy.query(filter, name, definition, values),
+        List<Iterable<String>> iterables = Lists.newArrayList();
+        for (IndexStoreStrategy s : strategies) {
+            iterables.add(s.query(filter, name, definition, values));
+        }
+        Cursor cursor = Cursors.newPathCursor(Iterables.concat(iterables),
                 settings);
         if (depth > 1) {
             cursor = Cursors.newAncestorCursor(cursor, depth - 1, settings);
@@ -201,6 +205,14 @@ public class PropertyIndexPlan {
         return filter;
     }
 
+    Set<IndexStoreStrategy> getStrategies(NodeState definition,
+            MountInfoProvider mountInfoProvider) {
+        boolean unique = definition
+                .getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME);
+        return Multiplexers.getStrategies(unique, mountInfoProvider,
+                definition, INDEX_CONTENT_NODE_NAME);
+    }
+
     //------------------------------------------------------------< Object >--
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexProvider.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexProvider.java Mon Jul 25 12:05:37 2016
@@ -21,7 +21,10 @@ import java.util.List;
 import javax.annotation.Nonnull;
 
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -40,8 +43,16 @@ import com.google.common.collect.Immutab
 @Service(QueryIndexProvider.class)
 public class PropertyIndexProvider implements QueryIndexProvider {
 
+    @Reference
+    private MountInfoProvider mountInfoProvider = Mounts.defaultMountInfoProvider();
+
     @Override @Nonnull
     public List<QueryIndex> getQueryIndexes(NodeState state) {
-        return ImmutableList.<QueryIndex>of(new PropertyIndex());
+        return ImmutableList.<QueryIndex>of(new PropertyIndex(mountInfoProvider));
+    }
+
+    public PropertyIndexProvider with(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+        return this;
     }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java Mon Jul 25 12:05:37 2016
@@ -80,6 +80,16 @@ public class ContentMirrorStoreStrategy
      */
     public static final int TRAVERSING_WARN = Integer.getInteger("oak.traversing.warn", 10000);
 
+    private final String indexName;
+
+    public ContentMirrorStoreStrategy() {
+        this(INDEX_CONTENT_NODE_NAME);
+    }
+
+    public ContentMirrorStoreStrategy(String indexName) {
+        this.indexName = indexName;
+    }
+
     @Override
     public void update(
             NodeBuilder index, String path,
@@ -163,27 +173,22 @@ public class ContentMirrorStoreStrategy
     }
 
     @Override
-    public Iterable<String> query(final Filter filter, final String indexName,
+    public Iterable<String> query(final Filter filter, final String name,
             final NodeState indexMeta, final Iterable<String> values) {
-        return query(filter, indexName, indexMeta, INDEX_CONTENT_NODE_NAME, values);
+        return query(filter, name, indexMeta, this.indexName, values);
     }
 
     @Override
     public long count(NodeState root, NodeState indexMeta, Set<String> values, int max) {
-        return count(root, indexMeta, INDEX_CONTENT_NODE_NAME, values, max);
+        return count(null, root, indexMeta, this.indexName, values, max);
     }
 
     @Override
     public long count(final Filter filter, NodeState root, NodeState indexMeta, Set<String> values, int max) {
-        return count(filter, root, indexMeta, INDEX_CONTENT_NODE_NAME, values, max);
+        return count(filter, root, indexMeta, this.indexName, values, max);
     }
 
-    public long count(NodeState root, NodeState indexMeta, final String indexStorageNodeName,
-            Set<String> values, int max) {
-        return count(null, root, indexMeta, indexStorageNodeName, values, max);
-    }
-
-    public long count(Filter filter, NodeState root, NodeState indexMeta, final String indexStorageNodeName,
+    long count(Filter filter, NodeState root, NodeState indexMeta, final String indexStorageNodeName,
             Set<String> values, int max) {
         NodeState index = indexMeta.getChildNode(indexStorageNodeName);
         long count = -1;
@@ -586,4 +591,8 @@ public class ContentMirrorStoreStrategy
         throw new UnsupportedOperationException();
    }
 
+    @Override
+    public String getIndexNodeName() {
+        return indexName;
+    }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/FilteringIndexStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/FilteringIndexStoreStrategy.java?rev=1753981&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/FilteringIndexStoreStrategy.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/FilteringIndexStoreStrategy.java Mon Jul 25 12:05:37 2016
@@ -0,0 +1,96 @@
+/*
+ * 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.property.strategy;
+
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.base.Predicate;
+
+/**
+ * A delegating IndexStoreStrategy that filters out updates that are not
+ * accepted by the given predicate
+ */
+public class FilteringIndexStoreStrategy implements IndexStoreStrategy {
+
+    private final IndexStoreStrategy strategy;
+    private final Predicate<String> filter;
+    private final boolean readOnly;
+
+    public FilteringIndexStoreStrategy(IndexStoreStrategy strategy,
+            Predicate<String> filter) {
+        this(strategy, filter, false);
+    }
+
+    public FilteringIndexStoreStrategy(IndexStoreStrategy strategy,
+            Predicate<String> filter, boolean readOnly) {
+        this.strategy = strategy;
+        this.filter = filter;
+        this.readOnly = readOnly;
+    }
+
+    @Override
+    public void update(NodeBuilder index, String path, String indexName,
+            NodeBuilder indexMeta, Set<String> beforeKeys, Set<String> afterKeys)
+            throws CommitFailedException {
+        if (filter.apply(path)) {
+            if (readOnly) {
+                throw new CommitFailedException(
+                        CommitFailedException.UNSUPPORTED, 0,
+                        "Unsupported commit to a read-only store!",
+                        new Throwable("Commit path: " + path));
+            }
+            strategy.update(index, path, indexName, indexMeta, beforeKeys,
+                    afterKeys);
+        }
+    }
+
+    @Override
+    public boolean exists(NodeBuilder index, String key) {
+        return strategy.exists(index, key);
+    }
+
+    @Override
+    public Iterable<String> query(Filter filter, String indexName,
+            NodeState indexMeta, Iterable<String> values) {
+        return strategy.query(filter, indexName, indexMeta, values);
+    }
+
+    @Override
+    public long count(NodeState root, NodeState indexMeta, Set<String> values,
+            int max) {
+        return strategy.count(root, indexMeta, values, max);
+    }
+
+    @Override
+    public long count(Filter filter, NodeState root, NodeState indexMeta,
+            Set<String> values, int max) {
+        return strategy.count(filter, root, indexMeta, values, max);
+    }
+
+    @Override
+    public String getIndexNodeName() {
+        return strategy.getIndexNodeName();
+    }
+
+}

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

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java Mon Jul 25 12:05:37 2016
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.util.Set;
 
+import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -41,7 +42,7 @@ public interface IndexStoreStrategy {
     void update(
         NodeBuilder index, String path,
         String indexName, NodeBuilder indexMeta,
-        Set<String> beforeKeys, Set<String> afterKeys);
+        Set<String> beforeKeys, Set<String> afterKeys) throws CommitFailedException;
 
     /**
      * Check whether an entry for the given key exists.
@@ -88,4 +89,6 @@ public interface IndexStoreStrategy {
      */
     long count(Filter filter, NodeState root, NodeState indexMeta, Set<String> values, int max);
 
+    String getIndexNodeName();
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java Mon Jul 25 12:05:37 2016
@@ -47,6 +47,16 @@ public class UniqueEntryStoreStrategy im
 
     static final Logger LOG = LoggerFactory.getLogger(UniqueEntryStoreStrategy.class);
 
+    private final String indexName;
+
+    public UniqueEntryStoreStrategy() {
+        this(INDEX_CONTENT_NODE_NAME);
+    }
+
+    public UniqueEntryStoreStrategy(String indexName) {
+        this.indexName = indexName;
+    }
+
     @Override
     public void update(
             NodeBuilder index, String path,
@@ -108,7 +118,7 @@ public class UniqueEntryStoreStrategy im
     @Override
     public Iterable<String> query(final Filter filter, final String indexName, 
             final NodeState indexMeta, final Iterable<String> values) {
-        final NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+        final NodeState index = indexMeta.getChildNode(getIndexNodeName());
         return new Iterable<String>() {
             @Override
             public Iterator<String> iterator() {
@@ -157,7 +167,7 @@ public class UniqueEntryStoreStrategy im
 
     @Override
     public long count(NodeState root, NodeState indexMeta, Set<String> values, int max) {
-        NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+        NodeState index = indexMeta.getChildNode(getIndexNodeName());
         long count = 0;
         if (values == null) {
             PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);
@@ -193,5 +203,9 @@ public class UniqueEntryStoreStrategy im
     public long count(final Filter filter, NodeState root, NodeState indexMeta, Set<String> values, int max) {
         return count(root, indexMeta, values, max);
     }
-    
+
+    @Override
+    public String getIndexNodeName() {
+        return indexName;
+    }
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java Mon Jul 25 12:05:37 2016
@@ -42,9 +42,11 @@ import com.google.common.collect.Sets;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.Multiplexers;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -54,8 +56,6 @@ import org.apache.jackrabbit.oak.spi.sta
  */
 class ReferenceEditor extends DefaultEditor implements IndexEditor {
 
-    private static final ContentMirrorStoreStrategy STORE = new ContentMirrorStoreStrategy();
-
     /** Parent editor, or {@code null} if this is the root editor. */
     private final ReferenceEditor parent;
 
@@ -101,13 +101,15 @@ class ReferenceEditor extends DefaultEdi
      */
     private final Set<String> newIds;
 
+    private final MountInfoProvider mountInfoProvider;
+
     /**
      * flag marking a reindex, case in which we don't need to keep track of the
      * newIds set
      */
     private boolean isReindex;
 
-    public ReferenceEditor(NodeBuilder definition, NodeState root) {
+    public ReferenceEditor(NodeBuilder definition, NodeState root,MountInfoProvider mountInfoProvider) {
         this.parent = null;
         this.name = null;
         this.path = "/";
@@ -119,6 +121,7 @@ class ReferenceEditor extends DefaultEdi
         this.rmWeakRefs = newHashMap();
         this.rmIds = newHashSet();
         this.newIds = newHashSet();
+        this.mountInfoProvider = mountInfoProvider;
     }
 
     private ReferenceEditor(ReferenceEditor parent, String name) {
@@ -134,6 +137,7 @@ class ReferenceEditor extends DefaultEdi
         this.rmIds = parent.rmIds;
         this.newIds = parent.newIds;
         this.isReindex = parent.isReindex;
+        this.mountInfoProvider = parent.mountInfoProvider;
     }
 
     /**
@@ -158,6 +162,8 @@ class ReferenceEditor extends DefaultEdi
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
         if (parent == null) {
+            Set<IndexStoreStrategy> refStores = getStrategies(false, REF_NAME);
+            Set<IndexStoreStrategy> weakRefStores = getStrategies(false, WEAK_REF_NAME);
             // update references
             for (Entry<String, Set<String>> ref : rmRefs.entrySet()) {
                 String uuid = ref.getKey();
@@ -166,7 +172,7 @@ class ReferenceEditor extends DefaultEdi
                 if (newRefs.containsKey(uuid)) {
                     add = newRefs.remove(uuid);
                 }
-                update(definition, REF_NAME, uuid, add, rm);
+                update(refStores, definition, REF_NAME, uuid, add, rm);
             }
             for (Entry<String, Set<String>> ref : newRefs.entrySet()) {
                 String uuid = ref.getKey();
@@ -175,10 +181,10 @@ class ReferenceEditor extends DefaultEdi
                 }
                 Set<String> add = ref.getValue();
                 Set<String> rm = emptySet();
-                update(definition, REF_NAME, uuid, add, rm);
+                update(refStores, definition, REF_NAME, uuid, add, rm);
             }
 
-            checkReferentialIntegrity(root, definition.getNodeState(),
+            checkReferentialIntegrity(refStores, root, definition.getNodeState(),
                     Sets.difference(rmIds, newIds));
 
             // update weak references
@@ -189,17 +195,22 @@ class ReferenceEditor extends DefaultEdi
                 if (newWeakRefs.containsKey(uuid)) {
                     add = newWeakRefs.remove(uuid);
                 }
-                update(definition, WEAK_REF_NAME, uuid, add, rm);
+                update(weakRefStores, definition, WEAK_REF_NAME, uuid, add, rm);
             }
             for (Entry<String, Set<String>> ref : newWeakRefs.entrySet()) {
                 String uuid = ref.getKey();
                 Set<String> add = ref.getValue();
                 Set<String> rm = emptySet();
-                update(definition, WEAK_REF_NAME, uuid, add, rm);
+                update(weakRefStores, definition, WEAK_REF_NAME, uuid, add, rm);
             }
         }
     }
 
+    Set<IndexStoreStrategy> getStrategies(boolean unique, String index) {
+        return Multiplexers.getStrategies(unique, mountInfoProvider,
+                definition, index);
+    }
+
     @Override
     public void propertyAdded(PropertyState after) {
         propertyChanged(null, after);
@@ -292,35 +303,43 @@ class ReferenceEditor extends DefaultEdi
         }
     }
 
-    private static void update(NodeBuilder child, String name, String key,
-            Set<String> add, Set<String> rm) {
-        NodeBuilder index = child.child(name);
-        Set<String> empty = of();
-        for (String p : rm) {
-            STORE.update(index, p, name, child, of(key), empty);
-        }
-        for (String p : add) {
-            // TODO do we still need to encode the values?
-            STORE.update(index, p, name, child, empty, of(key));
+    private void update(Set<IndexStoreStrategy> refStores,
+            NodeBuilder definition, String name, String key, Set<String> add,
+            Set<String> rm) throws CommitFailedException {
+        for (IndexStoreStrategy store : refStores) {
+            Set<String> empty = of();
+            for (String p : rm) {
+                NodeBuilder index = definition.child(store.getIndexNodeName());
+                store.update(index, p, name, definition, of(key), empty);
+            }
+            for (String p : add) {
+                // TODO do we still need to encode the values?
+                NodeBuilder index = definition.child(store.getIndexNodeName());
+                store.update(index, p, name, definition, empty, of(key));
+            }
         }
     }
 
-    private static boolean hasReferences(NodeState root,
-                                         NodeState definition,
-                                         String name,
-                                         String key) {
+    private static boolean hasReferences(IndexStoreStrategy refStore,
+                                  NodeState root,
+                                  NodeState definition,
+                                  String name,
+                                  String key) {
         return definition.hasChildNode(name)
-                && STORE.count(root, definition, name, of(key), 1) > 0;
+                && refStore.count(root, definition, of(key), 1) > 0;
     }
 
-    private static void checkReferentialIntegrity(NodeState root,
-                                                  NodeState definition,
-                                                  Set<String> idsOfRemovedNodes)
+    private static void checkReferentialIntegrity(Set<IndexStoreStrategy> refStores,
+                                           NodeState root,
+                                           NodeState definition,
+                                           Set<String> idsOfRemovedNodes)
             throws CommitFailedException {
-        for (String id : idsOfRemovedNodes) {
-            if (hasReferences(root, definition, REF_NAME, id)) {
-                throw new CommitFailedException(INTEGRITY, 1,
-                        "Unable to delete referenced node");
+        for (IndexStoreStrategy store : refStores) {
+            for (String id : idsOfRemovedNodes) {
+                if (hasReferences(store, root, definition, REF_NAME, id)) {
+                    throw new CommitFailedException(INTEGRITY, 1,
+                            "Unable to delete referenced node");
+                }
             }
         }
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditorProvider.java Mon Jul 25 12:05:37 2016
@@ -16,17 +16,20 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.reference;
 
-import javax.annotation.Nonnull;
-
 import static org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants.TYPE;
 
+import javax.annotation.Nonnull;
+
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -35,13 +38,21 @@ import org.apache.jackrabbit.oak.spi.sta
 @Property(name = IndexConstants.TYPE_PROPERTY_NAME , value = NodeReferenceConstants.TYPE, propertyPrivate = true)
 public class ReferenceEditorProvider implements IndexEditorProvider {
 
+    @Reference
+    private MountInfoProvider mountInfoProvider = Mounts.defaultMountInfoProvider();
+
     @Override
     public Editor getIndexEditor(@Nonnull String type, @Nonnull NodeBuilder definition,
             @Nonnull NodeState root, @Nonnull IndexUpdateCallback callback) {
         if (TYPE.equals(type)) {
-            return new ReferenceEditor(definition, root);
+            return new ReferenceEditor(definition, root, mountInfoProvider);
         }
         return null;
     }
 
+    public ReferenceEditorProvider with(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+        return this;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java Mon Jul 25 12:05:37 2016
@@ -31,9 +31,14 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.spi.query.Cursors.newPathCursor;
 
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
 
-import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.Multiplexers;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
@@ -43,6 +48,8 @@ import org.apache.jackrabbit.oak.spi.sta
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 
 /**
  * Provides a QueryIndex that does lookups for node references based on a custom
@@ -51,10 +58,18 @@ import com.google.common.collect.Immutab
  */
 class ReferenceIndex implements QueryIndex {
 
-    private static final ContentMirrorStoreStrategy STORE = new ContentMirrorStoreStrategy();
-
     private static final double COST = 1;
 
+    private final MountInfoProvider mountInfoProvider;
+
+    ReferenceIndex() {
+        this(Mounts.defaultMountInfoProvider());
+    }
+
+    ReferenceIndex(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+    }
+
     @Override
     public double getMinimumCost() {
         return COST;
@@ -110,15 +125,19 @@ class ReferenceIndex implements QueryInd
         return newPathCursor(new ArrayList<String>(), filter.getQueryEngineSettings());
     }
 
-    private static Cursor lookup(NodeState root, String uuid,
+    private Cursor lookup(NodeState root, String uuid,
             final String name, String index, Filter filter) {
         NodeState indexRoot = root.getChildNode(INDEX_DEFINITIONS_NAME)
                 .getChildNode(NAME);
         if (!indexRoot.exists()) {
             return newPathCursor(new ArrayList<String>(), filter.getQueryEngineSettings());
         }
-        Iterable<String> paths = STORE.query(new FilterImpl(), index + "("
-                + uuid + ")", indexRoot, index, ImmutableSet.of(uuid));
+        List<Iterable<String>> iterables = Lists.newArrayList();
+        for (IndexStoreStrategy s : getStrategies(indexRoot, mountInfoProvider, index)) {
+            iterables.add(s.query(new FilterImpl(), index + "("
+                    + uuid + ")", indexRoot, ImmutableSet.of(uuid)));
+        }
+        Iterable<String> paths = Iterables.concat(iterables);
 
         if (!"*".equals(name)) {
             paths = filter(paths, new Predicate<String>() {
@@ -137,6 +156,12 @@ class ReferenceIndex implements QueryInd
         return newPathCursor(paths, filter.getQueryEngineSettings());
     }
 
+    private static Set<IndexStoreStrategy> getStrategies(NodeState definition,
+            MountInfoProvider mountInfoProvider, String index) {
+        return Multiplexers.getStrategies(false, mountInfoProvider, definition,
+                index);
+    }
+
     @Override
     public String getPlan(Filter filter, NodeState root) {
         StringBuilder buff = new StringBuilder("reference");

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java Mon Jul 25 12:05:37 2016
@@ -21,7 +21,10 @@ import java.util.List;
 import javax.annotation.Nonnull;
 
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -35,9 +38,17 @@ import com.google.common.collect.Immutab
 @Service(QueryIndexProvider.class)
 public class ReferenceIndexProvider implements QueryIndexProvider {
 
+    @Reference
+    private MountInfoProvider mountInfoProvider = Mounts.defaultMountInfoProvider();
+
     @Override
     @Nonnull
     public List<QueryIndex> getQueryIndexes(NodeState state) {
-        return ImmutableList.<QueryIndex> of(new ReferenceIndex());
+        return ImmutableList.<QueryIndex> of(new ReferenceIndex(mountInfoProvider));
+    }
+
+    public ReferenceIndexProvider with(MountInfoProvider mountInfoProvider) {
+        this.mountInfoProvider = mountInfoProvider;
+        return this;
     }
 }

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/MultiplexersTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/MultiplexersTest.java?rev=1753981&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/MultiplexersTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/MultiplexersTest.java Mon Jul 25 12:05:37 2016
@@ -0,0 +1,62 @@
+/*
+ * 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.property;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.property.Multiplexers.getIndexNodeName;
+import static org.apache.jackrabbit.oak.plugins.index.property.Multiplexers.getNodeForMount;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jackrabbit.oak.plugins.multiplex.SimpleMountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.junit.Test;
+
+public class MultiplexersTest {
+
+    @Test
+    public void defaultSetup() throws Exception {
+        assertEquals(
+                INDEX_CONTENT_NODE_NAME,
+                getIndexNodeName(Mounts.defaultMountInfoProvider(), "/foo",
+                        INDEX_CONTENT_NODE_NAME));
+        assertEquals(INDEX_CONTENT_NODE_NAME,
+                getNodeForMount(Mounts.defaultMount(), INDEX_CONTENT_NODE_NAME));
+    }
+
+    @Test
+    public void customNodeName() throws Exception {
+        MountInfoProvider mip = SimpleMountInfoProvider.newBuilder()
+                .mount("foo", "/a", "/b").build();
+
+        Mount m = mip.getMountByName("foo");
+
+        assertEquals(":index",
+                getIndexNodeName(mip, "/foo", INDEX_CONTENT_NODE_NAME));
+        assertEquals(":index",
+                getNodeForMount(mip.getDefaultMount(), INDEX_CONTENT_NODE_NAME));
+
+        assertEquals(":" + m.getPathFragmentName() + "-index",
+                getIndexNodeName(mip, "/a", INDEX_CONTENT_NODE_NAME));
+        assertEquals(":" + m.getPathFragmentName() + "-index",
+                getNodeForMount(m, INDEX_CONTENT_NODE_NAME));
+    }
+}
\ No newline at end of file

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

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexTest.java Mon Jul 25 12:05:37 2016
@@ -21,6 +21,7 @@ import static org.apache.jackrabbit.JcrC
 import static org.apache.jackrabbit.JcrConstants.NT_BASE;
 import static org.apache.jackrabbit.JcrConstants.NT_FILE;
 import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_PATH;
 import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.createIndexDefinition;
@@ -30,6 +31,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
 import static org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
+import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.getNode;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -43,6 +45,7 @@ import org.apache.jackrabbit.oak.api.Typ
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.plugins.multiplex.SimpleMountInfoProvider;
 import org.apache.jackrabbit.oak.query.NodeStateNodeTypeInfoProvider;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
 import org.apache.jackrabbit.oak.query.ast.NodeTypeInfo;
@@ -53,6 +56,8 @@ import org.apache.jackrabbit.oak.query.i
 import org.apache.jackrabbit.oak.query.index.TraversingIndex;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -746,6 +751,107 @@ public class PropertyIndexTest {
         assertEquals("/oak:index/foo", idxDefn.getString(INDEX_PATH));
     }
 
+    @Test
+    public void singleMount() throws Exception {
+        NodeState root = INITIAL_CONTENT;
+
+        // Add index definition
+        NodeBuilder builder = root.builder();
+        NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo",
+                true, false, ImmutableSet.of("foo"), null);
+        index.setProperty("entryCount", -1);
+        NodeState before = builder.getNodeState();
+
+        // Add some content and process it through the property index hook
+        builder.child("a").setProperty("foo", "abc");
+        builder.child("b").child("x").setProperty("foo", "abc");
+        builder.child("a").child("x").setProperty("foo", "abc");
+        builder.child("m").child("n").setProperty("foo", "abc");
+        builder.child("m").child("n").child("o").setProperty("foo", "abc");
+        builder.child("m").setProperty("foo", "abc");
+
+        NodeState after = builder.getNodeState();
+
+        MountInfoProvider mip = SimpleMountInfoProvider.newBuilder()
+                .mount("foo", "/a", "/m/n")
+                .build();
+
+        Mount fooMount = mip.getMountByName("foo");
+        Mount defMount = mip.getDefaultMount();
+
+        EditorHook hook = new EditorHook(
+                new IndexUpdateProvider(new PropertyIndexEditorProvider().with(mip)));
+
+        NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
+
+        FilterImpl f = createFilter(indexed, NT_BASE);
+
+        // Query the index
+        PropertyIndexLookup lookup = new PropertyIndexLookup(indexed,mip);
+        assertEquals(ImmutableSet.of("a", "b/x", "a/x", "m", "m/n", "m/n/o"), find(lookup, "foo", "abc", f));
+        assertEquals(ImmutableSet.of(), find(lookup, "foo", "ghi", f));
+
+        assertTrue(getNode(indexed, "/oak:index/foo/:index").exists());
+
+        //Separate node for mount
+        assertTrue(getNode(indexed, "/oak:index/foo/"+ getNodeForMount(fooMount)).exists());
+
+        //Index entries for paths in foo mount should go to :oak:foo-index
+        assertTrue(getNode(indexed, pathInIndex(fooMount, "/oak:index/foo", "/a", "abc")).exists());
+        assertTrue(getNode(indexed, pathInIndex(fooMount, "/oak:index/foo", "/a/x", "abc")).exists());
+        assertTrue(getNode(indexed, pathInIndex(fooMount, "/oak:index/foo", "/m/n", "abc")).exists());
+        assertTrue(getNode(indexed, pathInIndex(fooMount, "/oak:index/foo", "/m/n/o", "abc")).exists());
+        assertFalse(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/a", "abc")).exists());
+        assertFalse(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/a/x", "abc")).exists());
+        assertFalse(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/m/n", "abc")).exists());
+        assertFalse(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/m/n/o", "abc")).exists());
+
+        //All other index entries should go to :index
+        assertTrue(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/b", "abc")).exists());
+        assertTrue(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/b/x", "abc")).exists());
+        assertTrue(getNode(indexed, pathInIndex(defMount, "/oak:index/foo", "/m", "abc")).exists());
+        assertFalse(getNode(indexed, pathInIndex(fooMount, "/oak:index/foo", "/b", "abc")).exists());
+        assertFalse(getNode(indexed, pathInIndex(fooMount, "/oak:index/foo", "/b/x", "abc")).exists());
+
+        //System.out.println(NodeStateUtils.toString(getNode(indexed, "/oak:index/foo")));
+
+    }
+
+    @Test(expected = CommitFailedException.class)
+    public void mountAndUniqueIndexes() throws Exception {
+        NodeState root = INITIAL_CONTENT;
+
+        // Add index definition
+        NodeBuilder builder = root.builder();
+        NodeBuilder index = createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo",
+                true, true, ImmutableSet.of("foo"), null);
+        index.setProperty("entryCount", -1);
+        NodeState before = builder.getNodeState();
+
+        MountInfoProvider mip = SimpleMountInfoProvider.newBuilder()
+                .mount("foo", "/a")
+                .build();
+
+        builder.child("a").setProperty("foo", "abc");
+        builder.child("b").setProperty("foo", Arrays.asList("abc", "def"),
+                Type.STRINGS);
+        NodeState after = builder.getNodeState();
+
+        EditorHook hook = new EditorHook(
+                new IndexUpdateProvider(new PropertyIndexEditorProvider().with(mip)));
+        // should throw
+        hook.processCommit(before, after, CommitInfo.EMPTY);
+    }
+
+    private static String pathInIndex(Mount mount,
+                                      String indexPath, String indexedPath, String indexedValue){
+        return indexPath + "/" + getNodeForMount(mount) + "/" + indexedValue + indexedPath;
+    }
+
+    private static String getNodeForMount(Mount mount) {
+        return Multiplexers.getNodeForMount(mount, INDEX_CONTENT_NODE_NAME);
+    }
+
     private int getResultSize(NodeState indexed, String name, String value){
         FilterImpl f = createFilter(indexed, NT_BASE);
 

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategyTest.java?rev=1753981&r1=1753980&r2=1753981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategyTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategyTest.java Mon Jul 25 12:05:37 2016
@@ -63,7 +63,7 @@ public class ContentMirrorStoreStrategyT
      * </ul>
      */
     @Test
-    public void testIndexPruning() {
+    public void testIndexPruning() throws CommitFailedException {
         IndexStoreStrategy store = new ContentMirrorStoreStrategy();
 
         NodeState root = EMPTY_NODE;

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexTest.java?rev=1753981&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexTest.java Mon Jul 25 12:05:37 2016
@@ -0,0 +1,150 @@
+/*
+ * 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.reference;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.multiplex.SimpleMountInfoProvider;
+import org.apache.jackrabbit.oak.query.NodeStateNodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.query.ast.NodeTypeInfo;
+import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
+import org.apache.jackrabbit.oak.query.ast.Operator;
+import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.junit.Test;
+
+import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+import static org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
+import static org.apache.jackrabbit.oak.spi.query.PropertyValues.newReference;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+public class ReferenceIndexTest {
+
+    @Test
+    public void basicReferenceHandling() throws Exception{
+        NodeState root = INITIAL_CONTENT;
+
+        NodeBuilder builder = root.builder();
+        NodeState before = builder.getNodeState();
+
+        builder.child("a").setProperty(createProperty("foo", "u1", Type.REFERENCE));
+        builder.child("b").setProperty(createProperty("foo", "u1", Type.REFERENCE));
+        builder.child("c").setProperty(createProperty("foo", "u2", Type.WEAKREFERENCE));
+
+        NodeState after = builder.getNodeState();
+        EditorHook hook = new EditorHook(
+                new IndexUpdateProvider(new ReferenceEditorProvider()));
+
+        NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
+        FilterImpl f = createFilter(indexed, NT_BASE);
+        f.restrictProperty("*", Operator.EQUAL, newReference("u1"), PropertyType.REFERENCE);
+
+        assertFilter(f, new ReferenceIndex(), indexed, of("/a", "/b"));
+
+        FilterImpl f2 = createFilter(indexed, NT_BASE);
+        f2.restrictProperty("*", Operator.EQUAL, newReference("u2"), PropertyType.WEAKREFERENCE);
+        assertFilter(f2, new ReferenceIndex(), indexed, of("/c"));
+    }
+
+    @Test
+    public void referenceHandlingWithMounts() throws Exception{
+        NodeState root = INITIAL_CONTENT;
+
+        NodeBuilder builder = root.builder();
+        NodeState before = builder.getNodeState();
+
+        builder.child("a").child("x").setProperty(createProperty("foo", "u1", Type.REFERENCE));
+        builder.child("b").setProperty(createProperty("foo", "u1", Type.REFERENCE));
+        builder.child("c").setProperty(createProperty("foo", "u1", Type.WEAKREFERENCE));
+
+        builder.child("d").setProperty(createProperty("foo", "u2", Type.WEAKREFERENCE));
+        builder.child("a").child("y").setProperty(createProperty("foo", "u1", Type.WEAKREFERENCE));
+
+        NodeState after = builder.getNodeState();
+
+        MountInfoProvider mip = SimpleMountInfoProvider.newBuilder()
+                .mount("foo", "/a")
+                .build();
+
+        EditorHook hook = new EditorHook(
+                new IndexUpdateProvider(new ReferenceEditorProvider().with(mip)));
+
+        ReferenceIndex referenceIndex = new ReferenceIndex(mip);
+
+        NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
+        FilterImpl f = createFilter(indexed, NT_BASE);
+        f.restrictProperty("*", Operator.EQUAL, newReference("u1"), PropertyType.REFERENCE);
+
+        // System.out.println(NodeStateUtils.toString(NodeStateUtils.getNode(indexed, "/oak:index/reference")));
+        assertFilter(f, referenceIndex, indexed, of("/a/x", "/b"));
+
+        FilterImpl f2 = createFilter(indexed, NT_BASE);
+        f2.restrictProperty("*", Operator.EQUAL, newReference("u1"), PropertyType.WEAKREFERENCE);
+        assertFilter(f2, referenceIndex, indexed, of("/c", "/a/y"));
+    }
+
+
+    //TODO Integrity check - Add node with id=1 and add a reference to that and then remove the node
+    //Removal
+
+    @SuppressWarnings("Duplicates")
+    private static FilterImpl createFilter(NodeState root, String nodeTypeName) {
+        NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(root);
+        NodeTypeInfo type = nodeTypes.getNodeTypeInfo(nodeTypeName);
+        SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
+        return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings());
+    }
+
+    private static List<String> assertFilter(Filter filter, QueryIndex queryIndex,
+                                             NodeState indexed, List<String> expected) {
+        Cursor cursor = queryIndex.query(filter, indexed);
+        List<String> paths = newArrayList();
+        while (cursor.hasNext()) {
+            paths.add(cursor.next().getPath());
+        }
+        Collections.sort(paths);
+        for (String p : expected) {
+            assertTrue("Expected path " + p + " not found", paths.contains(p));
+        }
+        assertEquals("Result set size is different \nExpected: " +
+                expected + "\nActual: " + paths, expected.size(), paths.size());
+        return paths;
+    }
+
+}
\ No newline at end of file

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