jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From alexparvule...@apache.org
Subject svn commit: r1421218 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/p2/ main/java/org/apache/jackrabbit/oak/plugins/index/property/ test/java/org/apache/jackrabbit/oak/plugins/index/p2/
Date Thu, 13 Dec 2012 10:53:23 GMT
Author: alexparvulescu
Date: Thu Dec 13 10:53:21 2012
New Revision: 1421218

URL: http://svn.apache.org/viewvc?rev=1421218&view=rev
Log:
OAK-511 Query PropertyIndex that stores the index content as nodes
 - initial impl
 - added index type checks to prevent index impl adding content to the wrong locations
 - reduced visibility of the PropertyIndex & Property2Index class

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2Index.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexHookProvider.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexProvider.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexQueryTest.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexTest.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/PropertyIndexLookup.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2Index.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2Index.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2Index.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2Index.java Thu Dec 13 10:53:21 2012
@@ -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;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.Type;
+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.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Sets;
+
+/**
+ * Provides a QueryIndex that does lookups against a property index
+ * 
+ * <p>
+ * To define a property index on a subtree you have to add an <code>oak:index</code> node.
+ * 
+ * Under it follows the index definition node that:
+ * <ul>
+ * <li>must be of type <code>oak:queryIndexDefinition</code></li>
+ * <li>must have the <code>type</code> property set to <b><code>p2</code></b></li>
+ * <li>contains the <code>propertyNames</code> property that indicates what property will be stored in the index</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Optionally you can specify the uniqueness constraint on a property index by
+ * setting the <code>unique</code> flag to <code>true</code>.
+ * </p>
+ * 
+ * <p>
+ * Note: <code>propertyNames</code> can be a list of properties, and it is optional.in case it is missing, the node name will be used as a property name reference value
+ * </p>
+ * 
+ * <p>
+ * Note: <code>reindex</code> is a property that when set to <code>true</code>, triggers a full content reindex.
+ * </p>
+ * 
+ * <pre>
+ * <code>
+ * {
+ *     NodeBuilder index = root.child("oak:index");
+ *     index.child("uuid")
+ *         .setProperty("jcr:primaryType", "oak:queryIndexDefinition", Type.NAME)
+ *         .setProperty("type", "p2")
+ *         .setProperty("propertyNames", "jcr:uuid")
+ *         .setProperty("unique", true)
+ *         .setProperty("reindex", true);
+ * }
+ * </code>
+ * </pre>
+ * 
+ * @see QueryIndex
+ * @see Property2IndexLookup
+ */
+class Property2Index implements QueryIndex {
+
+    public static final String TYPE = "p2";
+
+    private static final int MAX_STRING_LENGTH = 100; // TODO: configurable
+
+    static List<String> encode(PropertyValue value) {
+        List<String> values = new ArrayList<String>();
+
+        for (String v : value.getValue(Type.STRINGS)) {
+            try {
+                if (v.length() > MAX_STRING_LENGTH) {
+                    v = v.substring(0, MAX_STRING_LENGTH);
+                }
+                values.add(URLEncoder.encode(v, Charsets.UTF_8.name()));
+            } catch (UnsupportedEncodingException e) {
+                throw new IllegalStateException("UTF-8 is unsupported", e);
+            }
+        }
+        return values;
+    }
+
+    //--------------------------------------------------------< QueryIndex >--
+
+    @Override
+    public String getIndexName() {
+        return "oak:index";
+    }
+
+    @Override
+    public double getCost(Filter filter, NodeState root) {
+        Property2IndexLookup lookup = new Property2IndexLookup(root);
+        for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+            if (pr.firstIncluding && pr.lastIncluding
+                    && pr.first.equals(pr.last) // TODO: range queries
+                    && lookup.isIndexed(pr.propertyName, "/")) { // TODO: path
+                return lookup.getCost(pr.propertyName, pr.first);
+            }
+        }
+        // not an appropriate index
+        return Double.MAX_VALUE;
+    }
+
+    @Override
+    public Cursor query(Filter filter, NodeState root) {
+        Set<String> paths = null;
+
+        Property2IndexLookup lookup = new Property2IndexLookup(root);
+        for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+            if (pr.firstIncluding && pr.lastIncluding
+                    && pr.first.equals(pr.last) // TODO: range queries
+                    && lookup.isIndexed(pr.propertyName, "/")) { // TODO: path
+                Set<String> set = lookup.find(pr.propertyName, pr.first);
+                if (paths == null) {
+                    paths = Sets.newHashSet(set);
+                } else {
+                    paths.retainAll(set);
+                }
+            }
+        }
+
+        if (paths == null) {
+            throw new IllegalStateException("Property index is used even when no index is available for filter " + filter);
+        }
+        return Cursors.newPathCursor(paths);
+    }
+
+    @Override
+    public String getPlan(Filter filter, NodeState root) {
+        return "oak:index"; // TODO: better plans
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2Index.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,259 @@
+/*
+ * 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;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.p2.Property2Index.TYPE;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+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.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+
+/**
+ * {@link IndexHook} implementation that is responsible for keeping the
+ * {@link Property2Index} up to date.
+ * <p>
+ * There is a tree of PropertyIndexDiff objects, each object represents the
+ * changes at a given node.
+ * 
+ * @see Property2Index
+ * @see Property2IndexLookup
+ * 
+ */
+class Property2IndexDiff implements IndexHook {
+
+    /**
+     * The parent (null if this is the root node).
+     */
+    private final Property2IndexDiff parent;
+
+    /**
+     * The node (never null).
+     */
+    private final NodeBuilder node;
+
+    /**
+     * The node name (the path element). Null for the root node.
+     */
+    private final String name;
+
+    /**
+     * The path of the changed node (built lazily).
+     */
+    private String path;
+
+    /**
+     * Key: the property name. Value: the list of indexes (it is possible to
+     * have multiple indexes for the same property name).
+     */
+    private final Map<String, List<Property2IndexUpdate>> updates;
+
+    private Property2IndexDiff(
+            Property2IndexDiff parent,
+            NodeBuilder node, String name, String path,
+            Map<String, List<Property2IndexUpdate>> updates) {
+        this.parent = parent;
+        this.node = node;
+        this.name = name;
+        this.path = path;
+        this.updates = updates;
+
+        if (node != null && node.hasChildNode(INDEX_DEFINITIONS_NAME)) {
+            NodeBuilder index = node.child(INDEX_DEFINITIONS_NAME);
+            for (String indexName : index.getChildNodeNames()) {
+                NodeBuilder child = index.child(indexName);
+                if (isIndexNode(child)) {
+                    update(child, indexName);
+                }
+            }
+        }
+    }
+
+    private Property2IndexDiff(Property2IndexDiff parent, String name) {
+        this(parent, getChildNode(parent.node, name),
+                name, null, parent.updates);
+    }
+
+    public Property2IndexDiff(NodeBuilder root) {
+        this(null, root, null, "/",
+                new HashMap<String, List<Property2IndexUpdate>>());
+    }
+
+    private static NodeBuilder getChildNode(NodeBuilder node, String name) {
+        if (node != null && node.hasChildNode(name)) {
+            return node.child(name);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getPath() {
+        // build the path lazily
+        if (path == null) {
+            path = concat(parent.getPath(), name);
+        }
+        return path;
+    }
+
+    /**
+     * Get all the indexes for the given property name.
+     * 
+     * @param name the property name
+     * @return the indexes
+     */
+    private Iterable<Property2IndexUpdate> getIndexes(String name) {
+        List<Property2IndexUpdate> indexes = updates.get(name);
+        if (indexes != null) {
+            return indexes;
+        } else {
+            return ImmutableList.of();
+        }
+    }
+
+    private void update(NodeBuilder builder, String indexName) {
+        PropertyState ps = builder.getProperty("propertyNames");
+        Iterable<String> propertyNames = ps != null ? ps.getValue(Type.STRINGS)
+                : ImmutableList.of(indexName);
+        for (String pname : propertyNames) {
+            List<Property2IndexUpdate> list = this.updates.get(pname);
+            if (list == null) {
+                list = Lists.newArrayList();
+                this.updates.put(pname, list);
+            }
+            boolean exists = false;
+            for (Property2IndexUpdate piu : list) {
+                if (piu.getPath().equals(getPath())) {
+                    exists = true;
+                    break;
+                }
+            }
+            if (!exists) {
+                list.add(new Property2IndexUpdate(getPath(), builder));
+            }
+        }
+    }
+
+    private static boolean isIndexNode(NodeBuilder node) {
+        PropertyState ps = node.getProperty(JCR_PRIMARYTYPE);
+        boolean isNodeType = ps != null && !ps.isArray()
+                && ps.getValue(Type.STRING).equals(INDEX_DEFINITIONS_NODE_TYPE);
+        if (!isNodeType) {
+            return false;
+        }
+        PropertyState type = node.getProperty(TYPE_PROPERTY_NAME);
+        boolean isIndexType = type != null && !type.isArray()
+                && type.getValue(Type.STRING).equals(TYPE);
+        return isIndexType;
+    }
+
+    //-----------------------------------------------------< NodeStateDiff >--
+
+    @Override
+    public void propertyAdded(PropertyState after) {
+        for (Property2IndexUpdate update : getIndexes(after.getName())) {
+            update.insert(getPath(), after);
+        }
+    }
+
+    @Override
+    public void propertyChanged(PropertyState before, PropertyState after) {
+        for (Property2IndexUpdate update : getIndexes(after.getName())) {
+            update.remove(getPath(), before);
+            update.insert(getPath(), after);
+        }
+    }
+
+    @Override
+    public void propertyDeleted(PropertyState before) {
+        for (Property2IndexUpdate update : getIndexes(before.getName())) {
+            update.remove(getPath(), before);
+        }
+    }
+
+    @Override
+    public void childNodeAdded(String name, NodeState after) {
+        childNodeChanged(name, MemoryNodeState.EMPTY_NODE, after);
+    }
+
+    @Override
+    public void childNodeChanged(
+            String name, NodeState before, NodeState after) {
+        if (!NodeStateUtils.isHidden(name)) {
+            after.compareAgainstBaseState(before, child(name));
+        }
+    }
+
+    @Override
+    public void childNodeDeleted(String name, NodeState before) {
+        childNodeChanged(name, before, MemoryNodeState.EMPTY_NODE);
+    }
+
+    // -----------------------------------------------------< IndexHook >--
+
+    @Override
+    public void apply() throws CommitFailedException {
+        for (List<Property2IndexUpdate> updateList : updates.values()) {
+            for (Property2IndexUpdate update : updateList) {
+                update.apply();
+            }
+        }
+    }
+
+    @Override
+    public void reindex(NodeBuilder state) throws CommitFailedException {
+        boolean reindex = false;
+        for (List<Property2IndexUpdate> updateList : updates.values()) {
+            for (Property2IndexUpdate update : updateList) {
+                if (update.getAndResetReindexFlag()) {
+                    reindex = true;
+                }
+            }
+        }
+        if (reindex) {
+            state.getNodeState().compareAgainstBaseState(
+                    MemoryNodeState.EMPTY_NODE,
+                    new Property2IndexDiff(null, state, null, "/", updates));
+        }
+    }
+
+    @Override
+    public IndexHook child(String name) {
+        return new Property2IndexDiff(this, name);
+    }
+
+    @Override
+    public void close() throws IOException {
+        updates.clear();
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexDiff.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexHookProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexHookProvider.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexHookProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexHookProvider.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+import static org.apache.jackrabbit.oak.plugins.index.p2.Property2Index.TYPE;
+
+import java.util.List;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.plugins.index.IndexHookProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Service that provides PropertyIndex based IndexHooks.
+ * 
+ * @see Property2IndexDiff
+ * @see IndexHookProvider
+ * 
+ */
+@Component
+@Service(IndexHookProvider.class)
+public class Property2IndexHookProvider implements IndexHookProvider {
+
+    @Override
+    public List<? extends IndexHook> getIndexHooks(String type,
+            NodeBuilder builder) {
+        if (TYPE.equals(type)) {
+            return ImmutableList.of(new Property2IndexDiff(builder));
+        }
+        return ImmutableList.of();
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexHookProvider.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,209 @@
+/*
+ * 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;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+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.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Is responsible for querying the property index content.
+ * 
+ * <p>
+ * This class can be used directly on a subtree where there is an index defined
+ * by supplying a {@link NodeState} root.
+ * </p>
+ * 
+ * <pre>
+ * <code>
+ * {
+ *     NodeState state = ... // get a node state
+ *     Property2IndexLookup lookup = new Property2IndexLookup(state);
+ *     Set<String> hits = lookup.find("foo", PropertyValues.newString("xyz"));
+ * }
+ * </code>
+ * </pre>
+ */
+public class Property2IndexLookup {
+
+    private final NodeState root;
+
+    public Property2IndexLookup(NodeState root) {
+        this.root = root;
+    }
+
+    /**
+     * Checks whether the named property is indexed somewhere along the given
+     * path. Lookup starts at the current path (at the root of this object) and
+     * traverses down the path.
+     * 
+     * @param name property name
+     * @param path lookup path
+     * @return true if the property is indexed
+     */
+    public boolean isIndexed(String name, String path) {
+        if (getIndexDefinitionNode(name) != null) {
+            return true;
+        }
+
+        // TODO use PathUtils
+        if (path.startsWith("/")) {
+            path = path.substring(1);
+        }
+        int slash = path.indexOf('/');
+        if (slash == -1) {
+            return false;
+        }
+
+        NodeState child = root.getChildNode(path.substring(0, slash));
+        return new Property2IndexLookup(child).isIndexed(
+                name, path.substring(slash));
+    }
+
+    /**
+     * Searches for a given <code>String<code> value within this index.
+     * 
+     * <p><b>Note</b> if the property you are looking for is not of type <code>String<code>, 
+     * the converted key value might not match the index key, and there will be no hits on the index.</p>
+     * 
+     * @param name the property name
+     * @param value the property value
+     * @return the set of matched paths
+     */
+    public Set<String> find(String name, String value) {
+        return find(name, PropertyValues.newString(value));
+    }
+
+    /**
+     * Searches for a given value within this index.
+     * 
+     * @param name the property name
+     * @param value the property value
+     * @return the set of matched paths
+     */
+    public Set<String> find(String name, PropertyValue value) {
+        Set<String> paths = Sets.newHashSet();
+
+        NodeState state = getIndexDefinitionNode(name);
+        if (state != null && state.getChildNode(":index") != null) {
+            NodeState property;
+            state = state.getChildNode(":index");
+            for (String p : Property2Index.encode(value)) {
+                property = state.getChildNode(p);
+                if (property != null) {
+                    // We have an entry for this value, so use it
+                    for (String path : property.getChildNodeNames()) {
+                        paths.add(path);
+                    }
+                }
+            }
+        } else {
+            // No index available, so first check this node for a match
+            PropertyState property = root.getProperty(name);
+            if (property != null) {
+                if (value.isArray()) {
+                    // let query engine handle multi-valued look ups
+                    // simply return all nodes that have this property
+                    paths.add("");
+                } else {
+                    // does it match any of the values of this property?
+                    for (int i = 0; i < property.count(); i++) {
+                        if (property.getValue(value.getType(), i).equals(value.getValue(value.getType()))) {
+                            paths.add("");
+                            // no need to check for more matches in this property
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // ... and then recursively look up from the rest of the tree
+            for (ChildNodeEntry entry : root.getChildNodeEntries()) {
+                String base = entry.getName();
+                Property2IndexLookup lookup =
+                        new Property2IndexLookup(entry.getNodeState());
+                for (String path : lookup.find(name, value)) {
+                    if (path.isEmpty()) {
+                        paths.add(base);
+                    } else {
+                        paths.add(base + "/" + path);
+                    }
+                }
+            }
+        }
+
+        return 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)) {
+                PropertyState property = state.getProperty(p);
+                if (property != null) {
+                    cost += property.count();
+                }
+            }
+        } else {
+            cost = Double.MAX_VALUE;
+        }
+        return cost;
+    }
+
+    /**
+     * Get the node with the index definition node for the given property.
+     * 
+     * @param name the property name
+     * @return the node where the index definition is stored, or null if no
+     *         index definition node was found
+     */
+    @Nullable
+    private NodeState getIndexDefinitionNode(String name) {
+        NodeState state = root.getChildNode(INDEX_DEFINITIONS_NAME);
+        if (state != null) {
+            for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+                PropertyState type = entry.getNodeState().getProperty(IndexConstants.TYPE_PROPERTY_NAME);
+                if(type == null || type.isArray() || !Property2Index.TYPE.equals(type.getValue(Type.STRING))){
+                    continue;
+                }
+                PropertyState names = entry.getNodeState().getProperty("propertyNames");
+                if (names != null) {
+                    for (int i = 0; i < names.count(); i++) {
+                        if (name.equals(names.getValue(Type.STRING, i))) {
+                            return entry.getNodeState();
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexLookup.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexProvider.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexProvider.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A provider for property indexes.
+ * <p>
+ * Even if there are multiple index definitions, there is only actually one
+ * PropertyIndex instance, which is used for all indexes.
+ * 
+ * @see Property2Index
+ * 
+ */
+public class Property2IndexProvider implements QueryIndexProvider {
+
+    @Override @Nonnull
+    public List<QueryIndex> getQueryIndexes(NodeState state) {
+        return ImmutableList.<QueryIndex>of(new Property2Index());
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexProvider.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,172 @@
+/*
+ * 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;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.PropertyType;
+
+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.spi.query.PropertyValues;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Takes care of applying the updates to the index content.
+ * <p>
+ * The changes are temporarily added to an in-memory structure, and then applied
+ * to the node.
+ */
+class Property2IndexUpdate {
+
+    /**
+     * The path of the index definition (where the index data is stored).
+     */
+    private final String path;
+
+    /**
+     * The node where the index definition is stored.
+     */
+    private final NodeBuilder node;
+
+    /**
+     * The set of added values / paths. The key of the map is the property value
+     * (encoded as a string), the value of the map is a set of paths that where
+     * added.
+     */
+    private final Map<String, Set<String>> insert;
+
+    /**
+     * The set of removed values / paths. The key of the map is the property
+     * value (encoded as a string), the value of the map is a set of paths that
+     * were removed.
+     */
+    private final Map<String, Set<String>> remove;
+
+    public Property2IndexUpdate(String path, NodeBuilder node) {
+        this.path = path;
+        this.node = node;
+        this.insert = Maps.newHashMap();
+        this.remove = Maps.newHashMap();
+    }
+
+    String getPath() {
+        return path;
+    }
+
+    /**
+     * A property value was added at the given path.
+     * 
+     * @param path the path
+     * @param value the value
+     */
+    public void insert(String path, PropertyState value) {
+        Preconditions.checkArgument(path.startsWith(this.path));
+        putValues(insert, path.substring(this.path.length()), value);
+    }
+
+    /**
+     * A property value was removed at the given path.
+     * 
+     * @param path the path
+     * @param value the value
+     */
+    public void remove(String path, PropertyState value) {
+        Preconditions.checkArgument(path.startsWith(this.path));
+        putValues(remove, path.substring(this.path.length()), value);
+    }
+
+    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));
+            for (String key : keys) {
+                Set<String> paths = map.get(key);
+                if (paths == null) {
+                    paths = Sets.newHashSet();
+                    map.put(key, paths);
+                }
+                paths.add(path);
+            }
+        }
+    }
+
+    boolean getAndResetReindexFlag() {
+        boolean reindex = node.getProperty(REINDEX_PROPERTY_NAME) != null
+                && node.getProperty(REINDEX_PROPERTY_NAME).getValue(
+                        Type.BOOLEAN);
+        node.setProperty(REINDEX_PROPERTY_NAME, false);
+        return reindex;
+    }
+
+    /**
+     * Try to apply the changes to the index content (to the ":index" node.
+     * 
+     * @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);
+                for (String rm : paths) {
+                    child.removeNode(rm);
+                }
+                if (child.getChildNodeCount() == 0) {
+                    index.removeNode(encoded);
+                }
+            }
+        }
+
+        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();
+                if (!child.hasChildNode(add)) {
+                    child.child(add);
+                    addIterator.remove();
+                }
+            }
+            long childCount = child.getChildNodeCount();
+            if (childCount == 0) {
+                index.removeNode(encoded);
+            } else if (unique && childCount > 1) {
+                throw new CommitFailedException(
+                        "Uniqueness constraint violated");
+            }
+        }
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexUpdate.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

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=1421218&r1=1421217&r2=1421218&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 Thu Dec 13 10:53:21 2012
@@ -77,7 +77,7 @@ import com.google.common.collect.Sets;
  * @see QueryIndex
  * @see PropertyIndexLookup
  */
-public class PropertyIndex implements QueryIndex {
+class PropertyIndex implements QueryIndex {
 
     public static final String TYPE = "property";
 

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=1421218&r1=1421217&r2=1421218&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 Thu Dec 13 10:53:21 2012
@@ -25,6 +25,7 @@ import javax.annotation.Nullable;
 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.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -189,6 +190,10 @@ public class PropertyIndexLookup {
         NodeState state = root.getChildNode(INDEX_DEFINITIONS_NAME);
         if (state != null) {
             for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+                PropertyState type = entry.getNodeState().getProperty(IndexConstants.TYPE_PROPERTY_NAME);
+                if(type == null || type.isArray() || !PropertyIndex.TYPE.equals(type.getValue(Type.STRING))){
+                    continue;
+                }
                 PropertyState names = entry.getNodeState().getProperty("propertyNames");
                 if (names != null) {
                     for (int i = 0; i < names.count(); i++) {

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexQueryTest.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexQueryTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexQueryTest.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.nodetype.InitialContent;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Tests the query engine using the default index implementation: the
+ * {@link Property2IndexProvider}
+ */
+public class Property2IndexQueryTest extends AbstractQueryTest {
+
+    @Override
+    protected ContentRepository createRepository() {
+        return new Oak().with(new InitialContent())
+                .with(new Property2IndexProvider())
+                .with(new Property2IndexHookProvider())
+                .createContentRepository();
+    }
+
+    @Override
+    protected void createTestIndexNode() throws Exception {
+        Tree index = root.getTree("/");
+        Tree indexDef = index.addChild(INDEX_DEFINITIONS_NAME).addChild(
+                TEST_INDEX_NAME);
+        indexDef.setProperty(JcrConstants.JCR_PRIMARYTYPE,
+                INDEX_DEFINITIONS_NODE_TYPE, Type.NAME);
+        indexDef.setProperty(TYPE_PROPERTY_NAME, Property2Index.TYPE);
+        indexDef.setProperty(REINDEX_PROPERTY_NAME, true);
+        indexDef.setProperty(PropertyStates
+                .createProperty("propertyNames",
+                        ImmutableList.of(JCR_PRIMARYTYPE, JCR_MIXINTYPES),
+                        Type.STRINGS));
+        root.commit();
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexQueryTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexTest.java?rev=1421218&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexTest.java Thu Dec 13 10:53:21 2012
@@ -0,0 +1,138 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.IndexHook;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class Property2IndexTest {
+
+    private static final int MANY = 100;
+
+    @Test
+    public void testPropertyLookup() throws Exception {
+        NodeState root = MemoryNodeState.EMPTY_NODE;
+
+        // Add index definition
+        NodeBuilder builder = root.builder();
+        builder.child("oak:index").child("foo")
+                .setProperty("jcr:primaryType", "oak:queryIndexDefinition", Type.NAME)
+                .setProperty("type", "p2")
+                .setProperty("propertyNames", "foo");
+        NodeState before = builder.getNodeState();
+
+        // Add some content and process it through the property index hook
+        builder = before.builder();
+        builder.child("a").setProperty("foo", "abc");
+        builder.child("b").setProperty("foo", Arrays.asList("abc", "def"), Type.STRINGS);
+        // plus lots of dummy content to highlight the benefit of indexing
+        for (int i = 0; i < MANY; i++) {
+            builder.child("n" + i).setProperty("foo", "xyz");
+        }
+        NodeState after = builder.getNodeState();
+
+        // First check lookups without an index
+        Property2IndexLookup lookup = new Property2IndexLookup(after);
+        long withoutIndex = System.nanoTime();
+        assertEquals(ImmutableSet.of("a", "b"), lookup.find("foo", "abc"));
+        assertEquals(ImmutableSet.of("b"), lookup.find("foo", "def"));
+        assertEquals(ImmutableSet.of(), lookup.find("foo", "ghi"));
+        assertEquals(MANY, lookup.find("foo", "xyz").size());
+        withoutIndex = System.nanoTime() - withoutIndex;
+
+        // ... then see how adding an index affects the code
+        IndexHook p = new Property2IndexDiff(builder);
+        after.compareAgainstBaseState(before, p);
+        p.apply();
+        p.close();
+
+        lookup = new Property2IndexLookup(builder.getNodeState());
+        long withIndex = System.nanoTime();
+        assertEquals(ImmutableSet.of("a", "b"), lookup.find("foo", "abc"));
+        assertEquals(ImmutableSet.of("b"), lookup.find("foo", "def"));
+        assertEquals(ImmutableSet.of(), lookup.find("foo", "ghi"));
+        assertEquals(MANY, lookup.find("foo", "xyz").size());
+        withIndex = System.nanoTime() - withIndex;
+
+        // System.out.println("Index performance ratio: " + withoutIndex/withIndex);
+        // assertTrue(withoutIndex > withIndex);
+    }
+
+    @Test
+    public void testCustomConfigPropertyLookup() throws Exception {
+        NodeState root = MemoryNodeState.EMPTY_NODE;
+
+        // Add index definition
+        NodeBuilder builder = root.builder();
+        builder.child("oak:index").child("fooIndex")
+                .setProperty("jcr:primaryType", "oak:queryIndexDefinition", Type.NAME)
+                .setProperty("type", "p2")
+                .setProperty("propertyNames", Arrays.asList("foo", "extrafoo"), Type.STRINGS);
+        NodeState before = builder.getNodeState();
+
+        // Add some content and process it through the property index hook
+        builder = before.builder();
+        builder.child("a").setProperty("foo", "abc").setProperty("extrafoo", "pqr");
+        builder.child("b").setProperty("foo", Arrays.asList("abc", "def"), Type.STRINGS);
+        // plus lots of dummy content to highlight the benefit of indexing
+        for (int i = 0; i < MANY; i++) {
+            builder.child("n" + i).setProperty("foo", "xyz");
+        }
+        NodeState after = builder.getNodeState();
+
+        // First check lookups without an index
+        Property2IndexLookup lookup = new Property2IndexLookup(after);
+        long withoutIndex = System.nanoTime();
+        assertEquals(ImmutableSet.of("a", "b"), lookup.find("foo", "abc"));
+        assertEquals(ImmutableSet.of("b"), lookup.find("foo", "def"));
+        assertEquals(ImmutableSet.of(), lookup.find("foo", "ghi"));
+        assertEquals(MANY, lookup.find("foo", "xyz").size());
+        assertEquals(ImmutableSet.of("a"), lookup.find("extrafoo", "pqr"));
+        assertEquals(ImmutableSet.of(), lookup.find("pqr", "foo"));
+        withoutIndex = System.nanoTime() - withoutIndex;
+
+        // ... then see how adding an index affects the code
+        IndexHook p = new Property2IndexDiff(builder);
+        after.compareAgainstBaseState(before, p);
+        p.apply();
+        p.close();
+
+        lookup = new Property2IndexLookup(builder.getNodeState());
+        long withIndex = System.nanoTime();
+        assertEquals(ImmutableSet.of("a", "b"), lookup.find("foo", "abc"));
+        assertEquals(ImmutableSet.of("b"), lookup.find("foo", "def"));
+        assertEquals(ImmutableSet.of(), lookup.find("foo", "ghi"));
+        assertEquals(MANY, lookup.find("foo", "xyz").size());
+        assertEquals(ImmutableSet.of("a"), lookup.find("extrafoo", "pqr"));
+        assertEquals(ImmutableSet.of(), lookup.find("pqr", "foo"));
+        withIndex = System.nanoTime() - withIndex;
+
+        // System.out.println("Index performance ratio: " + withoutIndex/withIndex);
+        // assertTrue(withoutIndex > withIndex);
+    }
+
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/p2/Property2IndexTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message