Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAK-11214 Support 'IN' restrictions for functions #1813

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,10 @@ public Oak with(@NotNull Whiteboard whiteboard) {
LOG.info("Registered improved cost feature: " + QueryEngineSettings.FT_NAME_IMPROVED_IS_NULL_COST);
closer.register(improvedIsNullCostFeature);
queryEngineSettings.setImprovedIsNullCostFeature(improvedIsNullCostFeature);
Feature optimizeInRestrictionsForFunctions = newFeature(QueryEngineSettings.FT_OPTIMIZE_IN_RESTRICTIONS_FOR_FUNCTIONS, whiteboard);
LOG.info("Registered optimize in restrictions for functions feature: " + QueryEngineSettings.FT_OPTIMIZE_IN_RESTRICTIONS_FOR_FUNCTIONS);
closer.register(optimizeInRestrictionsForFunctions);
queryEngineSettings.setOptimizeInRestrictionsForFunctions(optimizeInRestrictionsForFunctions);
}

return this;
Expand Down Expand Up @@ -987,6 +991,10 @@ public void setImprovedIsNullCostFeature(@Nullable Feature feature) {
settings.setImprovedIsNullCostFeature(feature);
}

public void setOptimizeInRestrictionsForFunctions(@Nullable Feature feature) {
settings.setOptimizeInRestrictionsForFunctions(feature);
}

@Override
public void setQueryValidatorPattern(String key, String pattern, String comment, boolean failQuery) {
settings.getQueryValidator().setPattern(key, pattern, comment, failQuery);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public class QueryEngineSettings implements QueryEngineSettingsMBean, QueryLimit

public static final String FT_NAME_IMPROVED_IS_NULL_COST = "FT_OAK-10532";

public static final String FT_OPTIMIZE_IN_RESTRICTIONS_FOR_FUNCTIONS = "FT_OAK-11214";

public static final int DEFAULT_PREFETCH_COUNT = Integer.getInteger(OAK_QUERY_PREFETCH_COUNT, -1);

public static final String OAK_QUERY_FAIL_TRAVERSAL = "oak.queryFailTraversal";
Expand Down Expand Up @@ -114,6 +116,7 @@ public class QueryEngineSettings implements QueryEngineSettingsMBean, QueryLimit

private Feature prefetchFeature;
private Feature improvedIsNullCostFeature;
private Feature optimizeInRestrictionsForFunctions;

private String autoOptionsMappingJson = "{}";
private QueryOptions.AutomaticQueryOptionsMapping autoOptionsMapping = new QueryOptions.AutomaticQueryOptionsMapping(autoOptionsMappingJson);
Expand Down Expand Up @@ -218,6 +221,16 @@ public boolean getImprovedIsNullCost() {
return improvedIsNullCostFeature == null || improvedIsNullCostFeature.isEnabled();
}

public void setOptimizeInRestrictionsForFunctions(@Nullable Feature feature) {
this.optimizeInRestrictionsForFunctions = feature;
}

@Override
public boolean getOptimizeInRestrictionsForFunctions() {
// enabled if the feature toggle is not used
return optimizeInRestrictionsForFunctions == null || optimizeInRestrictionsForFunctions.isEnabled();
}

public String getStrictPathRestriction() {
return strictPathRestriction.name();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
public void restrictList(FilterImpl f, List<PropertyValue> list) {
// "LOWER(x) IN (A, B)" implies x is not null
operand.restrict(f, Operator.NOT_EQUAL, null);
if (!f.getQueryLimits().getOptimizeInRestrictionsForFunctions()) {
return;
}
String fn = getFunction(f.getSelector());
if (fn != null) {
f.restrictPropertyAsList(QueryConstants.FUNCTION_RESTRICTION_PREFIX + fn, list);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ public void restrict(FilterImpl f, Operator operator, PropertyValue v) {

@Override
public void restrictList(FilterImpl f, List<PropertyValue> list) {
// optimizations of type "LOCALNAME(..) IN(A, B)" are not supported
if (!f.getQueryLimits().getOptimizeInRestrictionsForFunctions()) {
return;
}
String fn = getFunction(f.getSelector());
if (fn != null) {
f.restrictPropertyAsList(QueryConstants.FUNCTION_RESTRICTION_PREFIX + fn, list);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,13 @@ public void restrict(FilterImpl f, Operator operator, PropertyValue v) {

@Override
public void restrictList(FilterImpl f, List<PropertyValue> list) {
// optimizations of type "NAME(..) IN(A, B)" are not supported
if (!f.getQueryLimits().getOptimizeInRestrictionsForFunctions()) {
return;
}
String fn = getFunction(f.getSelector());
if (fn != null) {
f.restrictPropertyAsList(QueryConstants.FUNCTION_RESTRICTION_PREFIX + fn, list);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.jetbrains.annotations.NotNull;

import org.apache.jackrabbit.guava.common.base.Splitter;
import org.apache.jackrabbit.guava.common.collect.ImmutableSet;

public class NotFullTextSearchImpl extends FullTextSearchImpl {
private static final Set<String> KEYWORDS = Set.of("or");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ public void restrict(FilterImpl f, Operator operator, PropertyValue v) {

@Override
public void restrictList(FilterImpl f, List<PropertyValue> list) {
// optimizations of type "NAME(..) IN(A, B)" are not supported
if (!f.getQueryLimits().getOptimizeInRestrictionsForFunctions()) {
return;
}
String fn = getFunction(f.getSelector());
if (fn != null) {
f.restrictPropertyAsList(QueryConstants.FUNCTION_RESTRICTION_PREFIX + fn, list);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
public void restrictList(FilterImpl f, List<PropertyValue> list) {
// "UPPER(x) IN (A, B)" implies x is not null
operand.restrict(f, Operator.NOT_EQUAL, null);
if (!f.getQueryLimits().getOptimizeInRestrictionsForFunctions()) {
return;
}
String fn = getFunction(f.getSelector());
if (fn != null) {
f.restrictPropertyAsList(QueryConstants.FUNCTION_RESTRICTION_PREFIX + fn, list);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,39 @@ private Filter createFilterSQL(String sql) throws ParseException {
return q.createFilter(true);
}

@Test
public void functionBasedIndexOr() throws Exception {
String sql2 = "select [jcr:path] from [nt:base] where lower([test]) in('hello', 'world')";
assertEquals("Filter(query=select [jcr:path] from [nt:base] " +
"where lower([test]) in('hello', 'world'), " +
"path=*, property=[function*lower*@test=[in(hello, world)], " +
"test=[is not null]])", createFilterSQL(sql2).toString());

sql2 = "select [jcr:path] from [nt:base] where upper([test]) in('hello', 'world')";
assertEquals("Filter(query=select [jcr:path] from [nt:base] " +
"where upper([test]) in('hello', 'world'), " +
"path=*, property=[function*upper*@test=[in(hello, world)], " +
"test=[is not null]])", createFilterSQL(sql2).toString());

sql2 = "select [jcr:path] from [nt:base] where name() in('hello', 'world')";
assertEquals("Filter(query=select [jcr:path] from [nt:base] " +
"where name() in('hello', 'world'), " +
"path=*, property=[function*@:name=[in(hello, world)]])",
createFilterSQL(sql2).toString());

sql2 = "select [jcr:path] from [nt:base] where localname() in('hello', 'world')";
assertEquals("Filter(query=select [jcr:path] from [nt:base] " +
"where localname() in('hello', 'world'), " +
"path=*, property=[function*@:localname=[in(hello, world)]])",
createFilterSQL(sql2).toString());

sql2 = "select [jcr:path] from [nt:base] where path() in('/hello', '/world')";
assertEquals("Filter(query=select [jcr:path] from [nt:base] " +
"where path() in('/hello', '/world'), " +
"path=*, property=[function*@:path=[in(/hello, /world)]])",
createFilterSQL(sql2).toString());
}

@Test
public void functionBasedIndex() throws Exception {
String sql2 = "select [jcr:path] from [nt:base] where lower([test]) = 'hello'";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,22 @@ default boolean getImprovedIsNullCost() {
return true;
}

/**
* See OAK-11214. This method is used for backward compatibility
* (bug compatibility) only.
*
* @return true, except when backward compatibility for OAK-11214 is enabled
*/
default public boolean getOptimizeInRestrictionsForFunctions() {
return true;
}

boolean getFailTraversal();

default String getStrictPathRestriction() {
return StrictPathRestriction.DISABLE.name();
}

/**
* Retrieve the java package names / full qualified class names which should be
* ignored when finding the class starting a query
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/**
* This package contains oak query index related classes.
*/
@Version("3.0.0")
@Version("3.1.0")
package org.apache.jackrabbit.oak.spi.query;

import org.osgi.annotation.versioning.Version;
Loading