Skip to content

Commit 0c8b438

Browse files
javannanik9000
andauthored
Add support for runtime fields (#61776)
This commit includes the work that has been done on the runtime fields feature branch until now. The high level tasks are listed in #59332. The tasks that have not yet been completed can be worked on after merging the feature branch. We are adding a new x-pack plugin called runtime-fields that plugs in a custom mapper which allows to define runtime fields based on a script. The changes included in this commit that were made outside of the x-pack/plugin/runtime-fields directory are minimal and revolve around 1) making the ScriptService available while parsing index mappings so that the scripts associated to runtime fields can be compiled 2) sharing code to manipulate ranges etc. as it can be reused in runtime fields. Co-authored-by: Nik Everett <[email protected]>
1 parent b26584d commit 0c8b438

File tree

161 files changed

+13302
-179
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

161 files changed

+13302
-179
lines changed

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexUpgradeService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,16 @@ public class MetadataIndexUpgradeService {
6161
private final MapperRegistry mapperRegistry;
6262
private final IndexScopedSettings indexScopedSettings;
6363
private final SystemIndices systemIndices;
64+
private final ScriptService scriptService;
6465

6566
public MetadataIndexUpgradeService(Settings settings, NamedXContentRegistry xContentRegistry, MapperRegistry mapperRegistry,
66-
IndexScopedSettings indexScopedSettings, SystemIndices systemIndices) {
67+
IndexScopedSettings indexScopedSettings, SystemIndices systemIndices, ScriptService scriptService) {
6768
this.settings = settings;
6869
this.xContentRegistry = xContentRegistry;
6970
this.mapperRegistry = mapperRegistry;
7071
this.indexScopedSettings = indexScopedSettings;
7172
this.systemIndices = systemIndices;
73+
this.scriptService = scriptService;
7274
}
7375

7476
/**
@@ -188,7 +190,7 @@ public Set<Entry<String, NamedAnalyzer>> entrySet() {
188190
try (IndexAnalyzers fakeIndexAnalzyers =
189191
new IndexAnalyzers(analyzerMap, analyzerMap, analyzerMap)) {
190192
MapperService mapperService = new MapperService(indexSettings, fakeIndexAnalzyers, xContentRegistry, similarityService,
191-
mapperRegistry, () -> null, () -> false);
193+
mapperRegistry, () -> null, () -> false, scriptService);
192194
mapperService.merge(indexMetadata, MapperService.MergeReason.MAPPING_RECOVERY);
193195
}
194196
} catch (Exception ex) {

server/src/main/java/org/elasticsearch/index/IndexModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ public MapperService newIndexMapperService(NamedXContentRegistry xContentRegistr
505505
ScriptService scriptService) throws IOException {
506506
return new MapperService(indexSettings, analysisRegistry.build(indexSettings), xContentRegistry,
507507
new SimilarityService(indexSettings, scriptService, similarities), mapperRegistry,
508-
() -> { throw new UnsupportedOperationException("no index query shard context available"); }, () -> false);
508+
() -> { throw new UnsupportedOperationException("no index query shard context available"); }, () -> false, scriptService);
509509
}
510510

511511
/**

server/src/main/java/org/elasticsearch/index/IndexService.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,14 @@ public IndexService(
193193
assert indexAnalyzers != null;
194194
this.mapperService = new MapperService(indexSettings, indexAnalyzers, xContentRegistry, similarityService, mapperRegistry,
195195
// we parse all percolator queries as they would be parsed on shard 0
196-
() -> newQueryShardContext(0, null, System::currentTimeMillis, null), idFieldDataEnabled);
196+
() -> newQueryShardContext(0, null, System::currentTimeMillis, null), idFieldDataEnabled, scriptService);
197197
this.indexFieldData = new IndexFieldDataService(indexSettings, indicesFieldDataCache, circuitBreakerService, mapperService);
198198
if (indexSettings.getIndexSortConfig().hasIndexSort()) {
199199
// we delay the actual creation of the sort order for this index because the mapping has not been merged yet.
200200
// The sort order is validated right after the merge of the mapping later in the process.
201201
this.indexSortSupplier = () -> indexSettings.getIndexSortConfig().buildIndexSort(
202202
mapperService::fieldType,
203-
fieldType -> indexFieldData.getForField(fieldType, indexFieldData.index().getName(), () -> {
204-
throw new UnsupportedOperationException("search lookup not available for index sorting");
205-
})
203+
(fieldType, searchLookup) -> indexFieldData.getForField(fieldType, indexFieldData.index().getName(), searchLookup)
206204
);
207205
} else {
208206
this.indexSortSupplier = () -> null;

server/src/main/java/org/elasticsearch/index/IndexSortConfig.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@
2929
import org.elasticsearch.index.fielddata.IndexFieldData;
3030
import org.elasticsearch.index.mapper.MappedFieldType;
3131
import org.elasticsearch.search.MultiValueMode;
32+
import org.elasticsearch.search.lookup.SearchLookup;
3233
import org.elasticsearch.search.sort.SortOrder;
3334

3435
import java.util.Collections;
3536
import java.util.EnumSet;
3637
import java.util.List;
38+
import java.util.function.BiFunction;
3739
import java.util.function.Function;
40+
import java.util.function.Supplier;
3841

3942
/**
4043
* Holds all the information that is used to build the sort order of an index.
@@ -181,7 +184,7 @@ public boolean hasPrimarySortOnField(String field) {
181184
* or returns null if this index has no sort.
182185
*/
183186
public Sort buildIndexSort(Function<String, MappedFieldType> fieldTypeLookup,
184-
Function<MappedFieldType, IndexFieldData<?>> fieldDataLookup) {
187+
BiFunction<MappedFieldType, Supplier<SearchLookup>, IndexFieldData<?>> fieldDataLookup) {
185188
if (hasIndexSort() == false) {
186189
return null;
187190
}
@@ -200,7 +203,9 @@ public Sort buildIndexSort(Function<String, MappedFieldType> fieldTypeLookup,
200203
}
201204
IndexFieldData<?> fieldData;
202205
try {
203-
fieldData = fieldDataLookup.apply(ft);
206+
fieldData = fieldDataLookup.apply(ft, () -> {
207+
throw new UnsupportedOperationException("index sorting not supported on runtime field [" + ft.name() + "]");
208+
});
204209
} catch (Exception e) {
205210
throw new IllegalArgumentException("docvalues not found for index sort field:[" + sortSpec.field + "]", e);
206211
}

server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -481,25 +481,30 @@ protected void resize(int newSize) {
481481
public int size() {
482482
return count;
483483
}
484-
485484
}
486485

487-
public static final class Strings extends BinaryScriptDocValues<String> {
488-
486+
public static class Strings extends BinaryScriptDocValues<String> {
489487
public Strings(SortedBinaryDocValues in) {
490488
super(in);
491489
}
492490

493491
@Override
494-
public String get(int index) {
492+
public final String get(int index) {
495493
if (count == 0) {
496494
throw new IllegalStateException("A document doesn't have a value for a field! " +
497495
"Use doc[<field>].size()==0 to check if a document is missing a field!");
498496
}
499-
return values[index].get().utf8ToString();
497+
return bytesToString(values[index].get());
498+
}
499+
500+
/**
501+
* Convert the stored bytes to a String.
502+
*/
503+
protected String bytesToString(BytesRef bytes) {
504+
return bytes.utf8ToString();
500505
}
501506

502-
public String getValue() {
507+
public final String getValue() {
503508
return get(0);
504509
}
505510
}

server/src/main/java/org/elasticsearch/index/fielddata/plain/LeafDoubleFieldData.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
/**
3535
* Specialization of {@link LeafNumericFieldData} for floating-point numerics.
3636
*/
37-
abstract class LeafDoubleFieldData implements LeafNumericFieldData {
37+
public abstract class LeafDoubleFieldData implements LeafNumericFieldData {
3838

3939
private final long ramBytesUsed;
4040

41-
LeafDoubleFieldData(long ramBytesUsed) {
41+
protected LeafDoubleFieldData(long ramBytesUsed) {
4242
this.ramBytesUsed = ramBytesUsed;
4343
}
4444

server/src/main/java/org/elasticsearch/index/fielddata/plain/LeafLongFieldData.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@
2929
/**
3030
* Specialization of {@link LeafNumericFieldData} for integers.
3131
*/
32-
abstract class LeafLongFieldData implements LeafNumericFieldData {
32+
public abstract class LeafLongFieldData implements LeafNumericFieldData {
3333

3434
private final long ramBytesUsed;
3535
/**
3636
* Type of this field. Used to expose appropriate types in {@link #getScriptValues()}.
3737
*/
3838
private final NumericType numericType;
3939

40-
LeafLongFieldData(long ramBytesUsed, NumericType numericType) {
40+
protected LeafLongFieldData(long ramBytesUsed, NumericType numericType) {
4141
this.ramBytesUsed = ramBytesUsed;
4242
this.numericType = numericType;
4343
}

server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@
3131
import org.apache.lucene.search.IndexSortSortedNumericDocValuesRangeQuery;
3232
import org.apache.lucene.search.Query;
3333
import org.apache.lucene.search.TermQuery;
34-
import org.apache.lucene.util.BytesRef;
3534
import org.elasticsearch.ElasticsearchParseException;
3635
import org.elasticsearch.Version;
3736
import org.elasticsearch.common.Nullable;
3837
import org.elasticsearch.common.geo.ShapeRelation;
3938
import org.elasticsearch.common.joda.Joda;
39+
import org.elasticsearch.common.lucene.BytesRefs;
4040
import org.elasticsearch.common.time.DateFormatter;
4141
import org.elasticsearch.common.time.DateFormatters;
4242
import org.elasticsearch.common.time.DateMathParser;
@@ -63,6 +63,7 @@
6363
import java.util.List;
6464
import java.util.Locale;
6565
import java.util.Map;
66+
import java.util.function.BiFunction;
6667
import java.util.function.Function;
6768
import java.util.function.LongSupplier;
6869
import java.util.function.Supplier;
@@ -343,60 +344,83 @@ public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower
343344
DateMathParser parser = forcedDateParser == null
344345
? dateMathParser
345346
: forcedDateParser;
347+
return dateRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, parser, context, resolution, (l, u) -> {
348+
Query query = LongPoint.newRangeQuery(name(), l, u);
349+
if (hasDocValues()) {
350+
Query dvQuery = SortedNumericDocValuesField.newSlowRangeQuery(name(), l, u);
351+
query = new IndexOrDocValuesQuery(query, dvQuery);
352+
353+
if (context.indexSortedOnField(name())) {
354+
query = new IndexSortSortedNumericDocValuesRangeQuery(name(), l, u, query);
355+
}
356+
}
357+
return query;
358+
});
359+
}
360+
361+
public static Query dateRangeQuery(
362+
Object lowerTerm,
363+
Object upperTerm,
364+
boolean includeLower,
365+
boolean includeUpper,
366+
@Nullable ZoneId timeZone,
367+
DateMathParser parser,
368+
QueryShardContext context,
369+
Resolution resolution,
370+
BiFunction<Long, Long, Query> builder
371+
) {
372+
return handleNow(context, nowSupplier -> {
373+
long l, u;
374+
if (lowerTerm == null) {
375+
l = Long.MIN_VALUE;
376+
} else {
377+
l = parseToLong(lowerTerm, !includeLower, timeZone, parser, nowSupplier, resolution);
378+
if (includeLower == false) {
379+
++l;
380+
}
381+
}
382+
if (upperTerm == null) {
383+
u = Long.MAX_VALUE;
384+
} else {
385+
u = parseToLong(upperTerm, includeUpper, timeZone, parser, nowSupplier, resolution);
386+
if (includeUpper == false) {
387+
--u;
388+
}
389+
}
390+
return builder.apply(l, u);
391+
});
392+
}
393+
394+
/**
395+
* Handle {@code now} in queries.
396+
* @param context context from which to read the current time
397+
* @param builder build the query
398+
* @return the result of the builder, wrapped in {@link DateRangeIncludingNowQuery} if {@code now} was used.
399+
*/
400+
public static Query handleNow(QueryShardContext context, Function<LongSupplier, Query> builder) {
346401
boolean[] nowUsed = new boolean[1];
347402
LongSupplier nowSupplier = () -> {
348403
nowUsed[0] = true;
349404
return context.nowInMillis();
350405
};
351-
long l, u;
352-
if (lowerTerm == null) {
353-
l = Long.MIN_VALUE;
354-
} else {
355-
l = parseToLong(lowerTerm, !includeLower, timeZone, parser, nowSupplier);
356-
if (includeLower == false) {
357-
++l;
358-
}
359-
}
360-
if (upperTerm == null) {
361-
u = Long.MAX_VALUE;
362-
} else {
363-
u = parseToLong(upperTerm, includeUpper, timeZone, parser, nowSupplier);
364-
if (includeUpper == false) {
365-
--u;
366-
}
367-
}
368-
369-
Query query = LongPoint.newRangeQuery(name(), l, u);
370-
if (hasDocValues()) {
371-
Query dvQuery = SortedNumericDocValuesField.newSlowRangeQuery(name(), l, u);
372-
query = new IndexOrDocValuesQuery(query, dvQuery);
373-
374-
if (context.indexSortedOnField(name())) {
375-
query = new IndexSortSortedNumericDocValuesRangeQuery(name(), l, u, query);
376-
}
377-
}
378-
379-
if (nowUsed[0]) {
380-
query = new DateRangeIncludingNowQuery(query);
381-
}
382-
return query;
406+
Query query = builder.apply(nowSupplier);
407+
return nowUsed[0] ? new DateRangeIncludingNowQuery(query) : query;
383408
}
384409

385-
public long parseToLong(Object value, boolean roundUp,
386-
@Nullable ZoneId zone, @Nullable DateMathParser forcedDateParser, LongSupplier now) {
387-
DateMathParser dateParser = dateMathParser();
388-
if (forcedDateParser != null) {
389-
dateParser = forcedDateParser;
390-
}
410+
public long parseToLong(Object value, boolean roundUp, @Nullable ZoneId zone, DateMathParser dateParser, LongSupplier now) {
411+
dateParser = dateParser == null ? dateMathParser() : dateParser;
412+
return parseToLong(value, roundUp, zone, dateParser, now, resolution);
413+
}
391414

392-
String strValue;
393-
if (value instanceof BytesRef) {
394-
strValue = ((BytesRef) value).utf8ToString();
395-
} else {
396-
strValue = value.toString();
397-
}
398-
Instant instant = dateParser.parse(strValue, now, roundUp, zone);
399-
return resolution.convert(instant);
415+
public static long parseToLong(
416+
Object value,
417+
boolean roundUp,
418+
@Nullable ZoneId zone,
419+
DateMathParser dateParser,
420+
LongSupplier now,
421+
Resolution resolution
422+
) {
423+
return resolution.convert(dateParser.parse(BytesRefs.toString(value), now, roundUp, zone));
400424
}
401425

402426
@Override
@@ -416,7 +440,7 @@ public Relation isFieldWithinQuery(IndexReader reader,
416440

417441
long fromInclusive = Long.MIN_VALUE;
418442
if (from != null) {
419-
fromInclusive = parseToLong(from, !includeLower, timeZone, dateParser, context::nowInMillis);
443+
fromInclusive = parseToLong(from, !includeLower, timeZone, dateParser, context::nowInMillis, resolution);
420444
if (includeLower == false) {
421445
if (fromInclusive == Long.MAX_VALUE) {
422446
return Relation.DISJOINT;
@@ -427,7 +451,7 @@ public Relation isFieldWithinQuery(IndexReader reader,
427451

428452
long toInclusive = Long.MAX_VALUE;
429453
if (to != null) {
430-
toInclusive = parseToLong(to, includeUpper, timeZone, dateParser, context::nowInMillis);
454+
toInclusive = parseToLong(to, includeUpper, timeZone, dateParser, context::nowInMillis, resolution);
431455
if (includeUpper == false) {
432456
if (toInclusive == Long.MIN_VALUE) {
433457
return Relation.DISJOINT;

server/src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.index.query.QueryShardContext;
3434
import org.elasticsearch.index.similarity.SimilarityService;
3535
import org.elasticsearch.indices.mapper.MapperRegistry;
36+
import org.elasticsearch.script.ScriptService;
3637

3738
import java.util.HashMap;
3839
import java.util.Iterator;
@@ -54,26 +55,29 @@ public class DocumentMapperParser {
5455

5556
private final Map<String, Mapper.TypeParser> typeParsers;
5657
private final Map<String, MetadataFieldMapper.TypeParser> rootTypeParsers;
58+
private final ScriptService scriptService;
5759

5860
public DocumentMapperParser(IndexSettings indexSettings, MapperService mapperService, NamedXContentRegistry xContentRegistry,
59-
SimilarityService similarityService, MapperRegistry mapperRegistry, Supplier<QueryShardContext> queryShardContextSupplier) {
61+
SimilarityService similarityService, MapperRegistry mapperRegistry,
62+
Supplier<QueryShardContext> queryShardContextSupplier, ScriptService scriptService) {
6063
this.mapperService = mapperService;
6164
this.xContentRegistry = xContentRegistry;
6265
this.similarityService = similarityService;
6366
this.queryShardContextSupplier = queryShardContextSupplier;
67+
this.scriptService = scriptService;
6468
this.typeParsers = mapperRegistry.getMapperParsers();
6569
this.indexVersionCreated = indexSettings.getIndexVersionCreated();
6670
this.rootTypeParsers = mapperRegistry.getMetadataMapperParsers(indexVersionCreated);
6771
}
6872

6973
public Mapper.TypeParser.ParserContext parserContext() {
7074
return new Mapper.TypeParser.ParserContext(similarityService::getSimilarity, mapperService,
71-
typeParsers::get, indexVersionCreated, queryShardContextSupplier, null);
75+
typeParsers::get, indexVersionCreated, queryShardContextSupplier, null, scriptService);
7276
}
7377

7478
public Mapper.TypeParser.ParserContext parserContext(DateFormatter dateFormatter) {
7579
return new Mapper.TypeParser.ParserContext(similarityService::getSimilarity, mapperService,
76-
typeParsers::get, indexVersionCreated, queryShardContextSupplier, dateFormatter);
80+
typeParsers::get, indexVersionCreated, queryShardContextSupplier, dateFormatter, scriptService);
7781
}
7882

7983
public DocumentMapper parse(@Nullable String type, CompressedXContent source) throws MapperParsingException {

0 commit comments

Comments
 (0)