Author: chetanm
Date: Wed Aug 17 06:33:33 2016
New Revision: 1756580
URL: http://svn.apache.org/viewvc?rev=1756580&view=rev
Log:
OAK-4676 - Index definition on nt:base with analyzed property incorrectly
matches query with different property
Modified:
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/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/LucenePropertyIndexTest.java
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=1756580&r1=1756579&r2=1756580&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 Aug 17 06:33:33 2016
@@ -391,10 +391,6 @@ public final class IndexDefinition imple
return evaluatePathRestrictions;
}
- public boolean indexesAllTypes() {
- return indexesAllTypes;
- }
-
public Analyzer getAnalyzer(){
return analyzer;
}
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=1756580&r1=1756579&r2=1756580&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 Aug 17 06:33:33 2016
@@ -50,6 +50,7 @@ import static com.google.common.collect.
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static com.google.common.collect.Maps.newHashMap;
import static org.apache.jackrabbit.JcrConstants.JCR_SCORE;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
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;
@@ -352,9 +353,15 @@ class IndexPlanner {
if (nodePath != null
&& !indexingRule.isAggregated(nodePath)){
nonIndexedPaths.add(p);
- } else if (propertyPath != null
- && !indexingRule.isIndexed(propertyPath)){
- nonIndexedPaths.add(p);
+ } else if (propertyPath != null) {
+ PropertyDefinition pd =
indexingRule.getConfig(propertyPath);
+ //If given prop is not analyzed then its
+ //not indexed
+ if (pd == null){
+ nonIndexedPaths.add(p);
+ } else if (!pd.analyzed){
+ nonIndexedPaths.add(p);
+ }
}
if (nodeScopedTerm(propertyName)){
@@ -372,15 +379,44 @@ class IndexPlanner {
return false;
}
+ //where contains('jcr:content/bar', 'mountain OR valley') and
contains('jcr:content/foo', 'mountain OR valley')
+ //above query can be evaluated by index which indexes foo and bar with
restriction that both belong to same node
+ //by displacing the query path to evaluate on contains('bar', ...) and
filter out those parents which do not
+ //have jcr:content as parent. So ensure that relPaths size is 1 or 0
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 definition.indexesAllTypes();
+
+ //Such non indexed path can possibly be evaluated via any rule on
nt:base
+ //which can possibly index everything
+ IndexingRule rule = definition.getApplicableIndexingRule(NT_BASE);
+ if (rule == null){
+ return false;
+ }
+
+ for (String p : nonIndexedPaths){
+ //Index can only evaluate a node search jcr:content/*
+ //if it indexes node scope indexing is enabled
+ if (LucenePropertyIndex.isNodePath(p)){
+ if (!rule.isNodeFullTextIndexed()) {
+ return false;
+ }
+ } else {
+ //Index can only evaluate a property like jcr:content/type
+ //if it indexes 'type' and that too analyzed
+ String propertyName = PathUtils.getName(p);
+ PropertyDefinition pd = rule.getConfig(propertyName);
+ if (pd == null){
+ return false;
+ }
+ if (!pd.analyzed){
+ return false;
+ }
+ }
+ }
} else {
result.setParentPath("");
}
Modified:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java?rev=1756580&r1=1756579&r2=1756580&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexPlannerTest.java
Wed Aug 17 06:33:33 2016
@@ -45,6 +45,7 @@ import static org.junit.Assert.assertTru
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -62,6 +63,9 @@ import org.apache.jackrabbit.oak.query.a
import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
import org.apache.jackrabbit.oak.query.ast.Operator;
import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextAnd;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextContains;
+import org.apache.jackrabbit.oak.query.fulltext.FullTextExpression;
import org.apache.jackrabbit.oak.query.fulltext.FullTextParser;
import org.apache.jackrabbit.oak.query.index.FilterImpl;
import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -561,6 +565,178 @@ public class IndexPlannerTest {
assertNull(plan);
}
+ @Test
+ public void fullTextQuery_RelativePath1() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo"), "async");
+
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+ IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
FullTextParser.parse("bar", "mountain"));
+
+ //No plan for unindex property
+ assertNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_IndexAllProps() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("allProps"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn,
"indexRules/nt:base/properties/allProps");
+ foob.setProperty(LuceneIndexConstants.PROP_NAME,
LuceneIndexConstants.REGEX_ALL_PROPS);
+ foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+ foob.setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
+
+ FullTextExpression exp = FullTextParser.parse("bar", "mountain OR
valley");
+ exp = new FullTextContains("bar", "mountain OR valley", exp);
+ IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
exp);
+
+ //No plan for unindex property
+ assertNotNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_IndexAllProps_NodePathQuery() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("allProps"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn,
"indexRules/nt:base/properties/allProps");
+ foob.setProperty(LuceneIndexConstants.PROP_NAME,
LuceneIndexConstants.REGEX_ALL_PROPS);
+ foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+ foob.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+ foob.setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
+
+ //where contains('jcr:content/*', 'mountain OR valley') can be
evaluated by index
+ //on nt:base by evaluating on '.' and then checking if node name is
'jcr:content'
+ IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+ FullTextParser.parse("jcr:content/*", "mountain OR valley"));
+
+ //No plan for unindex property
+ assertNotNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_IndexAllProps_AggregatedNodePathQuery() throws
Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("allProps"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder agg =
defn.child(LuceneIndexConstants.AGGREGATES).child("nt:base").child("include0");
+ agg.setProperty(LuceneIndexConstants.AGG_PATH, "jcr:content");
+ agg.setProperty(LuceneIndexConstants.AGG_RELATIVE_NODE, true);
+
+ //where contains('jcr:content/*', 'mountain OR valley') can be
evaluated by index
+ //on nt:base by evaluating on '.' and then checking if node name is
'jcr:content'
+ IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+ FullTextParser.parse("jcr:content/*", "mountain OR valley"));
+
+ //No plan for unindex property
+ assertNotNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_IndexAllProps_NodePathQuery_NoPlan() throws
Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+ foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+ //where contains('jcr:content/*', 'mountain OR valley') can be
evaluated by index
+ //on nt:base by evaluating on '.' and then checking if node name is
'jcr:content'
+ IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+ FullTextParser.parse("jcr:content/*", "mountain OR valley"));
+
+ //No plan for unindex property
+ assertNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_NonAnalyzedProp_NoPlan() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo", "bar"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+
+ NodeBuilder barb = getNode(defn, "indexRules/nt:base/properties/bar");
+ barb.setProperty(LuceneIndexConstants.PROP_NAME, "bar");
+ barb.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+ //where contains('jcr:content/*', 'mountain OR valley') can be
evaluated by index
+ //on nt:base by evaluating on '.' and then checking if node name is
'jcr:content'
+ IndexPlanner planner = createPlannerForFulltext(defn.getNodeState(),
+ FullTextParser.parse("foo", "mountain OR valley"));
+
+ //No plan for unindex property
+ assertNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_RelativePropertyPaths() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo", "bar"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+ foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+ NodeBuilder barb = getNode(defn, "indexRules/nt:base/properties/bar");
+ barb.setProperty(LuceneIndexConstants.PROP_NAME, "bar");
+ barb.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+
+ //where contains('jcr:content/bar', 'mountain OR valley') and
contains('jcr:content/foo', 'mountain OR valley')
+ //above query can be evaluated by index which indexes foo and bar with
restriction that both belong to same node
+ //by displacing the query path to evaluate on contains('bar', ...) and
filter out those parents which do not
+ //have jcr:content as parent
+ FullTextExpression fooExp = FullTextParser.parse("jcr:content/bar",
"mountain OR valley");
+ FullTextExpression barExp = FullTextParser.parse("jcr:content/foo",
"mountain OR valley");
+ FullTextExpression exp = new FullTextAnd(Arrays.asList(fooExp,
barExp));
+ IndexPlanner planner =
createPlannerForFulltext(defn.getNodeState(),exp);
+
+ //No plan for unindex property
+ assertNotNull(planner.getPlan());
+ }
+
+ @Test
+ public void fullTextQuery_DisjointPropertyPaths() throws Exception{
+ NodeBuilder defn = newLucenePropertyIndexDefinition(builder, "test",
of("foo", "bar"), "async");
+
+ //Index all props and then perform fulltext
+ defn = IndexDefinition.updateDefinition(defn.getNodeState().builder());
+ NodeBuilder foob = getNode(defn, "indexRules/nt:base/properties/foo");
+ foob.setProperty(LuceneIndexConstants.PROP_NAME, "foo");
+ foob.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+ NodeBuilder barb = getNode(defn, "indexRules/nt:base/properties/bar");
+ barb.setProperty(LuceneIndexConstants.PROP_NAME, "bar");
+ barb.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+
+ FullTextExpression fooExp = FullTextParser.parse("metadata/bar",
"mountain OR valley");
+ FullTextExpression barExp = FullTextParser.parse("jcr:content/foo",
"mountain OR valley");
+ FullTextExpression exp = new FullTextAnd(Arrays.asList(fooExp,
barExp));
+ IndexPlanner planner =
createPlannerForFulltext(defn.getNodeState(),exp);
+
+ //No plan for unindex property
+ assertNull(planner.getPlan());
+ }
+
+ private IndexPlanner createPlannerForFulltext(NodeState defn,
FullTextExpression exp) throws IOException {
+ IndexNode node = createIndexNode(new IndexDefinition(root, defn));
+ FilterImpl filter = createFilter("nt:base");
+ filter.setFullTextConstraint(exp);
+ return new IndexPlanner(node, "/foo", filter,
Collections.<OrderEntry>emptyList());
+ }
+
private IndexNode createSuggestionOrSpellcheckIndex(String nodeType,
boolean
enableSuggestion,
boolean
enableSpellcheck) throws Exception {
Modified:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java?rev=1756580&r1=1756579&r2=1756580&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndexTest.java
Wed Aug 17 06:33:33 2016
@@ -2327,7 +2327,6 @@ public class LucenePropertyIndexTest ext
assertPlanAndQuery(query, "lucene:test1(/oak:index/test1)",
Collections.<String>emptyList());
}
- @Ignore("OAK-4676")
@Test
public void relativePropertyWithIndexOnNtBase() throws Exception {
Tree idx = createIndex("test1", of("propa"));