You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ch...@apache.org on 2014/11/26 09:06:53 UTC
svn commit: r1641771 [1/2] - in /jackrabbit/oak/trunk/oak-lucene/src:
main/java/org/apache/jackrabbit/oak/plugins/index/lucene/
main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/
test/java/org/apache/jackrabbit/oak/plugins/index/lucene/
Author: chetanm
Date: Wed Nov 26 08:06:52 2014
New Revision: 1641771
URL: http://svn.apache.org/r1641771
Log:
OAK-2268 - Support index time Aggregation of repository nodes
Refer to OAK-2268 for implementation notes
Added:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/Aggregate.java (with props)
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/AggregateTest.java (with props)
Removed:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/RelativeProperty.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/RelativePropertyTest.java
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/ConfigUtil.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinitionTest.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexAggregationTest2.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditorTest.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexQueryTest.java
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/TestUtil.java
Added: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/Aggregate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/Aggregate.java?rev=1641771&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/Aggregate.java (added)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/Aggregate.java Wed Nov 26 08:06:52 2014
@@ -0,0 +1,535 @@
+/*
+ * 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.lucene;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.annotation.CheckForNull;
+
+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.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.lucene.util.ConfigUtil;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.toArray;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.newArrayListWithCapacity;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+
+class Aggregate {
+ public static final String MATCH_ALL = "*";
+
+ /**
+ * recursive aggregation (for same type nodes) limit default value.
+ */
+ public static final int RECURSIVE_AGGREGATION_LIMIT_DEFAULT = 5;
+ private final String nodeTypeName;
+ private final List<? extends Include> includes;
+ final int reAggregationLimit;
+ private final List<NodeInclude> relativeNodeIncludes;
+
+ Aggregate(String nodeTypeName) {
+ this(nodeTypeName, Collections.<Include>emptyList());
+ }
+
+ Aggregate(String nodeTypeName, List<? extends Include> includes) {
+ this(nodeTypeName, includes, RECURSIVE_AGGREGATION_LIMIT_DEFAULT);
+ }
+
+ Aggregate(String nodeTypeName, List<? extends Include> includes,
+ int recursionLimit) {
+ this.nodeTypeName = nodeTypeName;
+ this.includes = ImmutableList.copyOf(includes);
+ this.reAggregationLimit = recursionLimit;
+ this.relativeNodeIncludes = findRelativeNodeIncludes(includes);
+ }
+
+ public List<? extends Include> getIncludes() {
+ return includes;
+ }
+
+ public void collectAggregates(NodeState root, ResultCollector collector) throws CommitFailedException {
+ if (nodeTypeName.equals(ConfigUtil.getPrimaryTypeName(root))) {
+ List<Matcher> matchers = createMatchers();
+ collectAggregates(root, matchers, collector);
+ }
+ }
+
+ public List<Matcher> createMatchers(AggregateRoot root){
+ List<Matcher> matchers = newArrayListWithCapacity(includes.size());
+ for (Include include : includes) {
+ matchers.add(new Matcher(this, include, root));
+ }
+ return matchers;
+ }
+
+ public boolean hasRelativeNodeInclude(String nodePath) {
+ for (NodeInclude ni : relativeNodeIncludes){
+ if (ni.matches(nodePath)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return nodeTypeName;
+ }
+
+ private static void collectAggregates(NodeState nodeState, List<Matcher> matchers,
+ ResultCollector collector) throws CommitFailedException {
+ for (ChildNodeEntry cne : nodeState.getChildNodeEntries()) {
+ List<Matcher> nextSet = newArrayListWithCapacity(matchers.size());
+ for (Matcher m : matchers) {
+ Matcher result = m.match(cne.getName(), cne.getNodeState());
+ if (result.getStatus() == Matcher.Status.MATCH_FOUND){
+ result.collectResults(collector);
+ }
+
+ if (result.getStatus() != Matcher.Status.FAIL){
+ nextSet.addAll(result.nextSet());
+ }
+ }
+ if (!nextSet.isEmpty()) {
+ collectAggregates(cne.getNodeState(), nextSet, collector);
+ }
+ }
+ }
+
+ private List<Matcher> createMatchers() {
+ List<Matcher> matchers = newArrayListWithCapacity(includes.size());
+ for (Include include : includes) {
+ matchers.add(new Matcher(this, include));
+ }
+ return matchers;
+ }
+
+ private static List<NodeInclude> findRelativeNodeIncludes(List<? extends Include> includes) {
+ List<NodeInclude> result = newArrayList();
+ for (Include i : includes){
+ if (i instanceof NodeInclude){
+ NodeInclude ni = (NodeInclude) i;
+ if (ni.relativeNode){
+ result.add(ni);
+ }
+ }
+ }
+ return ImmutableList.copyOf(result);
+ }
+
+ public static interface AggregateMapper {
+ @CheckForNull
+ Aggregate getAggregate(String nodeTypeName);
+ }
+
+ //~-----------------------------------------------------< Includes >
+
+ public static abstract class Include<T> {
+ protected final String[] elements;
+
+ public Include(String pattern) {
+ this.elements = computeElements(pattern);
+ }
+
+ public boolean match(String name, NodeState nodeState, int depth) {
+ String element = elements[depth];
+ if (MATCH_ALL.equals(element)) {
+ return true;
+ } else if (element.equals(name)) {
+ return true;
+ }
+ return false;
+ }
+
+ public int maxDepth() {
+ return elements.length;
+ }
+
+ public void collectResults(T rootInclude, String rootIncludePath,
+ String nodePath, NodeState nodeState, ResultCollector results)
+ throws CommitFailedException {
+ collectResults(nodePath, nodeState, results);
+ }
+
+ public void collectResults(String nodePath, NodeState nodeState,
+ ResultCollector results) throws CommitFailedException {
+
+ }
+
+ public abstract boolean aggregatesProperty(String name);
+
+ @CheckForNull
+ public Aggregate getAggregate(NodeState matchedNodeState) {
+ return null;
+ }
+ }
+
+ public static class NodeInclude extends Include<NodeInclude> {
+ final String primaryType;
+ final boolean relativeNode;
+ private final String pattern;
+ private final AggregateMapper aggMapper;
+
+ public NodeInclude(AggregateMapper mapper, String pattern) {
+ this(mapper, null, pattern, false);
+ }
+
+ public NodeInclude(AggregateMapper mapper, String primaryType, String pattern, boolean relativeNode) {
+ super(pattern);
+ this.pattern = pattern;
+ this.primaryType = primaryType;
+ this.aggMapper = mapper;
+ this.relativeNode = relativeNode;
+ }
+
+ @Override
+ public boolean match(String name, NodeState nodeState, int depth) {
+ //As per JR2 the primaryType is enforced on last element
+ //last segment -> add to collector if node type matches
+ if (depth == maxDepth() - 1
+ && primaryType != null
+ && !primaryType.equals(ConfigUtil.getPrimaryTypeName(nodeState))) {
+ return false;
+ }
+ return super.match(name, nodeState, depth);
+ }
+
+ @Override
+ public void collectResults(NodeInclude rootInclude, String rootIncludePath, String nodePath,
+ NodeState nodeState, ResultCollector results) throws CommitFailedException {
+ //For supporting jcr:contains(jcr:content, 'foo')
+ if (rootInclude != this && rootInclude.relativeNode){
+ results.onResult(new NodeIncludeResult(nodePath, rootIncludePath, nodeState));
+ }
+
+ //For supporting jcr:contains(., 'foo')
+ results.onResult(new NodeIncludeResult(nodePath, nodeState));
+ }
+
+ @Override
+ public boolean aggregatesProperty(String name) {
+ return true;
+ }
+
+ @Override
+ public Aggregate getAggregate(NodeState matchedNodeState) {
+ return aggMapper.getAggregate(ConfigUtil.getPrimaryTypeName(matchedNodeState));
+ }
+
+ @Override
+ public String toString() {
+ return "NodeInclude{" +
+ "primaryType='" + primaryType + '\'' +
+ ", relativeNode=" + relativeNode +
+ ", pattern='" + pattern + '\'' +
+ '}';
+ }
+
+ public boolean matches(String nodePath) {
+ List<String> pathElements = ImmutableList.copyOf(PathUtils.elements(nodePath));
+ if (pathElements.size() != elements.length){
+ return false;
+ }
+
+ for (int i = 0; i < elements.length; i++){
+ String element = elements[i];
+ if (MATCH_ALL.equals(element)) {
+ continue;
+ }
+
+ if (!element.equals(pathElements.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public static class PropertyInclude extends Include<PropertyInclude> {
+ private final PropertyDefinition propertyDefinition;
+ private final String propertyName;
+ private final Pattern pattern;
+ private final String parentPath;
+
+ public PropertyInclude(PropertyDefinition pd) {
+ super(getParentPath(pd.name));
+ this.propertyDefinition = pd;
+ this.propertyName = PathUtils.getName(pd.name);
+ this.parentPath = getParentPath(pd.name);
+
+ if (pd.isRegexp) {
+ pattern = Pattern.compile(propertyName);
+ } else {
+ pattern = null;
+ }
+ }
+
+ @Override
+ public void collectResults(String nodePath, NodeState nodeState, ResultCollector results)
+ throws CommitFailedException {
+ if (pattern != null) {
+ for (PropertyState ps : nodeState.getProperties()) {
+ if (pattern.matcher(ps.getName()).matches()) {
+ results.onResult(new PropertyIncludeResult(ps, propertyDefinition, parentPath));
+ }
+ }
+ } else {
+ PropertyState ps = nodeState.getProperty(propertyName);
+ if (ps != null) {
+ results.onResult(new PropertyIncludeResult(ps, propertyDefinition, parentPath));
+ }
+ }
+ }
+
+ @Override
+ public boolean aggregatesProperty(String name) {
+ if (pattern != null){
+ return pattern.matcher(name).matches();
+ }
+ return propertyName.equals(name);
+ }
+
+ @Override
+ public String toString() {
+ return propertyDefinition.toString();
+ }
+ }
+
+ public static interface ResultCollector {
+ void onResult(NodeIncludeResult result) throws CommitFailedException;
+
+ void onResult(PropertyIncludeResult result) throws CommitFailedException;
+ }
+
+ public static class NodeIncludeResult {
+ final NodeState nodeState;
+ final String nodePath;
+ final String rootIncludePath;
+
+ public NodeIncludeResult(String nodePath, NodeState nodeState) {
+ this(nodePath, null, nodeState);
+ }
+
+ public NodeIncludeResult(String nodePath, String rootIncludePath, NodeState nodeState) {
+ this.nodePath = nodePath;
+ this.nodeState = nodeState;
+ this.rootIncludePath = rootIncludePath;
+ }
+
+ public boolean isRelativeNode(){
+ return rootIncludePath != null;
+ }
+
+ @Override
+ public String toString() {
+ return "NodeIncludeResult{" +
+ "nodePath='" + nodePath + '\'' +
+ ", rootIncludePath='" + rootIncludePath + '\'' +
+ '}';
+ }
+ }
+
+ public static class PropertyIncludeResult {
+ final PropertyState propertyState;
+ final PropertyDefinition pd;
+ final String propertyPath;
+ final String nodePath;
+
+ public PropertyIncludeResult(PropertyState propertyState, PropertyDefinition pd,
+ String parentPath) {
+ this.propertyState = propertyState;
+ this.pd = pd;
+ this.nodePath = parentPath;
+ this.propertyPath = PathUtils.concat(parentPath, propertyState.getName());
+ }
+ }
+
+ public static interface AggregateRoot {
+ void markDirty();
+ }
+
+ public static class Matcher {
+ public static enum Status {CONTINUE, MATCH_FOUND, FAIL}
+
+ private static class RootState {
+ final AggregateRoot root;
+ final Aggregate rootAggregate;
+ final Include rootInclude;
+
+ private RootState(AggregateRoot root, Aggregate rootAggregate, Include rootInclude) {
+ this.root = root;
+ this.rootAggregate = rootAggregate;
+ this.rootInclude = rootInclude;
+ }
+ }
+
+ private final RootState rootState;
+ private final Include currentInclude;
+ /**
+ * Current depth in the include pattern.
+ */
+ private final int depth;
+ private final Status status;
+ private final NodeState matchedNodeState;
+ private final String currentPath;
+
+ private final List<String> aggregateStack;
+
+ public Matcher(Aggregate aggregate, Include currentInclude) {
+ this(aggregate, currentInclude, null);
+ }
+
+ public Matcher(Aggregate aggregate, Include include, AggregateRoot root) {
+ this.rootState = new RootState(root, aggregate, include);
+ this.depth = 0;
+ this.currentInclude = include;
+ this.status = Status.CONTINUE;
+ this.currentPath = null;
+ this.matchedNodeState = null;
+ this.aggregateStack = Collections.emptyList();
+ }
+
+ private Matcher(Matcher m, Status status, int depth) {
+ checkArgument(status == Status.FAIL);
+ this.rootState = m.rootState;
+ this.depth = depth;
+ this.currentInclude = m.currentInclude;
+ this.status = status;
+ this.currentPath = null;
+ this.matchedNodeState = null;
+ this.aggregateStack = m.aggregateStack;
+ }
+
+ private Matcher(Matcher m, Status status, int depth,
+ NodeState matchedNodeState, String currentPath) {
+ checkArgument(status != Status.FAIL);
+ this.rootState = m.rootState;
+ this.depth = depth;
+ this.currentInclude = m.currentInclude;
+ this.status = status;
+ this.matchedNodeState = matchedNodeState;
+ this.currentPath = currentPath;
+ this.aggregateStack = m.aggregateStack;
+ }
+
+ private Matcher(Matcher m, Include i, String currentPath) {
+ checkArgument(m.status == Status.MATCH_FOUND);
+ this.rootState = m.rootState;
+ this.depth = 0;
+ this.currentInclude = i;
+ this.status = Status.CONTINUE;
+ this.matchedNodeState = null;
+ this.currentPath = currentPath;
+
+ List<String> paths = newArrayList(m.aggregateStack);
+ paths.add(currentPath);
+ this.aggregateStack = ImmutableList.copyOf(paths);
+ }
+
+ public Matcher match(String name, NodeState nodeState) {
+ boolean result = currentInclude.match(name, nodeState, depth);
+ if (result){
+ if (hasMore()){
+ return new Matcher(this, Status.CONTINUE, depth, nodeState, path(name));
+ } else {
+ return new Matcher(this, Status.MATCH_FOUND, depth, nodeState, path(name));
+ }
+ } else {
+ return new Matcher(this, Status.FAIL, depth);
+ }
+ }
+
+ public Collection<Matcher> nextSet() {
+ checkArgument(status != Status.FAIL);
+
+ if (status == Status.MATCH_FOUND){
+ Aggregate nextAgg = currentInclude.getAggregate(matchedNodeState);
+ if (nextAgg != null){
+ int recursionLevel = aggregateStack.size() + 1;
+
+ if (recursionLevel >= rootState.rootAggregate.reAggregationLimit){
+ return Collections.emptyList();
+ }
+
+ List<Matcher> result = Lists.newArrayListWithCapacity(nextAgg.includes.size());
+ for (Include i : nextAgg.includes){
+ result.add(new Matcher(this, i, currentPath));
+ }
+ return result;
+ }
+ return Collections.emptyList();
+ }
+
+ return Collections.singleton(new Matcher(this, status, depth + 1,
+ null, currentPath));
+ }
+
+ public void collectResults(ResultCollector results)
+ throws CommitFailedException {
+ checkArgument(status == Status.MATCH_FOUND);
+ String rootIncludePath = aggregateStack.isEmpty() ? null : aggregateStack.get(0);
+ currentInclude.collectResults(rootState.rootInclude, rootIncludePath,
+ currentPath, matchedNodeState, results);
+ }
+
+ public void markRootDirty() {
+ checkArgument(status == Status.MATCH_FOUND);
+ rootState.root.markDirty();
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public boolean aggregatesProperty(String name) {
+ checkArgument(status == Status.MATCH_FOUND);
+ return currentInclude.aggregatesProperty(name);
+ }
+
+ private boolean hasMore() {
+ return depth < currentInclude.maxDepth() - 1;
+ }
+
+ private String path(String nodeName){
+ if (currentPath == null){
+ return nodeName;
+ } else {
+ return PathUtils.concat(currentPath, nodeName);
+ }
+ }
+ }
+
+ //~--------------------------------------------------< utility >
+
+ private static String[] computeElements(String path) {
+ return toArray(elements(path), String.class);
+ }
+
+}
Propchange: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/Aggregate.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldFactory.java Wed Nov 26 08:06:52 2014
@@ -104,6 +104,10 @@ public final class FieldFactory {
return new TextField(FULLTEXT, value, NO);
}
+ public static Field newFulltextField(String name, String value) {
+ return new TextField(FieldNames.createFulltextFieldName(name), value, NO);
+ }
+
/**
* Date values are saved with sec resolution
* @param date jcr data string
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/FieldNames.java Wed Nov 26 08:06:52 2014
@@ -45,7 +45,12 @@ public final class FieldNames {
/**
* Prefix for all field names that are fulltext indexed by property name.
*/
- public static final String FULLTEXT_PREFIX = ":full";
+ public static final String ANALYZED_FIELD_PREFIX = "full:";
+
+ /**
+ * Prefix used for storing fulltext of relative node
+ */
+ public static final String FULLTEXT_RELATIVE_NODE = "fullnode:";
/**
* Used to select only the PATH field from the lucene documents
@@ -66,6 +71,13 @@ public final class FieldNames {
}
public static String createAnalyzedFieldName(String pname) {
- return FULLTEXT_PREFIX + pname;
+ return ANALYZED_FIELD_PREFIX + pname;
+ }
+
+ public static String createFulltextFieldName(String nodeRelativePath) {
+ if (nodeRelativePath == null){
+ return FULLTEXT;
+ }
+ return FULLTEXT_RELATIVE_NODE + nodeRelativePath;
}
}
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexDefinition.java Wed Nov 26 08:06:52 2014
@@ -20,7 +20,6 @@
package org.apache.jackrabbit.oak.plugins.index.lucene;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -44,6 +43,7 @@ import org.apache.jackrabbit.JcrConstant
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
@@ -62,6 +62,7 @@ import static com.google.common.collect.
import static com.google.common.collect.Sets.newHashSet;
import static org.apache.jackrabbit.JcrConstants.NT_BASE;
import static org.apache.jackrabbit.oak.api.Type.NAMES;
+import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.DECLARING_NODE_TYPES;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_COUNT;
@@ -84,7 +85,7 @@ import static org.apache.jackrabbit.oak.
import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.NODE_TYPES_PATH;
import static org.apache.jackrabbit.oak.plugins.tree.TreeConstants.OAK_CHILD_ORDER;
-class IndexDefinition {
+class IndexDefinition implements Aggregate.AggregateMapper{
private static final Logger log = LoggerFactory.getLogger(IndexDefinition.class);
/**
@@ -123,12 +124,6 @@ class IndexDefinition {
private final Codec codec;
- private final Set<String> relativePropNames;
-
- private final List<RelativeProperty> relativeProperties;
-
- private final int relativePropsMaxLevels;
-
/**
* Defines the maximum estimated entry count configured.
* Defaults to {#DEFAULT_ENTRY_COUNT}
@@ -150,6 +145,10 @@ class IndexDefinition {
private final IndexFormatVersion version;
+ private final Map<String, Aggregate> aggregates;
+
+ private final boolean indexesAllTypes;
+
public IndexDefinition(NodeState root, NodeState defn) {
this(root, defn, null);
}
@@ -170,6 +169,8 @@ class IndexDefinition {
this.blobSize = getOptionalValue(defn, BLOB_SIZE, DEFAULT_BLOB_SIZE);
this.testMode = getOptionalValue(defn, LuceneIndexConstants.TEST_MODE, false);
+ this.aggregates = collectAggregates(defn);
+
NodeState rulesState = defn.getChildNode(LuceneIndexConstants.INDEX_RULES);
if (!rulesState.exists()){
rulesState = createIndexRules(defn).getNodeState();
@@ -179,12 +180,9 @@ class IndexDefinition {
this.indexRules = collectIndexRules(rulesState, definedIndexRules);
this.definedRules = ImmutableList.copyOf(definedIndexRules);
- this.relativeProperties = collectRelativeProperties(definedIndexRules);
this.fullTextEnabled = hasFulltextEnabledIndexRule(definedIndexRules);
this.propertyIndexEnabled = hasPropertyIndexEnabledIndexRule(definedIndexRules);
- this.relativePropNames = collectRelPropertyNames(relativeProperties);
- this.relativePropsMaxLevels = getRelPropertyMaxLevels(relativeProperties);
this.evaluatePathRestrictions = getOptionalValue(defn, EVALUATE_PATH_RESTRICTION, false);
String functionName = getOptionalValue(defn, LuceneIndexConstants.FUNC_NAME, null);
@@ -200,6 +198,8 @@ class IndexDefinition {
} else {
this.entryCount = DEFAULT_ENTRY_COUNT;
}
+
+ this.indexesAllTypes = areAllTypesIndexed();
}
public boolean isFullTextEnabled() {
@@ -241,36 +241,6 @@ class IndexDefinition {
return entryCount;
}
- public Collection<RelativeProperty> getRelativeProps() {
- return relativeProperties;
- }
-
- /**
- * Collects the relative properties where the property name matches given name. Note
- * that multiple relative properties can end with same name e.g. foo/bar, baz/bar
- *
- * @param name property name without path
- * @param relProps matching relative properties where the relative property path ends
- * with given name
- */
- public void collectRelPropsForName(String name, Collection<RelativeProperty> relProps){
- if(hasRelativeProperty(name)){
- for(RelativeProperty rp : relativeProperties){
- if(rp.name.equals(name)){
- relProps.add(rp);
- }
- }
- }
- }
-
- boolean hasRelativeProperties(){
- return !relativeProperties.isEmpty();
- }
-
- boolean hasRelativeProperty(String name) {
- return relativePropNames.contains(name);
- }
-
public IndexFormatVersion getVersion() {
return version;
}
@@ -287,34 +257,48 @@ class IndexDefinition {
return evaluatePathRestrictions;
}
+ public boolean indexesAllTypes() {
+ return indexesAllTypes;
+ }
+
@Override
public String toString() {
return "IndexDefinition : " + indexName;
}
- //~------------------------------------------< Internal >
+ //~---------------------------------------------------< Aggregates >
- private int getRelPropertyMaxLevels(Collection<RelativeProperty> props) {
- int max = -1;
- for (RelativeProperty prop : props) {
- max = Math.max(max, prop.ancestors.length);
+ @CheckForNull
+ public Aggregate getAggregate(String nodeType){
+ Aggregate agg = aggregates.get(nodeType);
+ return agg != null ? agg : null;
+ }
+
+ private Map<String, Aggregate> collectAggregates(NodeState defn) {
+ Map<String, Aggregate> aggregateMap = newHashMap();
+
+ for (ChildNodeEntry cne : defn.getChildNode(LuceneIndexConstants.AGGREGATES).getChildNodeEntries()) {
+ String nodeType = cne.getName();
+ int recursionLimit = getOptionalValue(cne.getNodeState(), LuceneIndexConstants.AGG_RECURSIVE_LIMIT,
+ Aggregate.RECURSIVE_AGGREGATION_LIMIT_DEFAULT);
+
+ List<Aggregate.Include> includes = newArrayList();
+ for (ChildNodeEntry include : cne.getNodeState().getChildNodeEntries()) {
+ NodeState is = include.getNodeState();
+ String primaryType = is.getString(LuceneIndexConstants.AGG_PRIMARY_TYPE);
+ String path = is.getString(LuceneIndexConstants.AGG_PATH);
+ boolean relativeNode = getOptionalValue(is, LuceneIndexConstants.AGG_RELATIVE_NODE, false);
+ if (path == null) {
+ log.warn("Aggregate pattern in {} does not have required property [{}]. {} aggregate rule would " +
+ "be ignored", this, LuceneIndexConstants.AGG_PATH, include.getName());
+ continue;
+ }
+ includes.add(new Aggregate.NodeInclude(this, primaryType, path, relativeNode));
+ }
+ aggregateMap.put(nodeType, new Aggregate(nodeType, includes, recursionLimit));
}
- return max;
- }
-
- public int getRelPropertyMaxLevels() {
- return relativePropsMaxLevels;
- }
- private Codec createCodec() {
- String codecName = getOptionalValue(definition, LuceneIndexConstants.CODEC_NAME, null);
- Codec codec = null;
- if (codecName != null) {
- codec = Codec.forName(codecName);
- } else if (fullTextEnabled) {
- codec = new OakCodec();
- }
- return codec;
+ return ImmutableMap.copyOf(aggregateMap);
}
//~---------------------------------------------------< IndexRule >
@@ -440,6 +424,11 @@ class IndexDefinition {
return ImmutableMap.copyOf(nt2rules);
}
+ private boolean areAllTypesIndexed() {
+ IndexingRule ntBaseRule = getApplicableIndexingRule(NT_BASE);
+ return ntBaseRule != null;
+ }
+
public class IndexingRule {
private final String baseNodeType;
private final String nodeTypeName;
@@ -453,9 +442,8 @@ class IndexDefinition {
final boolean fulltextEnabled;
final boolean propertyIndexEnabled;
- //TODO To be relooked with support for aggregation
- final Map<String,RelativeProperty> relativeProps;
- final Set<String> relativePropNames;
+ final Aggregate aggregate;
+ final Aggregate propAggregate;
IndexingRule(String nodeTypeName, NodeState config) {
@@ -469,14 +457,14 @@ class IndexDefinition {
this.propertyTypes = getSupportedTypes(config, INCLUDE_PROPERTY_TYPES, TYPES_ALLOW_ALL);
List<NamePattern> namePatterns = newArrayList();
- Map<String,RelativeProperty> relativeProps = newHashMap();
- this.propConfigs = collectPropConfigs(config, namePatterns, relativeProps);
+ List<Aggregate.Include> propIncludes = newArrayList();
+ this.propConfigs = collectPropConfigs(config, namePatterns, propIncludes);
+ this.propAggregate = new Aggregate(nodeTypeName, propIncludes);
+ this.aggregate = combine(propAggregate, nodeTypeName);
this.namePatterns = ImmutableList.copyOf(namePatterns);
this.fulltextEnabled = hasAnyFullTextEnabledProperty();
this.propertyIndexEnabled = hasAnyPropertyIndexConfigured();
- this.relativeProps = ImmutableMap.copyOf(relativeProps);
- this.relativePropNames = collectRelPropertyNames(this.relativeProps.values());
}
/**
@@ -497,9 +485,9 @@ class IndexDefinition {
this.inherited = original.inherited;
this.propertyTypes = original.propertyTypes;
this.fulltextEnabled = original.fulltextEnabled;
- this.relativeProps = original.relativeProps;
- this.relativePropNames = original.relativePropNames;
this.propertyIndexEnabled = original.propertyIndexEnabled;
+ this.propAggregate = original.propAggregate;
+ this.aggregate = combine(propAggregate, nodeTypeName);
}
/**
@@ -533,6 +521,10 @@ class IndexDefinition {
return str;
}
+ public boolean isAggregated(String nodePath) {
+ return aggregate.hasRelativeNodeInclude(nodePath);
+ }
+
/**
* Returns <code>true</code> if this rule applies to the given node
* <code>state</code>.
@@ -590,12 +582,12 @@ class IndexDefinition {
return IndexDefinition.includePropertyType(propertyTypes, type);
}
- public Collection<RelativeProperty> getRelativeProps() {
- return relativeProps.values();
+ public Aggregate getAggregate() {
+ return aggregate;
}
private Map<String, PropertyDefinition> collectPropConfigs(NodeState config, List<NamePattern> patterns,
- Map<String,RelativeProperty> relativeProps) {
+ List<Aggregate.Include> propAggregate) {
Map<String, PropertyDefinition> propDefns = newHashMap();
NodeState propNode = config.getChildNode(LuceneIndexConstants.PROP_NODE);
@@ -621,8 +613,8 @@ class IndexDefinition {
propDefns.put(pd.name, pd);
}
- if (RelativeProperty.isRelativeProperty(pd.name)){
- relativeProps.put(pd.name, new RelativeProperty(pd.name, pd));
+ if (isRelativeProperty(pd.name)){
+ propAggregate.add(new Aggregate.PropertyInclude(pd));
}
}
}
@@ -658,6 +650,16 @@ class IndexDefinition {
}
return false;
}
+
+ private Aggregate combine(Aggregate propAggregate, String nodeTypeName){
+ Aggregate nodeTypeAgg = IndexDefinition.this.getAggregate(nodeTypeName);
+ List<Aggregate.Include> includes = newArrayList();
+ includes.addAll(propAggregate.getIncludes());
+ if (nodeTypeAgg != null){
+ includes.addAll(nodeTypeAgg.getIncludes());
+ }
+ return new Aggregate(nodeTypeName, includes);
+ }
}
/**
@@ -772,7 +774,7 @@ class IndexDefinition {
String propNodeName = propName;
//For proper propName use the propName as childNode name
- if(RelativeProperty.isRelativeProperty(propName)
+ if(isRelativeProperty(propName)
|| propName.equals(includeAllProp)){
propNodeName = "prop" + i++;
}
@@ -834,17 +836,31 @@ class IndexDefinition {
private static NodeState getPropDefnNode(NodeState defn, String propName){
NodeState propNode = defn.getChildNode(LuceneIndexConstants.PROP_NODE);
NodeState propDefNode;
- if (RelativeProperty.isRelativeProperty(propName)) {
- propDefNode = new RelativeProperty(propName).getPropDefnNode(propNode);
+ if (isRelativeProperty(propName)) {
+ NodeState result = propNode;
+ for (String name : PathUtils.elements(propName)) {
+ result = result.getChildNode(name);
+ }
+ propDefNode = result;
} else {
propDefNode = propNode.getChildNode(propName);
}
return propDefNode.exists() ? propDefNode : null;
}
-
//~---------------------------------------------< utility >
+ private Codec createCodec() {
+ String codecName = getOptionalValue(definition, LuceneIndexConstants.CODEC_NAME, null);
+ Codec codec = null;
+ if (codecName != null) {
+ codec = Codec.forName(codecName);
+ } else if (fullTextEnabled) {
+ codec = new OakCodec();
+ }
+ return codec;
+ }
+
private static String determineIndexName(NodeState defn, String indexPath) {
String indexName = defn.getString(PROP_NAME);
if (indexName == null){
@@ -967,22 +983,6 @@ class IndexDefinition {
return false;
}
- private static Set<String> collectRelPropertyNames(Collection<RelativeProperty> props) {
- Set<String> propNames = newHashSet();
- for (RelativeProperty prop : props) {
- propNames.add(prop.name);
- }
- return ImmutableSet.copyOf(propNames);
- }
-
- private static List<RelativeProperty> collectRelativeProperties(List<IndexingRule> indexRules) {
- List<RelativeProperty> relProps = newArrayList();
- for (IndexingRule rule : indexRules){
- relProps.addAll(rule.getRelativeProps());
- }
- return ImmutableList.copyOf(relProps);
- }
-
private static void markAsNtUnstructured(NodeBuilder nb){
nb.setProperty(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED, Type.NAME);
}
@@ -1060,4 +1060,8 @@ class IndexDefinition {
private static boolean hasIndexingRules(NodeState defn) {
return defn.getChildNode(LuceneIndexConstants.INDEX_RULES).exists();
}
+
+ private static boolean isRelativeProperty(String propertyName){
+ return !isAbsolute(propertyName) && PathUtils.getNextSlash(propertyName, 0) > 0;
+ }
}
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlanner.java Wed Nov 26 08:06:52 2014
@@ -20,26 +20,37 @@
package org.apache.jackrabbit.oak.plugins.index.lucene;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.CheckForNull;
+import com.google.common.collect.Iterables;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.IndexingRule;
import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextTerm;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextVisitor;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.lucene.index.IndexReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getAncestorPath;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getDepth;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
import static org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
import static org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan;
import static org.apache.jackrabbit.oak.spi.query.QueryIndex.OrderEntry;
class IndexPlanner {
+ private static final Logger log = LoggerFactory.getLogger(IndexPlanner.class);
private final IndexDefinition defn;
private final Filter filter;
private final String indexPath;
@@ -88,9 +99,11 @@ class IndexPlanner {
}
private IndexPlan.Builder getPlanBuilder() {
+ log.debug("Evaluating plan with index definition {}", defn);
FullTextExpression ft = filter.getFullTextConstraint();
if (!defn.getVersion().isAtLeast(IndexFormatVersion.V2)){
+ log.debug("Index is old format. Not supported");
return null;
}
@@ -133,8 +146,11 @@ class IndexPlanner {
}
boolean evalPathRestrictions = canEvalPathRestrictions();
- //TODO For the full text case need to determine if all field names
- //used in fulltext expression are fulltext indexed or not
+ boolean canEvalAlFullText = canEvalAllFullText(indexingRule, ft);
+
+ if (ft != null && !canEvalAlFullText){
+ return null;
+ }
//Fulltext expression can also be like jcr:contains(jcr:content/metadata/@format, 'image')
@@ -157,6 +173,10 @@ class IndexPlanner {
costPerEntryFactor = 1;
}
+ if (ft == null){
+ result.enableNonFullTextConstraints();
+ }
+
return plan.setCostPerEntry(1.0 / costPerEntryFactor);
}
@@ -170,6 +190,71 @@ class IndexPlanner {
return null;
}
+ private boolean canEvalAllFullText(final IndexingRule indexingRule, FullTextExpression ft) {
+ if (ft == null){
+ return false;
+ }
+
+ final HashSet<String> relPaths = new HashSet<String>();
+ final HashSet<String> nonIndexedPaths = new HashSet<String>();
+ final AtomicBoolean relativeParentsFound = new AtomicBoolean();
+ ft.accept(new FullTextVisitor.FullTextVisitorBase() {
+ @Override
+ public boolean visit(FullTextTerm term) {
+ String p = term.getPropertyName();
+ String propertyPath = null;
+ String nodePath = null;
+ if (p == null) {
+ relPaths.add("");
+ } else if (p.startsWith("../") || p.startsWith("./")) {
+ relPaths.add(p);
+ relativeParentsFound.set(true);
+ } else if (getDepth(p) > 1) {
+ String parent = getParentPath(p);
+ if (LucenePropertyIndex.isNodePath(p)){
+ nodePath = parent;
+ } else {
+ propertyPath = p;
+ }
+ relPaths.add(parent);
+ } else {
+ propertyPath = p;
+ relPaths.add("");
+ }
+
+ if (nodePath != null
+ && !indexingRule.isAggregated(nodePath)){
+ nonIndexedPaths.add(p);
+ } else if (propertyPath != null
+ && !indexingRule.isIndexed(propertyPath)){
+ nonIndexedPaths.add(p);
+ }
+
+ return true;
+ }
+ });
+
+ if (relativeParentsFound.get()){
+ log.debug("Relative parents found {} which are not supported", relPaths);
+ return false;
+ }
+
+ if (!nonIndexedPaths.isEmpty()){
+ if (relPaths.size() > 1){
+ log.debug("Following relative property paths are not index", relPaths);
+ return false;
+ }
+ result.setParentPath(Iterables.getOnlyElement(relPaths, ""));
+ //Such path translation would only work if index contains
+ //all the nodes
+ return defn.indexesAllTypes();
+ } else {
+ result.setParentPath("");
+ }
+
+ return true;
+ }
+
private boolean canEvalPathRestrictions() {
if (filter.getPathRestriction() == Filter.PathRestriction.NO_RESTRICTION){
return false;
@@ -250,6 +335,7 @@ class IndexPlanner {
}
}
}
+ log.debug("No applicable IndexingRule found for any of the superTypes {}", filter.getSupertypes());
}
return null;
}
@@ -275,6 +361,11 @@ class IndexPlanner {
private List<PropertyDefinition> sortedProperties = newArrayList();
private Map<String, PropertyDefinition> propDefns = newHashMap();
+ private boolean nonFullTextConstraints;
+ private int parentDepth;
+ private String parentPathSegment;
+ private boolean relativize;
+
public PlanResult(String indexPath, IndexDefinition defn, IndexingRule indexingRule) {
this.indexPath = indexPath;
this.indexDefinition = defn;
@@ -288,5 +379,51 @@ class IndexPlanner {
public PropertyDefinition getOrderedProperty(int index){
return sortedProperties.get(index);
}
+
+ public boolean isPathTransformed(){
+ return relativize;
+ }
+
+ /**
+ * Transforms the given path if the query involved relative properties and index
+ * is not making use of aggregated properties. If the path
+ *
+ * @param path path to transform
+ * @return transformed path. Returns null if the path does not confirm to relative
+ * path requirements
+ */
+ @CheckForNull
+ public String transformPath(String path){
+ if (isPathTransformed()){
+ // get the base path
+ // ensure the path ends with the given
+ // relative path
+ if (!path.endsWith(parentPathSegment)) {
+ return null;
+ }
+ return getAncestorPath(path, parentDepth);
+ }
+ return path;
+ }
+
+ public boolean evaluateNonFullTextConstraints(){
+ return nonFullTextConstraints;
+ }
+
+ private void setParentPath(String relativePath){
+ parentPathSegment = "/" + relativePath;
+ if (relativePath.isEmpty()){
+ // we only restrict non-full-text conditions if there is
+ // no relative property in the full-text constraint
+ enableNonFullTextConstraints();
+ } else {
+ relativize = true;
+ parentDepth = getDepth(relativePath);
+ }
+ }
+
+ private void enableNonFullTextConstraints(){
+ nonFullTextConstraints = true;
+ }
}
}
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java Wed Nov 26 08:06:52 2014
@@ -154,4 +154,28 @@ public interface LuceneIndexConstants {
* Regex to allow inclusion of all immediate properties of the node
*/
String REGEX_ALL_PROPS = "^[^\\/]*$";
+
+ /**
+ * Node name storing the aggregate rules
+ */
+ String AGGREGATES = "aggregates";
+
+ String AGG_PRIMARY_TYPE = "primaryType";
+
+ /**
+ * Name of property which stores the aggregate include pattern like <code>jcr:content/metadata</code>
+ */
+ String AGG_PATH = "path";
+
+ /**
+ * Limit for maximum number of reaggregates allowed. For example if there is an aggregate of nt:folder
+ * and it also includes nt:folder then aggregation would traverse down untill this limit is hit
+ */
+ String AGG_RECURSIVE_LIMIT = "reaggregateLimit";
+
+ /**
+ * Boolean property indicating that separate fulltext field should be created for
+ * node represented by this pattern
+ */
+ String AGG_RELATIVE_NODE = "relativeNode";
}
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexEditor.java Wed Nov 26 08:06:52 2014
@@ -23,21 +23,29 @@ import static org.apache.jackrabbit.oak.
import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPathField;
import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldFactory.newPropertyField;
import static org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.ConfigUtil.getPrimaryTypeName;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.Blob;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
+import org.apache.jackrabbit.oak.plugins.index.lucene.Aggregate.Matcher;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
import org.apache.jackrabbit.oak.plugins.tree.ImmutableTree;
import org.apache.jackrabbit.oak.spi.commit.Editor;
@@ -67,7 +75,7 @@ import org.slf4j.LoggerFactory;
*
* @see LuceneIndex
*/
-public class LuceneIndexEditor implements IndexEditor {
+public class LuceneIndexEditor implements IndexEditor, Aggregate.AggregateRoot {
private static final Logger log =
LoggerFactory.getLogger(LuceneIndexEditor.class);
@@ -87,19 +95,19 @@ public class LuceneIndexEditor implement
private final NodeState root;
- private List<RelativeProperty> changedRelativeProps;
-
/**
* Flag indicating if the current tree being traversed has a deleted parent.
*/
private final boolean isDeleted;
- private final int deletedMaxLevels;
-
private ImmutableTree current;
private IndexDefinition.IndexingRule indexingRule;
+ private List<Matcher> currentMatchers = Collections.emptyList();
+
+ private final MatcherState matcherState;
+
LuceneIndexEditor(NodeState root, NodeBuilder definition, Analyzer analyzer,
IndexUpdateCallback updateCallback) throws CommitFailedException {
this.parent = null;
@@ -109,18 +117,19 @@ public class LuceneIndexEditor implement
updateCallback);
this.root = root;
this.isDeleted = false;
- this.deletedMaxLevels = -1;
+ this.matcherState = MatcherState.NONE;
}
private LuceneIndexEditor(LuceneIndexEditor parent, String name,
- boolean isDeleted, int deletedMaxLevels) {
+ MatcherState matcherState,
+ boolean isDeleted) {
this.parent = parent;
this.name = name;
this.path = null;
this.context = parent.context;
this.root = parent.root;
this.isDeleted = isDeleted;
- this.deletedMaxLevels = deletedMaxLevels;
+ this.matcherState = matcherState;
}
public String getPath() {
@@ -147,6 +156,10 @@ public class LuceneIndexEditor implement
}
indexingRule = getDefinition().getApplicableIndexingRule(current);
+
+ if (indexingRule != null) {
+ currentMatchers = indexingRule.getAggregate().createMatchers(this);
+ }
}
@Override
@@ -162,8 +175,8 @@ public class LuceneIndexEditor implement
}
}
- if (changedRelativeProps != null) {
- markParentsOnRelPropChange();
+ for (Matcher m : matcherState.affectedMatchers){
+ m.markRootDirty();
}
if (parent == null) {
@@ -182,30 +195,30 @@ public class LuceneIndexEditor implement
@Override
public void propertyAdded(PropertyState after) {
markPropertyChanged(after.getName());
- checkForRelativePropertyChange(after.getName());
+ checkAggregates(after.getName());
}
@Override
public void propertyChanged(PropertyState before, PropertyState after) {
markPropertyChanged(before.getName());
- checkForRelativePropertyChange(before.getName());
+ checkAggregates(before.getName());
}
@Override
public void propertyDeleted(PropertyState before) {
markPropertyChanged(before.getName());
- checkForRelativePropertyChange(before.getName());
+ checkAggregates(before.getName());
}
@Override
public Editor childNodeAdded(String name, NodeState after) {
- return new LuceneIndexEditor(this, name, false, -1);
+ return new LuceneIndexEditor(this, name, getMatcherState(name, after), false);
}
@Override
public Editor childNodeChanged(
String name, NodeState before, NodeState after) {
- return new LuceneIndexEditor(this, name, false, -1);
+ return new LuceneIndexEditor(this, name, getMatcherState(name, after), false);
}
@Override
@@ -228,18 +241,9 @@ public class LuceneIndexEditor implement
}
}
- if (getDefinition().hasRelativeProperties()) {
- int maxLevelsDown;
- if (isDeleted) {
- maxLevelsDown = deletedMaxLevels - 1;
- } else {
- maxLevelsDown = getDefinition()
- .getRelPropertyMaxLevels();
- }
- if (maxLevelsDown > 0) {
- // need to update aggregated properties on deletes
- return new LuceneIndexEditor(this, name, true, maxLevelsDown);
- }
+ MatcherState ms = getMatcherState(name, before);
+ if (!ms.isEmpty()){
+ return new LuceneIndexEditor(this, name, ms, true);
}
return null; // no need to recurse down the removed subtree
}
@@ -286,10 +290,10 @@ public class LuceneIndexEditor implement
dirty |= addTypedOrderedFields(fields, property, pname, pd);
}
- dirty |= indexProperty(path, fields, state, property, pname, pd);
+ dirty |= indexProperty(path, fields, state, property, pname, false, pd);
}
- dirty |= indexRelativeProperties(path, fields, state);
+ dirty |= indexAggregates(path, fields, state);
if (isUpdate && !dirty) {
// updated the state but had no relevant changes
@@ -320,15 +324,17 @@ public class LuceneIndexEditor implement
}
private boolean indexProperty(String path,
- List<Field> fields, NodeState state,
+ List<Field> fields,
+ NodeState state,
PropertyState property,
String pname,
+ boolean aggregateMode,
PropertyDefinition pd) throws CommitFailedException {
boolean includeTypeForFullText = indexingRule.includePropertyType(property.getType().tag());
if (Type.BINARY.tag() == property.getType().tag()
&& includeTypeForFullText) {
this.context.indexUpdate();
- fields.addAll(newBinary(property, state, path + "@" + pname));
+ fields.addAll(newBinary(property, state, null, path + "@" + pname));
return true;
} else {
boolean dirty = false;
@@ -345,7 +351,7 @@ public class LuceneIndexEditor implement
fields.add(newPropertyField(analyzedPropName, value, !pd.skipTokenization(pname), pd.stored));
}
- if (pd.nodeScopeIndex) {
+ if (pd.nodeScopeIndex && !aggregateMode) {
Field field = newFulltextField(value);
field.setBoost(pd.boost);
fields.add(field);
@@ -448,7 +454,7 @@ public class LuceneIndexEditor implement
}
private List<Field> newBinary(
- PropertyState property, NodeState state, String path) {
+ PropertyState property, NodeState state, String nodePath, String path) {
List<Field> fields = new ArrayList<Field>();
Metadata metadata = new Metadata();
if (JCR_DATA.equals(property.getName())) {
@@ -463,59 +469,180 @@ public class LuceneIndexEditor implement
}
for (Blob v : property.getValue(Type.BINARIES)) {
- fields.add(newFulltextField(parseStringValue(v, metadata, path)));
+ if (nodePath != null){
+ fields.add(newFulltextField(nodePath, parseStringValue(v, metadata, path)));
+ } else {
+ fields.add(newFulltextField(parseStringValue(v, metadata, path)));
+ }
+
}
return fields;
}
- private boolean indexRelativeProperties(String path, List<Field> fields, NodeState state) throws CommitFailedException {
+ //~-------------------------------------------------------< Aggregate >
+
+ @Override
+ public void markDirty() {
+ propertiesChanged = true;
+ }
+
+ private MatcherState getMatcherState(String name, NodeState after) {
+ List<Matcher> matched = Lists.newArrayList();
+ List<Matcher> inherited = Lists.newArrayList();
+ for (Matcher m : Iterables.concat(matcherState.inherited, currentMatchers)) {
+ Matcher result = m.match(name, after);
+ if (result.getStatus() == Matcher.Status.MATCH_FOUND){
+ matched.add(result);
+ }
+
+ if (result.getStatus() != Matcher.Status.FAIL){
+ inherited.addAll(result.nextSet());
+ }
+ }
+
+ if (!matched.isEmpty() || !inherited.isEmpty()) {
+ return new MatcherState(matched, inherited);
+ }
+ return MatcherState.NONE;
+ }
+
+ private boolean indexAggregates(final String path, final List<Field> fields,
+ final NodeState state) throws CommitFailedException {
+ final AtomicBoolean dirtyFlag = new AtomicBoolean();
+ indexingRule.getAggregate().collectAggregates(state, new Aggregate.ResultCollector() {
+ @Override
+ public void onResult(Aggregate.NodeIncludeResult result) throws CommitFailedException {
+ boolean dirty = indexAggregatedNode(path, fields, result);
+ if (dirty) {
+ dirtyFlag.set(true);
+ }
+ }
+
+ @Override
+ public void onResult(Aggregate.PropertyIncludeResult result) throws CommitFailedException {
+ boolean dirty = false;
+ if (result.pd.ordered) {
+ dirty |= addTypedOrderedFields(fields, result.propertyState,
+ result.propertyPath, result.pd);
+ }
+ dirty |= indexProperty(path, fields, state, result.propertyState,
+ result.propertyPath, true, result.pd);
+
+ if (dirty) {
+ dirtyFlag.set(true);
+ }
+ }
+ });
+ return dirtyFlag.get();
+ }
+
+ /**
+ * Create the fulltext field from the aggregated nodes. If result is for aggregate for a relative node
+ * include then
+ * @param path current node path
+ * @param fields indexed fields
+ * @param result aggregate result
+ * @return true if a field was created for passed node result
+ * @throws CommitFailedException
+ */
+ private boolean indexAggregatedNode(String path, List<Field> fields, Aggregate.NodeIncludeResult result)
+ throws CommitFailedException {
+ //rule for node being aggregated might be null if such nodes
+ //are not indexed on there own. In such cases we rely in current
+ //rule for some checks
+ IndexDefinition.IndexingRule ruleAggNode = context.getDefinition()
+ .getApplicableIndexingRule(getPrimaryTypeName(result.nodeState));
boolean dirty = false;
- for (RelativeProperty rp : indexingRule.getRelativeProps()){
- String pname = rp.propertyPath;
- PropertyState property = rp.getProperty(state);
+ for (PropertyState property : result.nodeState.getProperties()){
+ String pname = property.getName();
- if (property == null){
+ if (!isVisible(pname)) {
continue;
}
- if (rp.getPropertyDefinition().ordered) {
- dirty |= addTypedOrderedFields(fields, property, pname, rp.getPropertyDefinition());
+ //Check if type is indexed
+ int type = property.getType().tag();
+ if (ruleAggNode != null ) {
+ if (!ruleAggNode.includePropertyType(type)) {
+ continue;
+ }
+ } else if (!indexingRule.includePropertyType(type)){
+ continue;
}
- dirty |= indexProperty(path, fields, state, property, pname, rp.getPropertyDefinition());
+ if (Type.BINARY == property.getType()) {
+ String aggreagtedNodePath = PathUtils.concat(path, result.nodePath);
+ this.context.indexUpdate();
+ //Here the fulltext is being created for aggregate root hence nodePath passed
+ //should be null
+ String nodePath = result.isRelativeNode() ? result.rootIncludePath : null;
+ fields.addAll(newBinary(property, result.nodeState, nodePath, aggreagtedNodePath + "@" + pname));
+ dirty = true;
+ } else {
+ PropertyDefinition pd = null;
+ if (ruleAggNode != null){
+ pd = ruleAggNode.getConfig(pname);
+ }
+
+ if (pd != null && !pd.nodeScopeIndex){
+ continue;
+ }
+
+ for (String value : property.getValue(Type.STRINGS)) {
+ this.context.indexUpdate();
+ Field field = result.isRelativeNode() ?
+ newFulltextField(result.rootIncludePath, value) : newFulltextField(value) ;
+ if (pd != null) {
+ field.setBoost(pd.boost);
+ }
+ fields.add(field);
+ dirty = true;
+ }
+ }
}
return dirty;
}
- private void checkForRelativePropertyChange(String name) {
- if (isIndexable() && getDefinition().hasRelativeProperty(name)) {
- getDefinition().collectRelPropsForName(name, getChangedRelProps());
+ /**
+ * Determines which all matchers are affected by this property change
+ *
+ * @param name modified property name
+ */
+ private void checkAggregates(String name) {
+ for (Matcher m : matcherState.matched) {
+ if (!matcherState.affectedMatchers.contains(m)
+ && m.aggregatesProperty(name)) {
+ matcherState.affectedMatchers.add(m);
+ }
}
}
- private void markParentsOnRelPropChange() {
- for (RelativeProperty rp : changedRelativeProps) {
- LuceneIndexEditor p = this;
- for (String parentName : rp.ancestors) {
- if (p == null || !p.name.equals(parentName)) {
- p = null;
- break;
- }
- p = p.parent;
- }
+ private static class MatcherState {
+ final static MatcherState NONE = new MatcherState(Collections.<Matcher>emptyList(),
+ Collections.<Matcher>emptyList());
+
+ final List<Matcher> matched;
+ final List<Matcher> inherited;
+ final Set<Matcher> affectedMatchers;
- if (p != null) {
- p.relativePropertyChanged();
+ public MatcherState(List<Matcher> matched,
+ List<Matcher> inherited){
+ this.matched = matched;
+ this.inherited = inherited;
+
+ //Affected matches would only be used when there are
+ //some matched matchers
+ if (matched.isEmpty()){
+ affectedMatchers = Collections.emptySet();
+ } else {
+ affectedMatchers = Sets.newIdentityHashSet();
}
}
- }
- private List<RelativeProperty> getChangedRelProps(){
- if (changedRelativeProps == null) {
- changedRelativeProps = Lists.newArrayList();
+ public boolean isEmpty() {
+ return matched.isEmpty() && inherited.isEmpty();
}
- return changedRelativeProps;
}
private void markPropertyChanged(String name) {
@@ -534,10 +661,6 @@ public class LuceneIndexEditor implement
return indexingRule != null;
}
- private void relativePropertyChanged() {
- propertiesChanged = true;
- }
-
private String parseStringValue(Blob v, Metadata metadata, String path) {
WriteOutContentHandler handler = new WriteOutContentHandler();
try {
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java Wed Nov 26 08:06:52 2014
@@ -22,9 +22,7 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Deque;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -101,8 +99,6 @@ import static org.apache.jackrabbit.JcrC
import static org.apache.jackrabbit.oak.api.Type.LONG;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static org.apache.jackrabbit.oak.commons.PathUtils.denotesRoot;
-import static org.apache.jackrabbit.oak.commons.PathUtils.getAncestorPath;
-import static org.apache.jackrabbit.oak.commons.PathUtils.getDepth;
import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
import static org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.PATH;
import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
@@ -223,30 +219,19 @@ public class LucenePropertyIndex impleme
checkState(index != null, "The Lucene index is not available");
try {
FullTextExpression ft = filter.getFullTextConstraint();
- Set<String> relPaths = getRelativePaths(ft);
- if (relPaths.size() > 1) {
- return new MultiLuceneIndex(filter, root, relPaths).getPlan();
- }
- String parent = relPaths.size() == 0 ? "" : relPaths.iterator().next();
- // we only restrict non-full-text conditions if there is
- // no relative property in the full-text constraint
- boolean nonFullTextConstraints = parent.isEmpty();
StringBuilder sb = new StringBuilder("lucene:");
String path = pr(plan).indexPath;
sb.append(getIndexName(plan))
.append("(")
.append(path)
.append(") ");
- sb.append(getQuery(plan, null, nonFullTextConstraints, analyzer));
+ sb.append(getQuery(plan, null, analyzer));
if(plan.getSortOrder() != null && !plan.getSortOrder().isEmpty()){
sb.append(" ordering:").append(plan.getSortOrder());
}
if (ft != null) {
sb.append(" ft:(").append(ft).append(")");
}
- if (!parent.isEmpty()) {
- sb.append(" parent:").append(parent);
- }
return sb.toString();
} finally {
index.release();
@@ -262,17 +247,7 @@ public class LucenePropertyIndex impleme
public Cursor query(final IndexPlan plan, NodeState rootState) {
final Filter filter = plan.getFilter();
final Sort sort = getSort(plan);
- FullTextExpression ft = filter.getFullTextConstraint();
- Set<String> relPaths = getRelativePaths(ft);
- if (relPaths.size() > 1) {
- return new MultiLuceneIndex(filter, rootState, relPaths).query();
- }
-
- final String parent = relPaths.size() == 0 ? "" : relPaths.iterator().next();
- // we only restrict non-full-text conditions if there is
- // no relative property in the full-text constraint
- final boolean nonFullTextConstraints = parent.isEmpty();
- final int parentDepth = getDepth(parent);
+ final PlanResult pr = pr(plan);
QueryEngineSettings settings = filter.getQueryEngineSettings();
Iterator<LuceneResultRow> itr = new AbstractIterator<LuceneResultRow>() {
private final Deque<LuceneResultRow> queue = Queues.newArrayDeque();
@@ -299,17 +274,10 @@ public class LucenePropertyIndex impleme
if ("".equals(path)) {
path = "/";
}
- if (!parent.isEmpty()) {
- // TODO OAK-828 this breaks node aggregation
- // get the base path
- // ensure the path ends with the given
- // relative path
- // if (!path.endsWith("/" + parent)) {
- // continue;
- // }
- path = getAncestorPath(path, parentDepth);
+ if (pr.isPathTransformed()) {
+ path = pr.transformPath(path);
// avoid duplicate entries
- if (seenPaths.contains(path)) {
+ if (path == null || seenPaths.contains(path)) {
return null;
}
seenPaths.add(path);
@@ -331,8 +299,7 @@ public class LucenePropertyIndex impleme
checkState(indexNode != null);
try {
IndexSearcher searcher = indexNode.getSearcher();
- Query query = getQuery(plan, searcher.getIndexReader(),
- nonFullTextConstraints, analyzer);
+ Query query = getQuery(plan, searcher.getIndexReader(), analyzer);
TopDocs docs;
long time = System.currentTimeMillis();
if (lastDoc != null) {
@@ -382,6 +349,17 @@ public class LucenePropertyIndex impleme
return null;
}
+ /**
+ * In a fulltext term for jcr:contains(foo, 'bar') 'foo'
+ * is the property name. While in jcr:contains(foo/*, 'bar')
+ * 'foo' is node name
+ *
+ * @return true if the term is related to node
+ */
+ public static boolean isNodePath(String fulltextTermPath){
+ return fulltextTermPath.endsWith("/*");
+ }
+
private IndexNode acquireIndexNode(IndexPlan plan) {
return tracker.acquireIndexNode(pr(plan).indexPath);
}
@@ -427,54 +405,6 @@ public class LucenePropertyIndex impleme
}
/**
- * Get the set of relative paths of a full-text condition. For example, for
- * the condition "contains(a/b, 'hello') and contains(c/d, 'world'), the set
- * { "a", "c" } is returned. If there are no relative properties, then one
- * entry is returned (the empty string). If there is no expression, then an
- * empty set is returned.
- *
- * @param ft the full-text expression
- * @return the set of relative paths (possibly empty)
- */
- private static Set<String> getRelativePaths(FullTextExpression ft) {
- //TODO This would need to be relooked as with aggregation we would be
- //aggregating child properties also
- if (ft == null) {
- // there might be no full-text constraint when using the
- // LowCostLuceneIndexProvider which is used for testing
- // TODO if the LowCostLuceneIndexProvider is removed, we should do
- // the following instead:
-
- // throw new
- // IllegalStateException("Lucene index is used even when no full-text conditions are used for filter "
- // + filter);
-
- return Collections.emptySet();
- }
- final HashSet<String> relPaths = new HashSet<String>();
- ft.accept(new FullTextVisitor.FullTextVisitorBase() {
-
- @Override
- public boolean visit(FullTextTerm term) {
- String p = term.getPropertyName();
- if (p == null) {
- relPaths.add("");
- } else if (p.startsWith("../") || p.startsWith("./")) {
- throw new IllegalArgumentException("Relative parent is not supported:" + p);
- } else if (getDepth(p) > 1) {
- String parent = getParentPath(p);
- relPaths.add(parent);
- } else {
- relPaths.add("");
- }
- return true;
- }
- });
- return relPaths;
- }
-
-
- /**
* Get the Lucene query for the given filter.
*
* @param plan index plan containing filter details
@@ -486,8 +416,7 @@ public class LucenePropertyIndex impleme
* @param defn nodestate that contains the index definition
* @return the Lucene query
*/
- private static Query getQuery(IndexPlan plan, IndexReader reader,
- boolean nonFullTextConstraints, Analyzer analyzer) {
+ private static Query getQuery(IndexPlan plan, IndexReader reader, Analyzer analyzer) {
List<Query> qs = new ArrayList<Query>();
Filter filter = plan.getFilter();
FullTextExpression ft = filter.getFullTextConstraint();
@@ -498,7 +427,7 @@ public class LucenePropertyIndex impleme
// when using the LowCostLuceneIndexProvider
// which is used for testing
} else {
- qs.add(getFullTextQuery(defn, ft, analyzer, reader));
+ qs.add(getFullTextQuery(plan, ft, analyzer, reader));
}
@@ -527,7 +456,7 @@ public class LucenePropertyIndex impleme
throw new RuntimeException(e);
}
}
- } else if (nonFullTextConstraints) {
+ } else if (planResult.evaluateNonFullTextConstraints()) {
addNonFullTextConstraints(qs, plan, reader);
}
@@ -872,8 +801,9 @@ public class LucenePropertyIndex impleme
}
}
- static Query getFullTextQuery(final IndexDefinition defn, FullTextExpression ft,
+ static Query getFullTextQuery(final IndexPlan plan, FullTextExpression ft,
final Analyzer analyzer, final IndexReader reader) {
+ final PlanResult pr = pr(plan);
// a reference to the query, so it can be set in the visitor
// (a "non-local return")
final AtomicReference<Query> result = new AtomicReference<Query>();
@@ -883,7 +813,7 @@ public class LucenePropertyIndex impleme
public boolean visit(FullTextOr or) {
BooleanQuery q = new BooleanQuery();
for (FullTextExpression e : or.list) {
- Query x = getFullTextQuery(defn, e, analyzer, reader);
+ Query x = getFullTextQuery(plan, e, analyzer, reader);
q.add(x, SHOULD);
}
result.set(q);
@@ -894,7 +824,7 @@ public class LucenePropertyIndex impleme
public boolean visit(FullTextAnd and) {
BooleanQuery q = new BooleanQuery();
for (FullTextExpression e : and.list) {
- Query x = getFullTextQuery(defn, e, analyzer, reader);
+ Query x = getFullTextQuery(plan, e, analyzer, reader);
// Lucene can't deal with "must(must_not(x))"
if (x instanceof BooleanQuery) {
BooleanQuery bq = (BooleanQuery) x;
@@ -913,7 +843,7 @@ public class LucenePropertyIndex impleme
public boolean visit(FullTextTerm term) {
String p = term.getPropertyName();
if (p != null) {
- p = FieldNames.createAnalyzedFieldName(p);
+ p = getLuceneFieldName(p, pr);
}
Query q = tokenToQuery(term.getText(), p, analyzer, reader);
if (q == null) {
@@ -936,6 +866,24 @@ public class LucenePropertyIndex impleme
return result.get();
}
+ static String getLuceneFieldName(String p, PlanResult pr) {
+ if (isNodePath(p)){
+ if (pr.isPathTransformed()){
+ p = PathUtils.getName(p);
+ } else {
+ //Get rid of /* as aggregated fulltext field name is the
+ //node relative path
+ p = FieldNames.createFulltextFieldName(PathUtils.getParentPath(p));
+ }
+ } else {
+ if (pr.isPathTransformed()){
+ p = PathUtils.getName(p);
+ }
+ p = FieldNames.createAnalyzedFieldName(p);
+ }
+ return p;
+ }
+
static Query tokenToQuery(String text, String fieldName, Analyzer analyzer, IndexReader reader) {
if (analyzer == null) {
return null;
Modified: jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/ConfigUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/ConfigUtil.java?rev=1641771&r1=1641770&r2=1641771&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/ConfigUtil.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/ConfigUtil.java Wed Nov 26 08:06:52 2014
@@ -20,6 +20,7 @@
package org.apache.jackrabbit.oak.plugins.index.lucene.util;
import com.google.common.primitives.Ints;
+import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -45,4 +46,9 @@ public class ConfigUtil {
PropertyState ps = definition.getProperty(propName);
return ps == null ? defaultVal : ps.getValue(Type.DOUBLE).floatValue();
}
+
+ public static String getPrimaryTypeName(NodeState nodeState) {
+ PropertyState ps = nodeState.getProperty(JcrConstants.JCR_PRIMARYTYPE);
+ return (ps == null) ? JcrConstants.NT_BASE : ps.getValue(Type.NAME);
+ }
}