1515import org .elasticsearch .xpack .core .esql .action .ColumnInfo ;
1616import org .elasticsearch .xpack .esql .VerificationException ;
1717import org .elasticsearch .xpack .esql .core .type .DataType ;
18+ import org .elasticsearch .xpack .esql .plan .logical .join .Join ;
1819import org .elasticsearch .xpack .esql .plugin .EsqlPlugin ;
20+ import org .elasticsearch .xpack .spatial .SpatialPlugin ;
21+ import org .elasticsearch .xpack .unsignedlong .UnsignedLongMapperPlugin ;
22+ import org .elasticsearch .xpack .versionfield .VersionFieldPlugin ;
1923
24+ import java .util .ArrayList ;
2025import java .util .Collection ;
2126import java .util .HashMap ;
2227import java .util .HashSet ;
3136
3237import static org .elasticsearch .test .ESIntegTestCase .Scope .SUITE ;
3338import static org .elasticsearch .test .hamcrest .ElasticsearchAssertions .assertAcked ;
39+ import static org .elasticsearch .xpack .esql .core .type .DataType .AGGREGATE_METRIC_DOUBLE ;
3440import static org .elasticsearch .xpack .esql .core .type .DataType .BOOLEAN ;
3541import static org .elasticsearch .xpack .esql .core .type .DataType .BYTE ;
3642import static org .elasticsearch .xpack .esql .core .type .DataType .DATETIME ;
43+ import static org .elasticsearch .xpack .esql .core .type .DataType .DOC_DATA_TYPE ;
3744import static org .elasticsearch .xpack .esql .core .type .DataType .DOUBLE ;
3845import static org .elasticsearch .xpack .esql .core .type .DataType .FLOAT ;
3946import static org .elasticsearch .xpack .esql .core .type .DataType .HALF_FLOAT ;
4047import static org .elasticsearch .xpack .esql .core .type .DataType .INTEGER ;
4148import static org .elasticsearch .xpack .esql .core .type .DataType .IP ;
4249import static org .elasticsearch .xpack .esql .core .type .DataType .KEYWORD ;
4350import static org .elasticsearch .xpack .esql .core .type .DataType .LONG ;
51+ import static org .elasticsearch .xpack .esql .core .type .DataType .NULL ;
4452import static org .elasticsearch .xpack .esql .core .type .DataType .SHORT ;
4553import static org .elasticsearch .xpack .esql .core .type .DataType .TEXT ;
54+ import static org .elasticsearch .xpack .esql .core .type .DataType .TSID_DATA_TYPE ;
4655import static org .hamcrest .Matchers .containsString ;
4756import static org .hamcrest .Matchers .equalTo ;
4857import static org .hamcrest .Matchers .is ;
108117@ ClusterScope (scope = SUITE , numClientNodes = 1 , numDataNodes = 1 )
109118public class LookupJoinTypesIT extends ESIntegTestCase {
110119 protected Collection <Class <? extends Plugin >> nodePlugins () {
111- return List .of (EsqlPlugin .class , MapperExtrasPlugin .class );
120+ return List .of (
121+ EsqlPlugin .class ,
122+ MapperExtrasPlugin .class ,
123+ VersionFieldPlugin .class ,
124+ UnsignedLongMapperPlugin .class ,
125+ SpatialPlugin .class
126+ );
112127 }
113128
114129 private static final Map <String , TestConfigs > testConfigurations = new HashMap <>();
@@ -118,8 +133,7 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
118133 TestConfigs configs = testConfigurations .computeIfAbsent ("strings" , TestConfigs ::new );
119134 configs .addPasses (KEYWORD , KEYWORD );
120135 configs .addPasses (TEXT , KEYWORD );
121- configs .addFailsText (KEYWORD );
122- configs .addFailsText (TEXT );
136+ configs .addFailsUnsupported (KEYWORD , TEXT );
123137 }
124138
125139 // Test integer types
@@ -158,13 +172,37 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
158172 }
159173 }
160174 }
175+ // Tests for all unsupported types
176+ DataType [] unsupported = Join .UNSUPPORTED_TYPES ;
177+ {
178+ Collection <TestConfigs > existing = testConfigurations .values ();
179+ TestConfigs configs = testConfigurations .computeIfAbsent ("unsupported" , TestConfigs ::new );
180+ for (DataType type : unsupported ) {
181+ if (type == NULL
182+ || type == DOC_DATA_TYPE
183+ || type == TSID_DATA_TYPE
184+ || type == AGGREGATE_METRIC_DOUBLE
185+ || type .esType () == null
186+ || type .isCounter ()
187+ || DataType .isRepresentable (type ) == false ) {
188+ // Skip unmappable types, or types not supported in ES|QL in general
189+ continue ;
190+ }
191+ if (existingIndex (existing , type , type )) {
192+ // Skip existing configurations
193+ continue ;
194+ }
195+ configs .addFailsUnsupported (type , type );
196+ }
197+ }
161198
162199 // Tests for all types where left and right are the same type
163- DataType [] all = { BOOLEAN , LONG , INTEGER , DOUBLE , SHORT , BYTE , FLOAT , HALF_FLOAT , DATETIME , IP , KEYWORD };
200+ DataType [] supported = { BOOLEAN , LONG , INTEGER , DOUBLE , SHORT , BYTE , FLOAT , HALF_FLOAT , DATETIME , IP , KEYWORD };
164201 {
165202 Collection <TestConfigs > existing = testConfigurations .values ();
166203 TestConfigs configs = testConfigurations .computeIfAbsent ("same" , TestConfigs ::new );
167- for (DataType type : all ) {
204+ for (DataType type : supported ) {
205+ assertThat ("Claiming supported for unsupported type: " + type , List .of (unsupported ).contains (type ), is (false ));
168206 if (existingIndex (existing , type , type )) {
169207 // Skip existing configurations
170208 continue ;
@@ -173,12 +211,28 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
173211 }
174212 }
175213
214+ // Assert that unsupported types are not in the supported list
215+ for (DataType type : unsupported ) {
216+ assertThat ("Claiming supported for unsupported type: " + type , List .of (supported ).contains (type ), is (false ));
217+ }
218+
219+ // Assert that unsupported+supported covers all types:
220+ List <DataType > missing = new ArrayList <>();
221+ for (DataType type : DataType .values ()) {
222+ boolean isUnsupported = List .of (unsupported ).contains (type );
223+ boolean isSupported = List .of (supported ).contains (type );
224+ if (isUnsupported == false && isSupported == false ) {
225+ missing .add (type );
226+ }
227+ }
228+ assertThat (missing + " are not in the supported or unsupported list" , missing .size (), is (0 ));
229+
176230 // Tests for all other type combinations
177231 {
178232 Collection <TestConfigs > existing = testConfigurations .values ();
179233 TestConfigs configs = testConfigurations .computeIfAbsent ("others" , TestConfigs ::new );
180- for (DataType mainType : all ) {
181- for (DataType lookupType : all ) {
234+ for (DataType mainType : supported ) {
235+ for (DataType lookupType : supported ) {
182236 if (existingIndex (existing , mainType , lookupType )) {
183237 // Skip existing configurations
184238 continue ;
@@ -188,8 +242,6 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
188242 }
189243 }
190244
191- // TODO: Add tests for more types, eg. unsigned_long, version, spatial types, date/temporal types.
192-
193245 // Make sure we have never added two configurations with the same index name
194246 Set <String > knownTypes = new HashSet <>();
195247 for (TestConfigs configs : testConfigurations .values ()) {
@@ -227,6 +279,10 @@ public void testLookupJoinSame() {
227279 testLookupJoinTypes ("same" );
228280 }
229281
282+ public void testLookupJoinUnsupported () {
283+ testLookupJoinTypes ("unsupported" );
284+ }
285+
230286 public void testLookupJoinOthers () {
231287 testLookupJoinTypes ("others" );
232288 }
@@ -263,7 +319,7 @@ private void initIndexes(String group) {
263319 // Each lookup index will get a document with a field to join on, and a results field to get back
264320 (c ) -> assertAcked (
265321 prepareCreate (c .indexName ()).setSettings (settings .build ())
266- .setMapping (c .fieldName (), "type=" + c .lookupType ().esType (), "other" , "type=keyword" )
322+ .setMapping (c .fieldName (), "type=" + c .lookupType ().esType (). replace ( "cartesian_" , "" ) , "other" , "type=keyword" )
267323 )
268324 );
269325 }
@@ -299,10 +355,11 @@ private String mainPropertyFor(TestConfig config) {
299355 }
300356
301357 private static String sampleDataTextFor (DataType type ) {
302- return switch (type ) {
303- case KEYWORD , TEXT , DATETIME , DATE_NANOS , IP -> "\" " + sampleDataFor (type ) + "\" " ;
304- default -> String .valueOf (sampleDataFor (type ));
305- };
358+ var value = sampleDataFor (type );
359+ if (value instanceof String ) {
360+ return "\" " + value + "\" " ;
361+ }
362+ return String .valueOf (value );
306363 }
307364
308365 private static Object sampleDataFor (DataType type ) {
@@ -312,8 +369,11 @@ private static Object sampleDataFor(DataType type) {
312369 case IP -> "127.0.0.1" ;
313370 case KEYWORD , TEXT -> "key" ;
314371 case BYTE , SHORT , INTEGER -> 1 ;
315- case LONG -> 1L ;
372+ case LONG , UNSIGNED_LONG -> 1L ;
316373 case HALF_FLOAT , FLOAT , DOUBLE -> 1.0 ;
374+ case VERSION -> "1.2.19" ;
375+ case GEO_POINT , CARTESIAN_POINT -> "POINT (1.0 2.0)" ;
376+ case GEO_SHAPE , CARTESIAN_SHAPE -> "POLYGON ((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))" ;
317377 default -> throw new IllegalArgumentException ("Unsupported type: " + type );
318378 };
319379 }
@@ -366,13 +426,18 @@ private void addFails(DataType mainType, DataType lookupType) {
366426 );
367427 }
368428
369- private void addFailsText (DataType mainType ) {
429+ private void addFailsUnsupported (DataType mainType , DataType lookupType ) {
370430 String fieldName = "field_" + mainType .esType ();
371- String errorMessage = String .format (Locale .ROOT , "JOIN with right field [%s] of type [TEXT] is not supported" , fieldName );
431+ String errorMessage = String .format (
432+ Locale .ROOT ,
433+ "JOIN with right field [%s] of type [%s] is not supported" ,
434+ fieldName ,
435+ lookupType
436+ );
372437 add (
373438 new TestConfigFails <>(
374439 mainType ,
375- DataType . TEXT ,
440+ lookupType ,
376441 VerificationException .class ,
377442 e -> assertThat (e .getMessage (), containsString (errorMessage ))
378443 )
@@ -398,7 +463,7 @@ default String fieldName() {
398463 }
399464
400465 default String mainPropertySpec () {
401- return "\" " + fieldName () + "\" : { \" type\" : \" " + mainType ().esType () + "\" }" ;
466+ return "\" " + fieldName () + "\" : { \" type\" : \" " + mainType ().esType (). replaceAll ( "cartesian_" , "" ) + "\" }" ;
402467 }
403468
404469 /** Make sure the left index has the expected fields and types */
0 commit comments