2929import org .apache .lucene .store .Directory ;
3030import org .apache .lucene .tests .index .RandomIndexWriter ;
3131import org .apache .lucene .util .BytesRef ;
32+ import org .elasticsearch .common .bytes .BytesArray ;
3233import org .elasticsearch .common .geo .ShapeRelation ;
3334import org .elasticsearch .common .lucene .search .function .ScriptScoreQuery ;
35+ import org .elasticsearch .common .settings .Settings ;
36+ import org .elasticsearch .common .xcontent .XContentHelper ;
3437import org .elasticsearch .index .IndexVersion ;
3538import org .elasticsearch .index .fielddata .LongScriptFieldData ;
3639import org .elasticsearch .index .fielddata .ScriptDocValues ;
4245import org .elasticsearch .script .ScriptFactory ;
4346import org .elasticsearch .script .ScriptType ;
4447import org .elasticsearch .search .MultiValueMode ;
48+ import org .elasticsearch .search .lookup .SearchLookup ;
49+ import org .elasticsearch .xcontent .XContentFactory ;
50+ import org .elasticsearch .xcontent .XContentParserConfiguration ;
51+ import org .elasticsearch .xcontent .XContentType ;
4552
4653import java .io .IOException ;
4754import java .util .ArrayList ;
5259import static org .hamcrest .Matchers .containsInAnyOrder ;
5360import static org .hamcrest .Matchers .equalTo ;
5461import static org .hamcrest .Matchers .greaterThan ;
62+ import static org .hamcrest .Matchers .instanceOf ;
63+ import static org .hamcrest .Matchers .nullValue ;
5564
5665public class LongScriptFieldTypeTests extends AbstractNonTextScriptFieldTypeTestCase {
5766
@@ -302,6 +311,83 @@ public void testBlockLoader() throws IOException {
302311 }
303312 }
304313
314+ public void testBlockLoaderSourceOnlyRuntimeField () throws IOException {
315+ try (
316+ Directory directory = newDirectory ();
317+ RandomIndexWriter iw = new RandomIndexWriter (random (), directory , newIndexWriterConfig ().setMergePolicy (NoMergePolicy .INSTANCE ))
318+ ) {
319+ iw .addDocuments (
320+ List .of (
321+ List .of (new StoredField ("_source" , new BytesRef ("{\" test\" : [1]}" ))),
322+ List .of (new StoredField ("_source" , new BytesRef ("{\" test\" : [2]}" )))
323+ )
324+ );
325+ try (DirectoryReader reader = iw .getReader ()) {
326+ LongScriptFieldType fieldType = simpleSourceOnlyMappedFieldType ();
327+
328+ // Assert implementations:
329+ BlockLoader loader = fieldType .blockLoader (blContext (Settings .EMPTY , true ));
330+ assertThat (loader , instanceOf (LongScriptBlockDocValuesReader .LongScriptBlockLoader .class ));
331+ // ignored source doesn't support column at a time loading:
332+ var columnAtATimeLoader = loader .columnAtATimeReader (reader .leaves ().getFirst ());
333+ assertThat (columnAtATimeLoader , instanceOf (LongScriptBlockDocValuesReader .class ));
334+ var rowStrideReader = loader .rowStrideReader (reader .leaves ().getFirst ());
335+ assertThat (rowStrideReader , instanceOf (LongScriptBlockDocValuesReader .class ));
336+
337+ // Assert values:
338+ assertThat (blockLoaderReadValuesFromColumnAtATimeReader (reader , fieldType , 0 ), equalTo (List .of (1L , 2L )));
339+ assertThat (blockLoaderReadValuesFromColumnAtATimeReader (reader , fieldType , 1 ), equalTo (List .of (2L )));
340+ assertThat (blockLoaderReadValuesFromRowStrideReader (reader , fieldType ), equalTo (List .of (1L , 2L )));
341+ }
342+ }
343+ }
344+
345+ public void testBlockLoaderSourceOnlyRuntimeFieldWithSyntheticSource () throws IOException {
346+ var settings = Settings .builder ().put ("index.mapping.source.mode" , "synthetic" ).build ();
347+ try (
348+ Directory directory = newDirectory ();
349+ RandomIndexWriter iw = new RandomIndexWriter (random (), directory , newIndexWriterConfig ().setMergePolicy (NoMergePolicy .INSTANCE ))
350+ ) {
351+
352+ var document1 = createDocumentWithIgnoredSource ("[1]" );
353+ var document2 = createDocumentWithIgnoredSource ("[2]" );
354+
355+ iw .addDocuments (List .of (document1 , document2 ));
356+ try (DirectoryReader reader = iw .getReader ()) {
357+ LongScriptFieldType fieldType = simpleSourceOnlyMappedFieldType ();
358+
359+ // Assert implementations:
360+ BlockLoader loader = fieldType .blockLoader (blContext (settings , true ));
361+ assertThat (loader , instanceOf (FallbackSyntheticSourceBlockLoader .class ));
362+ // ignored source doesn't support column at a time loading:
363+ var columnAtATimeLoader = loader .columnAtATimeReader (reader .leaves ().getFirst ());
364+ assertThat (columnAtATimeLoader , nullValue ());
365+ var rowStrideReader = loader .rowStrideReader (reader .leaves ().getFirst ());
366+ assertThat (
367+ rowStrideReader .getClass ().getName (),
368+ equalTo ("org.elasticsearch.index.mapper.FallbackSyntheticSourceBlockLoader$IgnoredSourceRowStrideReader" )
369+ );
370+
371+ // Assert values:
372+ assertThat (blockLoaderReadValuesFromRowStrideReader (settings , reader , fieldType , true ), equalTo (List .of (1L , 2L )));
373+ }
374+ }
375+ }
376+
377+ private static LuceneDocument createDocumentWithIgnoredSource (String bytes ) throws IOException {
378+ var doc = new LuceneDocument ();
379+ var parser = XContentHelper .createParser (
380+ XContentParserConfiguration .EMPTY ,
381+ new BytesArray (bytes ),
382+ XContentFactory .xContent (XContentType .JSON ).type ()
383+ );
384+ parser .nextToken ();
385+ var nameValue = new IgnoredSourceFieldMapper .NameValue ("test" , 0 , XContentDataHelper .encodeToken (parser ), doc );
386+ var ignoredSourceFormat = IgnoredSourceFieldMapper .ignoredSourceFormat (IndexVersion .current ());
387+ ignoredSourceFormat .writeIgnoredFields (List .of (nameValue ));
388+ return doc ;
389+ }
390+
305391 @ Override
306392 protected Query randomTermsQuery (MappedFieldType ft , SearchExecutionContext ctx ) {
307393 return ft .termsQuery (List .of (randomLong ()), ctx );
@@ -312,6 +398,10 @@ protected LongScriptFieldType simpleMappedFieldType() {
312398 return build ("read_foo" , Map .of (), OnScriptError .FAIL );
313399 }
314400
401+ private LongScriptFieldType simpleSourceOnlyMappedFieldType () {
402+ return build ("read_test" , Map .of (), OnScriptError .FAIL );
403+ }
404+
315405 @ Override
316406 protected LongScriptFieldType loopFieldType () {
317407 return build ("loop" , Map .of (), OnScriptError .FAIL );
@@ -329,6 +419,32 @@ protected LongScriptFieldType build(String code, Map<String, Object> params, OnS
329419
330420 private static LongFieldScript .Factory factory (Script script ) {
331421 switch (script .getIdOrCode ()) {
422+ case "read_test" :
423+ return new LongFieldScript .Factory () {
424+ @ Override
425+ public LongFieldScript .LeafFactory newFactory (
426+ String fieldName ,
427+ Map <String , Object > params ,
428+ SearchLookup lookup ,
429+ OnScriptError onScriptError
430+ ) {
431+ return (ctx ) -> new LongFieldScript (fieldName , params , lookup , onScriptError , ctx ) {
432+ @ Override
433+ @ SuppressWarnings ("unchecked" )
434+ public void execute () {
435+ Map <String , Object > source = (Map <String , Object >) this .getParams ().get ("_source" );
436+ for (Object foo : (List <?>) source .get ("test" )) {
437+ emit (((Number ) foo ).longValue ());
438+ }
439+ };
440+ };
441+ }
442+
443+ @ Override
444+ public boolean isParsedFromSource () {
445+ return true ;
446+ }
447+ };
332448 case "read_foo" :
333449 return (fieldName , params , lookup , onScriptError ) -> (ctx ) -> new LongFieldScript (
334450 fieldName ,
0 commit comments