77
88package org .elasticsearch .xpack .esql .expression .function .fulltext ;
99
10+ import org .apache .lucene .util .BytesRef ;
1011import org .elasticsearch .common .io .stream .NamedWriteableRegistry ;
1112import org .elasticsearch .common .io .stream .StreamInput ;
1213import org .elasticsearch .common .io .stream .StreamOutput ;
2021import org .elasticsearch .xpack .esql .core .tree .NodeInfo ;
2122import org .elasticsearch .xpack .esql .core .tree .Source ;
2223import org .elasticsearch .xpack .esql .core .type .DataType ;
24+ import org .elasticsearch .xpack .esql .core .util .NumericUtils ;
2325import org .elasticsearch .xpack .esql .expression .function .Example ;
2426import org .elasticsearch .xpack .esql .expression .function .FunctionInfo ;
2527import org .elasticsearch .xpack .esql .expression .function .Param ;
2628import org .elasticsearch .xpack .esql .io .stream .PlanStreamInput ;
29+ import org .elasticsearch .xpack .esql .type .EsqlDataTypeConverter ;
2730
2831import java .io .IOException ;
2932import java .util .List ;
4649import static org .elasticsearch .xpack .esql .core .type .DataType .TEXT ;
4750import static org .elasticsearch .xpack .esql .core .type .DataType .UNSIGNED_LONG ;
4851import static org .elasticsearch .xpack .esql .core .type .DataType .VERSION ;
52+ import static org .elasticsearch .xpack .esql .expression .predicate .operator .comparison .EsqlBinaryComparison .formatIncompatibleTypesMessage ;
4953
5054/**
5155 * Full text function that performs a {@link QueryStringQuery} .
@@ -58,7 +62,7 @@ public class Match extends FullTextFunction implements Validatable {
5862
5963 private transient Boolean isOperator ;
6064
61- public static final Set <DataType > DATA_TYPES = Set .of (
65+ public static final Set <DataType > FIELD_DATA_TYPES = Set .of (
6266 KEYWORD ,
6367 TEXT ,
6468 BOOLEAN ,
@@ -71,6 +75,18 @@ public class Match extends FullTextFunction implements Validatable {
7175 UNSIGNED_LONG ,
7276 VERSION
7377 );
78+ public static final Set <DataType > QUERY_DATA_TYPES = Set .of (
79+ KEYWORD ,
80+ BOOLEAN ,
81+ DATETIME ,
82+ DATE_NANOS ,
83+ DOUBLE ,
84+ INTEGER ,
85+ IP ,
86+ LONG ,
87+ UNSIGNED_LONG ,
88+ VERSION
89+ );
7490
7591 @ FunctionInfo (
7692 returnType = "boolean" ,
@@ -87,7 +103,7 @@ public Match(
87103 ) Expression field ,
88104 @ Param (
89105 name = "query" ,
90- type = { "keyword" , "text" , " boolean" , "date" , "date_nanos" , "double" , "integer" , "ip" , "long" , "unsigned_long" , "version" },
106+ type = { "keyword" , "boolean" , "date" , "date_nanos" , "double" , "integer" , "ip" , "long" , "unsigned_long" , "version" },
91107 description = "Text you wish to find in the provided field."
92108 ) Expression matchQuery
93109 ) {
@@ -119,7 +135,7 @@ protected TypeResolution resolveNonQueryParamTypes() {
119135 return isNotNull (field , sourceText (), FIRST ).and (
120136 isType (
121137 field ,
122- DATA_TYPES ::contains ,
138+ FIELD_DATA_TYPES ::contains ,
123139 sourceText (),
124140 FIRST ,
125141 "keyword, text, boolean, date, date_nanos, double, integer, ip, long, unsigned_long, version"
@@ -131,13 +147,32 @@ protected TypeResolution resolveNonQueryParamTypes() {
131147 protected TypeResolution resolveQueryParamType () {
132148 return isType (
133149 query (),
134- DATA_TYPES ::contains ,
135- functionName (),
150+ QUERY_DATA_TYPES ::contains ,
151+ sourceText (),
136152 queryParamOrdinal (),
137- "keyword, text, boolean, date, date_nanos, double, integer, ip, long, unsigned_long, version"
153+ "keyword, boolean, date, date_nanos, double, integer, ip, long, unsigned_long, version"
138154 ).and (isNotNullAndFoldable (query (), sourceText (), queryParamOrdinal ()));
139155 }
140156
157+ @ Override
158+ protected TypeResolution checkParamCompatibility () {
159+ DataType fieldType = field ().dataType ();
160+ DataType queryType = query ().dataType ();
161+
162+ if ((fieldType == queryType ) || (queryType == KEYWORD )) {
163+ return TypeResolution .TYPE_RESOLVED ;
164+ }
165+
166+ if (fieldType .isNumeric () && queryType .isNumeric ()) {
167+ // When doing an unsigned long query, field must be an unsigned long
168+ if ((queryType == UNSIGNED_LONG && fieldType != UNSIGNED_LONG ) == false ) {
169+ return TypeResolution .TYPE_RESOLVED ;
170+ }
171+ }
172+
173+ return new TypeResolution (formatIncompatibleTypesMessage (fieldType , queryType , sourceText ()));
174+ }
175+
141176 @ Override
142177 public void validate (Failures failures ) {
143178 if (field instanceof FieldAttribute == false ) {
@@ -153,6 +188,23 @@ public void validate(Failures failures) {
153188 }
154189 }
155190
191+ @ Override
192+ public Object queryAsObject () {
193+ Object queryAsObject = query ().fold ();
194+
195+ if (queryAsObject instanceof BytesRef bytesRef ) {
196+ return switch (query ().dataType ()) {
197+ case IP -> EsqlDataTypeConverter .ipToString (bytesRef );
198+ case VERSION -> EsqlDataTypeConverter .versionToString (bytesRef );
199+ default -> bytesRef .utf8ToString ();
200+ };
201+ } else if (query ().dataType () == DataType .UNSIGNED_LONG ) {
202+ return NumericUtils .unsignedLongAsBigInteger ((Long ) queryAsObject );
203+ }
204+
205+ return queryAsObject ;
206+ }
207+
156208 @ Override
157209 public Expression replaceChildren (List <Expression > newChildren ) {
158210 return new Match (source (), newChildren .get (0 ), newChildren .get (1 ));
0 commit comments