Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
61a479b
First version - adding toQuery() to ShardContext
carlosdelest Jan 16, 2025
e2e996d
Remove the need to have toQuery() on ShardContext
carlosdelest Jan 16, 2025
e1ca189
Include shard contexts in ToEvaluator, so no need to differentiate in…
carlosdelest Jan 16, 2025
9aa9d78
Check disjunctions for pushing down to source
carlosdelest Jan 16, 2025
ed62f9f
Spotless
carlosdelest Jan 16, 2025
318b06e
Fix tests
carlosdelest Jan 16, 2025
b4221e4
Fix tests
carlosdelest Jan 16, 2025
6d5b813
[CI] Auto commit changes from spotless
Jan 16, 2025
bb46fd8
Spotless
carlosdelest Jan 16, 2025
ad575b5
Merge remote-tracking branch 'carlosdelest/non-issue/esql-full-text-f…
carlosdelest Jan 16, 2025
324f243
Don't allow disjunctions to be used with scoring
carlosdelest Jan 17, 2025
bce1934
Merge branch 'main' into non-issue/esql-full-text-functions-disjunctions
carlosdelest Jan 17, 2025
4456c24
Update docs/changelog/120291.yaml
carlosdelest Jan 17, 2025
92a3809
Add CSV tests
carlosdelest Jan 17, 2025
023daea
Update generated docs
carlosdelest Jan 17, 2025
51aa10e
Merge branch 'main' into non-issue/esql-full-text-functions-disjunctions
carlosdelest Jan 17, 2025
c97aeb7
Moves validation to Or class
carlosdelest Jan 20, 2025
42a8007
Move some checks from Or to FullTextFunction
carlosdelest Jan 20, 2025
31a7932
Merge remote-tracking branch 'carlosdelest/non-issue/esql-full-text-f…
carlosdelest Jan 20, 2025
ce30c76
Change method visibility
carlosdelest Jan 20, 2025
ee87d79
Merge branch 'main' into non-issue/esql-full-text-functions-disjunctions
carlosdelest Jan 20, 2025
f0eb7d8
Fix capabilities
carlosdelest Jan 20, 2025
3513999
Fix docs
carlosdelest Jan 20, 2025
b5c94d3
Merge remote-tracking branch 'carlosdelest/non-issue/esql-full-text-f…
carlosdelest Jan 20, 2025
a1d3cb3
Fix docs for scoring examples
carlosdelest Jan 21, 2025
6b1fc89
Merge branch 'main' into non-issue/esql-full-text-functions-disjunctions
carlosdelest Jan 21, 2025
d23998d
OR doesn't need to check for pushable full text functions
carlosdelest Jan 23, 2025
6022dad
Merge branch 'main' into non-issue/esql-full-text-functions-disjunctions
carlosdelest Jan 23, 2025
8e7a835
Add assertion to check correct type
carlosdelest Jan 27, 2025
9fdd24f
Minor doc fix
carlosdelest Jan 27, 2025
58677e4
Merge remote-tracking branch 'origin/main' into non-issue/esql-full-t…
carlosdelest Jan 27, 2025
1035871
Merge remote-tracking branch 'carlosdelest/non-issue/esql-full-text-f…
carlosdelest Jan 27, 2025
5df79d4
Added some comments to EvalMapper.toEvaluator
carlosdelest Jan 28, 2025
f55978a
Merge remote-tracking branch 'origin/main' into non-issue/esql-full-t…
carlosdelest Jan 28, 2025
6503146
Merge branch 'main' into non-issue/esql-full-text-functions-disjunctions
carlosdelest Jan 28, 2025
64b174e
Merge remote-tracking branch 'origin/main' into non-issue/esql-full-t…
carlosdelest Jan 28, 2025
9235b47
Merge remote-tracking branch 'carlosdelest/non-issue/esql-full-text-f…
carlosdelest Jan 28, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/120291.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 120291
summary: ESQL - Allow full text functions disjunctions for non-full text functions
area: ES|QL
type: feature
issues: []
18 changes: 14 additions & 4 deletions docs/reference/esql/esql-limitations.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ it is necessary to use the search function, like <<esql-match>>, in a <<esql-whe
directly after the <<esql-from>> source command, or close enough to it.
Otherwise, the query will fail with a validation error.
Another limitation is that any <<esql-where>> command containing a full-text search function
cannot also use disjunctions (`OR`) unless all functions used in the OR clauses are full-text functions themselves.
cannot use disjunctions (`OR`), unless:

* All functions used in the OR clauses are full-text functions themselves, or scoring is not used

For example, this query is valid:

Expand All @@ -131,19 +133,27 @@ FROM books
| WHERE MATCH(author, "Faulkner")
----

And this query will fail due to the disjunction:
And this query that uses a disjunction will succeed:

[source,esql]
----
FROM books
| WHERE MATCH(author, "Faulkner") OR QSTR("author: Hemingway")
----

However using scoring will fail because it uses a non full text function as part of the disjunction:

[source,esql]
----
FROM books METADATA _score
| WHERE MATCH(author, "Faulkner") OR author LIKE "Hemingway"
----

However this query will succeed because it uses full text functions on both `OR` clauses:
Scoring will work in the following query, as it uses full text functions on both `OR` clauses:

[source,esql]
----
FROM books
FROM books METADATA _score
| WHERE MATCH(author, "Faulkner") OR QSTR("author: Hemingway")
----

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ tasks.named("yamlRestCompatTestTransform").configure({ task ->
task.skipTest("esql/190_lookup_join/alias-repeated-index", "LOOKUP JOIN does not support index aliases for now")
task.skipTest("esql/190_lookup_join/alias-pattern-multiple", "LOOKUP JOIN does not support index aliases for now")
task.skipTest("esql/190_lookup_join/alias-pattern-single", "LOOKUP JOIN does not support index aliases for now")

task.skipTest("esql/180_match_operator/match with disjunctions", "Disjunctions in full text functions work now")
})

tasks.named('yamlRestCompatTest').configure {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.elasticsearch.compute.data.DocVector;
import org.elasticsearch.compute.data.IntVector;
import org.elasticsearch.compute.data.Page;
import org.elasticsearch.compute.operator.DriverContext;
import org.elasticsearch.compute.operator.EvalOperator;
import org.elasticsearch.core.Releasable;
import org.elasticsearch.core.Releasables;
Expand All @@ -44,19 +45,20 @@ public record ShardConfig(Query query, IndexSearcher searcher) {}

private final BlockFactory blockFactory;
private final ShardConfig[] shards;
private final int docChannel;

private ShardState[] perShardState = EMPTY_SHARD_STATES;

public LuceneQueryExpressionEvaluator(BlockFactory blockFactory, ShardConfig[] shards, int docChannel) {
public LuceneQueryExpressionEvaluator(BlockFactory blockFactory, ShardConfig[] shards) {
this.blockFactory = blockFactory;
this.shards = shards;
this.docChannel = docChannel;
}

@Override
public Block eval(Page page) {
DocVector docs = page.<DocBlock>getBlock(docChannel).asVector();
// Lucene based operators retrieve DocVectors as first block
Block block = page.getBlock(0);
assert block instanceof DocBlock : "LuceneQueryExpressionEvaluator expects DocBlock as input";
DocVector docs = (DocVector) block.asVector();
try {
if (docs.singleSegmentNonDecreasing()) {
return evalSingleSegmentNonDecreasing(docs).asBlock();
Expand Down Expand Up @@ -341,4 +343,17 @@ public void close() {
Releasables.closeExpectNoException(builder);
}
}

public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
private final ShardConfig[] shardConfigs;

public Factory(ShardConfig[] shardConfigs) {
this.shardConfigs = shardConfigs;
}

@Override
public EvalOperator.ExpressionEvaluator get(DriverContext context) {
return new LuceneQueryExpressionEvaluator(context.blockFactory(), shardConfigs);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ private List<Page> runQuery(Set<String> values, Query query, boolean shuffleDocs
);
LuceneQueryExpressionEvaluator luceneQueryEvaluator = new LuceneQueryExpressionEvaluator(
blockFactory,
new LuceneQueryExpressionEvaluator.ShardConfig[] { shard },
0
new LuceneQueryExpressionEvaluator.ShardConfig[] { shard }

);

List<Operator> operators = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,40 @@ emp_no:integer | first_name:keyword | last_name:keyword
10053 | Sanjiv | Zschoche
10069 | Margareta | Bierman
;

testKqlWithNonPushableDisjunctions
required_capability: kql_function
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where kql("title:lord") or length(title) > 130
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2714
4023
7140
8678
;

testKqlWithNonPushableDisjunctionsOnComplexExpressions
required_capability: kql_function
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where (kql("title:lord") and ratings > 4.5) or (kql("author:dostoevsky") and length(title) > 50)
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2924
4023
1937
7140
2714
;
Original file line number Diff line number Diff line change
Expand Up @@ -718,3 +718,40 @@ from books
title:text
The Hobbit or There and Back Again
;

testMatchWithNonPushableDisjunctions
required_capability: match_function
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where match(title, "lord") or length(title) > 130
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2714
4023
7140
8678
;

testMatchWithNonPushableDisjunctionsOnComplexExpressions
required_capability: match_function
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where (match(title, "lord") and ratings > 4.5) or (match(author, "dostoevsky") and length(title) > 50)
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2924
4023
1937
7140
2714
;
Original file line number Diff line number Diff line change
Expand Up @@ -684,3 +684,40 @@ from semantic_text
host:keyword | semantic_text_field:text
"host1" | live long and prosper
;

testMatchWithNonPushableDisjunctions
required_capability: match_operator_colon
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where title:"lord" or length(title) > 130
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2714
4023
7140
8678
;

testMatchWithNonPushableDisjunctionsOnComplexExpressions
required_capability: match_operator_colon
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where (title:"lord" and ratings > 4.5) or (author:"dostoevsky" and length(title) > 50)
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2924
4023
1937
7140
2714
;
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,40 @@ emp_no:integer | first_name:keyword | last_name:keyword
10053 | Sanjiv | Zschoche
10069 | Margareta | Bierman
;

testQstrWithNonPushableDisjunctions
required_capability: qstr_function
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where qstr("title:lord") or length(title) > 130
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2714
4023
7140
8678
;

testQstrWithNonPushableDisjunctionsOnComplexExpressions
required_capability: qstr_function
required_capability: full_text_functions_disjunctions_compute_engine

from books
| where (qstr("title:lord") and ratings > 4.5) or (qstr("author:dostoevsky") and length(title) > 50)
| keep book_no
;
ignoreOrder: true

book_no:keyword
2675
2924
4023
1937
7140
2714
;
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
import org.elasticsearch.xpack.esql.VerificationException;
import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
import org.elasticsearch.xpack.esql.action.EsqlQueryRequest;
import org.elasticsearch.xpack.esql.action.EsqlQueryResponse;
import org.junit.Before;

import java.util.List;
Expand All @@ -31,12 +29,6 @@ public void setupIndex() {
createAndPopulateIndex();
}

@Override
protected EsqlQueryResponse run(EsqlQueryRequest request) {
assumeTrue("match function capability not available", EsqlCapabilities.Cap.MATCH_FUNCTION.isEnabled());
return super.run(request);
}

public void testSimpleWhereMatch() {
var query = """
FROM test
Expand Down Expand Up @@ -230,20 +222,19 @@ public void testWhereMatchAfterStats() {
assertThat(error.getMessage(), containsString("Unknown column [content]"));
}

public void testWhereMatchWithFunctions() {
public void testWhereMatchNotPushedDown() {
var query = """
FROM test
| WHERE match(content, "fox") OR to_upper(content) == "FOX"
| WHERE match(content, "fox") OR length(content) < 20
| KEEP id
| SORT id
""";
var error = expectThrows(ElasticsearchException.class, () -> run(query));
assertThat(
error.getMessage(),
containsString(
"Invalid condition [match(content, \"fox\") OR to_upper(content) == \"FOX\"]. "
+ "Full text functions can be used in an OR condition,"
+ " but only if just full text functions are used in the OR condition"
)
);

try (var resp = run(query)) {
assertColumnNames(resp.columns(), List.of("id"));
assertColumnTypes(resp.columns(), List.of("integer"));
assertValues(resp.values(), List.of(List.of(1), List.of(2), List.of(6)));
}
}

public void testWhereMatchWithRow() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,20 +206,19 @@ public void testWhereMatchAfterStats() {
assertThat(error.getMessage(), containsString("Unknown column [content]"));
}

public void testWhereMatchWithFunctions() {
public void testWhereMatchNotPushedDown() {
var query = """
FROM test
| WHERE content:"fox" OR to_upper(content) == "FOX"
| WHERE content:"fox" OR length(content) < 20
| KEEP id
| SORT id
""";
var error = expectThrows(ElasticsearchException.class, () -> run(query));
assertThat(
error.getMessage(),
containsString(
"Invalid condition [content:\"fox\" OR to_upper(content) == \"FOX\"]. "
+ "Full text functions can be used in an OR condition, "
+ "but only if just full text functions are used in the OR condition"
)
);

try (var resp = run(query)) {
assertColumnNames(resp.columns(), List.of("id"));
assertColumnTypes(resp.columns(), List.of("integer"));
assertValues(resp.values(), List.of(List.of(1), List.of(2), List.of(6)));
}
}

public void testWhereMatchWithRow() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,11 @@ public enum Cap {
*/
LOOKUP_JOIN_NO_ALIASES(JOIN_LOOKUP_V12.isEnabled()),

/**
* Full text functions can be used in disjunctions as they are implemented in compute engine
*/
FULL_TEXT_FUNCTIONS_DISJUNCTIONS_COMPUTE_ENGINE,

/**
* Support match options in match function
*/
Expand Down
Loading