jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1528405 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property: PropertyIndexEditor.java PropertyIndexLookup.java strategy/UniqueEntryStoreStrategy.java
Date Wed, 02 Oct 2013 10:06:44 GMT
Author: thomasm
Date: Wed Oct  2 10:06:44 2013
New Revision: 1528405

URL: http://svn.apache.org/r1528405
Log:
OAK-1059 Property index: faster unique indexes using new storage strategy (2nd try)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
Modified:
    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/PropertyIndexLookup.java

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=1528405&r1=1528404&r2=1528405&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
Wed Oct  2 10:06:44 2013
@@ -45,6 +45,7 @@ import org.apache.jackrabbit.oak.plugins
 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.strategy.IndexStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -59,9 +60,13 @@ import org.apache.jackrabbit.oak.spi.sta
 class PropertyIndexEditor implements IndexEditor {
 
     /** Index storage strategy */
-    private static final IndexStoreStrategy STORE =
+    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;
 
@@ -79,6 +84,8 @@ class PropertyIndexEditor implements Ind
     private final Set<String> primaryTypes;
 
     private final Set<String> mixinTypes;
+    
+    private final boolean unique;
 
     private final Set<String> keysToCheckForUniqueness;
 
@@ -133,8 +140,10 @@ class PropertyIndexEditor implements Ind
 
         // keep track of modified keys for uniqueness checks
         if (definition.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
+            unique = true;
             this.keysToCheckForUniqueness = newHashSet();
         } else {
+            unique = false;
             this.keysToCheckForUniqueness = null;
         }
     }
@@ -147,6 +156,7 @@ class PropertyIndexEditor implements Ind
         this.propertyNames = parent.propertyNames;
         this.primaryTypes = parent.primaryTypes;
         this.mixinTypes = parent.mixinTypes;
+        this.unique = parent.unique;
         this.keysToCheckForUniqueness = parent.keysToCheckForUniqueness;
     }
 
@@ -205,6 +215,10 @@ class PropertyIndexEditor implements Ind
         }
     }
 
+    private static IndexStoreStrategy getStrategy(boolean unique) {
+        return unique ? UNIQUE : MIRROR;
+    }
+
     @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
@@ -217,9 +231,9 @@ class PropertyIndexEditor implements Ind
 
             if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()) {
                 NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME);
-                STORE.update(index, getPath(), beforeKeys, afterKeys);
 
-                if (keysToCheckForUniqueness != null) {
+                getStrategy(unique).update(index, getPath(), beforeKeys, afterKeys);
+                if (unique) {
                     keysToCheckForUniqueness.addAll(afterKeys);
                 }
             }
@@ -230,11 +244,11 @@ class PropertyIndexEditor implements Ind
             definition.child(INDEX_CONTENT_NODE_NAME);
 
             // check uniqueness constraints when leaving the root
-            if (keysToCheckForUniqueness != null
-                    && !keysToCheckForUniqueness.isEmpty()) {
+            if (unique && !keysToCheckForUniqueness.isEmpty()) {
                 NodeState indexMeta = definition.getNodeState();
+                IndexStoreStrategy s = getStrategy(unique);
                 for (String key : keysToCheckForUniqueness) {
-                    if (STORE.count(indexMeta, singleton(key), 2) > 1) {
+                    if (s.count(indexMeta, singleton(key), 2) > 1) {
                         throw new CommitFailedException(
                                 CONSTRAINT, 30,
                                 "Uniqueness constraint violated for key " + key);

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=1528405&r1=1528404&r2=1528405&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
Wed Oct  2 10:06:44 2013
@@ -34,8 +34,10 @@ import org.apache.jackrabbit.oak.api.Pro
 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.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.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -60,7 +62,13 @@ public class PropertyIndexLookup {
 
     private static final int MAX_COST = 100;
 
-    private final IndexStoreStrategy store = new ContentMirrorStoreStrategy();
+    /** Index storage strategy */
+    private static final IndexStoreStrategy MIRROR =
+            new ContentMirrorStoreStrategy();
+
+    /** Index storage strategy */
+    private static final IndexStoreStrategy UNIQUE =
+            new UniqueEntryStoreStrategy();
 
     private final NodeState root;
 
@@ -99,7 +107,14 @@ public class PropertyIndexLookup {
         if (indexMeta == null) {
             throw new IllegalArgumentException("No index for " + propertyName);
         }
-        return store.query(filter, propertyName, indexMeta, encode(value));
+        return getStrategy(indexMeta).query(filter, propertyName, indexMeta, encode(value));
+    }
+        
+    private static IndexStoreStrategy getStrategy(NodeState indexMeta) {
+        if (indexMeta.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
+            return UNIQUE;
+        }
+        return MIRROR;
     }
 
     public double getCost(Filter filter, String propertyName, PropertyValue value) {
@@ -107,7 +122,7 @@ public class PropertyIndexLookup {
         if (indexMeta == null) {
             return Double.POSITIVE_INFINITY;
         }
-        return store.count(indexMeta, encode(value), MAX_COST);
+        return getStrategy(indexMeta).count(indexMeta, encode(value), MAX_COST);
     }
 
     /**

Added: 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=1528405&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
Wed Oct  2 10:06:44 2013
@@ -0,0 +1,169 @@
+/*
+ * 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 static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.MultiStringPropertyState;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An IndexStoreStrategy implementation that saves the unique node in a single property.<br>
+ * This should reduce the number of nodes in the repository, and speed up access.<br>
+ * <br>
+ * For example for a node that is under {@code /test/node}, the index
+ * structure will be {@code /oak:index/index/@key}:
+ */
+public class UniqueEntryStoreStrategy implements IndexStoreStrategy {
+
+    static final Logger LOG = LoggerFactory.getLogger(UniqueEntryStoreStrategy.class);
+
+    @Override
+    public void update(
+            NodeBuilder index, String path,
+            Set<String> beforeKeys, Set<String> afterKeys) {
+        for (String key : beforeKeys) {
+            remove(index, key, path);
+        }
+        for (String key : afterKeys) {
+            insert(index, key, path);
+        }
+    }
+
+    private static void remove(NodeBuilder index, String key, String value) {
+        NodeBuilder builder = index.getChildNode(key);
+        if (builder.exists()) {
+            // there could be (temporarily) multiple entries
+            // we need to remove the right one
+            PropertyState s = builder.getProperty("entry");
+            if (s.count() == 1) {
+                builder.remove();
+            } else {
+                ArrayList<String> list = new ArrayList<String>();
+                for (int i = 0; i < s.count(); i++) {
+                    String r = s.getValue(Type.STRING, i);
+                    if (!r.equals(value)) {
+                        list.add(r);
+                    }
+                }
+                PropertyState s2 = MultiStringPropertyState.stringProperty("entry", list);
+                builder.setProperty(s2);
+            }
+        }
+    }
+
+    private static void insert(NodeBuilder index, String key, String value) {
+        NodeBuilder k = index.child(key);
+        ArrayList<String> list = new ArrayList<String>();
+        list.add(value);
+        if (k.hasProperty("entry")) {
+            // duplicate key (to detect duplicate entries)
+            // this is just set temporarily,
+            // while trying to add a duplicate entry
+            PropertyState s = k.getProperty("entry");
+            for (int i = 0; i < s.count(); i++) {
+                String r = s.getValue(Type.STRING, i);
+                list.add(r);
+            }
+        }
+        PropertyState s2 = MultiStringPropertyState.stringProperty("entry", list);
+        k.setProperty(s2);
+    }
+
+    @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);
+        return new Iterable<String>() {
+            @Override
+            public Iterator<String> iterator() {
+                if (values == null) {
+                    return new Iterator<String>() {
+                        
+                        Iterator<? extends ChildNodeEntry> it = index.getChildNodeEntries().iterator();
+
+                        @Override
+                        public boolean hasNext() {
+                            return it.hasNext();
+                        }
+
+                        @Override
+                        public String next() {
+                            PropertyState s = it.next().getNodeState().getProperty("entry");
+                            return s.getValue(Type.STRING, 0);
+                        }
+
+                        @Override
+                        public void remove() {
+                            it.remove();
+                        }
+                        
+                    };
+                }
+                ArrayList<String> list = new ArrayList<String>();
+                for (String p : values) {
+                    NodeState key = index.getChildNode(p);
+                    if (key.exists()) {
+                        // we have an entry for this value, so use it
+                        PropertyState s = key.getProperty("entry");
+                        String v = s.getValue(Type.STRING, 0);
+                        list.add(v);
+                    }
+                }
+                return list.iterator();
+            }
+        };
+    }
+
+    @Override
+    public long count(NodeState indexMeta, Set<String> values, int max) {
+        NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+        long count = 0;
+        if (values == null) {
+            PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);
+            if (ec != null) {
+                return ec.getValue(Type.LONG);
+            }
+            count = index.getChildNodeCount(max);
+            // "is not null" queries typically read more data
+            count *= 10;
+        } else if (values.size() == 1) {
+            NodeState k = index.getChildNode(values.iterator().next());
+            if (k.exists()) {
+                count = k.getProperty("entry").count();
+            } else {
+                count = 0;
+            }
+        } else {
+            count = values.size();
+        }
+        return count;
+    }
+
+}



Mime
View raw message