blob: 3bc11261f22eb593c1d6943bce9a7b64bdfe8f3a [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;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
import org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
import org.apache.jackrabbit.oak.query.AbstractQueryTest;
import org.junit.Test;
import java.util.Arrays;
import static java.util.Collections.singletonList;
import static javax.jcr.PropertyType.TYPENAME_DATE;
import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROPDEF_PROP_NODE_NAME;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
public abstract class PropertyIndexCommonTest extends AbstractQueryTest {
protected IndexOptions indexOptions;
protected TestRepository repositoryOptionsUtil;
protected void assertEventually(Runnable r) {
TestUtils.assertEventually(r,
((repositoryOptionsUtil.isAsync() ? repositoryOptionsUtil.defaultAsyncIndexingTimeInSeconds : 0) + 3000) * 5);
}
@Test
public void testBulkProcessorFlushLimit() throws Exception {
indexOptions.setIndex(root, "test1", indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false, "propa"));
Tree test = root.getTree("/").addChild("test");
for (int i = 1; i < 249; i++) {
test.addChild("a" + i).setProperty("propa", "foo" + i);
}
root.commit();
// 250 is the default flush limit for bulk processor, and we added just less than 250 nodes
// So once the index writer is closed , bulk Processor would be closed and all the 248 entries should be flushed.
// Make sure that the last entry is indexed correctly.
String propaQuery = "select [jcr:path] from [nt:base] where [propa] = 'foo248'";
assertEventually(() -> {
assertThat(explain(propaQuery), containsString(indexOptions.getIndexType() + ":test1"));
assertQuery(propaQuery, singletonList("/test/a248"));
});
// Now we test for 250 < nodes < 500
for (int i = 250; i < 300; i++) {
test.addChild("a" + i).setProperty("propa", "foo" + i);
}
root.commit();
String propaQuery2 = "select [jcr:path] from [nt:base] where [propa] = 'foo299'";
assertEventually(() -> {
assertThat(explain(propaQuery2), containsString(indexOptions.getIndexType() + ":test1"));
assertQuery(propaQuery2, singletonList("/test/a299"));
});
}
@Test
public void indexSelection() throws Exception {
indexOptions.setIndex(root, "test1", indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false, "propa", "propb"));
indexOptions.setIndex(root, "test2", indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false, "propc"));
Tree test = root.getTree("/").addChild("test");
test.addChild("a").setProperty("propa", "foo");
test.addChild("b").setProperty("propa", "foo");
test.addChild("c").setProperty("propa", "foo2");
test.addChild("d").setProperty("propc", "foo");
test.addChild("e").setProperty("propd", "foo");
root.commit();
String propaQuery = "select [jcr:path] from [nt:base] where [propa] = 'foo'";
assertEventually(() -> {
IndexDefinitionBuilder builder = indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false);
builder.includedPaths("/test")
.indexRule("nt:base")
.property("nodeName", PROPDEF_PROP_NODE_NAME);
indexOptions.setIndex(root, "test1", builder);
assertThat(explain(propaQuery), containsString(indexOptions.getIndexType() + ":test1"));
assertThat(explain("select [jcr:path] from [nt:base] where [propc] = 'foo'"),
containsString(indexOptions.getIndexType() + ":test2"));
assertQuery(propaQuery, Arrays.asList("/test/a", "/test/b"));
assertQuery("select [jcr:path] from [nt:base] where [propa] = 'foo2'", singletonList("/test/c"));
assertQuery("select [jcr:path] from [nt:base] where [propc] = 'foo'", singletonList("/test/d"));
});
}
//OAK-3825
@Test
public void nodeNameViaPropDefinition() throws Exception {
IndexDefinitionBuilder builder = indexOptions.createIndexDefinitionBuilder();
builder.noAsync();
builder.includedPaths("/test")
.indexRule("nt:base")
.property("nodeName", PROPDEF_PROP_NODE_NAME);
indexOptions.setIndex(root, "test1", builder);
root.commit();
//add content
Tree test = root.getTree("/").addChild("test");
test.addChild("foo");
test.addChild("camelCase");
test.addChild("sc").addChild("bar");
root.commit();
String queryPrefix = "select [jcr:path] from [nt:base] where ISDESCENDANTNODE('/test') AND ";
//test
String propabQuery = queryPrefix + "LOCALNAME() = 'foo'";
assertEventually(() -> {
String explanation = explain(propabQuery);
assertThat(explanation, containsString(indexOptions.getIndexType() + ":test1(/oak:index/test1) "));
//assertThat(explanation, containsString("{\"term\":{\":nodeName\":{\"value\":\"foo\","));
assertQuery(propabQuery, singletonList("/test/foo"));
assertQuery(queryPrefix + "LOCALNAME() = 'bar'", singletonList("/test/sc/bar"));
assertQuery(queryPrefix + "LOCALNAME() LIKE 'foo'", singletonList("/test/foo"));
assertQuery(queryPrefix + "LOCALNAME() LIKE 'camel%'", singletonList("/test/camelCase"));
assertQuery(queryPrefix + "NAME() = 'bar'", singletonList("/test/sc/bar"));
assertQuery(queryPrefix + "NAME() LIKE 'foo'", singletonList("/test/foo"));
assertQuery(queryPrefix + "NAME() LIKE 'camel%'", singletonList("/test/camelCase"));
});
}
@Test
public void emptyIndex() throws Exception {
indexOptions.setIndex(root, "test1", indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false, "propa", "propb"));
root.commit();
Tree test = root.getTree("/").addChild("test");
test.addChild("a");
test.addChild("b");
root.commit();
assertEventually(() -> assertThat(explain("select [jcr:path] from [nt:base] where [propa] = 'foo'"),
containsString(indexOptions.getIndexType() + ":test1")));
}
@Test
public void propertyExistenceQuery() throws Exception {
indexOptions.setIndex(root, "test1", indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false, "propa", "propb"));
root.commit();
Tree test = root.getTree("/").addChild("test");
test.addChild("a").setProperty("propa", "a");
test.addChild("b").setProperty("propa", "c");
test.addChild("c").setProperty("propb", "e");
root.commit();
assertEventually(() -> assertQuery("select [jcr:path] from [nt:base] where propa is not null",
Arrays.asList("/test/a", "/test/b")));
}
@Test
public void dateQuery() throws Exception {
Tree index = root.getTree("/");
Tree indexDefn = createTestIndexNode(index, indexOptions.getIndexType());
TestUtil.useV2(indexDefn);
Tree props = TestUtil.newRulePropTree(indexDefn, "nt:base");
Tree prop = TestUtil.enablePropertyIndex(props, "date", false);
prop.setProperty(FulltextIndexConstants.PROP_TYPE, TYPENAME_DATE);
root.commit();
Tree test = root.getTree("/").addChild("test");
Tree a = test.addChild("a");
Tree b = test.addChild("b");
Tree c = test.addChild("c");
Tree d = test.addChild("d");
a.setProperty("date", "2020-12-07T11:45:48.119Z", Type.DATE);
b.setProperty("date", "2020-12-07T17:23:33.933Z", Type.DATE);
c.setProperty("date", "2020-12-07T22:23:33.933Z", Type.DATE);
d.setProperty("date", "2020-12-07T10:23:33.933-09:00", Type.DATE);
root.commit();
assertEventually(() -> assertQuery("select [jcr:path] from [nt:base] where date > CAST('2020-12-06T12:32:35.886Z' AS DATE)",
Arrays.asList("/test/a", "/test/b", "/test/c", "/test/d")));
assertEventually(() -> assertQuery("select [jcr:path] from [nt:base] where date > CAST('2020-12-07T12:32:35.886Z' AS DATE) " +
"and date < CAST('2020-12-07T20:32:35.886Z' AS DATE)",
Arrays.asList("/test/b", "/test/d")));
assertEventually(() -> assertQuery("select [jcr:path] from [nt:base] where date < CAST('2020-12-07T11:23:33.933-09:00' AS DATE)",
Arrays.asList("/test/a", "/test/b", "/test/d")));
}
private String explain(String query) {
String explain = "explain " + query;
return executeQuery(explain, "JCR-SQL2").get(0);
}
}