Index: oak-core/pom.xml
===================================================================
--- oak-core/pom.xml (revision 1405804)
+++ oak-core/pom.xml (working copy)
@@ -50,6 +50,7 @@
org.apache.jackrabbit.oak.plugins.identifier,
org.apache.jackrabbit.oak.plugins.index,
org.apache.jackrabbit.oak.plugins.index.lucene,
+ org.apache.jackrabbit.oak.plugins.index.nodetype,
org.apache.jackrabbit.oak.plugins.index.property,
org.apache.jackrabbit.oak.plugins.memory,
org.apache.jackrabbit.oak.plugins.name,
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java (revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndex.java (revision 0)
@@ -0,0 +1,124 @@
+/*
+ * 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.nodetype;
+
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.core.ReadOnlyTree;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+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.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.Sets;
+
+/**
+ * NodeTypeIndex...
+ */
+class NodeTypeIndex implements QueryIndex, JcrConstants {
+
+ @Override
+ public double getCost(Filter filter, NodeState root) {
+ if (!hasNodeTypeRestriction(filter)) {
+ // this is not an appropriate index if the filter
+ // doesn't have a node type restriction
+ return Double.MAX_VALUE;
+ }
+ NodeTypeIndexLookup lookup = new NodeTypeIndexLookup(root);
+ if (lookup.isIndexed(filter.getPath())) {
+ return lookup.getCost(resolveNodeType(root, filter.getNodeType()));
+ } else {
+ return Double.MAX_VALUE;
+ }
+ }
+
+ @Override
+ public Cursor query(Filter filter, NodeState root) {
+ NodeTypeIndexLookup lookup = new NodeTypeIndexLookup(root);
+ if (!hasNodeTypeRestriction(filter) || !lookup.isIndexed(filter.getPath())) {
+ return Cursors.newTraversingCursor("?", filter, root);
+ }
+ return Cursors.newPathCursor(lookup.find(
+ resolveNodeType(root, filter.getNodeType())));
+ }
+
+ @Override
+ public String getPlan(Filter filter, NodeState root) {
+ // TODO: return plan according to query()
+ return null;
+ }
+
+ @Override
+ public String getIndexName() {
+ return "nodetype";
+ }
+
+ //----------------------------< internal >----------------------------------
+
+ private static boolean hasNodeTypeRestriction(Filter filter) {
+ return filter.getNodeType() != null
+ && !filter.getNodeType().equals(NT_BASE);
+ }
+
+ private static Iterable resolveNodeType(NodeState root, String nodeType) {
+ ReadOnlyNodeTypeManager ntMgr = new NTManager(root);
+ Set ntNames = Sets.newHashSet();
+ try {
+ NodeType nt = ntMgr.getNodeType(nodeType);
+ for (NodeTypeIterator types = nt.getSubtypes(); types.hasNext(); ) {
+ ntNames.add(types.nextNodeType().getName());
+ }
+ ntNames.add(nodeType);
+ } catch (RepositoryException e) {
+ // unknown node type
+ }
+ return ntNames;
+ }
+
+ private static final class NTManager extends ReadOnlyNodeTypeManager {
+
+ private final NodeState root;
+
+ NTManager(NodeState root) {
+ this.root = root;
+ }
+
+ @Override
+ protected Tree getTypes() {
+ Tree t = new ReadOnlyTree(root);
+ for (String name : PathUtils.elements(NodeTypeConstants.NODE_TYPES_PATH)) {
+ t = t.getChild(name);
+ if (t == null) {
+ break;
+ }
+ }
+ return t;
+ }
+ }
+}
Property changes on: oak-core\src\main\java\org\apache\jackrabbit\oak\plugins\index\nodetype\NodeTypeIndex.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision Rev URL
Added: svn:eol-style
+ native
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java (revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexLookup.java (revision 0)
@@ -0,0 +1,90 @@
+/*
+ * 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.nodetype;
+
+import java.util.Set;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexLookup;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.Sets;
+
+/**
+ * NodeTypeIndexLookup...
+ */
+class NodeTypeIndexLookup implements JcrConstants {
+
+ private final NodeState root;
+
+ public NodeTypeIndexLookup(NodeState root) {
+ this.root = root;
+ }
+
+ /**
+ * Returns true if a node type index lookup exists at the given
+ * path or further up the tree.
+ *
+ * @param path the path to check.
+ * @return true if a node type index exists; false
+ * otherwise.
+ */
+ public boolean isIndexed(String path) {
+ PropertyIndexLookup lookup = new PropertyIndexLookup(root);
+ if (lookup.isIndexed(JCR_PRIMARYTYPE, path)
+ && lookup.isIndexed(JCR_MIXINTYPES, path)) {
+ return true;
+ }
+
+ 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 NodeTypeIndexLookup(child).isIndexed(
+ path.substring(slash));
+ }
+
+ public double getCost(Iterable nodeTypes) {
+ PropertyValue ntNames = PropertyValues.newName(nodeTypes);
+ PropertyIndexLookup lookup = new PropertyIndexLookup(root);
+ return lookup.getCost(JCR_PRIMARYTYPE, ntNames)
+ + lookup.getCost(JCR_MIXINTYPES, ntNames);
+ }
+
+ /**
+ * Returns the paths that match the given node types.
+ *
+ * @param nodeTypes the names of the node types to match.
+ * @return the set of matched paths.
+ */
+ public Set find(Iterable nodeTypes) {
+ Set paths = Sets.newHashSet();
+ PropertyIndexLookup lookup = new PropertyIndexLookup(root);
+ PropertyValue ntNames = PropertyValues.newName(nodeTypes);
+ paths.addAll(lookup.find(JCR_PRIMARYTYPE, ntNames));
+ paths.addAll(lookup.find(JCR_MIXINTYPES, ntNames));
+ return paths;
+ }
+
+}
Property changes on: oak-core\src\main\java\org\apache\jackrabbit\oak\plugins\index\nodetype\NodeTypeIndexLookup.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision Rev URL
Added: svn:eol-style
+ native
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexProvider.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexProvider.java (revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/nodetype/NodeTypeIndexProvider.java (revision 0)
@@ -0,0 +1,41 @@
+/*
+ * 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.nodetype;
+
+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;
+
+/**
+ * NodeTypeIndexProvider...
+ */
+public class NodeTypeIndexProvider implements QueryIndexProvider {
+
+ @Nonnull
+ @Override
+ public List extends QueryIndex> getQueryIndexes(NodeState nodeState) {
+ return ImmutableList.of(new NodeTypeIndex());
+ }
+}
Property changes on: oak-core\src\main\java\org\apache\jackrabbit\oak\plugins\index\nodetype\NodeTypeIndexProvider.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision Rev URL
Added: svn:eol-style
+ native
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java (working copy)
@@ -19,27 +19,21 @@
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
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.query.index.IndexRowImpl;
-import org.apache.jackrabbit.oak.query.index.TraversingCursor;
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.IndexRow;
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;
-import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
-
/**
* Provides a QueryIndex that does lookups against a property index
*
@@ -113,8 +107,16 @@
@Override
public double getCost(Filter filter, NodeState root) {
- // TODO: proper cost calculation
- return 1.0;
+ PropertyIndexLookup lookup = new PropertyIndexLookup(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
@@ -136,9 +138,9 @@
}
if (paths != null) {
- return new PathCursor(paths);
+ return Cursors.newPathCursor(paths);
} else {
- return new TraversingCursor("?", filter, root);
+ return Cursors.newTraversingCursor("?", filter, root);
}
}
@@ -146,34 +148,4 @@
public String getPlan(Filter filter, NodeState root) {
return "oak:index"; // TODO: better plans
}
-
- private static class PathCursor implements Cursor {
-
- private final Iterator iterator;
-
- private String path;
-
- public PathCursor(Collection paths) {
- this.iterator = paths.iterator();
- }
-
- @Override
- public boolean next() {
- if (iterator.hasNext()) {
- path = iterator.next();
- return true;
- } else {
- path = null;
- return false;
- }
- }
-
- @Override
- public IndexRow currentRow() {
- // TODO support jcr:score and possibly rep:exceprt
- return new IndexRowImpl(isAbsolute(path) ? path : "/" + path);
- }
-
- }
-
}
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java (working copy)
@@ -158,6 +158,23 @@
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 : PropertyIndex.encode(value)) {
+ PropertyState property = state.getProperty(p);
+ if (property != null) {
+ cost += property.count();
+ }
+ }
+ } else {
+ cost = Double.MAX_VALUE;
+ }
+ return cost;
+ }
+
@Nullable
private NodeState getIndexDefinitionNode(String name) {
NodeState state = root.getChildNode(INDEX_DEFINITIONS_NAME);
@@ -175,5 +192,4 @@
}
return null;
}
-
}
\ No newline at end of file
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/InitialContent.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/InitialContent.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/InitialContent.java (working copy)
@@ -18,15 +18,19 @@
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.core.RootImpl;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
+import com.google.common.collect.ImmutableList;
+
/**
* {@code InitialContent} implements a {@link RepositoryInitializer} and
* registers built-in node types when the micro kernel becomes available.
@@ -96,11 +100,14 @@
.setProperty("propertyNames", "jcr:uuid")
.setProperty("reindex", true)
.setProperty("unique", true);
- index.child("primaryType")
+ index.child("nodetype")
.setProperty("jcr:primaryType", "oak:queryIndexDefinition", Type.NAME)
.setProperty("type", "property")
.setProperty("reindex", true)
- .setProperty("propertyNames", "jcr:primaryType");
+ .setProperty(PropertyStates.createProperty(
+ "propertyNames",
+ ImmutableList.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.JCR_MIXINTYPES),
+ Type.STRINGS));
// FIXME: user-mgt related unique properties (rep:authorizableId, rep:principalName) are implementation detail and not generic for repo
// FIXME OAK-396: rep:principalName only needs to be unique if defined with user/group nodes -> add defining nt-info to uniqueness constraint otherwise ac-editing will fail.
index.child("authorizableId")
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ChildNodeJoinConditionImpl.java (working copy)
@@ -64,13 +64,17 @@
@Override
public void restrict(FilterImpl f) {
- String p = parentSelector.currentPath();
- String c = childSelector.currentPath();
- if (f.getSelector() == parentSelector && c != null) {
- f.restrictPath(PathUtils.getParentPath(c), Filter.PathRestriction.EXACT);
+ if (f.getSelector() == parentSelector) {
+ String c = childSelector.currentPath();
+ if (c != null) {
+ f.restrictPath(PathUtils.getParentPath(c), Filter.PathRestriction.EXACT);
+ }
}
- if (f.getSelector() == childSelector && p != null) {
- f.restrictPath(p, Filter.PathRestriction.DIRECT_CHILDREN);
+ if (f.getSelector() == childSelector) {
+ String p = parentSelector.currentPath();
+ if (p != null) {
+ f.restrictPath(p, Filter.PathRestriction.DIRECT_CHILDREN);
+ }
}
}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java (working copy)
@@ -95,18 +95,22 @@
@Override
public void restrict(FilterImpl f) {
- PropertyValue p1 = selector1.currentProperty(property1Name);
- PropertyValue p2 = selector2.currentProperty(property2Name);
- if (f.getSelector() == selector1 && p2 != null) {
- if (!p2.isArray()) {
- // TODO support join on multi-valued properties
- f.restrictProperty(property1Name, Operator.EQUAL, p2);
+ if (f.getSelector() == selector1) {
+ PropertyValue p2 = selector2.currentProperty(property2Name);
+ if (p2 != null) {
+ if (!p2.isArray()) {
+ // TODO support join on multi-valued properties
+ f.restrictProperty(property1Name, Operator.EQUAL, p2);
+ }
}
}
- if (f.getSelector() == selector2 && p1 != null) {
- if (!p1.isArray()) {
- // TODO support join on multi-valued properties
- f.restrictProperty(property2Name, Operator.EQUAL, p1);
+ if (f.getSelector() == selector2) {
+ PropertyValue p1 = selector1.currentProperty(property1Name);
+ if (p1 != null) {
+ if (!p1.isArray()) {
+ // TODO support join on multi-valued properties
+ f.restrictProperty(property2Name, Operator.EQUAL, p1);
+ }
}
}
}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java (working copy)
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map.Entry;
+import javax.annotation.CheckForNull;
import javax.jcr.PropertyType;
import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -98,6 +99,7 @@
}
@Override
+ @CheckForNull
public String getNodeType() {
return nodeType;
}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingCursor.java (working copy)
@@ -1,141 +0,0 @@
-/*
- * 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.query.index;
-
-import static org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction.ALL_CHILDREN;
-
-import java.util.Deque;
-import java.util.Iterator;
-import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
-import org.apache.jackrabbit.oak.spi.query.Cursor;
-import org.apache.jackrabbit.oak.spi.query.Filter;
-import org.apache.jackrabbit.oak.spi.query.IndexRow;
-import org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Queues;
-
-/**
- * A cursor that reads all nodes in a given subtree.
- */
-public class TraversingCursor implements Cursor {
-
- private static final Logger LOG = LoggerFactory.getLogger(TraversingIndex.class);
-
- private final String statement;
-
- private final Filter filter;
-
- private final Deque> nodeIterators =
- Queues.newArrayDeque();
-
- private String parentPath;
-
- private String currentPath;
-
- private long readCount;
-
- public TraversingCursor(String statement, Filter filter, NodeState root) {
- this.statement = statement;
- this.filter = filter;
-
- String path = filter.getPath();
- parentPath = null;
- currentPath = "/";
- NodeState parent = null;
- NodeState node = root;
- if (!path.equals("/")) {
- for (String name : path.substring(1).split("/")) {
- parentPath = currentPath;
- currentPath = PathUtils.concat(parentPath, name);
-
- parent = node;
- node = parent.getChildNode(name);
-
- if (node == null) {
- // nothing can match this filter, leave nodes empty
- return;
- }
- }
- }
- PathRestriction restriciton = filter.getPathRestriction();
- switch (restriciton) {
- case EXACT:
- case ALL_CHILDREN:
- nodeIterators.add(Iterators.singletonIterator(
- new MemoryChildNodeEntry(currentPath, node)));
- parentPath = "";
- break;
- case PARENT:
- if (parent != null) {
- nodeIterators.add(Iterators.singletonIterator(
- new MemoryChildNodeEntry(parentPath, parent)));
- parentPath = "";
- }
- break;
- case DIRECT_CHILDREN:
- nodeIterators.add(node.getChildNodeEntries().iterator());
- parentPath = currentPath;
- break;
- default:
- throw new IllegalArgumentException("Unknown restriction: " + restriciton);
- }
- }
-
- @Override
- public IndexRow currentRow() {
- return new IndexRowImpl(currentPath);
- }
-
- @Override
- public boolean next() {
- while (!nodeIterators.isEmpty()) {
- Iterator extends ChildNodeEntry> iterator = nodeIterators.getLast();
- if (iterator.hasNext()) {
- ChildNodeEntry entry = iterator.next();
-
- readCount++;
- if (readCount % 100 == 0) {
- LOG.warn("Traversed " + readCount + " nodes with filter " + filter + " for query " + statement + "; consider creating an index or changing the query");
- }
-
- NodeState node = entry.getNodeState();
-
- String name = entry.getName();
- if (NodeStateUtils.isHidden(name)) {
- continue;
- }
- currentPath = PathUtils.concat(parentPath, name);
-
- if (filter.getPathRestriction() == ALL_CHILDREN) {
- nodeIterators.addLast(node.getChildNodeEntries().iterator());
- parentPath = currentPath;
- }
- return true;
- } else {
- nodeIterators.removeLast();
- parentPath = PathUtils.getParentPath(parentPath);
- }
- }
- currentPath = null;
- return false;
- }
-
-}
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/TraversingIndex.java (working copy)
@@ -20,6 +20,7 @@
import org.apache.jackrabbit.oak.commons.PathUtils;
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.QueryIndex;
import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -37,7 +38,7 @@
@Override
public Cursor query(Filter filter, NodeState root) {
- return new TraversingCursor(statement, filter, root);
+ return Cursors.newTraversingCursor(statement, filter, root);
}
@Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java (working copy)
@@ -125,7 +125,7 @@
for (QueryIndex index : getIndexes()) {
double cost = index.getCost(filter, root);
if (LOG.isDebugEnabled()) {
- LOG.debug("cost for " + index + " is " + cost);
+ LOG.debug("cost for " + index.getIndexName() + " is " + cost);
}
if (cost < bestCost) {
bestCost = cost;
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java (revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Cursors.java (revision 0)
@@ -0,0 +1,211 @@
+/*
+ * 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.spi.query;
+
+import java.util.Deque;
+import java.util.Iterator;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
+import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
+import org.apache.jackrabbit.oak.query.index.TraversingIndex;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Queues;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
+import static org.apache.jackrabbit.oak.spi.query.Filter.PathRestriction.ALL_CHILDREN;
+
+/**
+ * Cursors provides factory methods to create {@link Cursor}s.
+ */
+public class Cursors {
+
+ private Cursors() {
+ }
+
+ /**
+ * Creates a {@link Cursor} over paths.
+ *
+ * @param paths the paths to iterate over.
+ * @return the Cursor.
+ */
+ public static Cursor newPathCursor(Iterable paths) {
+ return new PathCursor(paths);
+ }
+
+ /**
+ * Returns a traversing cursor based on the path restriction in the given
+ * {@link Filter}.
+ *
+ * @param statement the query statement. This string is only used for
+ * logging purposes.
+ * @param filter the filter.
+ * @param root the root {@link NodeState}.
+ * @return the {@link Cursor}.
+ */
+ public static Cursor newTraversingCursor(String statement,
+ Filter filter,
+ NodeState root) {
+ return new TraversingCursor(statement, filter, root);
+ }
+
+ /**
+ * PathCursor implements a simple {@link Cursor} that iterates
+ * over a {@link String} based path {@link Iterable}.
+ */
+ private static class PathCursor implements Cursor {
+
+ private final Iterator iterator;
+
+ private String path;
+
+ public PathCursor(Iterable paths) {
+ this.iterator = paths.iterator();
+ }
+
+ @Override
+ public boolean next() {
+ if (iterator.hasNext()) {
+ path = iterator.next();
+ return true;
+ } else {
+ path = null;
+ return false;
+ }
+ }
+
+ @Override
+ public IndexRow currentRow() {
+ // TODO support jcr:score and possibly rep:exceprt
+ return new IndexRowImpl(isAbsolute(path) ? path : "/" + path);
+ }
+ }
+
+ /**
+ * A cursor that reads all nodes in a given subtree.
+ */
+ public static class TraversingCursor implements Cursor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TraversingIndex.class);
+
+ private final String statement;
+
+ private final Filter filter;
+
+ private final Deque> nodeIterators =
+ Queues.newArrayDeque();
+
+ private String parentPath;
+
+ private String currentPath;
+
+ private long readCount;
+
+ public TraversingCursor(String statement, Filter filter, NodeState root) {
+ this.statement = statement;
+ this.filter = filter;
+
+ String path = filter.getPath();
+ parentPath = null;
+ currentPath = "/";
+ NodeState parent = null;
+ NodeState node = root;
+ if (!path.equals("/")) {
+ for (String name : path.substring(1).split("/")) {
+ parentPath = currentPath;
+ currentPath = PathUtils.concat(parentPath, name);
+
+ parent = node;
+ node = parent.getChildNode(name);
+
+ if (node == null) {
+ // nothing can match this filter, leave nodes empty
+ return;
+ }
+ }
+ }
+ Filter.PathRestriction restriciton = filter.getPathRestriction();
+ switch (restriciton) {
+ case EXACT:
+ case ALL_CHILDREN:
+ nodeIterators.add(Iterators.singletonIterator(
+ new MemoryChildNodeEntry(currentPath, node)));
+ parentPath = "";
+ break;
+ case PARENT:
+ if (parent != null) {
+ nodeIterators.add(Iterators.singletonIterator(
+ new MemoryChildNodeEntry(parentPath, parent)));
+ parentPath = "";
+ }
+ break;
+ case DIRECT_CHILDREN:
+ nodeIterators.add(node.getChildNodeEntries().iterator());
+ parentPath = currentPath;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown restriction: " + restriciton);
+ }
+ }
+
+ @Override
+ public IndexRow currentRow() {
+ return new IndexRowImpl(currentPath);
+ }
+
+ @Override
+ public boolean next() {
+ while (!nodeIterators.isEmpty()) {
+ Iterator extends ChildNodeEntry> iterator = nodeIterators.getLast();
+ if (iterator.hasNext()) {
+ ChildNodeEntry entry = iterator.next();
+
+ readCount++;
+ if (readCount % 100 == 0) {
+ LOG.warn("Traversed " + readCount + " nodes with filter " + filter + " for query " + statement + "; consider creating an index or changing the query");
+ }
+
+ NodeState node = entry.getNodeState();
+
+ String name = entry.getName();
+ if (NodeStateUtils.isHidden(name)) {
+ continue;
+ }
+ currentPath = PathUtils.concat(parentPath, name);
+
+ if (filter.getPathRestriction() == ALL_CHILDREN) {
+ nodeIterators.addLast(node.getChildNodeEntries().iterator());
+ parentPath = currentPath;
+ }
+ return true;
+ } else {
+ nodeIterators.removeLast();
+ parentPath = PathUtils.getParentPath(parentPath);
+ }
+ }
+ currentPath = null;
+ return false;
+ }
+
+ }
+}
Property changes on: oak-core\src\main\java\org\apache\jackrabbit\oak\spi\query\Cursors.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision Rev URL
Added: svn:eol-style
+ native
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java (working copy)
@@ -20,6 +20,7 @@
import java.util.Collection;
+import javax.annotation.CheckForNull;
import javax.jcr.PropertyType;
import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -65,6 +66,10 @@
*/
String getPath();
+ /**
+ * @return the node type restriction or null if none is set.
+ */
+ @CheckForNull
String getNodeType();
/**
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java (revision 1405804)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java (working copy)
@@ -36,6 +36,7 @@
import org.apache.jackrabbit.oak.plugins.memory.DoublePropertyState;
import org.apache.jackrabbit.oak.plugins.memory.GenericPropertyState;
import org.apache.jackrabbit.oak.plugins.memory.LongPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState;
import org.apache.jackrabbit.oak.plugins.memory.MultiStringPropertyState;
import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
@@ -107,6 +108,11 @@
}
@Nonnull
+ public static PropertyValue newName(Iterable value) {
+ return new PropertyStateValue(MultiGenericPropertyState.nameProperty("", value));
+ }
+
+ @Nonnull
public static PropertyValue newPath(String value) {
return new PropertyStateValue(GenericPropertyState.pathProperty("", value));
}
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java (revision 1405804)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java (working copy)
@@ -29,6 +29,7 @@
import org.apache.jackrabbit.oak.plugins.index.IndexHookManager;
import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexHookProvider;
import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.nodetype.NodeTypeIndexProvider;
import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexHookProvider;
import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider;
import org.apache.jackrabbit.oak.plugins.name.NameValidatorProvider;
@@ -79,6 +80,7 @@
with(new AnnotatingConflictHandler());
with(new PropertyIndexProvider());
+ with(new NodeTypeIndexProvider());
with(new LuceneIndexProvider());
}
Index: oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
===================================================================
--- oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java (revision 1405804)
+++ oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java (working copy)
@@ -21,7 +21,13 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.NoSuchElementException;
+import java.util.Set;
+
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
@@ -32,6 +38,9 @@
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
+
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.commons.iterator.RowIterable;
import org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest;
import org.junit.Test;
@@ -196,4 +205,24 @@
}
}
+ @Test
+ public void nodeTypeConstraint() throws Exception {
+ Session session = getAdminSession();
+ Node root = session.getRootNode();
+ Node folder1 = root.addNode("folder1", "nt:folder");
+ Node folder2 = root.addNode("folder2", "nt:folder");
+ JcrUtils.putFile(folder1, "file", "text/plain",
+ new ByteArrayInputStream("foo bar".getBytes("UTF-8")));
+ folder2.addNode("folder3", "nt:folder");
+ session.save();
+
+ QueryManager qm = session.getWorkspace().getQueryManager();
+ Query q = qm.createQuery("//element(*, nt:folder)", Query.XPATH);
+ Set paths = new HashSet();
+ for (Row r : new RowIterable(q.execute().getRows())) {
+ paths.add(r.getPath());
+ }
+ assertEquals(new HashSet(Arrays.asList("/folder1", "/folder2", "/folder2/folder3")),
+ paths);
+ }
}