Skip to content

Commit 4b1eee8

Browse files
committed
Merge branch 'carlosdelest/esql-match-operator-colon' into carlosdelest/esql-match-operator-colon-scoring-demo
2 parents bdbdc52 + 3ab7f5d commit 4b1eee8

File tree

5 files changed

+86
-18
lines changed

5 files changed

+86
-18
lines changed

x-pack/plugin/esql/qa/testFixtures/src/main/resources/match-operator.csv-spec

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,19 @@ book_no:keyword | title:text
230230
2936 | Fellowship of the Ring 2ND Edition
231231
;
232232

233+
fuzzinessMatchUsingExpression
234+
required_capability: match_operator_colon
235+
236+
from books
237+
| eval fuzzy = 1 + 1
238+
| where title : "Felowhip"~fuzzy
239+
| keep book_no, title;
240+
ignoreOrder:true
241+
242+
book_no:keyword | title:text
243+
2936 | Fellowship of the Ring 2ND Edition
244+
;
245+
233246
fuzzinessMatchAuto
234247
required_capability: match_operator_colon
235248

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FOURTH;
4141
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
4242
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD;
43-
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable;
4443
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull;
4544
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNumeric;
4645
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;
@@ -145,16 +144,12 @@ protected TypeResolution resolveNonQueryParamTypes() {
145144
TypeResolution typeResolution = isNotNull(field, sourceText(), FIRST).and(isString(field, sourceText(), FIRST))
146145
.and(super.resolveNonQueryParamTypes());
147146
if (boost != null) {
148-
typeResolution = typeResolution.and(
149-
isNotNull(boost, sourceText(), THIRD).and(isNumeric(boost, sourceText(), THIRD).and(isFoldable(boost, sourceText(), THIRD)))
150-
);
147+
typeResolution = typeResolution.and(isNotNull(boost, sourceText(), THIRD).and(isNumeric(boost, sourceText(), THIRD)));
151148
}
152149
if (fuzziness != null) {
153150
typeResolution = typeResolution.and(
154151
isNotNull(fuzziness, sourceText(), FOURTH).and(
155-
isType(fuzziness, dt -> dt == DataType.INTEGER || dt == DataType.KEYWORD, sourceText(), FOURTH, "integer,keyword").and(
156-
isFoldable(fuzziness, sourceText(), FOURTH)
157-
)
152+
isType(fuzziness, dt -> dt == DataType.INTEGER || dt == DataType.KEYWORD, sourceText(), FOURTH, "integer,keyword")
158153
)
159154
);
160155
}
@@ -174,13 +169,44 @@ public void validate(Failures failures) {
174169
)
175170
);
176171
}
172+
173+
if (boost != null && boost.foldable() == false) {
174+
failures.add(
175+
Failure.fail(
176+
field,
177+
"[{}] {} boost must be evaluated to a constant. Value [{}] can't be resolved to a constant",
178+
functionName(),
179+
functionType(),
180+
boost.sourceText()
181+
)
182+
);
183+
}
184+
177185
if (fuzziness != null) {
178-
try {
179-
fuzziness();
180-
} catch (IllegalArgumentException | ElasticsearchParseException e) {
186+
if (fuzziness.foldable() == false) {
181187
failures.add(
182-
Failure.fail(field, "Invalid fuzziness value [{}] for [{}] {}", fuzziness.sourceText(), functionName(), functionType())
188+
Failure.fail(
189+
field,
190+
"[{}] {} fuzziness must be evaluated to a constant. Value [{}] can't be resolved to a constant",
191+
functionName(),
192+
functionType(),
193+
fuzziness.sourceText()
194+
)
183195
);
196+
} else {
197+
try {
198+
fuzziness();
199+
} catch (IllegalArgumentException | ElasticsearchParseException e) {
200+
failures.add(
201+
Failure.fail(
202+
field,
203+
"Invalid fuzziness value [{}] for [{}] {}",
204+
fuzziness.sourceText(),
205+
functionName(),
206+
functionType()
207+
)
208+
);
209+
}
184210
}
185211
}
186212
}
@@ -218,7 +244,9 @@ public Fuzziness fuzziness() {
218244
}
219245

220246
Object fuzinessAsObject = fuzziness.fold();
221-
if (fuzinessAsObject instanceof BytesRef bytesRefValue) {
247+
if (fuzinessAsObject instanceof String stringValue) {
248+
return Fuzziness.fromString(stringValue);
249+
} else if (fuzinessAsObject instanceof BytesRef bytesRefValue) {
222250
return Fuzziness.fromString(bytesRefValue.utf8ToString());
223251
} else if (fuzinessAsObject instanceof Integer intValue) {
224252
return Fuzziness.fromEdits(intValue);

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,13 @@ public void testMatchOperatorWithDisjunctions() {
12521252
checkWithDisjunctions(":", "first_name : \"Anna\"", "operator");
12531253
}
12541254

1255+
public void testMatchOperatorUnknownColumns() {
1256+
assumeTrue("skipping because MATCH operator is not enabled", EsqlCapabilities.Cap.MATCH_OPERATOR_COLON.isEnabled());
1257+
1258+
assertEquals("1:38: Unknown column [x]", error("from test | where first_name: \"Anna\"~x"));
1259+
assertEquals("1:30: Unknown column [x]", error("from test | where first_name^x: \"Anna\""));
1260+
}
1261+
12551262
private void checkWithDisjunctions(String functionName, String functionInvocation, String functionType) {
12561263
assertEquals(
12571264
LoggerMessageFormat.format(

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.elasticsearch.xpack.esql.EsqlTestUtils;
1919
import org.elasticsearch.xpack.esql.TestBlockFactory;
2020
import org.elasticsearch.xpack.esql.VerificationException;
21+
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
2122
import org.elasticsearch.xpack.esql.analysis.Analyzer;
2223
import org.elasticsearch.xpack.esql.analysis.AnalyzerContext;
2324
import org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils;
@@ -38,7 +39,6 @@
3839
import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Or;
3940
import org.elasticsearch.xpack.esql.core.expression.predicate.nulls.IsNotNull;
4041
import org.elasticsearch.xpack.esql.core.expression.predicate.operator.comparison.BinaryComparison;
41-
import org.elasticsearch.xpack.esql.core.tree.Source;
4242
import org.elasticsearch.xpack.esql.core.type.DataType;
4343
import org.elasticsearch.xpack.esql.core.type.EsField;
4444
import org.elasticsearch.xpack.esql.core.util.Holder;
@@ -5609,6 +5609,30 @@ public void testMatchWithNonIndexedColumnCurrentlyUnsupported() {
56095609
);
56105610
}
56115611

5612+
public void testMatchOperatorExpressionParameters() {
5613+
assumeTrue("skipping because MATCH operator is not enabled", EsqlCapabilities.Cap.MATCH_OPERATOR_COLON.isEnabled());
5614+
5615+
final String header = "Found 1 problem\nline ";
5616+
VerificationException e = expectThrows(VerificationException.class, () -> plan("""
5617+
from test | eval fuzzy = 1 + salary | where first_name:"Anna"~fuzzy"""));
5618+
assertTrue(e.getMessage().startsWith("Found "));
5619+
assertEquals(
5620+
"1:45: [:] operator fuzziness must be evaluated to a constant. Value [fuzzy] can't be resolved to a constant",
5621+
e.getMessage().substring(header.length())
5622+
);
5623+
5624+
e = expectThrows(VerificationException.class, () -> plan("from test | eval boost = 1 + salary | where first_name^boost:\"Anna\""));
5625+
assertTrue(e.getMessage().startsWith("Found "));
5626+
assertEquals(
5627+
"1:45: [:] operator boost must be evaluated to a constant. Value [boost] can't be resolved to a constant",
5628+
e.getMessage().substring(header.length())
5629+
);
5630+
5631+
// These should work
5632+
plan("from test | eval fuzzy = 1 + 1 | where first_name:\"Anna\"~fuzzy");
5633+
plan("from test | eval boost = 1.0 + 0.3 | where first_name^boost:\"Anna\"");
5634+
}
5635+
56125636
public void testMatchFunctionIsNotNullable() {
56135637
String queryText = """
56145638
row n = null | eval text = n + 5 | where match(text::keyword, "Anna")
@@ -5620,8 +5644,4 @@ public void testMatchFunctionIsNotNullable() {
56205644
containsString("[MATCH] function cannot operate on [text::keyword], which is not a field from an index mapping")
56215645
);
56225646
}
5623-
5624-
private Literal nullOf(DataType dataType) {
5625-
return new Literal(Source.EMPTY, null, dataType);
5626-
}
56275647
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2328,7 +2328,7 @@ public void checkMatchOperatorWithFuzziness(String fuzzinessValue, Fuzziness fuz
23282328
var matchField = (UnresolvedAttribute) match.field();
23292329
assertThat(matchField.name(), equalTo("field"));
23302330
assertThat(match.query().fold(), equalTo("value"));
2331-
assertNull(match.boost());
2331+
assertThat(match.boost(), equalTo(1.0));
23322332
assertThat(match.fuzziness(), equalTo(fuzinessExpected));
23332333
}
23342334

0 commit comments

Comments
 (0)