2323import org .elasticsearch .compute .data .Block ;
2424import org .elasticsearch .compute .data .BlockFactory ;
2525import org .elasticsearch .compute .data .LongBlock ;
26- import org .elasticsearch .compute .data .LongVector ;
2726import org .elasticsearch .compute .lucene .DataPartitioning ;
2827import org .elasticsearch .compute .lucene .LuceneSliceQueue ;
2928import org .elasticsearch .compute .lucene .LuceneSourceOperator ;
5453import org .elasticsearch .threadpool .ThreadPool ;
5554import org .elasticsearch .xpack .core .async .AsyncExecutionId ;
5655import org .elasticsearch .xpack .esql .core .expression .Alias ;
56+ import org .elasticsearch .xpack .esql .core .expression .Expression ;
5757import org .elasticsearch .xpack .esql .core .expression .FieldAttribute ;
58+ import org .elasticsearch .xpack .esql .core .expression .Literal ;
5859import org .elasticsearch .xpack .esql .core .expression .ReferenceAttribute ;
5960import org .elasticsearch .xpack .esql .core .tree .Source ;
6061import org .elasticsearch .xpack .esql .core .type .DataType ;
6162import org .elasticsearch .xpack .esql .enrich .LookupFromIndexOperator ;
6263import org .elasticsearch .xpack .esql .enrich .MatchConfig ;
64+ import org .elasticsearch .xpack .esql .expression .predicate .operator .comparison .GreaterThan ;
6365import org .elasticsearch .xpack .esql .planner .EsPhysicalOperationProviders ;
6466import org .elasticsearch .xpack .esql .planner .PhysicalSettings ;
6567import org .elasticsearch .xpack .esql .planner .PlannerUtils ;
7577import java .util .Map ;
7678import java .util .Set ;
7779import java .util .concurrent .CopyOnWriteArrayList ;
80+ import java .util .function .Predicate ;
7881
7982import static org .elasticsearch .test .ListMatcher .matchesList ;
8083import static org .elasticsearch .test .MapMatcher .assertMap ;
8386
8487public class LookupFromIndexIT extends AbstractEsqlIntegTestCase {
8588 public void testKeywordKey () throws IOException {
86- runLookup (List .of (DataType .KEYWORD ), new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "cc" , "dd" } }));
89+ runLookup (List .of (DataType .KEYWORD ), new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "cc" , "dd" } }), null );
8790 }
8891
8992 public void testJoinOnTwoKeys () throws IOException {
9093 runLookup (
9194 List .of (DataType .KEYWORD , DataType .LONG ),
92- new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "cc" , "dd" }, new Long [] { 12L , 33L , 1L , 42L } })
95+ new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "cc" , "dd" }, new Long [] { 12L , 33L , 1L , 42L } }),
96+ null
9397 );
9498 }
9599
@@ -101,7 +105,8 @@ public void testJoinOnThreeKeys() throws IOException {
101105 new String [] { "aa" , "bb" , "cc" , "dd" },
102106 new Long [] { 12L , 33L , 1L , 42L },
103107 new String [] { "one" , "two" , "three" , "four" }, }
104- )
108+ ),
109+ null
105110 );
106111 }
107112
@@ -114,25 +119,35 @@ public void testJoinOnFourKeys() throws IOException {
114119 new Long [] { 12L , 33L , 1L , 42L },
115120 new String [] { "one" , "two" , "three" , "four" },
116121 new Integer [] { 1 , 2 , 3 , 4 }, }
117- )
122+ ),
123+ buildGreaterThanFilter (1L )
118124 );
119125 }
120126
121127 public void testLongKey () throws IOException {
122- runLookup (List .of (DataType .LONG ), new UsingSingleLookupTable (new Object [][] { new Long [] { 12L , 33L , 1L } }));
128+ runLookup (
129+ List .of (DataType .LONG ),
130+ new UsingSingleLookupTable (new Object [][] { new Long [] { 12L , 33L , 1L } }),
131+ buildGreaterThanFilter (0L )
132+ );
123133 }
124134
125135 /**
126136 * LOOKUP multiple results match.
127137 */
128138 public void testLookupIndexMultiResults () throws IOException {
129- runLookup (List .of (DataType .KEYWORD ), new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "bb" , "dd" } }));
139+ runLookup (
140+ List .of (DataType .KEYWORD ),
141+ new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "bb" , "dd" } }),
142+ buildGreaterThanFilter (-1L )
143+ );
130144 }
131145
132146 public void testJoinOnTwoKeysMultiResults () throws IOException {
133147 runLookup (
134148 List .of (DataType .KEYWORD , DataType .LONG ),
135- new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "bb" , "dd" }, new Long [] { 12L , 1L , 1L , 42L } })
149+ new UsingSingleLookupTable (new Object [][] { new String [] { "aa" , "bb" , "bb" , "dd" }, new Long [] { 12L , 1L , 1L , 42L } }),
150+ null
136151 );
137152 }
138153
@@ -144,12 +159,13 @@ public void testJoinOnThreeKeysMultiResults() throws IOException {
144159 new String [] { "aa" , "bb" , "bb" , "dd" },
145160 new Long [] { 12L , 1L , 1L , 42L },
146161 new String [] { "one" , "two" , "two" , "four" } }
147- )
162+ ),
163+ null
148164 );
149165 }
150166
151167 interface PopulateIndices {
152- void populate (int docCount , List <String > expected ) throws IOException ;
168+ void populate (int docCount , List <String > expected , Predicate < Integer > filter ) throws IOException ;
153169 }
154170
155171 class UsingSingleLookupTable implements PopulateIndices {
@@ -171,7 +187,7 @@ class UsingSingleLookupTable implements PopulateIndices {
171187 }
172188
173189 @ Override
174- public void populate (int docCount , List <String > expected ) {
190+ public void populate (int docCount , List <String > expected , Predicate < Integer > filter ) {
175191 List <IndexRequestBuilder > docs = new ArrayList <>();
176192 int numFields = lookupData .length ;
177193 int numRows = lookupData [0 ].length ;
@@ -190,8 +206,13 @@ public void populate(int docCount, List<String> expected) {
190206 } else {
191207 keyString = String .join ("," , key .stream ().map (String ::valueOf ).toArray (String []::new ));
192208 }
193- for (Integer match : matches .get (key )) {
194- expected .add (keyString + ":" + match );
209+ List <Integer > filteredMatches = matches .get (key ).stream ().filter (filter ).toList ();
210+ if (filteredMatches .isEmpty ()) {
211+ expected .add (keyString + ":null" );
212+ } else {
213+ for (Integer match : filteredMatches ) {
214+ expected .add (keyString + ":" + match );
215+ }
195216 }
196217 }
197218 for (int i = 0 ; i < numRows ; i ++) {
@@ -207,7 +228,21 @@ public void populate(int docCount, List<String> expected) {
207228 }
208229 }
209230
210- private void runLookup (List <DataType > keyTypes , PopulateIndices populateIndices ) throws IOException {
231+ Expression buildGreaterThanFilter (long value ) {
232+ FieldAttribute filterAttribute = new FieldAttribute (
233+ Source .EMPTY ,
234+ "l" ,
235+ new org .elasticsearch .xpack .esql .core .type .EsField (
236+ "l" ,
237+ org .elasticsearch .xpack .esql .core .type .DataType .LONG ,
238+ java .util .Collections .emptyMap (),
239+ true
240+ )
241+ );
242+ return new GreaterThan (Source .EMPTY , filterAttribute , new Literal (Source .EMPTY , value , DataType .LONG ));
243+ }
244+
245+ private void runLookup (List <DataType > keyTypes , PopulateIndices populateIndices , Expression filter ) throws IOException {
211246 String [] fieldMappers = new String [keyTypes .size () * 2 ];
212247 for (int i = 0 ; i < keyTypes .size (); i ++) {
213248 fieldMappers [2 * i ] = "key" + i ;
@@ -236,9 +271,22 @@ private void runLookup(List<DataType> keyTypes, PopulateIndices populateIndices)
236271
237272 client ().admin ().cluster ().prepareHealth (TEST_REQUEST_TIMEOUT ).setWaitForGreenStatus ().get ();
238273
274+ Predicate <Integer > filterPredicate = l -> true ;
275+ if (filter != null ) {
276+ if (filter instanceof GreaterThan gt
277+ && gt .left () instanceof FieldAttribute fa
278+ && fa .name ().equals ("l" )
279+ && gt .right () instanceof Literal lit ) {
280+ long value = ((Number ) lit .value ()).longValue ();
281+ filterPredicate = l -> l > value ;
282+ } else {
283+ fail ("Unsupported filter type in test baseline generation: " + filter );
284+ }
285+ }
286+
239287 int docCount = between (10 , 1000 );
240288 List <String > expected = new ArrayList <>(docCount );
241- populateIndices .populate (docCount , expected );
289+ populateIndices .populate (docCount , expected , filterPredicate );
242290
243291 /*
244292 * Find the data node hosting the only shard of the source index.
@@ -330,7 +378,7 @@ private void runLookup(List<DataType> keyTypes, PopulateIndices populateIndices)
330378 "lookup" ,
331379 List .of (new Alias (Source .EMPTY , "l" , new ReferenceAttribute (Source .EMPTY , "l" , DataType .LONG ))),
332380 Source .EMPTY ,
333- null
381+ filter
334382 );
335383 DriverContext driverContext = driverContext ();
336384 try (
@@ -344,7 +392,7 @@ private void runLookup(List<DataType> keyTypes, PopulateIndices populateIndices)
344392 for (int i = 0 ; i < keyTypes .size (); i ++) {
345393 keyBlocks .add (page .getBlock (i + 1 ));
346394 }
347- LongVector loadedBlock = page .<LongBlock >getBlock (keyTypes .size () + 1 ). asVector ( );
395+ LongBlock loadedBlock = page .<LongBlock >getBlock (keyTypes .size () + 1 );
348396 for (int p = 0 ; p < page .getPositionCount (); p ++) {
349397 StringBuilder result = new StringBuilder ();
350398 for (int j = 0 ; j < keyBlocks .size (); j ++) {
@@ -360,7 +408,11 @@ private void runLookup(List<DataType> keyTypes, PopulateIndices populateIndices)
360408 }
361409
362410 }
363- result .append (":" + loadedBlock .getLong (p ));
411+ if (loadedBlock .isNull (p )) {
412+ result .append (":null" );
413+ } else {
414+ result .append (":" + loadedBlock .getLong (loadedBlock .getFirstValueIndex (p )));
415+ }
364416 results .add (result .toString ());
365417 }
366418 } finally {
0 commit comments