Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java	(revision 1421762)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java	(working copy)
@@ -34,6 +34,8 @@
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.plugins.index.p2.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.p2.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -52,6 +54,8 @@
  */
 class Property2IndexDiff implements IndexHook {
 
+    private final IndexStoreStrategy store = new ContentMirrorStoreStrategy();
+
     /**
      * The parent (null if this is the root node).
      */
@@ -159,7 +163,7 @@
                 }
             }
             if (!exists) {
-                list.add(new Property2IndexUpdate(getPath(), builder));
+                list.add(new Property2IndexUpdate(getPath(), builder, store));
             }
         }
     }
Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java	(revision 1421762)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java	(working copy)
@@ -25,8 +25,9 @@
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 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.p2.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.p2.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -53,6 +54,8 @@
  */
 public class Property2IndexLookup {
 
+    private final IndexStoreStrategy store = new ContentMirrorStoreStrategy();
+
     private final NodeState root;
 
     public Property2IndexLookup(NodeState root) {
@@ -114,13 +117,7 @@
         NodeState state = getIndexDefinitionNode(name);
         if (state != null && state.getChildNode(":index") != null) {
             state = state.getChildNode(":index");
-            for (String p : Property2Index.encode(value)) {
-                NodeState property = state.getChildNode(p);
-                if (property != null) {
-                    // We have an entry for this value, so use it
-                    getMatchingPaths(property, "", paths);
-                }
-            }
+            paths.addAll(store.find(state, Property2Index.encode(value)));
         } else {
             // No index available, so first check this node for a match
             PropertyState property = root.getProperty(name);
@@ -159,47 +156,18 @@
         return paths;
     }
 
-    private void getMatchingPaths(NodeState state, String path,
-            Set<String> paths) {
-        PropertyState ps = state.getProperty("match");
-        if (ps != null && !ps.isArray() && ps.getValue(Type.BOOLEAN)) {
-            paths.add(path);
-        }
-        for (ChildNodeEntry c : state.getChildNodeEntries()) {
-            String name = c.getName();
-            NodeState childState = c.getNodeState();
-            getMatchingPaths(childState, PathUtils.concat(path, name), paths);
-        }
-    }
-
     public double getCost(String name, PropertyValue value) {
         double cost = 0.0;
         NodeState state = getIndexDefinitionNode(name);
         if (state != null && state.getChildNode(":index") != null) {
             state = state.getChildNode(":index");
-            for (String p : Property2Index.encode(value)) {
-                cost += countMatchingLeaves(state.getChildNode(p));
-            }
+            cost += store.count(state, Property2Index.encode(value));
         } else {
             cost = Double.POSITIVE_INFINITY;
         }
         return cost;
     }
 
-    static int countMatchingLeaves(NodeState state) {
-        if (state == null) {
-            return 0;
-        }
-        int count = 0;
-        if (state.getProperty("match") != null) {
-            count++;
-        }
-        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
-            count += countMatchingLeaves(entry.getNodeState());
-        }
-        return count;
-    }
-
     /**
      * Get the node with the index definition node for the given property.
      * 
Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/strategy/IndexStoreStrategy.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/strategy/IndexStoreStrategy.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/strategy/IndexStoreStrategy.java	(revision 0)
@@ -0,0 +1,37 @@
+/*
+ * 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.p2.strategy;
+
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+public interface IndexStoreStrategy {
+
+    void remove(NodeBuilder index, String key, Iterable<String> values)
+            throws CommitFailedException;
+
+    void insert(NodeBuilder index, String key, boolean unique,
+            Iterable<String> values) throws CommitFailedException;
+
+    Set<String> find(NodeState index, Iterable<String> values);
+
+    int count(NodeState index, Iterable<String> values);
+
+}
Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/strategy/ContentMirrorStoreStrategy.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/strategy/ContentMirrorStoreStrategy.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/strategy/ContentMirrorStoreStrategy.java	(revision 0)
@@ -0,0 +1,151 @@
+/*
+ * 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.p2.strategy;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+public class ContentMirrorStoreStrategy implements IndexStoreStrategy {
+
+    @Override
+    public void remove(NodeBuilder index, String key, Iterable<String> values) {
+        if (!index.hasChildNode(key)) {
+            return;
+        }
+        NodeBuilder child = index.child(key);
+        Queue<NodeBuilder> parentQueue = new LinkedList<NodeBuilder>();
+        for (String rm : values) {
+            if (PathUtils.denotesRoot(rm)) {
+                child.removeProperty("match");
+            } else {
+                NodeBuilder indexEntry = child;
+                Iterator<String> segments = PathUtils.elements(rm).iterator();
+                while (segments.hasNext()) {
+                    String segment = segments.next();
+                    if (segments.hasNext()) {
+                        parentQueue.add(indexEntry);
+                        indexEntry = indexEntry.child(segment);
+                    } else {
+                        // last segment
+                        if (indexEntry.hasChildNode(segment)) {
+                            indexEntry.removeNode(segment);
+                        }
+                    }
+                }
+            }
+        }
+        // prune the index: remove all children that have no children
+        // and no "match" property progressing bottom up
+        while (!parentQueue.isEmpty()) {
+            NodeBuilder node = parentQueue.poll();
+            for (String name : node.getChildNodeNames()) {
+                NodeBuilder segment = node.child(name);
+                if (segment.getChildNodeCount() == 0
+                        && segment.getProperty("match") == null) {
+                    segment.removeNode(name);
+                }
+            }
+        }
+        // finally remove the index node if empty
+        if (child.getChildNodeCount() == 0) {
+            index.removeNode(key);
+        }
+    }
+
+    @Override
+    public void insert(NodeBuilder index, String key, boolean unique,
+            Iterable<String> values) throws CommitFailedException {
+        NodeBuilder child = index.child(key);
+
+        for (String add : values) {
+            NodeBuilder indexEntry = child;
+            Iterator<String> segments = PathUtils.elements(add).iterator();
+            while (segments.hasNext()) {
+                String segment = segments.next();
+                indexEntry = indexEntry.child(segment);
+            }
+            indexEntry.setProperty("match", true);
+        }
+        long matchCount = countMatchingLeaves(child.getNodeState());
+        if (matchCount == 0) {
+            index.removeNode(key);
+        } else if (unique && matchCount > 1) {
+            throw new CommitFailedException("Uniqueness constraint violated");
+        }
+    }
+
+    static int countMatchingLeaves(NodeState state) {
+        if (state == null) {
+            return 0;
+        }
+        int count = 0;
+        if (state.getProperty("match") != null) {
+            count++;
+        }
+        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+            count += countMatchingLeaves(entry.getNodeState());
+        }
+        return count;
+    }
+
+    @Override
+    public Set<String> find(NodeState index, Iterable<String> values) {
+        Set<String> paths = new HashSet<String>();
+        for (String p : values) {
+            NodeState property = index.getChildNode(p);
+            if (property != null) {
+                // We have an entry for this value, so use it
+                getMatchingPaths(property, "", paths);
+            }
+        }
+        return paths;
+    }
+
+    private void getMatchingPaths(NodeState state, String path,
+            Set<String> paths) {
+        PropertyState ps = state.getProperty("match");
+        if (ps != null && !ps.isArray() && ps.getValue(Type.BOOLEAN)) {
+            paths.add(path);
+        }
+        for (ChildNodeEntry c : state.getChildNodeEntries()) {
+            String name = c.getName();
+            NodeState childState = c.getNodeState();
+            getMatchingPaths(childState, PathUtils.concat(path, name), paths);
+        }
+    }
+
+    @Override
+    public int count(NodeState index, Iterable<String> values) {
+        int count = 0;
+        for (String p : values) {
+            count += countMatchingLeaves(index.getChildNode(p));
+        }
+        return count;
+    }
+
+}
Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java	(revision 1421762)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java	(working copy)
@@ -18,11 +18,8 @@
 
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
 
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
 import java.util.Set;
 
 import javax.jcr.PropertyType;
@@ -30,7 +27,7 @@
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.p2.strategy.IndexStoreStrategy;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 
@@ -46,6 +43,8 @@
  */
 class Property2IndexUpdate {
 
+    private final IndexStoreStrategy store;
+
     /**
      * The path of the index definition (where the index data is stored).
      */
@@ -70,9 +69,10 @@
      */
     private final Map<String, Set<String>> remove;
 
-    public Property2IndexUpdate(String path, NodeBuilder node) {
+    public Property2IndexUpdate(String path, NodeBuilder node, IndexStoreStrategy store) {
         this.path = path;
         this.node = node;
+        this.store = store;
         this.insert = Maps.newHashMap();
         this.remove = Maps.newHashMap();
     }
@@ -84,8 +84,10 @@
     /**
      * A property value was added at the given path.
      * 
-     * @param path the path
-     * @param value the value
+     * @param path
+     *            the path
+     * @param value
+     *            the value
      */
     public void insert(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
@@ -95,8 +97,10 @@
     /**
      * A property value was removed at the given path.
      * 
-     * @param path the path
-     * @param value the value
+     * @param path
+     *            the path
+     * @param value
+     *            the value
      */
     public void remove(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
@@ -106,14 +110,15 @@
     private static void putValues(Map<String, Set<String>> map, String path,
             PropertyState value) {
         if (value.getType().tag() != PropertyType.BINARY) {
-            List<String> keys = Property2Index.encode(PropertyValues.create(value));
+            List<String> keys = Property2Index.encode(PropertyValues
+                    .create(value));
             for (String key : keys) {
                 Set<String> paths = map.get(key);
                 if (paths == null) {
                     paths = Sets.newHashSet();
                     map.put(key, paths);
                 }
-                if("".equals(path)){
+                if ("".equals(path)) {
                     path = "/";
                 }
                 paths.add(path);
@@ -132,81 +137,18 @@
     /**
      * Try to apply the changes to the index content (to the ":index" node.
      * 
-     * @throws CommitFailedException if a unique index was violated
+     * @throws CommitFailedException
+     *             if a unique index was violated
      */
     public void apply() throws CommitFailedException {
         boolean unique = node.getProperty("unique") != null
                 && node.getProperty("unique").getValue(Type.BOOLEAN);
         NodeBuilder index = node.child(":index");
-
         for (Map.Entry<String, Set<String>> entry : remove.entrySet()) {
-            String encoded = entry.getKey();
-            Set<String> paths = entry.getValue();
-            if (index.hasChildNode(encoded)) {
-                NodeBuilder child = index.child(encoded);
-                Queue<NodeBuilder> parentQueue = new LinkedList<NodeBuilder>();
-                for (String rm : paths) {
-                    if (PathUtils.denotesRoot(rm)) {
-                        child.removeProperty("match");
-                    } else {
-                        NodeBuilder indexEntry = child;
-                        Iterator<String> segments = PathUtils.elements(rm)
-                                .iterator();
-                        while (segments.hasNext()) {
-                            String segment = segments.next();
-                            if (segments.hasNext()) {
-                                parentQueue.add(indexEntry);
-                                indexEntry = indexEntry.child(segment);
-                            } else {
-                                // last segment
-                                if (indexEntry.hasChildNode(segment)) {
-                                    indexEntry.removeNode(segment);
-                                }
-                            }
-                        }
-                    }
-                }
-                // prune the index: remove all children that have no children
-                // and no "match" property progressing bottom up
-                while (!parentQueue.isEmpty()) {
-                    NodeBuilder node = parentQueue.poll();
-                    for (String name : node.getChildNodeNames()) {
-                        NodeBuilder segment = node.child(name);
-                        if (segment.getChildNodeCount() == 0
-                                && segment.getProperty("match") == null) {
-                            segment.removeNode(name);
-                        }
-                    }
-                }
-                // finally remove the index node if empty
-                if (child.getChildNodeCount() == 0) {
-                    index.removeNode(encoded);
-                }
-            }
+            store.remove(index, entry.getKey(), entry.getValue());
         }
         for (Map.Entry<String, Set<String>> entry : insert.entrySet()) {
-            String encoded = entry.getKey();
-            Set<String> paths = entry.getValue();
-            NodeBuilder child = index.child(encoded);
-            Iterator<String> addIterator = paths.iterator();
-            while (addIterator.hasNext()) {
-                String add = addIterator.next();
-                NodeBuilder indexEntry = child;
-                Iterator<String> segments = PathUtils.elements(add).iterator();
-                while (segments.hasNext()) {
-                    String segment = segments.next();
-                    indexEntry = indexEntry.child(segment);
-                }
-                indexEntry.setProperty("match", true);
-            }
-            long matchCount = Property2IndexLookup.countMatchingLeaves(child
-                    .getNodeState());
-            if (matchCount == 0) {
-                index.removeNode(encoded);
-            } else if (unique && matchCount > 1) {
-                throw new CommitFailedException(
-                        "Uniqueness constraint violated");
-            }
+            store.insert(index, entry.getKey(), unique, entry.getValue());
         }
     }
 
