1313import org .apache .http .client .config .RequestConfig ;
1414import org .apache .http .util .EntityUtils ;
1515import org .apache .lucene .tests .util .TimeUnits ;
16+ import org .elasticsearch .action .admin .indices .create .CreateIndexResponse ;
1617import org .elasticsearch .client .Request ;
1718import org .elasticsearch .client .RequestOptions ;
1819import org .elasticsearch .client .Response ;
@@ -697,13 +698,26 @@ private Map<String, Object> fetchMvLongs() throws IOException {
697698 public void testLookupExplosion () throws IOException {
698699 int sensorDataCount = 400 ;
699700 int lookupEntries = 10000 ;
700- Map <?, ?> map = lookupExplosion (sensorDataCount , lookupEntries );
701+ Map <?, ?> map = lookupExplosion (sensorDataCount , lookupEntries , 1 );
701702 assertMap (map , matchesMap ().extraOk ().entry ("values" , List .of (List .of (sensorDataCount * lookupEntries ))));
702703 }
703704
705+ public void testLookupExplosionManyFields () throws IOException {
706+ int sensorDataCount = 400 ;
707+ int lookupEntries = 1000 ;
708+ int joinFieldsCount = 990 ;
709+ Map <?, ?> map = lookupExplosion (sensorDataCount , lookupEntries , joinFieldsCount );
710+ assertMap (map , matchesMap ().extraOk ().entry ("values" , List .of (List .of (sensorDataCount * lookupEntries ))));
711+ }
712+
713+ public void testLookupExplosionManyMatchesManyFields () throws IOException {
714+ // 1500, 10000 is enough locally, but some CI machines need more.
715+ assertCircuitBreaks (attempt -> lookupExplosion (attempt * 1500 , 10000 , 30 ));
716+ }
717+
704718 public void testLookupExplosionManyMatches () throws IOException {
705719 // 1500, 10000 is enough locally, but some CI machines need more.
706- assertCircuitBreaks (attempt -> lookupExplosion (attempt * 1500 , 10000 ));
720+ assertCircuitBreaks (attempt -> lookupExplosion (attempt * 1500 , 10000 , 1 ));
707721 }
708722
709723 public void testLookupExplosionNoFetch () throws IOException {
@@ -730,11 +744,18 @@ public void testLookupExplosionBigStringManyMatches() throws IOException {
730744 assertCircuitBreaks (attempt -> lookupExplosionBigString (attempt * 500 , 1 ));
731745 }
732746
733- private Map <String , Object > lookupExplosion (int sensorDataCount , int lookupEntries ) throws IOException {
747+ private Map <String , Object > lookupExplosion (int sensorDataCount , int lookupEntries , int joinFieldsCount ) throws IOException {
734748 try {
735- lookupExplosionData (sensorDataCount , lookupEntries );
749+ lookupExplosionData (sensorDataCount , lookupEntries , joinFieldsCount );
736750 StringBuilder query = startQuery ();
737- query .append ("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(location)\" }" );
751+ query .append ("FROM sensor_data | LOOKUP JOIN sensor_lookup ON " );
752+ for (int i = 0 ; i < joinFieldsCount ; i ++) {
753+ if (i != 0 ) {
754+ query .append ("," );
755+ }
756+ query .append ("id" ).append (i );
757+ }
758+ query .append (" | STATS COUNT(location)\" }" );
738759 return responseAsMap (query (query .toString (), null ));
739760 } finally {
740761 deleteIndex ("sensor_data" );
@@ -744,24 +765,24 @@ private Map<String, Object> lookupExplosion(int sensorDataCount, int lookupEntri
744765
745766 private Map <String , Object > lookupExplosionNoFetch (int sensorDataCount , int lookupEntries ) throws IOException {
746767 try {
747- lookupExplosionData (sensorDataCount , lookupEntries );
768+ lookupExplosionData (sensorDataCount , lookupEntries , 1 );
748769 StringBuilder query = startQuery ();
749- query .append ("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(*)\" }" );
770+ query .append ("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id0 | STATS COUNT(*)\" }" );
750771 return responseAsMap (query (query .toString (), null ));
751772 } finally {
752773 deleteIndex ("sensor_data" );
753774 deleteIndex ("sensor_lookup" );
754775 }
755776 }
756777
757- private void lookupExplosionData (int sensorDataCount , int lookupEntries ) throws IOException {
758- initSensorData (sensorDataCount , 1 );
759- initSensorLookup (lookupEntries , 1 , i -> "73.9857 40.7484" );
778+ private void lookupExplosionData (int sensorDataCount , int lookupEntries , int joinFieldCount ) throws IOException {
779+ initSensorData (sensorDataCount , 1 , joinFieldCount );
780+ initSensorLookup (lookupEntries , 1 , i -> "73.9857 40.7484" , joinFieldCount );
760781 }
761782
762783 private Map <String , Object > lookupExplosionBigString (int sensorDataCount , int lookupEntries ) throws IOException {
763784 try {
764- initSensorData (sensorDataCount , 1 );
785+ initSensorData (sensorDataCount , 1 , 1 );
765786 initSensorLookupString (lookupEntries , 1 , i -> {
766787 int target = Math .toIntExact (ByteSizeValue .ofMb (1 ).getBytes ());
767788 StringBuilder str = new StringBuilder (Math .toIntExact (ByteSizeValue .ofMb (2 ).getBytes ()));
@@ -772,7 +793,7 @@ private Map<String, Object> lookupExplosionBigString(int sensorDataCount, int lo
772793 return str .toString ();
773794 });
774795 StringBuilder query = startQuery ();
775- query .append ("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id | STATS COUNT(string)\" }" );
796+ query .append ("FROM sensor_data | LOOKUP JOIN sensor_lookup ON id0 | STATS COUNT(string)\" }" );
776797 return responseAsMap (query (query .toString (), null ));
777798 } finally {
778799 deleteIndex ("sensor_data" );
@@ -794,11 +815,11 @@ public void testEnrichExplosionManyMatches() throws IOException {
794815
795816 private Map <String , Object > enrichExplosion (int sensorDataCount , int lookupEntries ) throws IOException {
796817 try {
797- initSensorData (sensorDataCount , 1 );
818+ initSensorData (sensorDataCount , 1 , 1 );
798819 initSensorEnrich (lookupEntries , 1 , i -> "73.9857 40.7484" );
799820 try {
800821 StringBuilder query = startQuery ();
801- query .append ("FROM sensor_data | ENRICH sensor ON id | STATS COUNT(*)\" }" );
822+ query .append ("FROM sensor_data | ENRICH sensor ON id0 | STATS COUNT(*)\" }" );
802823 return responseAsMap (query (query .toString (), null ));
803824 } finally {
804825 Request delete = new Request ("DELETE" , "/_enrich/policy/sensor" );
@@ -958,25 +979,42 @@ private void initMvLongsIndex(int docs, int fields, int fieldValues) throws IOEx
958979 initIndex ("mv_longs" , bulk .toString ());
959980 }
960981
961- private void initSensorData (int docCount , int sensorCount ) throws IOException {
982+ private void initSensorData (int docCount , int sensorCount , int joinFieldCount ) throws IOException {
962983 logger .info ("loading sensor data" );
963- createIndex ("sensor_data" , Settings .builder ().put (IndexSettings .MODE .getKey (), IndexMode .LOOKUP .getName ()).build (), """
964- {
965- "properties": {
966- "@timestamp": { "type": "date" },
967- "id": { "type": "long" },
984+ // We cannot go over 1000 fields, due to failed on parsing mappings on index creation
985+ // [sensor_data] java.lang.IllegalArgumentException: Limit of total fields [1000] has been exceeded
986+ assertTrue ("Too many columns, it will throw an exception later" , joinFieldCount <= 990 );
987+ StringBuilder createIndexBuilder = new StringBuilder ();
988+ createIndexBuilder .append ("""
989+ {
990+ "properties": {
991+ "@timestamp": { "type": "date" },
992+ """ );
993+ for (int i = 0 ; i < joinFieldCount ; i ++) {
994+ createIndexBuilder .append ("\" id" ).append (i ).append ("\" : { \" type\" : \" long\" }," );
995+ }
996+ createIndexBuilder .append ("""
968997 "value": { "type": "double" }
969998 }
970999 }""" );
1000+ CreateIndexResponse response = createIndex (
1001+ "sensor_data" ,
1002+ Settings .builder ().put (IndexSettings .MODE .getKey (), IndexMode .LOOKUP .getName ()).build (),
1003+ createIndexBuilder .toString ()
1004+ );
1005+ assertTrue (response .isAcknowledged ());
9711006 int docsPerBulk = 1000 ;
9721007 long firstDate = DateFieldMapper .DEFAULT_DATE_TIME_FORMATTER .parseMillis ("2025-01-01T00:00:00Z" );
9731008
9741009 StringBuilder data = new StringBuilder ();
9751010 for (int i = 0 ; i < docCount ; i ++) {
9761011 data .append (String .format (Locale .ROOT , """
9771012 {"create":{}}
978- {"timestamp":"%s", "id": %d, "value": %f}
979- """ , DateFieldMapper .DEFAULT_DATE_TIME_FORMATTER .formatMillis (i * 10L + firstDate ), i % sensorCount , i * 1.1 ));
1013+ {"timestamp":"%s",""" , DateFieldMapper .DEFAULT_DATE_TIME_FORMATTER .formatMillis (i * 10L + firstDate )));
1014+ for (int j = 0 ; j < joinFieldCount ; j ++) {
1015+ data .append (String .format (Locale .ROOT , "\" id%d\" :%d, " , j , i % sensorCount ));
1016+ }
1017+ data .append (String .format (Locale .ROOT , "\" value\" : %f}\n " , i * 1.1 ));
9801018 if (i % docsPerBulk == docsPerBulk - 1 ) {
9811019 bulk ("sensor_data" , data .toString ());
9821020 data .setLength (0 );
@@ -985,23 +1023,42 @@ private void initSensorData(int docCount, int sensorCount) throws IOException {
9851023 initIndex ("sensor_data" , data .toString ());
9861024 }
9871025
988- private void initSensorLookup (int lookupEntries , int sensorCount , IntFunction <String > location ) throws IOException {
1026+ private void initSensorLookup (int lookupEntries , int sensorCount , IntFunction <String > location , int joinFieldsCount )
1027+ throws IOException {
9891028 logger .info ("loading sensor lookup" );
990- createIndex ("sensor_lookup" , Settings .builder ().put (IndexSettings .MODE .getKey (), IndexMode .LOOKUP .getName ()).build (), """
1029+ // cannot go over 1000 fields, due to failed on parsing mappings on index creation
1030+ // [sensor_data] java.lang.IllegalArgumentException: Limit of total fields [1000] has been exceeded
1031+ assertTrue ("Too many join on fields, it will throw an exception later" , joinFieldsCount <= 990 );
1032+ StringBuilder createIndexBuilder = new StringBuilder ();
1033+ createIndexBuilder .append ("""
9911034 {
9921035 "properties": {
993- "id": { "type": "long" },
1036+ """ );
1037+ for (int i = 0 ; i < joinFieldsCount ; i ++) {
1038+ createIndexBuilder .append ("\" id" ).append (i ).append ("\" : { \" type\" : \" long\" }," );
1039+ }
1040+ createIndexBuilder .append ("""
9941041 "location": { "type": "geo_point" }
9951042 }
9961043 }""" );
1044+ CreateIndexResponse response = createIndex (
1045+ "sensor_lookup" ,
1046+ Settings .builder ().put (IndexSettings .MODE .getKey (), IndexMode .LOOKUP .getName ()).build (),
1047+ createIndexBuilder .toString ()
1048+ );
1049+ assertTrue (response .isAcknowledged ());
9971050 int docsPerBulk = 1000 ;
9981051 StringBuilder data = new StringBuilder ();
9991052 for (int i = 0 ; i < lookupEntries ; i ++) {
10001053 int sensor = i % sensorCount ;
10011054 data .append (String .format (Locale .ROOT , """
10021055 {"create":{}}
1003- {"id": %d, "location": "POINT(%s)"}
1004- """ , sensor , location .apply (sensor )));
1056+ {""" ));
1057+ for (int j = 0 ; j < joinFieldsCount ; j ++) {
1058+ data .append (String .format (Locale .ROOT , "\" id%d\" :%d, " , j , sensor ));
1059+ }
1060+ data .append (String .format (Locale .ROOT , """
1061+ "location": "POINT(%s)"}\n """ , location .apply (sensor )));
10051062 if (i % docsPerBulk == docsPerBulk - 1 ) {
10061063 bulk ("sensor_lookup" , data .toString ());
10071064 data .setLength (0 );
@@ -1015,7 +1072,7 @@ private void initSensorLookupString(int lookupEntries, int sensorCount, IntFunct
10151072 createIndex ("sensor_lookup" , Settings .builder ().put (IndexSettings .MODE .getKey (), IndexMode .LOOKUP .getName ()).build (), """
10161073 {
10171074 "properties": {
1018- "id ": { "type": "long" },
1075+ "id0 ": { "type": "long" },
10191076 "string": { "type": "text" }
10201077 }
10211078 }""" );
@@ -1025,7 +1082,7 @@ private void initSensorLookupString(int lookupEntries, int sensorCount, IntFunct
10251082 int sensor = i % sensorCount ;
10261083 data .append (String .format (Locale .ROOT , """
10271084 {"create":{}}
1028- {"id ": %d, "string": "%s"}
1085+ {"id0 ": %d, "string": "%s"}
10291086 """ , sensor , string .apply (sensor )));
10301087 if (i % docsPerBulk == docsPerBulk - 1 ) {
10311088 bulk ("sensor_lookup" , data .toString ());
@@ -1036,15 +1093,15 @@ private void initSensorLookupString(int lookupEntries, int sensorCount, IntFunct
10361093 }
10371094
10381095 private void initSensorEnrich (int lookupEntries , int sensorCount , IntFunction <String > location ) throws IOException {
1039- initSensorLookup (lookupEntries , sensorCount , location );
1096+ initSensorLookup (lookupEntries , sensorCount , location , 1 );
10401097 logger .info ("loading sensor enrich" );
10411098
10421099 Request create = new Request ("PUT" , "/_enrich/policy/sensor" );
10431100 create .setJsonEntity ("""
10441101 {
10451102 "match": {
10461103 "indices": "sensor_lookup",
1047- "match_field": "id ",
1104+ "match_field": "id0 ",
10481105 "enrich_fields": ["location"]
10491106 }
10501107 }
0 commit comments