blob: 9f3cbf123c3739302c021afdcdeb1592df2f2caf [file] [log] [blame]
/*
* 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 static com.google.common.collect.ImmutableList.of;
import static com.google.common.collect.Lists.newArrayList;
import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.NT_FILE;
import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
import java.util.ArrayList;
import java.util.Calendar;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.InitialContentHelper;
import org.apache.jackrabbit.oak.Oak;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.ContentRepository;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
import org.apache.jackrabbit.oak.plugins.index.aggregate.SimpleNodeAggregator;
import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.newNodeAggregator;
import static org.apache.jackrabbit.oak.plugins.index.lucene.TestUtil.useV2;
import static org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState.binaryProperty;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
import org.apache.jackrabbit.oak.query.AbstractQueryTest;
import org.apache.jackrabbit.oak.spi.commit.Observer;
import org.apache.jackrabbit.oak.spi.query.QueryIndex;
import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
public class LuceneIndexAggregationTest extends AbstractQueryTest {
@Override
protected void createTestIndexNode() throws Exception {
Tree index = root.getTree("/");
Tree indexDefn = createTestIndexNode(index, LuceneIndexConstants.TYPE_LUCENE);
useV2(indexDefn);
//Aggregates
newNodeAggregator(indexDefn)
.newRuleWithName(NT_FILE, newArrayList(JCR_CONTENT, JCR_CONTENT + "/*"))
.newRuleWithName(NT_FOLDER, newArrayList("myFile", "subfolder/subsubfolder/file"));
//Include all properties
Tree props = TestUtil.newRulePropTree(indexDefn, "nt:base");
TestUtil.enableForFullText(props, FulltextIndexConstants.REGEX_ALL_PROPS, true);
root.commit();
}
@Override
protected ContentRepository createRepository() {
LowCostLuceneIndexProvider provider = new LowCostLuceneIndexProvider();
return new Oak(new MemoryNodeStore(InitialContentHelper.INITIAL_CONTENT))
.with(new OpenSecurityProvider())
.with((QueryIndexProvider)provider.with(getNodeAggregator()))
.with((Observer) provider)
.with(new LuceneIndexEditorProvider())
.createContentRepository();
}
/**
* <code>
* <aggregate primaryType="nt:file">
* <include>jcr:content</include>
* <include>jcr:content/*</include>
* <include-property>jcr:content/jcr:lastModified</include-property>
* </aggregate>
* <code>
*
*/
private static QueryIndex.NodeAggregator getNodeAggregator() {
return new SimpleNodeAggregator()
.newRuleWithName(NT_FILE, newArrayList(JCR_CONTENT, JCR_CONTENT + "/*"))
.newRuleWithName(NT_FOLDER, newArrayList("myFile", "subfolder/subsubfolder/file"));
}
/**
* simple index aggregation from jcr:content to nt:file
*
*/
@Test
public void testNtFileAggregate() throws Exception {
String sqlBase = "SELECT * FROM [nt:file] as f WHERE";
String sqlCat = sqlBase + " CONTAINS (f.*, 'cat')";
String sqlDog = sqlBase + " CONTAINS (f.*, 'dog')";
Tree file = root.getTree("/").addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
root.commit();
assertQuery(sqlDog, ImmutableList.of("/myFile"));
// update jcr:data
root.getTree("/")
.getChild("myFile")
.getChild(JCR_CONTENT)
.setProperty(
binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy cat."));
root.commit();
assertQuery(sqlDog, new ArrayList<String>());
assertQuery(sqlCat, ImmutableList.of("/myFile"));
// replace jcr:content with unstructured
root.getTree("/").getChild("myFile").getChild(JCR_CONTENT).remove();
Tree unstrContent = root.getTree("/").getChild("myFile")
.addChild(JCR_CONTENT);
unstrContent.setProperty(JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED,
Type.NAME);
Tree foo = unstrContent.addChild("foo");
foo.setProperty(JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED,
Type.NAME);
foo.setProperty("text", "the quick brown fox jumps over the lazy dog.");
root.commit();
assertQuery(sqlDog, ImmutableList.of("/myFile"));
assertQuery(sqlCat, new ArrayList<String>());
// remove foo
root.getTree("/").getChild("myFile").getChild(JCR_CONTENT)
.getChild("foo").remove();
root.commit();
assertQuery(sqlDog, new ArrayList<String>());
assertQuery(sqlCat, new ArrayList<String>());
// replace jcr:content again with resource
root.getTree("/").getChild("myFile").getChild(JCR_CONTENT).remove();
resource = root.getTree("/").getChild("myFile").addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy cat."));
root.commit();
assertQuery(sqlDog, new ArrayList<String>());
assertQuery(sqlCat, ImmutableList.of("/myFile"));
}
@Test
public void testChildNodeWithOr() throws Exception {
Tree file = root.getTree("/").addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
resource.setProperty("jcr:title", "title");
resource.setProperty("jcr:description", "description");
root.commit();
String matchContentSimple = "//element(*, nt:file)[(jcr:contains(jcr:content, 'dog'))]";
assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFile"));
String matchContent = " //element(*, nt:file)[(jcr:contains(jcr:content, 'dog') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
assertQuery(matchContent, "xpath", ImmutableList.of("/myFile"));
String matchTitle = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'title') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
assertQuery(matchTitle, "xpath", ImmutableList.of("/myFile"));
String matchDesc = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'description'))]";
assertQuery(matchDesc, "xpath", ImmutableList.of("/myFile"));
String matchNone = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
assertQuery(matchNone, "xpath", new ArrayList<String>());
}
@Test
public void testChildNodeWithOrComposite() throws Exception {
Tree folder = root.getTree("/").addChild("myFolder");
folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
Tree file = folder.addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
file.setProperty("jcr:title", "title");
file.setProperty("jcr:description", "description");
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
root.commit();
String matchContentSimple = "//element(*, nt:folder)[(jcr:contains(myFile, 'dog'))]";
assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFolder"));
String matchContent = " //element(*, nt:folder)[(jcr:contains(myFile, 'dog') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
assertQuery(matchContent, "xpath", ImmutableList.of("/myFolder"));
String matchTitle = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'title') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
assertQuery(matchTitle, "xpath", ImmutableList.of("/myFolder"));
String matchDesc = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'description'))]";
assertQuery(matchDesc, "xpath", ImmutableList.of("/myFolder"));
String matchNone = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
assertQuery(matchNone, "xpath", new ArrayList<String>());
String matchOnlyTitleOr = " //element(*, nt:folder)[(jcr:contains(myFile/@jcr:title, 'title') or jcr:contains(myFile/@jcr:title, 'unknown') )]";
assertQuery(matchOnlyTitleOr, "xpath", ImmutableList.of("/myFolder"));
}
@Test
public void testNodeTypes() throws Exception {
Tree folder = root.getTree("/").addChild("myFolder");
folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
Tree file = folder.addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
file.setProperty("jcr:title", "title");
file.setProperty("jcr:description", "description");
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
root.commit();
String matchContentSimple = "//*[( jcr:contains(., 'dog') and @jcr:primaryType = 'nt:file' )]";
assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFolder/myFile"));
String matchContentDouble = "//*[( jcr:contains(., 'dog') and (@jcr:primaryType = 'nt:file' or @jcr:primaryType = 'nt:folder') )]";
assertQuery(matchContentDouble, "xpath", ImmutableList.of("/myFolder", "/myFolder/myFile"));
}
@Test
public void testNodeTypesDeep() throws Exception {
Tree folder = root.getTree("/").addChild("myFolder");
folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
Tree folder2 = folder.addChild("subfolder");
folder2.setProperty(JCR_PRIMARYTYPE, "nt:unstructured", Type.NAME);
Tree folder3 = folder2.addChild("subsubfolder");
folder3.setProperty(JCR_PRIMARYTYPE, "nt:unstructured", Type.NAME);
file(folder3, "file");
root.commit();
String xpath = "//element(*, nt:folder)[jcr:contains(., 'dog')]";
assertQuery(xpath, "xpath", ImmutableList.of("/myFolder"));
}
private static void file(Tree parent, String name) {
Tree file = parent.addChild(name);
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
}
@Test
public void testChildNodeProperty() throws Exception {
Tree file = root.getTree("/").addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
resource.setProperty("jcr:title", "title");
resource.setProperty("jcr:description", "description");
root.commit();
String matchChildSimple = "//*[( jcr:contains(@jcr:title, 'title') )]";
assertQuery(matchChildSimple, "xpath", ImmutableList.of("/myFile/jcr:content"));
String matchChildWithStar = "//*[( jcr:contains(., 'dog') and jcr:contains(@jcr:title, 'title') )]";
assertQuery(matchChildWithStar, "xpath", ImmutableList.of("/myFile/jcr:content"));
}
@Test
public void testChildNodeProperty2() throws Exception {
Tree file = root.getTree("/").addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
resource.setProperty("jcr:title", "title");
resource.setProperty("jcr:description", "description");
Tree file2 = root.getTree("/").addChild("myFile2");
file2.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
Tree resource2 = file2.addChild(JCR_CONTENT);
resource2.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource2.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
resource2.setProperty("jcr:title", "other");
resource.setProperty("jcr:description", "title");
root.commit();
String matchChildSimple = "//*[( jcr:contains(jcr:content/@jcr:title, 'title') )]";
assertQuery(matchChildSimple, "xpath", ImmutableList.of("/myFile"));
}
@Test
public void testPreventDoubleAggregation() throws Exception {
Tree file = root.getTree("/").addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
file.setProperty("jcr:title", "fox");
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
root.commit();
String matchChildSimple = "//element(*, nt:file)[( jcr:contains(., 'fox') )]";
assertQuery(matchChildSimple, "xpath",
ImmutableList.of("/myFile"));
}
@Test
public void testDifferentNodes() throws Exception {
Tree folder = root.getTree("/").addChild("myFolder");
folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
Tree file = folder.addChild("myFile");
file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
file.setProperty("jcr:title", "title");
file.setProperty("jcr:description", "description");
Tree resource = file.addChild(JCR_CONTENT);
resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
resource.setProperty("jcr:lastModified", Calendar.getInstance());
resource.setProperty("jcr:encoding", "UTF-8");
resource.setProperty("jcr:mimeType", "text/plain");
resource.setProperty(binaryProperty(JCR_DATA,
"the quick brown fox jumps over the lazy dog."));
root.commit();
assertQuery(
"//element(*, nt:file)[jcr:contains(., 'dog')]",
"xpath", ImmutableList.of("/myFolder/myFile"));
assertQuery(
"//element(*, nt:file)[jcr:contains(., 'title')]",
"xpath", ImmutableList.of("/myFolder/myFile"));
assertQuery(
"//element(*, nt:file)[jcr:contains(., 'dog') and jcr:contains(., 'title')]",
"xpath", ImmutableList.of("/myFolder/myFile"));
// double aggregation dupes
assertQuery(
"//*[(jcr:contains(., 'dog') or jcr:contains(jcr:content, 'dog') )]",
"xpath", ImmutableList.of("/myFolder", "/myFolder/myFile", "/myFolder/myFile/jcr:content"));
}
@Test
public void oak3371AggregateV2() throws CommitFailedException {
oak3371();
}
@Test
public void oak3371AggregateV1() throws CommitFailedException {
Tree indexdef = root.getTree("/oak:index/" + TEST_INDEX_NAME);
assertNotNull(indexdef);
assertTrue(indexdef.exists());
indexdef.setProperty(FulltextIndexConstants.COMPAT_MODE, 1L);
indexdef.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
root.commit();
oak3371();
}
private void oak3371() throws CommitFailedException {
setTraversalEnabled(false);
Tree test, t;
test = root.getTree("/").addChild("test");
t = test.addChild("a");
t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
t.setProperty("foo", "bar");
t = test.addChild("b");
t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
t.setProperty("foo", "cat");
t = test.addChild("c");
t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
t = test.addChild("d");
t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
t.setProperty("foo", "bar cat");
root.commit();
assertQuery(
"SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND CONTAINS(foo, 'bar')",
of("/test/a", "/test/d"));
assertQuery(
"SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND NOT CONTAINS(foo, 'bar')",
of("/test/b", "/test/c"));
assertQuery(
"SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND CONTAINS(foo, 'bar cat')",
of("/test/d"));
assertQuery(
"SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND NOT CONTAINS(foo, 'bar cat')",
of("/test/c"));
setTraversalEnabled(true);
}
}