Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6213d87
WIP numeric fields native synthetic array support
jordan-powers Mar 7, 2025
7fe15da
Disable native synthetic source support within nested contexts
jordan-powers Mar 10, 2025
3d79319
Fix hasValue logic for case of empty array
jordan-powers Mar 10, 2025
e3961ec
Fix missing field name for all-null arrays
jordan-powers Mar 10, 2025
484dabb
Add LongOffsetDocValuesLoaderTests.java
jordan-powers Mar 10, 2025
f0d6914
Always store array offsets
jordan-powers Mar 11, 2025
188a478
Use abstract getRandomValue for testSynthesizeArrayRandom
jordan-powers Mar 11, 2025
1ea276b
Use correct fieldType in verifySyntheticArrayInObject
jordan-powers Mar 11, 2025
0656ba6
Avoid string-encoding expected values
jordan-powers Mar 11, 2025
b790084
Don't assume response will be array in NativeArrayIntegrationTestCase
jordan-powers Mar 11, 2025
2533b0b
Add LongSyntheticSourceNativeArrayIntegrationTests
jordan-powers Mar 11, 2025
578fc42
Fix x-pack build issues
jordan-powers Mar 11, 2025
7bfd82b
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 11, 2025
3d7347c
Update docs/changelog/124594.yaml
jordan-powers Mar 11, 2025
6ab26af
Revert "Always store array offsets"
jordan-powers Mar 12, 2025
c514dc4
Remove NumberType#sortableLongValue
jordan-powers Mar 12, 2025
e19c6e5
Revert "Don't assume response will be array in NativeArrayIntegration…
jordan-powers Mar 12, 2025
52e2aba
Merge remote-tracking branch 'origin/array-offset-encoding-numbers' i…
jordan-powers Mar 13, 2025
86d6c6b
Add testSynthesizeArrayRandomIgnoresMalformed
jordan-powers Mar 13, 2025
ed90189
Convert FieldLoader to FieldLoaderLayer
jordan-powers Mar 13, 2025
e08929f
Mask offsets field in roundtrip tests
jordan-powers Mar 13, 2025
5ce5bcc
Fix testSyntheticSourceKeepArrays expecting precision to be preserved
jordan-powers Mar 13, 2025
0743b31
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 13, 2025
1bd67e0
Fix UnsignedLongFieldMapperTests
jordan-powers Mar 13, 2025
f6793c1
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 13, 2025
f27a52e
Account for duplicate doc values in loader layer
jordan-powers Mar 17, 2025
d4185eb
Fix value cast in Byte number type
jordan-powers Mar 17, 2025
82031c1
Remove unused leafName from field loader layer
jordan-powers Mar 17, 2025
cd56130
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 17, 2025
4d2b559
Allow multiple documents in testSynthesizeArrayRandomIgnoresMalformed
jordan-powers Mar 17, 2025
316b94c
Add test verifying offset encoding is disabled in nested contexts
jordan-powers Mar 17, 2025
65c1bc4
Add test with duplicate values to LongOffsetDocValuesLoaderTests
jordan-powers Mar 18, 2025
a4cebf6
Fix deduplication logic in doc values loader
jordan-powers Mar 18, 2025
1cfa022
Add duplicate values to testOffsetArrayRandom
jordan-powers Mar 18, 2025
321f652
Add doc values loader tests for other numeric types
jordan-powers Mar 18, 2025
1b3472b
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 18, 2025
96534a0
Add native array integration tests for other numeric types
jordan-powers Mar 19, 2025
61831f1
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 19, 2025
4efc7fe
Remove 'Object' from type parameter
jordan-powers Mar 19, 2025
9ca9c7c
Fix native array integration tests
jordan-powers Mar 19, 2025
47f3bbc
Merge remote-tracking branch 'upstream/main' into array-offset-encodi…
jordan-powers Mar 19, 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
5 changes: 5 additions & 0 deletions docs/changelog/124594.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 124594
summary: Store arrays offsets for numeric fields natively with synthetic source
area: Mapping
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ private static Version parseUnchecked(String version) {
public static final IndexVersion SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY_KEYWORD = def(9_013_0_00, Version.LUCENE_10_1_0);
public static final IndexVersion SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY_IP = def(9_014_0_00, Version.LUCENE_10_1_0);
public static final IndexVersion ADD_RESCORE_PARAMS_TO_QUANTIZED_VECTORS = def(9_015_0_00, Version.LUCENE_10_1_0);
public static final IndexVersion SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY_NUMBER = def(9_016_0_00, Version.LUCENE_10_1_0);
/*
* STOP! READ THIS FIRST! No, really,
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,8 @@ public boolean newDynamicLongField(DocumentParserContext context, String name) t
ScriptCompiler.NONE,
context.indexSettings().getSettings(),
context.indexSettings().getIndexVersionCreated(),
context.indexSettings().getMode()
context.indexSettings().getMode(),
context.indexSettings().sourceKeepMode()
),
context
);
Expand All @@ -371,7 +372,8 @@ public boolean newDynamicDoubleField(DocumentParserContext context, String name)
ScriptCompiler.NONE,
context.indexSettings().getSettings(),
context.indexSettings().getIndexVersionCreated(),
context.indexSettings().getMode()
context.indexSettings().getMode(),
context.indexSettings().sourceKeepMode()
),
context
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ static String getOffsetsFieldName(
&& sourceKeepMode == Mapper.SourceKeepMode.ARRAYS
&& hasDocValues
&& isStored == false
&& context.isInNestedContext() == false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. Can you add a test to NativeArrayIntegrationTestCase, which checks that if leaf array field has nested parent field, then we always fall back to ignored source?

&& fieldMapperBuilder.copyTo.copyToFields().isEmpty()
&& fieldMapperBuilder.multiFieldsBuilder.hasMultiFields() == false
&& indexVersionSupportStoringArraysNatively(indexCreatedVersion, minSupportedVersionMain)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.index.fielddata.FieldDataContext;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
Expand Down Expand Up @@ -70,6 +71,7 @@
import java.io.IOException;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -80,6 +82,8 @@
import java.util.function.BiFunction;
import java.util.function.Function;

import static org.elasticsearch.index.mapper.FieldArrayContext.getOffsetsFieldName;

/** A {@link FieldMapper} for numeric types: byte, short, int, long, float and double. */
public class NumberFieldMapper extends FieldMapper {

Expand Down Expand Up @@ -127,20 +131,31 @@ public static final class Builder extends FieldMapper.DimensionBuilder {
private final IndexVersion indexCreatedVersion;

private final IndexMode indexMode;
private final SourceKeepMode indexSourceKeepMode;

public Builder(
String name,
NumberType type,
ScriptCompiler compiler,
Settings settings,
IndexVersion indexCreatedVersion,
IndexMode mode
IndexMode mode,
SourceKeepMode indexSourceKeepMode
) {
this(name, type, compiler, IGNORE_MALFORMED_SETTING.get(settings), COERCE_SETTING.get(settings), indexCreatedVersion, mode);
this(
name,
type,
compiler,
IGNORE_MALFORMED_SETTING.get(settings),
COERCE_SETTING.get(settings),
indexCreatedVersion,
mode,
indexSourceKeepMode
);
}

public static Builder docValuesOnly(String name, NumberType type, IndexVersion indexCreatedVersion) {
Builder builder = new Builder(name, type, ScriptCompiler.NONE, false, false, indexCreatedVersion, null);
Builder builder = new Builder(name, type, ScriptCompiler.NONE, false, false, indexCreatedVersion, null, null);
builder.indexed.setValue(false);
builder.dimension.setValue(false);
return builder;
Expand All @@ -153,7 +168,8 @@ public Builder(
boolean ignoreMalformedByDefault,
boolean coerceByDefault,
IndexVersion indexCreatedVersion,
IndexMode mode
IndexMode mode,
SourceKeepMode indexSourceKeepMode
) {
super(name);
this.type = type;
Expand Down Expand Up @@ -209,6 +225,8 @@ public Builder(

this.script.precludesParameters(ignoreMalformed, coerce, nullValue);
addScriptValidation(script, indexed, hasDocValues);

this.indexSourceKeepMode = indexSourceKeepMode;
}

Builder nullValue(Number number) {
Expand Down Expand Up @@ -272,7 +290,16 @@ public NumberFieldMapper build(MapperBuilderContext context) {
MappedFieldType ft = new NumberFieldType(context.buildFullName(leafName()), this, context.isSourceSynthetic());
hasScript = script.get() != null;
onScriptError = onScriptErrorParam.getValue();
return new NumberFieldMapper(leafName(), ft, builderParams(this, context), context.isSourceSynthetic(), this);
String offsetsFieldName = getOffsetsFieldName(
context,
indexSourceKeepMode,
hasDocValues.getValue(),
stored.getValue(),
this,
indexCreatedVersion,
IndexVersions.SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY_NUMBER
);
return new NumberFieldMapper(leafName(), ft, builderParams(this, context), context.isSourceSynthetic(), this, offsetsFieldName);
}
}

Expand Down Expand Up @@ -445,13 +472,8 @@ private static void validateFiniteValue(float value) {
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return new SortedNumericDocValuesSyntheticFieldLoader(fieldName, fieldSimpleName, ignoreMalformed) {
@Override
protected void writeValue(XContentBuilder b, long value) throws IOException {
b.value(HalfFloatPoint.sortableShortToHalfFloat((short) value));
}
};
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(HalfFloatPoint.sortableShortToHalfFloat((short) value));
}

@Override
Expand Down Expand Up @@ -634,13 +656,8 @@ private static void validateFiniteValue(float value) {
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return new SortedNumericDocValuesSyntheticFieldLoader(fieldName, fieldSimpleName, ignoreMalformed) {
@Override
protected void writeValue(XContentBuilder b, long value) throws IOException {
b.value(NumericUtils.sortableIntToFloat((int) value));
}
};
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(NumericUtils.sortableIntToFloat((int) value));
}

@Override
Expand Down Expand Up @@ -789,13 +806,8 @@ private static void validateParsed(double value) {
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return new SortedNumericDocValuesSyntheticFieldLoader(fieldName, fieldSimpleName, ignoreMalformed) {
@Override
protected void writeValue(XContentBuilder b, long value) throws IOException {
b.value(NumericUtils.sortableLongToDouble(value));
}
};
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(NumericUtils.sortableLongToDouble(value));
}

@Override
Expand Down Expand Up @@ -838,12 +850,12 @@ public Number parsePoint(byte[] value) {
}

@Override
public Short parse(XContentParser parser, boolean coerce) throws IOException {
public Byte parse(XContentParser parser, boolean coerce) throws IOException {
int value = parser.intValue(coerce);
if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
throw new IllegalArgumentException("Value [" + value + "] is out of range for a byte");
}
return (short) value;
return (byte) value;
}

@Override
Expand Down Expand Up @@ -912,8 +924,8 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder(
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return NumberType.syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed);
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(value);
}

@Override
Expand Down Expand Up @@ -1030,8 +1042,8 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder(
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return NumberType.syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed);
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(value);
}

@Override
Expand Down Expand Up @@ -1222,8 +1234,8 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder(
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return NumberType.syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed);
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(value);
}

@Override
Expand Down Expand Up @@ -1374,8 +1386,8 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder(
}

@Override
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed);
public void writeValue(XContentBuilder b, long value) throws IOException {
b.value(value);
}

@Override
Expand Down Expand Up @@ -1427,7 +1439,15 @@ private boolean isOutOfRange(Object value) {
this.name = name;
this.numericType = numericType;
this.parser = createTypeParserWithLegacySupport(
(n, c) -> new Builder(n, this, c.scriptCompiler(), c.getSettings(), c.indexVersionCreated(), c.getIndexSettings().getMode())
(n, c) -> new Builder(
n,
this,
c.scriptCompiler(),
c.getSettings(),
c.indexVersionCreated(),
c.getIndexSettings().getMode(),
c.getIndexSettings().sourceKeepMode()
)
);
}

Expand Down Expand Up @@ -1658,17 +1678,13 @@ public double reduceToStoredPrecision(double value) {
return ((Number) value).doubleValue();
}

abstract SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed);
abstract void writeValue(XContentBuilder builder, long longValue) throws IOException;

private static SourceLoader.SyntheticFieldLoader syntheticLongFieldLoader(
String fieldName,
String fieldSimpleName,
boolean ignoreMalformed
) {
SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) {
return new SortedNumericDocValuesSyntheticFieldLoader(fieldName, fieldSimpleName, ignoreMalformed) {
@Override
protected void writeValue(XContentBuilder b, long value) throws IOException {
b.value(value);
public void writeValue(XContentBuilder b, long value) throws IOException {
NumberType.this.writeValue(b, value);
}
};
}
Expand Down Expand Up @@ -2037,15 +2053,18 @@ public MetricType getMetricType() {
private boolean allowMultipleValues;
private final IndexVersion indexCreatedVersion;
private final boolean isSyntheticSource;
private final String offsetsFieldName;

private final IndexMode indexMode;
private final SourceKeepMode indexSourceKeepMode;

private NumberFieldMapper(
String simpleName,
MappedFieldType mappedFieldType,
BuilderParams builderParams,
boolean isSyntheticSource,
Builder builder
Builder builder,
String offsetsFieldName
) {
super(simpleName, mappedFieldType, builderParams);
this.type = builder.type;
Expand All @@ -2066,6 +2085,8 @@ private NumberFieldMapper(
this.indexCreatedVersion = builder.indexCreatedVersion;
this.isSyntheticSource = isSyntheticSource;
this.indexMode = builder.indexMode;
this.offsetsFieldName = offsetsFieldName;
this.indexSourceKeepMode = builder.indexSourceKeepMode;
}

boolean coerce() {
Expand All @@ -2082,6 +2103,11 @@ public NumberFieldType fieldType() {
return (NumberFieldType) super.fieldType();
}

@Override
public String getOffsetFieldName() {
return offsetsFieldName;
}

public NumberType type() {
return type;
}
Expand Down Expand Up @@ -2110,7 +2136,17 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
}
if (value != null) {
indexValue(context, value);
} else {
value = fieldType().nullValue;
}
if (offsetsFieldName != null && context.isImmediateParentAnArray() && context.canAddIgnoredField()) {
if (value != null) {
context.getOffSetContext().recordOffset(offsetsFieldName, (Comparable<?>) value);
} else {
context.getOffSetContext().recordNull(offsetsFieldName);
}
}

}

/**
Expand Down Expand Up @@ -2171,11 +2207,16 @@ protected void indexScriptValues(

@Override
public FieldMapper.Builder getMergeBuilder() {
return new Builder(leafName(), type, scriptCompiler, ignoreMalformedByDefault, coerceByDefault, indexCreatedVersion, indexMode)
.dimension(dimension)
.metric(metricType)
.allowMultipleValues(allowMultipleValues)
.init(this);
return new Builder(
leafName(),
type,
scriptCompiler,
ignoreMalformedByDefault,
coerceByDefault,
indexCreatedVersion,
indexMode,
indexSourceKeepMode
).dimension(dimension).metric(metricType).allowMultipleValues(allowMultipleValues).init(this);
}

@Override
Expand All @@ -2187,10 +2228,23 @@ public void doValidate(MappingLookup lookup) {
}
}

private SourceLoader.SyntheticFieldLoader docValuesSyntheticFieldLoader() {
if (offsetsFieldName != null) {
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>();
layers.add(new SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(fullPath(), offsetsFieldName, type::writeValue));
if (ignoreMalformed.value()) {
layers.add(new CompositeSyntheticFieldLoader.MalformedValuesLayer(fullPath()));
}
return new CompositeSyntheticFieldLoader(leafName(), fullPath(), layers);
} else {
return type.syntheticFieldLoader(fullPath(), leafName(), ignoreMalformed.value());
}
}

@Override
protected SyntheticSourceSupport syntheticSourceSupport() {
if (hasDocValues) {
return new SyntheticSourceSupport.Native(() -> type.syntheticFieldLoader(fullPath(), leafName(), ignoreMalformed.value()));
return new SyntheticSourceSupport.Native(this::docValuesSyntheticFieldLoader);
}

return super.syntheticSourceSupport();
Expand Down
Loading