1
0
Эх сурвалжийг харах

[8.x] Backport semantic text string support (#116235)

* semantic_text as string type in ES|QL - support for functions and operators (#115243)

* fix tests

* Add CSV tests

* Add function tests

* Refactor tests

* spotless

* Use DataType.stringTypes() where possible

* Add tests for conditional functions and expressions

* Fix tests after merge

* Reorder semantic_text evaluators and tests

* Re-ordered two more places for SEMANTIC_TEXT after TEXT

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Craig Taverner <craig@amanzi.com>

* Fix release tests for semantic_text (#116202)

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Craig Taverner <craig@amanzi.com>
Ioana Tagirta 11 сар өмнө
parent
commit
325a3051cb
55 өөрчлөгдсөн 1221 нэмэгдсэн , 100 устгасан
  1. 2 2
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java
  2. 4 0
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/mapping-semantic_text.json
  3. 4 4
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/semantic_text.csv
  4. 943 0
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/semantic_text.csv-spec
  5. 4 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Verifier.java
  6. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java
  7. 3 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToCartesianPoint.java
  8. 3 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToCartesianShape.java
  9. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDateNanos.java
  10. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java
  11. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java
  12. 3 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToGeoPoint.java
  13. 3 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToGeoShape.java
  14. 3 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java
  15. 4 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java
  16. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java
  17. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java
  18. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java
  19. 3 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java
  20. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/Equals.java
  21. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThan.java
  22. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThanOrEqual.java
  23. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThan.java
  24. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThanOrEqual.java
  25. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/NotEquals.java
  26. 4 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypeConverter.java
  27. 3 3
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java
  28. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/CaseTests.java
  29. 16 14
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java
  30. 16 14
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java
  31. 11 19
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/FromBase64Tests.java
  32. 11 19
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBase64Tests.java
  33. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java
  34. 16 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAppendTests.java
  35. 17 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSliceTests.java
  36. 14 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSortTests.java
  37. 3 2
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatTests.java
  38. 14 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftTests.java
  39. 10 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthTests.java
  40. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java
  41. 16 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RepeatTests.java
  42. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReverseTests.java
  43. 13 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightTests.java
  44. 2 3
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitTests.java
  45. 19 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringTests.java
  46. 2 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java
  47. 2 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java
  48. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java
  49. 2 2
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/EqualsTests.java
  50. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThanOrEqualTests.java
  51. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThanTests.java
  52. 18 2
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java
  53. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThanOrEqualTests.java
  54. 1 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThanTests.java
  55. 4 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/EsqlDataTypeConverterTests.java

+ 2 - 2
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java

@@ -370,7 +370,7 @@ public enum DataType {
     }
 
     public static boolean isString(DataType t) {
-        return t == KEYWORD || t == TEXT;
+        return t == KEYWORD || t == TEXT || t == SEMANTIC_TEXT;
     }
 
     public static boolean isPrimitiveAndSupported(DataType t) {
@@ -585,7 +585,7 @@ public enum DataType {
     }
 
     public DataType noText() {
-        return this == TEXT ? KEYWORD : this;
+        return isString(this) ? KEYWORD : this;
     }
 
     /**

+ 4 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/mapping-semantic_text.json

@@ -68,6 +68,10 @@
     },
     "value": {
       "type": "long"
+    },
+    "st_base64": {
+      "type": "semantic_text",
+      "inference_id": "test_sparse_inference"
     }
   }
 }

+ 4 - 4
x-pack/plugin/esql/qa/testFixtures/src/main/resources/semantic_text.csv

@@ -1,4 +1,4 @@
-_id:keyword,semantic_text_field:semantic_text,st_bool:semantic_text,st_cartesian_point:semantic_text,st_cartesian_shape:semantic_text,st_datetime:semantic_text,st_double:semantic_text,st_geopoint:semantic_text,st_geoshape:semantic_text,st_integer:semantic_text,st_ip:semantic_text,st_long:semantic_text,st_unsigned_long:semantic_text,st_version:semantic_text,st_multi_value:semantic_text,st_unicode:semantic_text,host:keyword,description:text,value:long
-1,live long and prosper,false,"POINT(4297.11 -1475.53)",,1953-09-02T00:00:00.000Z,5.20128E11,"POINT(42.97109630194 14.7552534413725)","POLYGON ((30 10\, 40 40\, 20 40\, 10 20\, 30 10))",23,1.1.1.1,2147483648,2147483648,1.2.3,["Hello there!", "This is a random value", "for testing purposes"],你吃饭了吗,"host1","some description1",1001
-2,all we have to decide is what to do with the time that is given to us,true,"POINT(7580.93 2272.77)",,2023-09-24T15:57:00.000Z,4541.11,"POINT(37.97109630194 21.7552534413725)","POLYGON ((30 10\, 40 40\, 20 40\, 10 20\, 30 10))",122,1.1.2.1,123,2147483648.2,9.0.0,["nice to meet you", "bye bye!"],["谢谢", "对不起我的中文不好"],"host2","some description2",1002
-3,be excellent to each other,,,,,,,,,,,,,,,"host3","some description3",1003
+_id:keyword,semantic_text_field:semantic_text,st_bool:semantic_text,st_cartesian_point:semantic_text,st_cartesian_shape:semantic_text,st_datetime:semantic_text,st_double:semantic_text,st_geopoint:semantic_text,st_geoshape:semantic_text,st_integer:semantic_text,st_ip:semantic_text,st_long:semantic_text,st_unsigned_long:semantic_text,st_version:semantic_text,st_multi_value:semantic_text,st_unicode:semantic_text,host:keyword,description:text,value:long,st_base64:semantic_text
+1,live long and prosper,false,"POINT(4297.11 -1475.53)",,1953-09-02T00:00:00.000Z,5.20128E11,"POINT(42.97109630194 14.7552534413725)","POLYGON ((30 10\, 40 40\, 20 40\, 10 20\, 30 10))",23,1.1.1.1,2147483648,2147483648,1.2.3,["Hello there!", "This is a random value", "for testing purposes"],你吃饭了吗,"host1","some description1",1001,ZWxhc3RpYw==
+2,all we have to decide is what to do with the time that is given to us,true,"POINT(7580.93 2272.77)",,2023-09-24T15:57:00.000Z,4541.11,"POINT(37.97109630194 21.7552534413725)","POLYGON ((30 10\, 40 40\, 20 40\, 10 20\, 30 10))",122,1.1.2.1,123,2147483648.2,9.0.0,["nice to meet you", "bye bye!"],["谢谢", "对不起我的中文不好"],"host2","some description2",1002,aGVsbG8=
+3,be excellent to each other,,,,,,,,,,,,,,,"host3","some description3",1003,

+ 943 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/semantic_text.csv-spec

@@ -173,3 +173,946 @@ host:keyword | semantic_text_field:semantic_text
 "host2"      | all we have to decide is what to do with the time that is given to us
 "host3"      | be excellent to each other
 ;
+
+case
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = case(st_ip == "1.1.1.1", "okay", "try again")
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | okay
+2           | try again
+3           | try again
+;
+
+coalesce
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = coalesce(st_version, st_ip, semantic_text_field)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | 1.2.3
+2           | 9.0.0
+3           | be excellent to each other
+;
+
+greatest
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = greatest(semantic_text_field, st_version)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | live long and prosper
+2           | all we have to decide is what to do with the time that is given to us
+3           | null
+;
+
+least
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = least(semantic_text_field, st_version)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | 1.2.3
+2           | 9.0.0
+3           | null
+;
+
+convertToBool
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_bool(st_bool)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:bool
+1           | false
+2           | true
+3           | null
+;
+
+convertToCartesianPoint
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_cartesianpoint(st_cartesian_point)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:cartesian_point
+1           | "POINT(4297.11 -1475.53)"
+2           | "POINT(7580.93 2272.77)"
+3           | null
+;
+
+convertToCartesianShape
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_cartesianshape(st_cartesian_shape)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:cartesian_shape
+1           | null
+2           | null
+3           | null
+;
+
+convertToDatetime
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_datetime(st_datetime)
+| KEEP _id, result
+| SORT _id, result
+;
+
+_id:keyword|result:datetime
+1          | 1953-09-02T00:00:00.000Z
+2          | 2023-09-24T15:57:00.000Z
+3          | null
+;
+
+convertToDouble
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_double(st_double)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword|result:double
+1          | 5.20128E11
+2          | 4541.11
+3          | null
+;
+
+convertToGeopoint
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_geopoint(st_geopoint)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:geo_point
+1           | "POINT(42.97109630194 14.7552534413725)"
+2           | "POINT(37.97109630194 21.7552534413725)"
+3           | null
+;
+
+convertToGeoshape
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_geoshape(st_geoshape)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:geo_shape
+1           | "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"
+2           | "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"
+3           | null
+;
+
+convertToInteger
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_integer(st_integer)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:integer
+1           | 23
+2           | 122
+3           | null
+;
+
+convertToIp
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_ip(st_ip)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:ip
+1           | 1.1.1.1
+2           | 1.1.2.1
+3           | null
+;
+
+convertToLong
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_long(st_long)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:long
+1           | 2147483648
+2           | 123
+3           | null
+;
+
+convertToUnsignedLong
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_unsigned_long(st_unsigned_long)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:unsigned_long
+1           | 2147483648
+2           | 2147483648.2
+3           | null
+;
+
+convertToVersion
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_version(st_version)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:version
+1           | 1.2.3
+2           | 9.0.0
+3           | null
+;
+
+concat
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = concat("<em>", semantic_text_field, "</em>")
+| KEEP result
+| SORT result
+;
+
+result:keyword
+<em>all we have to decide is what to do with the time that is given to us</em>
+<em>be excellent to each other</em>
+<em>live long and prosper</em>
+;
+
+endsWith
+required_capability: semantic_text_type
+
+FROM semantic_text
+| WHERE ends_with(semantic_text_field, "er")
+| KEEP semantic_text_field
+| SORT semantic_text_field
+;
+
+semantic_text_field:semantic_text
+be excellent to each other
+live long and prosper
+;
+
+fromBase64
+required_capability: semantic_text_type
+FROM semantic_text
+| EVAL result = from_base64(st_base64)
+| SORT result
+| KEEP result
+;
+
+result:keyword
+elastic
+hello
+null
+;
+
+left
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = left(semantic_text_field, 2)
+| SORT result
+| KEEP result
+;
+
+result:keyword
+al
+be
+li
+;
+
+length
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = length(st_version)
+| KEEP result
+| SORT result
+;
+
+result:integer
+5
+5
+null
+;
+
+locate
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = locate(semantic_text_field, "all")
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:integer
+1           | 0
+2           | 1
+3           | 0
+;
+
+ltrim
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = ltrim(semantic_text_field)
+| SORT result
+| KEEP result
+;
+
+result:keyword
+all we have to decide is what to do with the time that is given to us
+be excellent to each other
+live long and prosper
+;
+
+repeat
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = repeat(semantic_text_field, 2)
+| WHERE length(semantic_text_field) < 25
+| KEEP result
+;
+
+result:keyword
+live long and prosperlive long and prosper
+;
+
+replace
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = replace(semantic_text_field, "excellent", "good")
+| WHERE length(semantic_text_field) < 30
+| KEEP result
+| SORT result
+;
+
+result:keyword
+be good to each other
+live long and prosper
+;
+
+right
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = right(semantic_text_field, 2)
+| KEEP result
+| SORT result
+;
+
+result:keyword
+er
+er
+us
+;
+
+rtrim
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = rtrim(semantic_text_field)
+| KEEP result
+| SORT result
+;
+
+result:keyword
+all we have to decide is what to do with the time that is given to us
+be excellent to each other
+live long and prosper
+;
+
+split
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = split(st_version, ".")
+| SORT _id
+| KEEP result
+;
+
+result:keyword
+["1", "2", "3"]
+["9", "0", "0"]
+null
+;
+
+startsWith
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = starts_with(semantic_text_field, "be")
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:bool
+1           | false
+2           | false
+3           | true
+;
+
+substring
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = substring(semantic_text_field, 2, 1)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | i
+2           | l
+3           | e
+;
+
+toBase64
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_base64(st_integer)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | MjM=
+2           | MTIy
+3           | null
+;
+
+toLower
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_lower(st_cartesian_point)
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:keyword
+1           | point(4297.11 -1475.53)
+2           | point(7580.93 2272.77)
+3           | null
+;
+
+toUpper
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = to_upper(semantic_text_field)
+| KEEP _id, result
+| SORT _id
+; 
+
+_id:keyword | result:keyword
+1           | LIVE LONG AND PROSPER
+2           | ALL WE HAVE TO DECIDE IS WHAT TO DO WITH THE TIME THAT IS GIVEN TO US
+3           | BE EXCELLENT TO EACH OTHER
+;
+
+trim
+required_capability: semantic_text_type
+
+FROM semantic_text
+| EVAL result = trim(semantic_text_field)
+| SORT result
+| KEEP result
+;
+
+result:keyword
+all we have to decide is what to do with the time that is given to us
+be excellent to each other
+live long and prosper
+;
+
+mvAppend
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_append(st_multi_value, st_long)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | ["Hello there!", "This is a random value", "for testing purposes", "2147483648"] 
+2            | ["nice to meet you", "bye bye!", "123"]
+3            | null
+;
+
+mvConcat
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_concat(st_multi_value, "; ")
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | Hello there!; This is a random value; for testing purposes
+2            | nice to meet you; bye bye!
+3            | null
+;
+
+mvCount
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_count(st_multi_value)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:integer
+1            | 3
+2            | 2
+3            | null
+;
+
+mvDedupe
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_dedupe(st_multi_value)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | ["Hello there!", "This is a random value", "for testing purposes"]
+2            | ["nice to meet you", "bye bye!"]
+3            | null
+;
+
+mvFirst
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_first(st_multi_value)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | Hello there!
+2            | nice to meet you
+3            | null
+;
+
+mvLast
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_last(st_multi_value)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | for testing purposes
+2            | bye bye!
+3            | null
+;
+
+mvMax
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_max(st_multi_value)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | for testing purposes
+2            | nice to meet you
+3            | null
+;
+
+mvMin
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_min(st_multi_value)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | Hello there!
+2            | bye bye!
+3            | null
+;
+
+mvSlice
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_slice(st_multi_value, 1, 2)
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | ["This is a random value", "for testing purposes"]
+2            | bye bye!
+3            | null
+;
+
+mvSort
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_sort(st_multi_value, "ASC")
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | ["Hello there!", "This is a random value", "for testing purposes"]
+2            | ["bye bye!", "nice to meet you"]
+3            | null
+;
+
+mvZip
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = mv_zip(st_multi_value, st_multi_value, " + ")
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:keyword
+1            | ["Hello there! + Hello there!", "This is a random value + This is a random value", "for testing purposes + for testing purposes"]
+2            | ["nice to meet you + nice to meet you", "bye bye! + bye bye!"]
+3            | null
+;
+
+equalityWithConstant
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_ip == "1.1.1.1"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | false
+3            | null
+;
+
+equalityBetweenFields
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_long == st_unsigned_long
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | false
+3            | null
+;
+
+inequalityWithConstant
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_ip != "1.1.1.1"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | true
+3            | null
+;
+
+inequalityBetweenFields
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_long != st_unsigned_long
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | true
+3            | null
+;
+
+lessThanWithConstant
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field < "bye!"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | true
+3            | true
+;
+
+lessThanBetweenFields
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field < st_version
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | false
+3            | null
+;
+
+
+lessThanOrEqualToWithConstant
+required_capability: semantic_text_type
+
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field <= "be excellent to each other"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | true
+3            | true
+;
+
+lessThanOrEqualToBetweenFields
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_integer <= st_long
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | true
+3            | null
+;
+
+greaterThanWithConstant
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field > "bye!"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | false
+3            | false
+;
+
+greaterThanBetweenFields
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field > st_version
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | true
+3            | null
+;
+
+greaterThanOrEqualToWithConstant
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field >= "be excellent to each other"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | false
+3            | true
+;
+
+greaterThanOrEqualToBetweenFields
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_integer >= st_long
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | false
+3            | null
+;
+
+isNull
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_integer IS NULL
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | false
+3            | true
+;
+
+isNotNull
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_integer IS NOT NULL
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | true
+3            | false
+;
+
+cast
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_bool::BOOL
+| KEEP _id, result
+| SORT _id
+;
+
+_id:keyword | result:bool
+1           | false
+2           | true
+3           | null
+;
+
+in
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_integer IN ("123", "23")
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | false
+3            | null
+;
+
+like
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = semantic_text_field LIKE "all*"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | false
+2            | true
+3            | false
+;
+
+rlike
+required_capability: semantic_text_type
+
+FROM semantic_text METADATA _id
+| EVAL result = st_version RLIKE "[0-9].[0-9].[0-9]"
+| KEEP _id, result
+| SORT _id
+;
+
+_id: keyword | result:bool
+1            | true
+2            | true
+3            | null
+;

+ 4 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Verifier.java

@@ -8,6 +8,7 @@
 package org.elasticsearch.xpack.esql.analysis;
 
 import org.elasticsearch.common.logging.LoggerMessageFormat;
+import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
 import org.elasticsearch.xpack.esql.common.Failure;
 import org.elasticsearch.xpack.esql.core.capabilities.Unresolvable;
 import org.elasticsearch.xpack.esql.core.expression.Alias;
@@ -505,6 +506,9 @@ public class Verifier {
         List<DataType> allowed = new ArrayList<>();
         allowed.add(DataType.KEYWORD);
         allowed.add(DataType.TEXT);
+        if (EsqlCapabilities.Cap.SEMANTIC_TEXT_TYPE.isEnabled()) {
+            allowed.add(DataType.SEMANTIC_TEXT);
+        }
         allowed.add(DataType.IP);
         allowed.add(DataType.DATETIME);
         allowed.add(DataType.VERSION);

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java

@@ -28,6 +28,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToBoolean;
@@ -44,6 +45,7 @@ public class ToBoolean extends AbstractConvertFunction {
         Map.entry(BOOLEAN, (field, source) -> field),
         Map.entry(KEYWORD, ToBooleanFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToBooleanFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToBooleanFromStringEvaluator.Factory::new),
         Map.entry(DOUBLE, ToBooleanFromDoubleEvaluator.Factory::new),
         Map.entry(LONG, ToBooleanFromLongEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToBooleanFromUnsignedLongEvaluator.Factory::new),

+ 3 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToCartesianPoint.java

@@ -25,6 +25,7 @@ import java.util.Map;
 
 import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_POINT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToSpatial;
 
@@ -38,7 +39,8 @@ public class ToCartesianPoint extends AbstractConvertFunction {
     private static final Map<DataType, BuildFactory> EVALUATORS = Map.ofEntries(
         Map.entry(CARTESIAN_POINT, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToCartesianPointFromStringEvaluator.Factory::new),
-        Map.entry(TEXT, ToCartesianPointFromStringEvaluator.Factory::new)
+        Map.entry(TEXT, ToCartesianPointFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToCartesianPointFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(

+ 3 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToCartesianShape.java

@@ -26,6 +26,7 @@ import java.util.Map;
 import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_POINT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_SHAPE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToSpatial;
 
@@ -40,7 +41,8 @@ public class ToCartesianShape extends AbstractConvertFunction {
         Map.entry(CARTESIAN_POINT, (fieldEval, source) -> fieldEval),
         Map.entry(CARTESIAN_SHAPE, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToCartesianShapeFromStringEvaluator.Factory::new),
-        Map.entry(TEXT, ToCartesianShapeFromStringEvaluator.Factory::new)
+        Map.entry(TEXT, ToCartesianShapeFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToCartesianShapeFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDateNanos.java

@@ -32,6 +32,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS;
 import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.DEFAULT_DATE_NANOS_FORMATTER;
@@ -49,6 +50,7 @@ public class ToDateNanos extends AbstractConvertFunction {
         Map.entry(LONG, ToDateNanosFromLongEvaluator.Factory::new),
         Map.entry(KEYWORD, ToDateNanosFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToDateNanosFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToDateNanosFromStringEvaluator.Factory::new),
         Map.entry(DOUBLE, ToDateNanosFromDoubleEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToLongFromUnsignedLongEvaluator.Factory::new)
         /*

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java

@@ -30,6 +30,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.dateTimeToLong;
@@ -47,6 +48,7 @@ public class ToDatetime extends AbstractConvertFunction {
         Map.entry(LONG, (field, source) -> field),
         Map.entry(KEYWORD, ToDatetimeFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToDatetimeFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToDatetimeFromStringEvaluator.Factory::new),
         Map.entry(DOUBLE, ToLongFromDoubleEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToLongFromUnsignedLongEvaluator.Factory::new),
         Map.entry(INTEGER, ToLongFromIntEvaluator.Factory::new) // CastIntToLongEvaluator would be a candidate, but not MV'd

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java

@@ -30,6 +30,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToDouble;
@@ -44,6 +45,7 @@ public class ToDouble extends AbstractConvertFunction {
         Map.entry(DATETIME, ToDoubleFromLongEvaluator.Factory::new), // CastLongToDoubleEvaluator would be a candidate, but not MV'd
         Map.entry(KEYWORD, ToDoubleFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToDoubleFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToDoubleFromStringEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToDoubleFromUnsignedLongEvaluator.Factory::new),
         Map.entry(LONG, ToDoubleFromLongEvaluator.Factory::new), // CastLongToDoubleEvaluator would be a candidate, but not MV'd
         Map.entry(INTEGER, ToDoubleFromIntEvaluator.Factory::new), // CastIntToDoubleEvaluator would be a candidate, but not MV'd

+ 3 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToGeoPoint.java

@@ -25,6 +25,7 @@ import java.util.Map;
 
 import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_POINT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToSpatial;
 
@@ -38,7 +39,8 @@ public class ToGeoPoint extends AbstractConvertFunction {
     private static final Map<DataType, BuildFactory> EVALUATORS = Map.ofEntries(
         Map.entry(GEO_POINT, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToGeoPointFromStringEvaluator.Factory::new),
-        Map.entry(TEXT, ToGeoPointFromStringEvaluator.Factory::new)
+        Map.entry(TEXT, ToGeoPointFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToGeoPointFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(

+ 3 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToGeoShape.java

@@ -26,6 +26,7 @@ import java.util.Map;
 import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_POINT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToSpatial;
 
@@ -40,7 +41,8 @@ public class ToGeoShape extends AbstractConvertFunction {
         Map.entry(GEO_POINT, (fieldEval, source) -> fieldEval),
         Map.entry(GEO_SHAPE, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToGeoShapeFromStringEvaluator.Factory::new),
-        Map.entry(TEXT, ToGeoShapeFromStringEvaluator.Factory::new)
+        Map.entry(TEXT, ToGeoShapeFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToGeoShapeFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(

+ 3 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java

@@ -25,6 +25,7 @@ import java.util.Map;
 
 import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToIP;
 
@@ -34,7 +35,8 @@ public class ToIP extends AbstractConvertFunction {
     private static final Map<DataType, BuildFactory> EVALUATORS = Map.ofEntries(
         Map.entry(IP, (field, source) -> field),
         Map.entry(KEYWORD, ToIPFromStringEvaluator.Factory::new),
-        Map.entry(TEXT, ToIPFromStringEvaluator.Factory::new)
+        Map.entry(TEXT, ToIPFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToIPFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(

+ 4 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java

@@ -25,11 +25,13 @@ import java.util.List;
 import java.util.Map;
 
 import static org.elasticsearch.xpack.esql.core.type.DataType.BOOLEAN;
+import static org.elasticsearch.xpack.esql.core.type.DataType.COUNTER_INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME;
 import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.core.type.DataTypeConverter.safeToInt;
@@ -49,10 +51,11 @@ public class ToInteger extends AbstractConvertFunction {
         Map.entry(DATETIME, ToIntegerFromLongEvaluator.Factory::new),
         Map.entry(KEYWORD, ToIntegerFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToIntegerFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToIntegerFromStringEvaluator.Factory::new),
         Map.entry(DOUBLE, ToIntegerFromDoubleEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToIntegerFromUnsignedLongEvaluator.Factory::new),
         Map.entry(LONG, ToIntegerFromLongEvaluator.Factory::new),
-        Map.entry(DataType.COUNTER_INTEGER, (fieldEval, source) -> fieldEval)
+        Map.entry(COUNTER_INTEGER, (fieldEval, source) -> fieldEval)
     );
 
     @FunctionInfo(

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java

@@ -31,6 +31,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.core.type.DataTypeConverter.safeDoubleToLong;
@@ -47,6 +48,7 @@ public class ToLong extends AbstractConvertFunction {
         Map.entry(BOOLEAN, ToLongFromBooleanEvaluator.Factory::new),
         Map.entry(KEYWORD, ToLongFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToLongFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToLongFromStringEvaluator.Factory::new),
         Map.entry(DOUBLE, ToLongFromDoubleEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToLongFromUnsignedLongEvaluator.Factory::new),
         Map.entry(INTEGER, ToLongFromIntEvaluator.Factory::new), // CastIntToLongEvaluator would be a candidate, but not MV'd

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java

@@ -36,6 +36,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION;
@@ -60,6 +61,7 @@ public class ToString extends AbstractConvertFunction implements EvaluatorMapper
         Map.entry(LONG, ToStringFromLongEvaluator.Factory::new),
         Map.entry(INTEGER, ToStringFromIntEvaluator.Factory::new),
         Map.entry(TEXT, (fieldEval, source) -> fieldEval),
+        Map.entry(SEMANTIC_TEXT, (fieldEval, source) -> fieldEval),
         Map.entry(VERSION, ToStringFromVersionEvaluator.Factory::new),
         Map.entry(UNSIGNED_LONG, ToStringFromUnsignedLongEvaluator.Factory::new),
         Map.entry(GEO_POINT, ToStringFromGeoPointEvaluator.Factory::new),

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java

@@ -30,6 +30,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.booleanToUnsignedLong;
@@ -51,6 +52,7 @@ public class ToUnsignedLong extends AbstractConvertFunction {
         Map.entry(BOOLEAN, ToUnsignedLongFromBooleanEvaluator.Factory::new),
         Map.entry(KEYWORD, ToUnsignedLongFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToUnsignedLongFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToUnsignedLongFromStringEvaluator.Factory::new),
         Map.entry(DOUBLE, ToUnsignedLongFromDoubleEvaluator.Factory::new),
         Map.entry(LONG, ToUnsignedLongFromLongEvaluator.Factory::new),
         Map.entry(INTEGER, ToUnsignedLongFromIntEvaluator.Factory::new)

+ 3 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java

@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToVersion;
@@ -38,7 +39,8 @@ public class ToVersion extends AbstractConvertFunction {
     private static final Map<DataType, BuildFactory> EVALUATORS = Map.ofEntries(
         Map.entry(VERSION, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToVersionFromStringEvaluator.Factory::new),
-        Map.entry(TEXT, ToVersionFromStringEvaluator.Factory::new)
+        Map.entry(TEXT, ToVersionFromStringEvaluator.Factory::new),
+        Map.entry(SEMANTIC_TEXT, ToVersionFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(

+ 1 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/Equals.java

@@ -42,6 +42,7 @@ public class Equals extends EsqlBinaryComparison implements Negatable<EsqlBinary
         Map.entry(DataType.CARTESIAN_SHAPE, EqualsGeometriesEvaluator.Factory::new),
         Map.entry(DataType.KEYWORD, EqualsKeywordsEvaluator.Factory::new),
         Map.entry(DataType.TEXT, EqualsKeywordsEvaluator.Factory::new),
+        Map.entry(DataType.SEMANTIC_TEXT, EqualsKeywordsEvaluator.Factory::new),
         Map.entry(DataType.VERSION, EqualsKeywordsEvaluator.Factory::new),
         Map.entry(DataType.IP, EqualsKeywordsEvaluator.Factory::new)
     );

+ 1 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThan.java

@@ -37,6 +37,7 @@ public class GreaterThan extends EsqlBinaryComparison implements Negatable<EsqlB
         Map.entry(DataType.DATE_NANOS, GreaterThanLongsEvaluator.Factory::new),
         Map.entry(DataType.KEYWORD, GreaterThanKeywordsEvaluator.Factory::new),
         Map.entry(DataType.TEXT, GreaterThanKeywordsEvaluator.Factory::new),
+        Map.entry(DataType.SEMANTIC_TEXT, GreaterThanKeywordsEvaluator.Factory::new),
         Map.entry(DataType.VERSION, GreaterThanKeywordsEvaluator.Factory::new),
         Map.entry(DataType.IP, GreaterThanKeywordsEvaluator.Factory::new)
     );

+ 1 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThanOrEqual.java

@@ -37,6 +37,7 @@ public class GreaterThanOrEqual extends EsqlBinaryComparison implements Negatabl
         Map.entry(DataType.DATE_NANOS, GreaterThanOrEqualLongsEvaluator.Factory::new),
         Map.entry(DataType.KEYWORD, GreaterThanOrEqualKeywordsEvaluator.Factory::new),
         Map.entry(DataType.TEXT, GreaterThanOrEqualKeywordsEvaluator.Factory::new),
+        Map.entry(DataType.SEMANTIC_TEXT, GreaterThanOrEqualKeywordsEvaluator.Factory::new),
         Map.entry(DataType.VERSION, GreaterThanOrEqualKeywordsEvaluator.Factory::new),
         Map.entry(DataType.IP, GreaterThanOrEqualKeywordsEvaluator.Factory::new)
     );

+ 1 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThan.java

@@ -37,6 +37,7 @@ public class LessThan extends EsqlBinaryComparison implements Negatable<EsqlBina
         Map.entry(DataType.DATE_NANOS, LessThanLongsEvaluator.Factory::new),
         Map.entry(DataType.KEYWORD, LessThanKeywordsEvaluator.Factory::new),
         Map.entry(DataType.TEXT, LessThanKeywordsEvaluator.Factory::new),
+        Map.entry(DataType.SEMANTIC_TEXT, LessThanKeywordsEvaluator.Factory::new),
         Map.entry(DataType.VERSION, LessThanKeywordsEvaluator.Factory::new),
         Map.entry(DataType.IP, LessThanKeywordsEvaluator.Factory::new)
     );

+ 1 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThanOrEqual.java

@@ -37,6 +37,7 @@ public class LessThanOrEqual extends EsqlBinaryComparison implements Negatable<E
         Map.entry(DataType.DATE_NANOS, LessThanOrEqualLongsEvaluator.Factory::new),
         Map.entry(DataType.KEYWORD, LessThanOrEqualKeywordsEvaluator.Factory::new),
         Map.entry(DataType.TEXT, LessThanOrEqualKeywordsEvaluator.Factory::new),
+        Map.entry(DataType.SEMANTIC_TEXT, LessThanOrEqualKeywordsEvaluator.Factory::new),
         Map.entry(DataType.VERSION, LessThanOrEqualKeywordsEvaluator.Factory::new),
         Map.entry(DataType.IP, LessThanOrEqualKeywordsEvaluator.Factory::new)
     );

+ 1 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/NotEquals.java

@@ -42,6 +42,7 @@ public class NotEquals extends EsqlBinaryComparison implements Negatable<EsqlBin
         Map.entry(DataType.CARTESIAN_SHAPE, NotEqualsGeometriesEvaluator.Factory::new),
         Map.entry(DataType.KEYWORD, NotEqualsKeywordsEvaluator.Factory::new),
         Map.entry(DataType.TEXT, NotEqualsKeywordsEvaluator.Factory::new),
+        Map.entry(DataType.SEMANTIC_TEXT, NotEqualsKeywordsEvaluator.Factory::new),
         Map.entry(DataType.VERSION, NotEqualsKeywordsEvaluator.Factory::new),
         Map.entry(DataType.IP, NotEqualsKeywordsEvaluator.Factory::new)
     );

+ 4 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypeConverter.java

@@ -74,6 +74,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
 import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
 import static org.elasticsearch.xpack.esql.core.type.DataType.NULL;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TIME_DURATION;
 import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG;
@@ -365,6 +366,9 @@ public class EsqlDataTypeConverter {
             }
         }
         if (isString(left) && isString(right)) {
+            if (left == SEMANTIC_TEXT || right == SEMANTIC_TEXT) {
+                return KEYWORD;
+            }
             if (left == TEXT || right == TEXT) {
                 return TEXT;
             }

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

@@ -1481,20 +1481,20 @@ public class VerifierTests extends ESTestCase {
         assertEquals(
             "1:26: first argument of [\"3 days\"::date_period == to_dateperiod(\"3 days\")] must be "
                 + "[boolean, cartesian_point, cartesian_shape, date_nanos, datetime, double, geo_point, geo_shape, integer, ip, keyword, "
-                + "long, text, unsigned_long or version], found value [\"3 days\"::date_period] type [date_period]",
+                + "long, semantic_text, text, unsigned_long or version], found value [\"3 days\"::date_period] type [date_period]",
             error("row x = \"3 days\" | where \"3 days\"::date_period == to_dateperiod(\"3 days\")")
         );
 
         assertEquals(
             "1:26: first argument of [\"3 hours\"::time_duration <= to_timeduration(\"3 hours\")] must be "
-                + "[date_nanos, datetime, double, integer, ip, keyword, long, text, unsigned_long or version], "
+                + "[date_nanos, datetime, double, integer, ip, keyword, long, semantic_text, text, unsigned_long or version], "
                 + "found value [\"3 hours\"::time_duration] type [time_duration]",
             error("row x = \"3 days\" | where \"3 hours\"::time_duration <= to_timeduration(\"3 hours\")")
         );
 
         assertEquals(
             "1:19: second argument of [first_name <= to_timeduration(\"3 hours\")] must be "
-                + "[date_nanos, datetime, double, integer, ip, keyword, long, text, unsigned_long or version], "
+                + "[date_nanos, datetime, double, integer, ip, keyword, long, semantic_text, text, unsigned_long or version], "
                 + "found value [to_timeduration(\"3 hours\")] type [time_duration]",
             error("from test | where first_name <= to_timeduration(\"3 hours\")")
         );

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/CaseTests.java

@@ -60,7 +60,7 @@ public class CaseTests extends AbstractScalarFunctionTestCase {
             DataType.NULL
         ).collect(Collectors.toList());
         if (Build.current().isSnapshot()) {
-            t.add(DataType.DATE_NANOS);
+            t.addAll(DataType.UNDER_CONSTRUCTION.keySet());
         }
         TYPES = unmodifiableList(t);
     }

+ 16 - 14
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java

@@ -40,21 +40,23 @@ public class GreatestTests extends AbstractScalarFunctionTestCase {
         builder.expectFlattenedInt(IntStream::max);
         builder.expectFlattenedLong(LongStream::max);
         List<TestCaseSupplier> suppliers = builder.suppliers();
-        suppliers.add(
-            new TestCaseSupplier(
-                "(a, b)",
-                List.of(DataType.KEYWORD, DataType.KEYWORD),
-                () -> new TestCaseSupplier.TestCase(
-                    List.of(
-                        new TestCaseSupplier.TypedData(new BytesRef("a"), DataType.KEYWORD, "a"),
-                        new TestCaseSupplier.TypedData(new BytesRef("b"), DataType.KEYWORD, "b")
-                    ),
-                    "GreatestBytesRefEvaluator[values=[MvMax[field=Attribute[channel=0]], MvMax[field=Attribute[channel=1]]]]",
-                    DataType.KEYWORD,
-                    equalTo(new BytesRef("b"))
+        for (DataType stringType : DataType.stringTypes()) {
+            suppliers.add(
+                new TestCaseSupplier(
+                    "(a, b)",
+                    List.of(stringType, stringType),
+                    () -> new TestCaseSupplier.TestCase(
+                        List.of(
+                            new TestCaseSupplier.TypedData(new BytesRef("a"), stringType, "a"),
+                            new TestCaseSupplier.TypedData(new BytesRef("b"), stringType, "b")
+                        ),
+                        "GreatestBytesRefEvaluator[values=[MvMax[field=Attribute[channel=0]], MvMax[field=Attribute[channel=1]]]]",
+                        stringType,
+                        equalTo(new BytesRef("b"))
+                    )
                 )
-            )
-        );
+            );
+        }
         suppliers.add(
             new TestCaseSupplier(
                 "(a, b)",

+ 16 - 14
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java

@@ -39,21 +39,23 @@ public class LeastTests extends AbstractScalarFunctionTestCase {
         builder.expectFlattenedInt(IntStream::min);
         builder.expectFlattenedLong(LongStream::min);
         List<TestCaseSupplier> suppliers = builder.suppliers();
-        suppliers.add(
-            new TestCaseSupplier(
-                "(a, b)",
-                List.of(DataType.KEYWORD, DataType.KEYWORD),
-                () -> new TestCaseSupplier.TestCase(
-                    List.of(
-                        new TestCaseSupplier.TypedData(new BytesRef("a"), DataType.KEYWORD, "a"),
-                        new TestCaseSupplier.TypedData(new BytesRef("b"), DataType.KEYWORD, "b")
-                    ),
-                    "LeastBytesRefEvaluator[values=[MvMin[field=Attribute[channel=0]], MvMin[field=Attribute[channel=1]]]]",
-                    DataType.KEYWORD,
-                    equalTo(new BytesRef("a"))
+        for (DataType stringType : DataType.stringTypes()) {
+            suppliers.add(
+                new TestCaseSupplier(
+                    "(a, b)",
+                    List.of(stringType, stringType),
+                    () -> new TestCaseSupplier.TestCase(
+                        List.of(
+                            new TestCaseSupplier.TypedData(new BytesRef("a"), stringType, "a"),
+                            new TestCaseSupplier.TypedData(new BytesRef("b"), stringType, "b")
+                        ),
+                        "LeastBytesRefEvaluator[values=[MvMin[field=Attribute[channel=0]], MvMin[field=Attribute[channel=1]]]]",
+                        stringType,
+                        equalTo(new BytesRef("a"))
+                    )
                 )
-            )
-        );
+            );
+        }
         suppliers.add(
             new TestCaseSupplier(
                 "(a, b)",

+ 11 - 19
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/FromBase64Tests.java

@@ -35,25 +35,17 @@ public class FromBase64Tests extends AbstractScalarFunctionTestCase {
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
         List<TestCaseSupplier> suppliers = new ArrayList<>();
-        suppliers.add(new TestCaseSupplier(List.of(DataType.KEYWORD), () -> {
-            BytesRef input = new BytesRef(randomAlphaOfLength(6));
-            return new TestCaseSupplier.TestCase(
-                List.of(new TestCaseSupplier.TypedData(input, DataType.KEYWORD, "string")),
-                "FromBase64Evaluator[field=Attribute[channel=0]]",
-                DataType.KEYWORD,
-                equalTo(new BytesRef(Base64.getDecoder().decode(input.utf8ToString().getBytes(StandardCharsets.UTF_8))))
-            );
-        }));
-
-        suppliers.add(new TestCaseSupplier(List.of(DataType.TEXT), () -> {
-            BytesRef input = new BytesRef(randomAlphaOfLength(54));
-            return new TestCaseSupplier.TestCase(
-                List.of(new TestCaseSupplier.TypedData(input, DataType.TEXT, "string")),
-                "FromBase64Evaluator[field=Attribute[channel=0]]",
-                DataType.KEYWORD,
-                equalTo(new BytesRef(Base64.getDecoder().decode(input.utf8ToString().getBytes(StandardCharsets.UTF_8))))
-            );
-        }));
+        for (DataType dataType : DataType.stringTypes()) {
+            suppliers.add(new TestCaseSupplier(List.of(dataType), () -> {
+                BytesRef input = new BytesRef(randomAlphaOfLength(54));
+                return new TestCaseSupplier.TestCase(
+                    List.of(new TestCaseSupplier.TypedData(input, dataType, "string")),
+                    "FromBase64Evaluator[field=Attribute[channel=0]]",
+                    DataType.KEYWORD,
+                    equalTo(new BytesRef(Base64.getDecoder().decode(input.utf8ToString().getBytes(StandardCharsets.UTF_8))))
+                );
+            }));
+        }
 
         return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers, (v, p) -> "string");
     }

+ 11 - 19
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBase64Tests.java

@@ -36,25 +36,17 @@ public class ToBase64Tests extends AbstractScalarFunctionTestCase {
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
         List<TestCaseSupplier> suppliers = new ArrayList<>();
-        suppliers.add(new TestCaseSupplier(List.of(DataType.KEYWORD), () -> {
-            BytesRef input = (BytesRef) randomLiteral(DataType.KEYWORD).value();
-            return new TestCaseSupplier.TestCase(
-                List.of(new TestCaseSupplier.TypedData(input, DataType.KEYWORD, "string")),
-                "ToBase64Evaluator[field=Attribute[channel=0]]",
-                DataType.KEYWORD,
-                equalTo(new BytesRef(Base64.getEncoder().encode(input.utf8ToString().getBytes(StandardCharsets.UTF_8))))
-            );
-        }));
-
-        suppliers.add(new TestCaseSupplier(List.of(DataType.TEXT), () -> {
-            BytesRef input = (BytesRef) randomLiteral(DataType.TEXT).value();
-            return new TestCaseSupplier.TestCase(
-                List.of(new TestCaseSupplier.TypedData(input, DataType.TEXT, "string")),
-                "ToBase64Evaluator[field=Attribute[channel=0]]",
-                DataType.KEYWORD,
-                equalTo(new BytesRef(Base64.getEncoder().encode(input.utf8ToString().getBytes(StandardCharsets.UTF_8))))
-            );
-        }));
+        for (DataType dataType : DataType.stringTypes()) {
+            suppliers.add(new TestCaseSupplier(List.of(dataType), () -> {
+                BytesRef input = (BytesRef) randomLiteral(dataType).value();
+                return new TestCaseSupplier.TestCase(
+                    List.of(new TestCaseSupplier.TypedData(input, dataType, "string")),
+                    "ToBase64Evaluator[field=Attribute[channel=0]]",
+                    DataType.KEYWORD,
+                    equalTo(new BytesRef(Base64.getEncoder().encode(input.utf8ToString().getBytes(StandardCharsets.UTF_8))))
+                );
+            }));
+        }
 
         return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers, (v, p) -> "string");
     }

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java

@@ -123,7 +123,7 @@ public abstract class AbstractMultivalueFunctionTestCase extends AbstractScalarF
         Function<DataType, DataType> expectedDataType,
         BiFunction<Integer, Stream<BytesRef>, Matcher<Object>> matcher
     ) {
-        for (DataType type : new DataType[] { DataType.KEYWORD, DataType.TEXT, DataType.IP, DataType.VERSION }) {
+        for (DataType type : new DataType[] { DataType.KEYWORD, DataType.TEXT, DataType.SEMANTIC_TEXT, DataType.IP, DataType.VERSION }) {
             if (type != DataType.IP) {
                 cases.add(
                     new TestCaseSupplier(

+ 16 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAppendTests.java

@@ -171,6 +171,22 @@ public class MvAppendTests extends AbstractScalarFunctionTestCase {
             );
         }));
 
+        suppliers.add(new TestCaseSupplier(List.of(DataType.SEMANTIC_TEXT, DataType.SEMANTIC_TEXT), () -> {
+            List<Object> field1 = randomList(1, 10, () -> randomLiteral(DataType.SEMANTIC_TEXT).value());
+            List<Object> field2 = randomList(1, 10, () -> randomLiteral(DataType.SEMANTIC_TEXT).value());
+            var result = new ArrayList<>(field1);
+            result.addAll(field2);
+            return new TestCaseSupplier.TestCase(
+                List.of(
+                    new TestCaseSupplier.TypedData(field1, DataType.SEMANTIC_TEXT, "field1"),
+                    new TestCaseSupplier.TypedData(field2, DataType.SEMANTIC_TEXT, "field2")
+                ),
+                "MvAppendBytesRefEvaluator[field1=Attribute[channel=0], field2=Attribute[channel=1]]",
+                DataType.SEMANTIC_TEXT,
+                equalTo(result)
+            );
+        }));
+
         suppliers.add(new TestCaseSupplier(List.of(DataType.IP, DataType.IP), () -> {
             List<Object> field1 = randomList(1, 10, () -> randomLiteral(DataType.IP).value());
             List<Object> field2 = randomList(1, 10, () -> randomLiteral(DataType.IP).value());

+ 17 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSliceTests.java

@@ -255,6 +255,23 @@ public class MvSliceTests extends AbstractScalarFunctionTestCase {
             );
         }));
 
+        suppliers.add(new TestCaseSupplier(List.of(DataType.SEMANTIC_TEXT, DataType.INTEGER, DataType.INTEGER), () -> {
+            List<Object> field = randomList(1, 10, () -> randomLiteral(DataType.SEMANTIC_TEXT).value());
+            int length = field.size();
+            int start = randomIntBetween(0, length - 1);
+            int end = randomIntBetween(start, length - 1);
+            return new TestCaseSupplier.TestCase(
+                List.of(
+                    new TestCaseSupplier.TypedData(field, DataType.SEMANTIC_TEXT, "field"),
+                    new TestCaseSupplier.TypedData(start, DataType.INTEGER, "start"),
+                    new TestCaseSupplier.TypedData(end, DataType.INTEGER, "end")
+                ),
+                "MvSliceBytesRefEvaluator[field=Attribute[channel=0], start=Attribute[channel=1], end=Attribute[channel=2]]",
+                DataType.SEMANTIC_TEXT,
+                equalTo(start == end ? field.get(start) : field.subList(start, end + 1))
+            );
+        }));
+
         suppliers.add(new TestCaseSupplier(List.of(DataType.IP, DataType.INTEGER, DataType.INTEGER), () -> {
             List<Object> field = randomList(1, 10, () -> randomLiteral(DataType.IP).value());
             int length = field.size();

+ 14 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSortTests.java

@@ -171,6 +171,20 @@ public class MvSortTests extends AbstractScalarFunctionTestCase {
             );
         }));
 
+        suppliers.add(new TestCaseSupplier(List.of(DataType.SEMANTIC_TEXT, DataType.KEYWORD), () -> {
+            List<Object> field = randomList(1, 10, () -> randomLiteral(DataType.SEMANTIC_TEXT).value());
+            BytesRef order = new BytesRef("ASC");
+            return new TestCaseSupplier.TestCase(
+                List.of(
+                    new TestCaseSupplier.TypedData(field, DataType.SEMANTIC_TEXT, "field"),
+                    new TestCaseSupplier.TypedData(order, DataType.KEYWORD, "order").forceLiteral()
+                ),
+                "MvSortBytesRef[field=Attribute[channel=0], order=true]",
+                DataType.SEMANTIC_TEXT,
+                equalTo(field.size() == 1 ? field.iterator().next() : field.stream().sorted().toList())
+            );
+        }));
+
         suppliers.add(new TestCaseSupplier(List.of(DataType.IP, DataType.KEYWORD), () -> {
             List<Object> field = randomList(1, 10, () -> randomLiteral(DataType.IP).value());
             BytesRef order = new BytesRef("DESC");

+ 3 - 2
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatTests.java

@@ -48,7 +48,7 @@ public class ConcatTests extends AbstractScalarFunctionTestCase {
         for (int length = 4; length < 100; length++) {
             suppliers(suppliers, length);
         }
-        Set<DataType> supported = Set.of(DataType.NULL, DataType.KEYWORD, DataType.TEXT);
+        Set<DataType> supported = Set.of(DataType.NULL, DataType.KEYWORD, DataType.TEXT, DataType.SEMANTIC_TEXT);
         List<Set<DataType>> supportedPerPosition = List.of(supported, supported);
         for (DataType lhs : DataType.types()) {
             if (lhs == DataType.NULL || DataType.isRepresentable(lhs) == false) {
@@ -72,6 +72,7 @@ public class ConcatTests extends AbstractScalarFunctionTestCase {
         if (length > 3) {
             suppliers.add(supplier("ascii", DataType.KEYWORD, length, () -> randomAlphaOfLengthBetween(1, 10)));
             suppliers.add(supplier("unicode", DataType.TEXT, length, () -> randomRealisticUnicodeOfLengthBetween(1, 10)));
+            suppliers.add(supplier("unicode", DataType.SEMANTIC_TEXT, length, () -> randomRealisticUnicodeOfLengthBetween(1, 10)));
         } else {
             add(suppliers, "ascii", length, () -> randomAlphaOfLengthBetween(1, 10));
             add(suppliers, "unicode", length, () -> randomRealisticUnicodeOfLengthBetween(1, 10));
@@ -99,7 +100,7 @@ public class ConcatTests extends AbstractScalarFunctionTestCase {
 
     private static void add(List<TestCaseSupplier> suppliers, String name, int length, Supplier<String> valueSupplier) {
         Map<Integer, List<List<DataType>>> permutations = new HashMap<Integer, List<List<DataType>>>();
-        List<DataType> supportedDataTypes = List.of(DataType.KEYWORD, DataType.TEXT);
+        List<DataType> supportedDataTypes = DataType.stringTypes().stream().toList();
         permutations.put(0, List.of(List.of(DataType.KEYWORD), List.of(DataType.TEXT)));
         for (int v = 0; v < length - 1; v++) {
             List<List<DataType>> current = permutations.get(v);

+ 14 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftTests.java

@@ -167,6 +167,20 @@ public class LeftTests extends AbstractScalarFunctionTestCase {
             );
         }));
 
+        suppliers.add(new TestCaseSupplier("semantic_text as input", List.of(DataType.SEMANTIC_TEXT, DataType.INTEGER), () -> {
+            String text = randomUnicodeOfLengthBetween(1, 64);
+            int length = between(1, text.length());
+            return new TestCaseSupplier.TestCase(
+                List.of(
+                    new TestCaseSupplier.TypedData(new BytesRef(text), DataType.SEMANTIC_TEXT, "str"),
+                    new TestCaseSupplier.TypedData(length, DataType.INTEGER, "length")
+                ),
+                "LeftEvaluator[str=Attribute[channel=0], length=Attribute[channel=1]]",
+                DataType.KEYWORD,
+                equalTo(new BytesRef(unicodeLeftSubstring(text, length)))
+            );
+        }));
+
         return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers, (v, p) -> switch (p) {
             case 0 -> "string";
             case 1 -> "integer";

+ 10 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthTests.java

@@ -73,6 +73,16 @@ public class LengthTests extends AbstractScalarFunctionTestCase {
                     DataType.INTEGER,
                     equalTo(expectedLength)
                 )
+            ),
+            new TestCaseSupplier(
+                title + " with semantic_text",
+                List.of(DataType.SEMANTIC_TEXT),
+                () -> new TestCaseSupplier.TestCase(
+                    List.of(new TestCaseSupplier.TypedData(new BytesRef(text.get()), DataType.SEMANTIC_TEXT, "f")),
+                    "LengthEvaluator[val=Attribute[channel=0]]",
+                    DataType.INTEGER,
+                    equalTo(expectedLength)
+                )
             )
         );
     }

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RLikeTests.java

@@ -125,7 +125,7 @@ public class RLikeTests extends AbstractScalarFunctionTestCase {
     }
 
     private static void cases(List<TestCaseSupplier> cases, String title, Supplier<TextAndPattern> textAndPattern, boolean expected) {
-        for (DataType type : new DataType[] { DataType.KEYWORD, DataType.TEXT }) {
+        for (DataType type : DataType.stringTypes()) {
             cases.add(new TestCaseSupplier(title + " with " + type.esType(), List.of(type, type, DataType.BOOLEAN), () -> {
                 TextAndPattern v = textAndPattern.get();
                 return new TestCaseSupplier.TestCase(

+ 16 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RepeatTests.java

@@ -62,6 +62,22 @@ public class RepeatTests extends AbstractScalarFunctionTestCase {
             );
         }));
 
+        cases.add(
+            new TestCaseSupplier("Repeat basic test with semantic_text input", List.of(DataType.SEMANTIC_TEXT, DataType.INTEGER), () -> {
+                String text = randomAlphaOfLength(10);
+                int number = between(0, 10);
+                return new TestCaseSupplier.TestCase(
+                    List.of(
+                        new TestCaseSupplier.TypedData(new BytesRef(text), DataType.SEMANTIC_TEXT, "str"),
+                        new TestCaseSupplier.TypedData(number, DataType.INTEGER, "number")
+                    ),
+                    "RepeatEvaluator[str=Attribute[channel=0], number=Attribute[channel=1]]",
+                    DataType.KEYWORD,
+                    equalTo(new BytesRef(text.repeat(number)))
+                );
+            })
+        );
+
         cases.add(new TestCaseSupplier("Repeat with number zero", List.of(DataType.KEYWORD, DataType.INTEGER), () -> {
             String text = randomAlphaOfLength(10);
             int number = 0;

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReverseTests.java

@@ -33,7 +33,7 @@ public class ReverseTests extends AbstractScalarFunctionTestCase {
     public static Iterable<Object[]> parameters() {
         List<TestCaseSupplier> suppliers = new ArrayList<>();
 
-        for (DataType stringType : new DataType[] { DataType.KEYWORD, DataType.TEXT }) {
+        for (DataType stringType : DataType.stringTypes()) {
             for (var supplier : TestCaseSupplier.stringCases(stringType)) {
                 suppliers.add(makeSupplier(supplier));
             }

+ 13 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightTests.java

@@ -166,6 +166,19 @@ public class RightTests extends AbstractScalarFunctionTestCase {
                 equalTo(new BytesRef(unicodeRightSubstring(text, length)))
             );
         }));
+        suppliers.add(new TestCaseSupplier("ascii as semantic_text", List.of(DataType.SEMANTIC_TEXT, DataType.INTEGER), () -> {
+            String text = randomAlphaOfLengthBetween(1, 64);
+            int length = between(1, text.length());
+            return new TestCaseSupplier.TestCase(
+                List.of(
+                    new TestCaseSupplier.TypedData(new BytesRef(text), DataType.SEMANTIC_TEXT, "str"),
+                    new TestCaseSupplier.TypedData(length, DataType.INTEGER, "length")
+                ),
+                "RightEvaluator[str=Attribute[channel=0], length=Attribute[channel=1]]",
+                DataType.KEYWORD,
+                equalTo(new BytesRef(unicodeRightSubstring(text, length)))
+            );
+        }));
         return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers, (v, p) -> switch (p) {
             case 0 -> "string";
             case 1 -> "integer";

+ 2 - 3
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitTests.java

@@ -42,9 +42,8 @@ public class SplitTests extends AbstractScalarFunctionTestCase {
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
         List<TestCaseSupplier> suppliers = new ArrayList<>();
-        List<DataType> supportedDataTyes = List.of(DataType.KEYWORD, DataType.TEXT);
-        for (DataType sType : supportedDataTyes) {
-            for (DataType dType : supportedDataTyes) {
+        for (DataType sType : DataType.stringTypes()) {
+            for (DataType dType : DataType.stringTypes()) {
                 suppliers.add(new TestCaseSupplier("split test " + sType.toString() + " " + dType.toString(), List.of(sType, dType), () -> {
                     String delimiter = randomAlphaOfLength(1);
                     List<BytesRef> strings = IntStream.range(0, between(1, 5))

+ 19 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringTests.java

@@ -72,6 +72,25 @@ public class SubstringTests extends AbstractScalarFunctionTestCase {
                         );
                     }
                 ),
+                new TestCaseSupplier(
+                    "Substring basic test with semantic_text input",
+                    List.of(DataType.SEMANTIC_TEXT, DataType.INTEGER, DataType.INTEGER),
+                    () -> {
+                        int start = between(1, 8);
+                        int length = between(1, 10 - start);
+                        String text = randomAlphaOfLength(10);
+                        return new TestCaseSupplier.TestCase(
+                            List.of(
+                                new TestCaseSupplier.TypedData(new BytesRef(text), DataType.SEMANTIC_TEXT, "str"),
+                                new TestCaseSupplier.TypedData(start, DataType.INTEGER, "start"),
+                                new TestCaseSupplier.TypedData(length, DataType.INTEGER, "end")
+                            ),
+                            "SubstringEvaluator[str=Attribute[channel=0], start=Attribute[channel=1], length=Attribute[channel=2]]",
+                            DataType.KEYWORD,
+                            equalTo(new BytesRef(text.substring(start - 1, start + length - 1)))
+                        );
+                    }
+                ),
                 new TestCaseSupplier("Substring empty string", List.of(DataType.TEXT, DataType.INTEGER, DataType.INTEGER), () -> {
                     int start = between(1, 8);
                     int length = between(1, 10 - start);

+ 2 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java

@@ -45,6 +45,8 @@ public class ToLowerTests extends AbstractConfigurationFunctionTestCase {
         suppliers.add(supplier("keyword unicode", DataType.KEYWORD, () -> randomUnicodeOfLengthBetween(1, 10)));
         suppliers.add(supplier("text ascii", DataType.TEXT, () -> randomAlphaOfLengthBetween(1, 10)));
         suppliers.add(supplier("text unicode", DataType.TEXT, () -> randomUnicodeOfLengthBetween(1, 10)));
+        suppliers.add(supplier("semantic_text ascii", DataType.SEMANTIC_TEXT, () -> randomAlphaOfLengthBetween(1, 10)));
+        suppliers.add(supplier("semantic_text unicode", DataType.SEMANTIC_TEXT, () -> randomUnicodeOfLengthBetween(1, 10)));
 
         // add null as parameter
         return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers, (v, p) -> "string");

+ 2 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java

@@ -45,6 +45,8 @@ public class ToUpperTests extends AbstractConfigurationFunctionTestCase {
         suppliers.add(supplier("keyword unicode", DataType.KEYWORD, () -> randomUnicodeOfLengthBetween(1, 10)));
         suppliers.add(supplier("text ascii", DataType.TEXT, () -> randomAlphaOfLengthBetween(1, 10)));
         suppliers.add(supplier("text unicode", DataType.TEXT, () -> randomUnicodeOfLengthBetween(1, 10)));
+        suppliers.add(supplier("semantic_text ascii", DataType.SEMANTIC_TEXT, () -> randomAlphaOfLengthBetween(1, 10)));
+        suppliers.add(supplier("semantic_text unicode", DataType.SEMANTIC_TEXT, () -> randomUnicodeOfLengthBetween(1, 10)));
 
         // add null as parameter
         return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers, (v, p) -> "string");

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/WildcardLikeTests.java

@@ -53,7 +53,7 @@ public class WildcardLikeTests extends AbstractScalarFunctionTestCase {
     }
 
     private static void addCases(List<TestCaseSupplier> suppliers) {
-        for (DataType type : new DataType[] { DataType.KEYWORD, DataType.TEXT }) {
+        for (DataType type : new DataType[] { DataType.KEYWORD, DataType.TEXT, DataType.SEMANTIC_TEXT }) {
             suppliers.add(new TestCaseSupplier(" with " + type.esType(), List.of(type, type), () -> {
                 BytesRef str = new BytesRef(randomAlphaOfLength(5));
                 String patternString = randomAlphaOfLength(2);

+ 2 - 2
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/EqualsTests.java

@@ -217,8 +217,8 @@ public class EqualsTests extends AbstractScalarFunctionTestCase {
     }
 
     private static String typeErrorString =
-        "boolean, cartesian_point, cartesian_shape, datetime, date_nanos, double, geo_point, geo_shape, integer, ip, keyword, long, text, "
-            + "unsigned_long or version";
+        "boolean, cartesian_point, cartesian_shape, datetime, date_nanos, double, geo_point, geo_shape, integer, ip, keyword, long,"
+            + " semantic_text, text, unsigned_long or version";
 
     @Override
     protected Expression build(Source source, List<Expression> args) {

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThanOrEqualTests.java

@@ -150,7 +150,7 @@ public class GreaterThanOrEqualTests extends AbstractScalarFunctionTestCase {
                     o,
                     v,
                     t,
-                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, text, unsigned_long or version"
+                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, semantic_text, text, unsigned_long or version"
                 )
             )
         );

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/GreaterThanTests.java

@@ -150,7 +150,7 @@ public class GreaterThanTests extends AbstractScalarFunctionTestCase {
                     o,
                     v,
                     t,
-                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, text, unsigned_long or version"
+                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, semantic_text, text, unsigned_long or version"
                 )
             )
         );

+ 18 - 2
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InTests.java

@@ -187,8 +187,24 @@ public class InTests extends AbstractFunctionTestCase {
             );
         }));
 
-        for (DataType type1 : new DataType[] { DataType.KEYWORD, DataType.TEXT }) {
-            for (DataType type2 : new DataType[] { DataType.KEYWORD, DataType.TEXT }) {
+        suppliers.add(new TestCaseSupplier("semantic_text", List.of(DataType.SEMANTIC_TEXT, DataType.SEMANTIC_TEXT), () -> {
+            List<Object> inlist = randomList(items, items, () -> randomLiteral(DataType.SEMANTIC_TEXT).value());
+            Object field = inlist.get(0);
+            List<TestCaseSupplier.TypedData> args = new ArrayList<>(inlist.size() + 1);
+            for (Object i : inlist) {
+                args.add(new TestCaseSupplier.TypedData(i, DataType.SEMANTIC_TEXT, "inlist" + i));
+            }
+            args.add(new TestCaseSupplier.TypedData(field, DataType.SEMANTIC_TEXT, "field"));
+            return new TestCaseSupplier.TestCase(
+                args,
+                matchesPattern("InBytesRefEvaluator.*"),
+                DataType.BOOLEAN,
+                equalTo(inlist.contains(field))
+            );
+        }));
+
+        for (DataType type1 : DataType.stringTypes()) {
+            for (DataType type2 : DataType.stringTypes()) {
                 if (type1 == type2 || items > 1) continue;
                 suppliers.add(new TestCaseSupplier(type1 + " " + type2, List.of(type1, type2), () -> {
                     List<Object> inlist = randomList(items, items, () -> randomLiteral(type1).value());

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThanOrEqualTests.java

@@ -150,7 +150,7 @@ public class LessThanOrEqualTests extends AbstractScalarFunctionTestCase {
                     o,
                     v,
                     t,
-                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, text, unsigned_long or version"
+                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, semantic_text, text, unsigned_long or version"
                 )
             )
         );

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/LessThanTests.java

@@ -150,7 +150,7 @@ public class LessThanTests extends AbstractScalarFunctionTestCase {
                     o,
                     v,
                     t,
-                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, text, unsigned_long or version"
+                    (l, p) -> "date_nanos, datetime, double, integer, ip, keyword, long, semantic_text, text, unsigned_long or version"
                 )
             )
         );

+ 4 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/EsqlDataTypeConverterTests.java

@@ -30,11 +30,13 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.HALF_FLOAT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
 import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
 import static org.elasticsearch.xpack.esql.core.type.DataType.NULL;
 import static org.elasticsearch.xpack.esql.core.type.DataType.OBJECT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.PARTIAL_AGG;
 import static org.elasticsearch.xpack.esql.core.type.DataType.SCALED_FLOAT;
+import static org.elasticsearch.xpack.esql.core.type.DataType.SEMANTIC_TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.SHORT;
 import static org.elasticsearch.xpack.esql.core.type.DataType.SOURCE;
 import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT;
@@ -69,6 +71,8 @@ public class EsqlDataTypeConverterTests extends ESTestCase {
                 } else if ((isString(dataType1) && isString(dataType2))) {
                     if (dataType1 == dataType2) {
                         assertEqualsCommonType(dataType1, dataType2, dataType1);
+                    } else if (dataType1 == SEMANTIC_TEXT || dataType2 == SEMANTIC_TEXT) {
+                        assertEqualsCommonType(dataType1, dataType2, KEYWORD);
                     } else {
                         assertEqualsCommonType(dataType1, dataType2, TEXT);
                     }