|
@@ -16,6 +16,7 @@ import org.elasticsearch.compute.data.ElementType;
|
|
|
import org.elasticsearch.core.Tuple;
|
|
|
import org.elasticsearch.index.IndexMode;
|
|
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
|
|
+import org.elasticsearch.index.query.CoordinatorRewriteContext;
|
|
|
import org.elasticsearch.index.query.QueryBuilder;
|
|
|
import org.elasticsearch.index.query.SearchExecutionContext;
|
|
|
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
|
|
@@ -52,6 +53,7 @@ import java.util.LinkedHashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Set;
|
|
|
import java.util.function.Consumer;
|
|
|
+import java.util.function.Predicate;
|
|
|
|
|
|
import static java.util.Arrays.asList;
|
|
|
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES;
|
|
@@ -179,10 +181,10 @@ public class PlannerUtils {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Extracts the ES query for the <code>@timestamp</code> field for the passed plan.
|
|
|
+ * Extracts a filter that can be used to skip unmatched shards on the coordinator.
|
|
|
*/
|
|
|
- public static QueryBuilder requestTimestampFilter(PhysicalPlan plan) {
|
|
|
- return detectFilter(plan, "@timestamp");
|
|
|
+ public static QueryBuilder canMatchFilter(PhysicalPlan plan) {
|
|
|
+ return detectFilter(plan, CoordinatorRewriteContext.SUPPORTED_FIELDS::contains);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -190,12 +192,11 @@ public class PlannerUtils {
|
|
|
* We currently only use this filter for the @timestamp field, which is always a date field. Any tests that wish to use this should
|
|
|
* take care to not use it with TEXT fields.
|
|
|
*/
|
|
|
- static QueryBuilder detectFilter(PhysicalPlan plan, String fieldName) {
|
|
|
+ static QueryBuilder detectFilter(PhysicalPlan plan, Predicate<String> fieldName) {
|
|
|
// first position is the REST filter, the second the query filter
|
|
|
- var requestFilter = new QueryBuilder[] { null, null };
|
|
|
-
|
|
|
+ final List<QueryBuilder> requestFilters = new ArrayList<>();
|
|
|
plan.forEachDown(FragmentExec.class, fe -> {
|
|
|
- requestFilter[0] = fe.esFilter();
|
|
|
+ requestFilters.add(fe.esFilter());
|
|
|
// detect filter inside the query
|
|
|
fe.fragment().forEachUp(Filter.class, f -> {
|
|
|
// the only filter that can be pushed down is that on top of the relation
|
|
@@ -208,7 +209,7 @@ public class PlannerUtils {
|
|
|
for (var exp : conjunctions) {
|
|
|
var refs = new AttributeSet(exp.references());
|
|
|
// remove literals or attributes that match by name
|
|
|
- boolean matchesField = refs.removeIf(e -> fieldName.equals(e.name()));
|
|
|
+ boolean matchesField = refs.removeIf(e -> fieldName.test(e.name()));
|
|
|
// the expression only contains the target reference
|
|
|
// and the expression is pushable (functions can be fully translated)
|
|
|
if (matchesField && refs.isEmpty() && canPushToSource(exp)) {
|
|
@@ -216,13 +217,13 @@ public class PlannerUtils {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if (matches.size() > 0) {
|
|
|
- requestFilter[1] = TRANSLATOR_HANDLER.asQuery(Predicates.combineAnd(matches)).asBuilder();
|
|
|
+ if (matches.isEmpty() == false) {
|
|
|
+ requestFilters.add(TRANSLATOR_HANDLER.asQuery(Predicates.combineAnd(matches)).asBuilder());
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- return Queries.combine(FILTER, asList(requestFilter));
|
|
|
+ return Queries.combine(FILTER, requestFilters);
|
|
|
}
|
|
|
|
|
|
/**
|