Skip to content
2 changes: 1 addition & 1 deletion docs/changelog/123381.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pr: 123381
summary: Push down `StarsWith` and `EndsWith` functions to Lucene
summary: Push down `StartsWith` and `EndsWith` functions to Lucene
area: ES|QL
type: enhancement
issues:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,99 @@ description:text
;


lucenePushdownMultipleWhere

from hosts
| where starts_with(host, "bet")
| keep host, host_group
| sort host, host_group
| where ends_with(host_group, "cluster");

host:keyword | host_group:text
beta | Kubernetes cluster
beta | Kubernetes cluster
beta | Kubernetes cluster
;

lucenePushdownMultipleIndices

from airports* metadata _index
| where starts_with(name::keyword, "Sahn") and ends_with(abbrev, "UH")
| keep abbrev, name, _index
| sort abbrev, name, _index;

abbrev:keyword | name:text | _index:keyword
LUH | Sahnewal | airports
LUH | Sahnewal | airports_mp
LUH | Sahnewal | airports_no_doc_values
LUH | Sahnewal | airports_not_indexed
LUH | Sahnewal | airports_not_indexed_nor_doc_values
LUH | Sahnewal | airports_web
;

lucenePushdownOr

from airports
| where starts_with(name::keyword, "Sahn") or ends_with(abbrev, "UH")
| keep abbrev, name
| sort abbrev, name;

abbrev:keyword | name:text
AUH | Abu Dhabi Int'l
LUH | Sahnewal
RUH | King Khalid Int'l
;

lucenePushdownMultipleOr

from airports
| where starts_with(name::keyword, "Sahn") or ends_with(abbrev, "UH") or starts_with(abbrev, "OOL")
| keep abbrev, name
| sort abbrev, name;

abbrev:keyword | name:text
AUH | Abu Dhabi Int'l
LUH | Sahnewal
OOL | Gold Coast
RUH | King Khalid Int'l
;

lucenePushdownMultipleAnd

from airports metadata _index
| where starts_with(name::keyword, "Sahn") and ends_with(abbrev, "UH")
| where ends_with(name::keyword, "al")
| keep abbrev, name, _index
| sort abbrev, name, _index;

abbrev:keyword | name:text | _index:keyword
LUH | Sahnewal | airports
;

lucenePushdownMixAndOr

from airports
| where starts_with(name::keyword, "Sahn") and (starts_with(name::keyword, "Abc") or ends_with(abbrev, "UH"))
| keep abbrev, name, scalerank
| sort abbrev, name;

abbrev:keyword | name:text | scalerank:integer
LUH | Sahnewal | 9
;

lucenePushdownMixOrAnd

from airports* metadata _index
| where starts_with(name::keyword, "Sahn") or (starts_with(abbrev, "G") and ends_with(name::keyword, "Falls Int'l"))
| where ends_with(_index, "airports")
| keep abbrev, name, scalerank, _index
| sort abbrev;

abbrev:keyword | name:text | scalerank:integer | _index:keyword
GTF | Great Falls Int'l | 8 | airports
LUH | Sahnewal | 9 | airports
;

toLowerRow#[skip:-8.12.99]
// tag::to_lower[]
ROW message = "Some Text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,45 @@ public void testPushMultipleBinaryLogicFilters() {
assertThat(rq.to(), nullValue());
}

public void testPushMultipleFunctions() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add as a javadoc the expected physicalplan tree. I know some methods in this class do not have this, but I (or we) try to have this consistent throughout the class. Thanks.

var plan = physicalPlan("""
from airports
| where starts_with(first_name, "*Firs") or ends_with(first_name, "irst*")
| where ends_with(last_name, "ast")
""");

var optimized = optimizedPlan(plan);
var topLimit = as(optimized, LimitExec.class);
var exchange = asRemoteExchange(topLimit.child());
var project = as(exchange.child(), ProjectExec.class);
var fieldExtract = as(project.child(), FieldExtractExec.class);
var source = source(fieldExtract.child());
assertThat(source.estimatedRowSize(), equalTo(allFieldRowSize + Integer.BYTES));

var andBool = as(source.query(), BoolQueryBuilder.class);
assertThat(andBool.must(), hasSize(2));
assertThat(andBool.should(), hasSize(0));

var orBool = as(andBool.must().get(0), BoolQueryBuilder.class);
assertThat(orBool.should(), hasSize(2));
assertThat(orBool.must(), hasSize(0));

var orStartsWith = as(sv(orBool.should().get(0), "first_name"), WildcardQueryBuilder.class);
assertThat(orStartsWith.fieldName(), equalTo("first_name"));
assertThat(orStartsWith.caseInsensitive(), equalTo(false));
assertThat(orStartsWith.value(), equalTo("\\*Firs*"));

var orEndsWith = as(sv(orBool.should().get(1), "first_name"), WildcardQueryBuilder.class);
assertThat(orEndsWith.fieldName(), equalTo("first_name"));
assertThat(orEndsWith.caseInsensitive(), equalTo(false));
assertThat(orEndsWith.value(), equalTo("*irst\\*"));

var andEndsWith = as(sv(andBool.must().get(1), "last_name"), WildcardQueryBuilder.class);
assertThat(andEndsWith.fieldName(), equalTo("last_name"));
assertThat(andEndsWith.caseInsensitive(), equalTo(false));
assertThat(andEndsWith.value(), equalTo("*ast"));
}

public void testLimit() {
var optimized = optimizedPlan(physicalPlan("""
from test
Expand Down