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
|