Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a8d6c64
Default indexed to false for keyword dimension fields
romseygeek Oct 23, 2025
1293cf4
[CI] Auto commit changes from spotless
Oct 23, 2025
553632c
Merge branch 'main' into dimensions/sparse-indexes
felixbarny Oct 23, 2025
40bfeb3
Same for IpFieldMapper
romseygeek Oct 23, 2025
0ca780c
Merge remote-tracking branch 'romseygeek/dimensions/sparse-indexes' i…
romseygeek Oct 23, 2025
54f19a0
[CI] Auto commit changes from spotless
Oct 23, 2025
d059b5f
Merge branch 'main' into dimensions/sparse-indexes
felixbarny Nov 4, 2025
ccc5f32
Merge remote-tracking branch 'origin/main' into dimensions/sparse-ind…
romseygeek Nov 7, 2025
eae14b0
Merge remote-tracking branch 'origin/main' into dimensions/sparse-ind…
romseygeek Nov 11, 2025
12cf479
wip
romseygeek Nov 11, 2025
eb86816
Use IndexType in TermBasedFieldType constructors
romseygeek Nov 11, 2025
e133d94
Merge branch 'kft/constructor-index-type' into dimensions/sparse-indexes
romseygeek Nov 11, 2025
86e5275
Use sparse indexes for dimension fields
romseygeek Nov 12, 2025
c75fd0a
Merge remote-tracking branch 'origin/main' into dimensions/sparse-ind…
romseygeek Nov 12, 2025
1a9b3a8
Merge remote-tracking branch 'origin/main' into dimensions/sparse-ind…
romseygeek Nov 12, 2025
ddd9647
iter
romseygeek Nov 12, 2025
b1c1ab7
Merge remote-tracking branch 'romseygeek/dimensions/sparse-indexes' i…
romseygeek Nov 12, 2025
ef2141a
Merge branch 'main' into dimensions/sparse-indexes
romseygeek Nov 12, 2025
57b0a61
precommit
romseygeek Nov 12, 2025
b8e2e24
Merge remote-tracking branch 'romseygeek/dimensions/sparse-indexes' i…
romseygeek Nov 12, 2025
667953e
iter
romseygeek Nov 12, 2025
38ee47a
iter
romseygeek Nov 12, 2025
59abb1d
Merge remote-tracking branch 'origin/main' into dimensions/sparse-ind…
romseygeek Nov 12, 2025
1780fc5
Merge branch 'main' into dimensions/sparse-indexes
romseygeek Nov 13, 2025
cefc10e
Merge branch 'main' into dimensions/sparse-indexes
romseygeek Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.breaker.NoopCircuitBreaker;
import org.elasticsearch.common.logging.LogConfigurator;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.util.BigArrays;
Expand Down Expand Up @@ -227,50 +226,44 @@ private static BlockLoader blockLoader(String name) {
break;
}
ft.freeze();
return new KeywordFieldMapper.KeywordFieldType(
w.name,
ft,
Lucene.KEYWORD_ANALYZER,
Lucene.KEYWORD_ANALYZER,
Lucene.KEYWORD_ANALYZER,
new KeywordFieldMapper.Builder(name, defaultIndexSettings()).docValues(ft.docValuesType() != DocValuesType.NONE),
syntheticSource
).blockLoader(new MappedFieldType.BlockLoaderContext() {
@Override
public String indexName() {
return "benchmark";
}
return new KeywordFieldMapper.KeywordFieldType(w.name, ft, syntheticSource).blockLoader(
new MappedFieldType.BlockLoaderContext() {
@Override
public String indexName() {
return "benchmark";
}

@Override
public IndexSettings indexSettings() {
throw new UnsupportedOperationException();
}
@Override
public IndexSettings indexSettings() {
throw new UnsupportedOperationException();
}

@Override
public MappedFieldType.FieldExtractPreference fieldExtractPreference() {
return MappedFieldType.FieldExtractPreference.NONE;
}
@Override
public MappedFieldType.FieldExtractPreference fieldExtractPreference() {
return MappedFieldType.FieldExtractPreference.NONE;
}

@Override
public SearchLookup lookup() {
throw new UnsupportedOperationException();
}
@Override
public SearchLookup lookup() {
throw new UnsupportedOperationException();
}

@Override
public Set<String> sourcePaths(String name) {
return Set.of(name);
}
@Override
public Set<String> sourcePaths(String name) {
return Set.of(name);
}

@Override
public String parentField(String field) {
throw new UnsupportedOperationException();
}
@Override
public String parentField(String field) {
throw new UnsupportedOperationException();
}

@Override
public FieldNamesFieldMapper.FieldNamesFieldType fieldNames() {
return FieldNamesFieldMapper.FieldNamesFieldType.get(true);
@Override
public FieldNamesFieldMapper.FieldNamesFieldType fieldNames() {
return FieldNamesFieldMapper.FieldNamesFieldType.get(true);
}
}
});
);
}
throw new IllegalArgumentException("can't read [" + name + "]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,13 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
}
long scaledValue = encode(doubleValue, scalingFactor);

NumberFieldMapper.NumberType.LONG.addFields(context.doc(), fieldType().name(), scaledValue, indexed, hasDocValues, stored);
NumberFieldMapper.NumberType.LONG.addFields(
context.doc(),
fieldType().name(),
scaledValue,
IndexType.points(indexed, hasDocValues),
stored
);

if (shouldStoreOffsets) {
context.getOffSetContext().recordOffset(offsetsFieldName, scaledValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
tokenCount = countPositions(analyzer, fullPath(), value, enablePositionIncrements);
}

NumberFieldMapper.NumberType.INTEGER.addFields(context.doc(), fieldType().name(), tokenCount, index, hasDocValues, store);
NumberFieldMapper.NumberType.INTEGER.addFields(
context.doc(),
fieldType().name(),
tokenCount,
IndexType.points(index, hasDocValues),
store
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.elasticsearch.index.mapper.BlockLoader;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.FieldTypeTestCase;
import org.elasticsearch.index.mapper.IndexType;
import org.elasticsearch.index.mapper.KeywordFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MappingParserContext;
Expand Down Expand Up @@ -295,9 +296,8 @@ public void testBlockLoaderDoesNotUseSyntheticSourceDelegateWhenIgnoreAboveIsSet

KeywordFieldMapper.KeywordFieldType syntheticSourceDelegate = new KeywordFieldMapper.KeywordFieldType(
"child",
mock(FieldType.class),
mock(NamedAnalyzer.class),
mock(NamedAnalyzer.class),
IndexType.terms(true, true),
new TextSearchInfo(mock(FieldType.class), null, mock(NamedAnalyzer.class), mock(NamedAnalyzer.class)),
mock(NamedAnalyzer.class),
builder,
true
Expand Down Expand Up @@ -344,9 +344,8 @@ public void testBlockLoaderDoesNotUseSyntheticSourceDelegateWhenIgnoreAboveIsSet

KeywordFieldMapper.KeywordFieldType syntheticSourceDelegate = new KeywordFieldMapper.KeywordFieldType(
"child",
mock(FieldType.class),
mock(NamedAnalyzer.class),
mock(NamedAnalyzer.class),
IndexType.terms(true, true),
new TextSearchInfo(mock(FieldType.class), null, mock(NamedAnalyzer.class), mock(NamedAnalyzer.class)),
mock(NamedAnalyzer.class),
builder,
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.mapper.DocumentParserContext;
import org.elasticsearch.index.mapper.IndexType;
import org.elasticsearch.index.mapper.LuceneDocument;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperService;
Expand Down Expand Up @@ -301,7 +302,7 @@ public void testDuel() throws Exception {
document.add(new TextField(entry.getKey(), value, Field.Store.NO));
}
for (Integer intValue : intValues) {
NumberFieldMapper.NumberType.INTEGER.addFields(document, "int_field", intValue, true, true, false);
NumberFieldMapper.NumberType.INTEGER.addFields(document, "int_field", intValue, IndexType.points(true, true), false);
}
MemoryIndex memoryIndex = MemoryIndex.fromDocument(document, new WhitespaceAnalyzer());
duelRun(queryStore, memoryIndex, shardSearcher);
Expand Down Expand Up @@ -424,7 +425,13 @@ public void testDuel2() throws Exception {
}

for (int[] range : ranges) {
NumberFieldMapper.NumberType.INTEGER.addFields(document, "int_field", between(range[0], range[1]), true, true, false);
NumberFieldMapper.NumberType.INTEGER.addFields(
document,
"int_field",
between(range[0], range[1]),
IndexType.points(true, true),
false
);
logger.info("Test with document: {}" + document);
MemoryIndex memoryIndex = MemoryIndex.fromDocument(document, new WhitespaceAnalyzer());
duelRun(queryStore, memoryIndex, shardSearcher);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void postParse(DocumentParserContext context) {
return;
}
final int value = context.sourceToParse().source().length();
NumberType.INTEGER.addFields(context.doc(), fullPath(), value, true, true, true);
NumberType.INTEGER.addFields(context.doc(), fullPath(), value, IndexType.points(true, true), true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ private static Version parseUnchecked(String version) {
public static final IndexVersion REENABLED_TIMESTAMP_DOC_VALUES_SPARSE_INDEX = def(9_042_0_00, Version.LUCENE_10_3_1);
public static final IndexVersion SKIPPERS_ENABLED_BY_DEFAULT = def(9_043_0_00, Version.LUCENE_10_3_1);
public static final IndexVersion TIME_SERIES_USE_SYNTHETIC_ID = def(9_044_0_00, Version.LUCENE_10_3_1);
public static final IndexVersion TIME_SERIES_DIMENSIONS_USE_SKIPPERS = def(9_045_0_00, Version.LUCENE_10_3_1);

/*
* STOP! READ THIS FIRST! No, really,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private static BooleanFieldMapper toType(FieldMapper in) {
public static final class Builder extends FieldMapper.DimensionBuilder {

private final Parameter<Boolean> docValues = Parameter.docValuesParam(m -> toType(m).hasDocValues, true);
private final Parameter<Boolean> indexed = Parameter.indexParam(m -> toType(m).indexed, true);
private final Parameter<Boolean> indexed;
private final Parameter<Boolean> stored = Parameter.storeParam(m -> toType(m).stored, false);
private final Parameter<Explicit<Boolean>> ignoreMalformed;
private final Parameter<Boolean> nullValue = new Parameter<>(
Expand Down Expand Up @@ -117,20 +117,9 @@ public Builder(String name, ScriptCompiler scriptCompiler, IndexSettings indexSe
IGNORE_MALFORMED_SETTING.get(indexSettings.getSettings())
);
this.script.precludesParameters(ignoreMalformed, nullValue);
this.dimension = TimeSeriesParams.dimensionParam(m -> toType(m).fieldType().isDimension(), docValues::get);
this.indexed = Parameter.indexParam(m -> toType(m).indexed, indexSettings, dimension);
addScriptValidation(script, indexed, docValues);
this.dimension = TimeSeriesParams.dimensionParam(m -> toType(m).fieldType().isDimension()).addValidator(v -> {
if (v && (indexed.getValue() == false || docValues.getValue() == false)) {
throw new IllegalArgumentException(
"Field ["
+ TimeSeriesParams.TIME_SERIES_DIMENSION_PARAM
+ "] requires that ["
+ indexed.name
+ "] and ["
+ docValues.name
+ "] are true"
);
}
});
}

public Builder dimension(boolean dimension) {
Expand All @@ -152,16 +141,30 @@ protected Parameter<?>[] getParameters() {
dimension };
}

private IndexType indexType() {
if (indexed.get() && indexSettings.getIndexVersionCreated().isLegacyIndexVersion() == false) {
return IndexType.terms(true, docValues.getValue());
}
if (docValues.get() == false) {
return IndexType.NONE;
}
if (dimension.get()
&& indexSettings.useDocValuesSkipper()
&& indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.TIME_SERIES_DIMENSIONS_USE_SKIPPERS)) {
return IndexType.skippers();
}
return IndexType.docValuesOnly();
}

@Override
public BooleanFieldMapper build(MapperBuilderContext context) {
if (inheritDimensionParameterFromParentObject(context)) {
dimension(true);
}
MappedFieldType ft = new BooleanFieldType(
context.buildFullName(leafName()),
indexed.getValue() && indexSettings.getIndexVersionCreated().isLegacyIndexVersion() == false,
indexType(),
stored.getValue(),
docValues.getValue(),
nullValue.getValue(),
scriptValues(),
meta.getValue(),
Expand Down Expand Up @@ -215,32 +218,27 @@ public static final class BooleanFieldType extends TermBasedFieldType {

public BooleanFieldType(
String name,
boolean isIndexed,
IndexType indexType,
boolean isStored,
boolean hasDocValues,
Boolean nullValue,
FieldValues<Boolean> scriptValues,
Map<String, String> meta,
boolean isDimension,
boolean isSyntheticSource
) {
super(name, IndexType.terms(isIndexed, hasDocValues), isStored, TextSearchInfo.SIMPLE_MATCH_ONLY, meta);
super(name, indexType, isStored, TextSearchInfo.SIMPLE_MATCH_ONLY, meta);
this.nullValue = nullValue;
this.scriptValues = scriptValues;
this.isDimension = isDimension;
this.isSyntheticSource = isSyntheticSource;
}

public BooleanFieldType(String name) {
this(name, true);
}

public BooleanFieldType(String name, boolean isIndexed) {
this(name, isIndexed, true);
this(name, IndexType.terms(true, true));
}

public BooleanFieldType(String name, boolean isIndexed, boolean hasDocValues) {
this(name, isIndexed, isIndexed, hasDocValues, false, null, Collections.emptyMap(), false, false);
public BooleanFieldType(String name, IndexType indexType) {
this(name, indexType, true, false, null, Collections.emptyMap(), false, false);
}

@Override
Expand Down Expand Up @@ -601,7 +599,11 @@ private void indexValue(DocumentParserContext context, Boolean value) {
context.doc().add(new StoredField(fieldType().name(), value ? "T" : "F"));
}
if (hasDocValues) {
context.doc().add(new SortedNumericDocValuesField(fieldType().name(), value ? 1 : 0));
if (fieldType().indexType.hasDocValuesSkipper()) {
context.doc().add(SortedNumericDocValuesField.indexedField(fieldType().name(), value ? 1 : 0));
} else {
context.doc().add(new SortedNumericDocValuesField(fieldType().name(), value ? 1 : 0));
}
} else {
context.addToFieldNames(fieldType().name());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,21 @@ public static Parameter<Boolean> indexParam(Function<FieldMapper, Boolean> initi
return Parameter.boolParam("index", false, initializer, defaultValue);
}

public static Parameter<Boolean> indexParam(
Function<FieldMapper, Boolean> initializer,
IndexSettings indexSettings,
Supplier<Boolean> isDimension
) {
return Parameter.boolParam(
"index",
false,
initializer,
() -> isDimension.get() == false
|| indexSettings.useDocValuesSkipper() == false
|| indexSettings.getIndexVersionCreated().before(IndexVersions.TIME_SERIES_DIMENSIONS_USE_SKIPPERS)
);
}

public static Parameter<Boolean> storeParam(Function<FieldMapper, Boolean> initializer, boolean defaultValue) {
return Parameter.boolParam("store", false, initializer, defaultValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public Builder(String name, ScriptCompiler scriptCompiler, IndexSettings indexSe
}
});
// We allow `time_series_dimension` parameter to be parsed, but only allow it to be `false`
this.dimension = TimeSeriesParams.dimensionParam(m -> false).addValidator(v -> {
this.dimension = TimeSeriesParams.dimensionParam(m -> false, () -> true).addValidator(v -> {
if (v) {
throw new IllegalArgumentException(
"Parameter [" + TimeSeriesParams.TIME_SERIES_DIMENSION_PARAM + "] cannot be set to geo_point"
Expand Down
24 changes: 24 additions & 0 deletions server/src/main/java/org/elasticsearch/index/mapper/IndexType.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

package org.elasticsearch.index.mapper;

import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.DocValuesSkipIndexType;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexOptions;

import java.util.Objects;

/**
Expand Down Expand Up @@ -118,6 +123,25 @@ public static IndexType terms(boolean isIndexed, boolean hasDocValues) {
return new IndexType(isIndexed, false, false, false, hasDocValues, false);
}

/**
* @return a terms-based IndexType from a lucene FieldType
*/
public static IndexType terms(FieldType fieldType) {
if (fieldType.indexOptions() == IndexOptions.NONE) {
if (fieldType.docValuesType() == DocValuesType.NONE) {
return NONE;
}
if (fieldType.docValuesSkipIndexType() == DocValuesSkipIndexType.NONE) {
return docValuesOnly();
}
return skippers();
}
if (fieldType.docValuesType() == DocValuesType.NONE) {
return terms(true, false);
}
return terms(true, true);
}

/**
* @return an IndexType with docValuesSkippers
*/
Expand Down
Loading