Skip to content

Fix behavior for _index LIKE for ESQL #130019

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

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
6060269
Get basic case to work
julian-elastic Jun 25, 2025
7dce974
Add ExpressionQueryBuilder with serialization
julian-elastic Jun 25, 2025
4984c12
[CI] Auto commit changes from spotless
elasticsearchmachine Jun 26, 2025
5a17cee
Get _index like working newToNew
elasticsearchmachine Jun 26, 2025
f34f352
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jun 26, 2025
3ad0404
[CI] Auto commit changes from spotless
elasticsearchmachine Jun 26, 2025
480db29
Move functionality from AutomatonTranslatable to TranslationAware
julian-elastic Jun 26, 2025
7484410
Remove AutomatonQueryBuilder and EsqlAutomatonQuery
julian-elastic Jun 26, 2025
77c541d
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jun 26, 2025
a8d5b44
Clean up
julian-elastic Jun 26, 2025
541fc6d
Clean up and comments
julian-elastic Jun 26, 2025
1664b6b
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jun 26, 2025
55850ba
working on getting the tests to pass in bwc mode
julian-elastic Jun 26, 2025
fed0223
[CI] Auto commit changes from spotless
elasticsearchmachine Jun 26, 2025
817336d
Merge branch 'main' into indexLike_v3
julian-elastic Jun 27, 2025
521da24
Revert "Remove AutomatonQueryBuilder and EsqlAutomatonQuery"
julian-elastic Jun 27, 2025
faba3e0
Remove skipped check
julian-elastic Jun 27, 2025
2f4bace
Fix unit tests failing
julian-elastic Jun 27, 2025
5cef3fb
[CI] Auto commit changes from spotless
elasticsearchmachine Jun 27, 2025
31c59d9
Fix transport protocol issues with the unit tests
julian-elastic Jun 27, 2025
ccafea4
Clean up classes no longer needed
julian-elastic Jun 27, 2025
8d032fe
[CI] Auto commit changes from spotless
elasticsearchmachine Jun 27, 2025
bc11f30
bigfix
julian-elastic Jun 27, 2025
f31bbf4
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jun 27, 2025
b3f65ed
Fix more failing UTs
julian-elastic Jun 27, 2025
b6c162d
Merge branch 'main' into indexLike_v3
julian-elastic Jun 27, 2025
4bb44b6
Fix more failing UTs
julian-elastic Jun 27, 2025
c51b759
[CI] Auto commit changes from spotless
elasticsearchmachine Jun 27, 2025
b6d26fb
Revert change in SearchShardsRequest
julian-elastic Jun 27, 2025
b3bb0b8
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jun 27, 2025
eb1deb9
Revert change in SearchShardsRequest
julian-elastic Jun 27, 2025
aff0fac
Address code review feedback part 1
julian-elastic Jun 30, 2025
359a7c6
Merge branch 'main' into indexLike_v3
julian-elastic Jun 30, 2025
0ce123c
Update docs/changelog/130019.yaml
julian-elastic Jul 1, 2025
f0b6e5a
Address code review feedback part 2 (still needs UT with the flag set…
julian-elastic Jul 1, 2025
5c3d8b5
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jul 1, 2025
b619623
[CI] Auto commit changes from spotless
elasticsearchmachine Jul 1, 2025
b776435
UT fixes
julian-elastic Jul 1, 2025
35544dd
Merge remote-tracking branch 'origin/indexLike_v3' into indexLike_v3
julian-elastic Jul 1, 2025
29d14ac
Merge branch 'main' into indexLike_v3
julian-elastic Jul 1, 2025
bca38b2
Fix merge errors
julian-elastic Jul 1, 2025
9cc0527
Add Supplier for Automaton and CharacterRunAutomaton
julian-elastic Jul 1, 2025
5b852f0
Fix UT errors
julian-elastic Jul 1, 2025
99364d7
Fix UT errors
julian-elastic Jul 1, 2025
a251c09
Change behavior for LIKE LIST and _index
julian-elastic Jul 1, 2025
3aaa248
Plan serialization?
nik9000 Jul 1, 2025
bd1f56c
fixup
nik9000 Jul 1, 2025
e1eaf49
Merge remote-tracking branch 'julian-elastic/indexLike_v3' into index…
nik9000 Jul 1, 2025
7cd4f37
Address no commits
julian-elastic Jul 2, 2025
3af20cc
Fix failing UTs
julian-elastic Jul 2, 2025
fb79e61
[CI] Auto commit changes from spotless
elasticsearchmachine Jul 2, 2025
f5b22f3
Merge branch 'main' into indexLike_v3
julian-elastic Jul 3, 2025
238fd3b
Remove some debugging print statements
julian-elastic Jul 3, 2025
288c86e
[CI] Auto commit changes from spotless
elasticsearchmachine Jul 3, 2025
7d5b692
Move flag to EsqlFlags
nik9000 Jul 3, 2025
6d9ea08
Merge remote-tracking branch 'julian-elastic/indexLike_v3' into index…
nik9000 Jul 3, 2025
9758344
Clean up and fix UTs
julian-elastic Jul 3, 2025
0996c39
[CI] Auto commit changes from spotless
elasticsearchmachine Jul 3, 2025
0afe31a
Fix UT fail
julian-elastic Jul 3, 2025
442ea48
Update docs/changelog/130019.yaml
julian-elastic Jul 7, 2025
8540550
Address code review comments
julian-elastic Jul 7, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ public class QueryPlanningBenchmark {
private EsqlParser defaultParser;
private Analyzer manyFieldsAnalyzer;
private LogicalPlanOptimizer defaultOptimizer;
private Configuration config;

@Setup
public void setup() {

var config = new Configuration(
this.config = new Configuration(
DateUtils.UTC,
Locale.US,
null,
Expand Down Expand Up @@ -116,7 +116,7 @@ public void setup() {
}

private LogicalPlan plan(EsqlParser parser, Analyzer analyzer, LogicalPlanOptimizer optimizer, String query) {
var parsed = parser.createStatement(query, new QueryParams(), telemetry);
var parsed = parser.createStatement(query, new QueryParams(), telemetry, config);
var analyzed = analyzer.analyze(parsed);
var optimized = optimizer.optimize(analyzed);
return optimized;
Expand Down
6 changes: 6 additions & 0 deletions docs/changelog/130019.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 130019
summary: Fix behavior for `_index` LIKE for ESQL
area: ES|QL
type: bug
issues:
- 129511
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ static TransportVersion def(int id) {
public static final TransportVersion PROJECT_STATE_REGISTRY_RECORDS_DELETIONS = def(9_113_0_00);
public static final TransportVersion ESQL_SERIALIZE_TIMESERIES_FIELD_TYPE = def(9_114_0_00);
public static final TransportVersion ML_INFERENCE_IBM_WATSONX_COMPLETION_ADDED = def(9_115_0_00);
public static final TransportVersion ESQL_FIXED_INDEX_LIKE = def(9_116_0_00);
/*
* STOP! READ THIS FIRST! No, really,
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.core.Nullable;
Expand All @@ -23,6 +25,7 @@

import java.util.Collection;
import java.util.Map;
import java.util.function.Supplier;

/**
* A {@link MappedFieldType} that has the same value for all documents.
Expand Down Expand Up @@ -135,9 +138,47 @@ public final Query wildcardQuery(String value, boolean caseInsensitive, QueryRew
}
}

/**
* Returns a query that matches all documents or no documents
* It usually calls {@link #wildcardQuery(String, boolean, QueryRewriteContext)}
* except for IndexFieldType which overrides this method to use its own matching logic.
*/
public Query wildcardLikeQuery(String value, boolean caseInsensitive, QueryRewriteContext context) {
return wildcardQuery(value, caseInsensitive, context);
}

@Override
public final boolean fieldHasValue(FieldInfos fieldInfos) {
// We consider constant field types to always have value.
return true;
}

/**
* Returns the constant value of this field as a string.
* Based on the field type, we need to get it in a different way.
*/
public abstract String getConstantFieldValue(SearchExecutionContext context);

/**
* Returns a query that matches all documents or no documents
* depending on whether the constant value of this field matches or not
*/
@Override
public Query automatonQuery(
Supplier<Automaton> automatonSupplier,
Supplier<CharacterRunAutomaton> characterRunAutomatonSupplier,
@Nullable MultiTermQuery.RewriteMethod method,
SearchExecutionContext context,
String description
) {
CharacterRunAutomaton compiled = characterRunAutomatonSupplier.get();
boolean matches = compiled.run(getConstantFieldValue(context));
if (matches) {
return new MatchAllDocsQuery();
} else {
return new MatchNoDocsQuery(
"The \"" + context.getFullyQualifiedIndex().getName() + "\" query was rewritten to a \"match_none\" query."
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
package org.elasticsearch.index.mapper;

import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.fielddata.FieldData;
import org.elasticsearch.index.fielddata.FieldDataContext;
import org.elasticsearch.index.fielddata.IndexFieldData;
Expand All @@ -27,6 +31,7 @@

import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class IndexFieldMapper extends MetadataFieldMapper {

Expand Down Expand Up @@ -102,6 +107,38 @@ public StoredFieldsSpec storedFieldsSpec() {
};
}

@Override
public Query wildcardLikeQuery(
String value,
@Nullable MultiTermQuery.RewriteMethod method,
boolean caseInsensitve,
SearchExecutionContext context
) {
String indexName = context.getFullyQualifiedIndex().getName();
return getWildcardLikeQuery(value, caseInsensitve, indexName);
}

@Override
public Query wildcardLikeQuery(String value, boolean caseInsensitive, QueryRewriteContext context) {
String indexName = context.getFullyQualifiedIndex().getName();
return getWildcardLikeQuery(value, caseInsensitive, indexName);
}

private static Query getWildcardLikeQuery(String value, boolean caseInsensitve, String indexName) {
if (caseInsensitve) {
value = value.toLowerCase(Locale.ROOT);
indexName = indexName.toLowerCase(Locale.ROOT);
}
if (Regex.simpleMatch(value, indexName)) {
return new MatchAllDocsQuery();
}
return new MatchNoDocsQuery("The \"" + indexName + "\" query was rewritten to a \"match_none\" query.");
}

@Override
public String getConstantFieldValue(SearchExecutionContext context) {
return context.getFullyQualifiedIndex().getName();
}
}

public IndexFieldMapper() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ protected boolean matches(String pattern, boolean caseInsensitive, QueryRewriteC
return Regex.simpleMatch(pattern, indexMode, caseInsensitive);
}

@Override
public String getConstantFieldValue(SearchExecutionContext context) {
return context.getIndexSettings().getMode().getName();
}

@Override
public Query existsQuery(SearchExecutionContext context) {
return new MatchAllDocsQuery();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiTerms;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.Automata;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import org.apache.lucene.util.automaton.CompiledAutomaton;
import org.apache.lucene.util.automaton.CompiledAutomaton.AUTOMATON_TYPE;
import org.apache.lucene.util.automaton.Operations;
Expand All @@ -51,6 +53,7 @@
import org.elasticsearch.index.fielddata.SourceValueFetcherSortedBinaryIndexFieldData;
import org.elasticsearch.index.fielddata.StoredFieldSortedBinaryIndexFieldData;
import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData;
import org.elasticsearch.index.query.AutomatonQueryWithDescription;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.index.similarity.SimilarityProvider;
import org.elasticsearch.script.Script;
Expand Down Expand Up @@ -82,6 +85,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;

import static org.apache.lucene.index.IndexWriter.MAX_TERM_LENGTH;
import static org.elasticsearch.core.Strings.format;
Expand Down Expand Up @@ -1042,6 +1046,17 @@ public IndexSortConfig getIndexSortConfig() {
public boolean hasDocValuesSkipper() {
return hasDocValuesSkipper;
}

@Override
public Query automatonQuery(
Supplier<Automaton> automatonSupplier,
Supplier<CharacterRunAutomaton> characterRunAutomatonSupplier,
@Nullable MultiTermQuery.RewriteMethod method,
SearchExecutionContext context,
String description
) {
return new AutomatonQueryWithDescription(new Term(name()), automatonSupplier.get(), description);
}
}

private final boolean indexed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.cluster.metadata.IndexMetadata;
Expand Down Expand Up @@ -54,6 +56,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;

import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;

Expand Down Expand Up @@ -329,6 +332,19 @@ public final Query wildcardQuery(String value, @Nullable MultiTermQuery.RewriteM
return wildcardQuery(value, method, false, context);
}

/**
* Similar to wildcardQuery, except that we change the behavior for ESQL
* to behave like a string LIKE query, where the value is matched as a string
*/
public Query wildcardLikeQuery(
String value,
@Nullable MultiTermQuery.RewriteMethod method,
boolean caseInsensitve,
SearchExecutionContext context
) {
return wildcardQuery(value, method, caseInsensitve, context);
}

public Query wildcardQuery(
String value,
@Nullable MultiTermQuery.RewriteMethod method,
Expand Down Expand Up @@ -370,6 +386,23 @@ public Query regexpQuery(
);
}

/**
* Returns a Lucine pushable Query for the current field
* For now can only be AutomatonQuery or MatchAllDocsQuery() or MatchNoDocsQuery()
*/
public Query automatonQuery(
Supplier<Automaton> automatonSupplier,
Supplier<CharacterRunAutomaton> characterRunAutomatonSupplier,
@Nullable MultiTermQuery.RewriteMethod method,
SearchExecutionContext context,
String description
) {
throw new QueryShardException(
context,
"Can only use automaton queries on keyword fields - not on [" + name + "] which is of type [" + typeName() + "]"
);
}

public Query existsQuery(SearchExecutionContext context) {
if (hasDocValues() || getTextSearchInfo().hasNorms()) {
return new FieldExistsQuery(name());
Expand Down

This file was deleted.

Loading
Loading