Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -19,6 +19,8 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.index.IndexVersions;
import org.elasticsearch.index.fielddata.FieldData;
import org.elasticsearch.index.fielddata.FieldDataContext;
import org.elasticsearch.index.fielddata.IndexFieldData;
Expand All @@ -32,6 +34,7 @@
import org.elasticsearch.index.mapper.BlockDocValuesReader;
import org.elasticsearch.index.mapper.BlockLoader;
import org.elasticsearch.index.mapper.BlockSourceReader;
import org.elasticsearch.index.mapper.CompositeSyntheticFieldLoader;
import org.elasticsearch.index.mapper.DocumentParserContext;
import org.elasticsearch.index.mapper.FallbackSyntheticSourceBlockLoader;
import org.elasticsearch.index.mapper.FieldMapper;
Expand All @@ -40,6 +43,8 @@
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.index.mapper.SimpleMappedFieldType;
import org.elasticsearch.index.mapper.SortedNumericDocValuesSyntheticFieldLoader;
import org.elasticsearch.index.mapper.SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer;
import org.elasticsearch.index.mapper.SourceLoader;
import org.elasticsearch.index.mapper.SourceValueFetcher;
import org.elasticsearch.index.mapper.TextSearchInfo;
import org.elasticsearch.index.mapper.TimeSeriesParams;
Expand Down Expand Up @@ -67,6 +72,8 @@
import java.util.Objects;
import java.util.Set;

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

/** A {@link FieldMapper} for scaled floats. Values are internally multiplied
* by a scaling factor and rounded to the closest long. */
public class ScaledFloatFieldMapper extends FieldMapper {
Expand Down Expand Up @@ -125,12 +132,34 @@ public static class Builder extends FieldMapper.Builder {
private final Parameter<TimeSeriesParams.MetricType> metric;

private final IndexMode indexMode;
private final IndexVersion indexCreatedVersion;
private final SourceKeepMode indexSourceKeepMode;

public Builder(String name, Settings settings, IndexMode indexMode) {
this(name, IGNORE_MALFORMED_SETTING.get(settings), COERCE_SETTING.get(settings), indexMode);
public Builder(
String name,
Settings settings,
IndexMode indexMode,
IndexVersion indexCreatedVersion,
SourceKeepMode indexSourceKeepMode
) {
this(
name,
IGNORE_MALFORMED_SETTING.get(settings),
COERCE_SETTING.get(settings),
indexMode,
indexCreatedVersion,
indexSourceKeepMode
);
}

public Builder(String name, boolean ignoreMalformedByDefault, boolean coerceByDefault, IndexMode indexMode) {
public Builder(
String name,
boolean ignoreMalformedByDefault,
boolean coerceByDefault,
IndexMode indexMode,
IndexVersion indexCreatedVersion,
SourceKeepMode indexSourceKeepMode
) {
super(name);
this.ignoreMalformed = Parameter.explicitBoolParam(
"ignore_malformed",
Expand Down Expand Up @@ -159,6 +188,8 @@ public Builder(String name, boolean ignoreMalformedByDefault, boolean coerceByDe
);
}
});
this.indexCreatedVersion = indexCreatedVersion;
this.indexSourceKeepMode = indexSourceKeepMode;
}

Builder scalingFactor(double scalingFactor) {
Expand Down Expand Up @@ -200,11 +231,35 @@ public ScaledFloatFieldMapper build(MapperBuilderContext context) {
coerce.getValue().value(),
context.isSourceSynthetic()
);
return new ScaledFloatFieldMapper(leafName(), type, builderParams(this, context), context.isSourceSynthetic(), this);
String offsetsFieldName = getOffsetsFieldName(
context,
indexSourceKeepMode,
hasDocValues.getValue(),
stored.getValue(),
this,
indexCreatedVersion,
IndexVersions.SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY
);
return new ScaledFloatFieldMapper(
leafName(),
type,
builderParams(this, context),
context.isSourceSynthetic(),
this,
offsetsFieldName
);
}
}

public static final TypeParser PARSER = new TypeParser((n, c) -> new Builder(n, c.getSettings(), c.getIndexSettings().getMode()));
public static final TypeParser PARSER = new TypeParser(
(n, c) -> new Builder(
n,
c.getSettings(),
c.getIndexSettings().getMode(),
c.indexVersionCreated(),
c.getIndexSettings().sourceKeepMode()
)
);

public static final class ScaledFloatFieldType extends SimpleMappedFieldType {

Expand Down Expand Up @@ -532,12 +587,17 @@ public String toString() {
private final TimeSeriesParams.MetricType metricType;
private final IndexMode indexMode;

private final IndexVersion indexCreatedVersion;
private final String offsetsFieldName;
private final SourceKeepMode indexSourceKeepMode;

private ScaledFloatFieldMapper(
String simpleName,
ScaledFloatFieldType mappedFieldType,
BuilderParams builderParams,
boolean isSourceSynthetic,
Builder builder
Builder builder,
String offsetsFieldName
) {
super(simpleName, mappedFieldType, builderParams);
this.isSourceSynthetic = isSourceSynthetic;
Expand All @@ -552,6 +612,9 @@ private ScaledFloatFieldMapper(
this.coerceByDefault = builder.coerce.getDefaultValue().value();
this.metricType = builder.metric.getValue();
this.indexMode = builder.indexMode;
this.indexCreatedVersion = builder.indexCreatedVersion;
this.offsetsFieldName = offsetsFieldName;
this.indexSourceKeepMode = builder.indexSourceKeepMode;
}

boolean coerce() {
Expand All @@ -563,6 +626,11 @@ public boolean ignoreMalformed() {
return ignoreMalformed.value();
}

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

@Override
public ScaledFloatFieldType fieldType() {
return (ScaledFloatFieldType) super.fieldType();
Expand All @@ -575,7 +643,9 @@ protected String contentType() {

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

@Override
Expand Down Expand Up @@ -605,11 +675,16 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
value = numericValue;
}

boolean shouldStoreOffsets = offsetsFieldName != null && context.isImmediateParentAnArray() && context.canAddIgnoredField();

if (value == null) {
value = nullValue;
}

if (value == null) {
if (shouldStoreOffsets) {
context.getOffSetContext().recordNull(offsetsFieldName);
}
return;
}

Expand All @@ -635,6 +710,10 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio

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

if (shouldStoreOffsets) {
context.getOffSetContext().recordOffset(offsetsFieldName, scaledValue);
}

if (hasDocValues == false && (indexed || stored)) {
context.addToFieldNames(fieldType().name());
}
Expand Down Expand Up @@ -776,17 +855,34 @@ public int docValueCount() {
}
}

private SourceLoader.SyntheticFieldLoader docValuesSyntheticFieldLoader() {
if (offsetsFieldName != null) {
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>(2);
layers.add(
new SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(
fullPath(),
offsetsFieldName,
(b, value) -> b.value(decodeForSyntheticSource(value, scalingFactor))
)
);
if (ignoreMalformed.value()) {
layers.add(new CompositeSyntheticFieldLoader.MalformedValuesLayer(fullPath()));
}
return new CompositeSyntheticFieldLoader(leafName(), fullPath(), layers);
} else {
return new SortedNumericDocValuesSyntheticFieldLoader(fullPath(), leafName(), ignoreMalformed.value()) {
@Override
protected void writeValue(XContentBuilder b, long value) throws IOException {
b.value(decodeForSyntheticSource(value, scalingFactor));
}
};
}
}

@Override
protected SyntheticSourceSupport syntheticSourceSupport() {
if (hasDocValues) {
return new SyntheticSourceSupport.Native(
() -> new SortedNumericDocValuesSyntheticFieldLoader(fullPath(), leafName(), ignoreMalformed.value()) {
@Override
protected void writeValue(XContentBuilder b, long value) throws IOException {
b.value(decodeForSyntheticSource(value, scalingFactor));
}
}
);
return new SyntheticSourceSupport.Native(this::docValuesSyntheticFieldLoader);
}

return super.syntheticSourceSupport();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.DocumentParsingException;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.NumberFieldMapperTests;
Expand Down Expand Up @@ -365,6 +366,24 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed)
return new ScaledFloatSyntheticSourceSupport(ignoreMalformed);
}

@Override
protected SyntheticSourceSupport syntheticSourceSupportForKeepTests(boolean ignoreMalformed, Mapper.SourceKeepMode sourceKeepMode) {
return new ScaledFloatSyntheticSourceSupport(ignoreMalformed) {
@Override
public SyntheticSourceExample example(int maxVals) {
var example = super.example(maxVals);
// Need the expectedForSyntheticSource as inputValue since MapperTestCase#testSyntheticSourceKeepArrays
// uses the inputValue as both the input and expected.
return new SyntheticSourceExample(
example.expectedForSyntheticSource(),
example.expectedForSyntheticSource(),
example.expectedForBlockLoader(),
example.mapping()
);
}
};
}

private static class ScaledFloatSyntheticSourceSupport implements SyntheticSourceSupport {
private final boolean ignoreMalformedEnabled;
private final double scalingFactor = randomDoubleBetween(0, Double.MAX_VALUE, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,14 @@ public void testFieldData() throws IOException {
}

public void testFetchSourceValue() throws IOException {
MappedFieldType mapper = new ScaledFloatFieldMapper.Builder("field", false, false, null).scalingFactor(100)
MappedFieldType mapper = new ScaledFloatFieldMapper.Builder("field", false, false, null, null, null).scalingFactor(100)
.build(MapperBuilderContext.root(false, false))
.fieldType();
assertEquals(List.of(3.14), fetchSourceValue(mapper, 3.1415926));
assertEquals(List.of(3.14), fetchSourceValue(mapper, "3.1415"));
assertEquals(List.of(), fetchSourceValue(mapper, ""));

MappedFieldType nullValueMapper = new ScaledFloatFieldMapper.Builder("field", false, false, null).scalingFactor(100)
MappedFieldType nullValueMapper = new ScaledFloatFieldMapper.Builder("field", false, false, null, null, null).scalingFactor(100)
.nullValue(2.71)
.build(MapperBuilderContext.root(false, false))
.fieldType();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.index.mapper.extras;

import org.elasticsearch.index.mapper.OffsetDocValuesLoaderTestCase;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Collection;

import static java.util.Collections.singletonList;

public class ScaledFloatOffsetDocValuesLoaderTests extends OffsetDocValuesLoaderTestCase {
private static final double TEST_SCALING_FACTOR = 10.0;

@Override
protected Collection<? extends Plugin> getPlugins() {
return singletonList(new MapperExtrasPlugin());
}

@Override
protected void minimalMapping(XContentBuilder b) throws IOException {
b.field("type", "scaled_float").field("scaling_factor", TEST_SCALING_FACTOR);
}

public void testOffsetArray() throws Exception {
verifyOffsets("{\"field\":[1.0,10.0,100.0,0.1,10.0,1.0,0.1,100.0]}");
verifyOffsets("{\"field\":[10.0,null,1.0,null,5.0,null,null,6.3,1.5]}");
}

@Override
protected String getFieldTypeName() {
fail("Should not be called because minimalMapping is overridden");
return null;
}

@Override
protected Double randomValue() {
return randomLong() / TEST_SCALING_FACTOR;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.index.mapper.extras;

import com.carrotsearch.randomizedtesting.generators.RandomStrings;

import org.elasticsearch.index.mapper.NativeArrayIntegrationTestCase;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Collection;

import static java.util.Collections.singletonList;

public class ScaledFloatSyntheticSourceNativeArrayIntegrationTests extends NativeArrayIntegrationTestCase {
private static final double TEST_SCALING_FACTOR = 10.0;

@Override
protected Collection<Class<? extends Plugin>> getPlugins() {
return singletonList(MapperExtrasPlugin.class);
}

@Override
protected void minimalMapping(XContentBuilder b) throws IOException {
b.field("type", "scaled_float").field("scaling_factor", TEST_SCALING_FACTOR);
}

@Override
protected String getFieldTypeName() {
fail("Should not be called because minimalMapping is overridden");
return null;
}

@Override
protected Object getRandomValue() {
return randomLong() / TEST_SCALING_FACTOR;
}

@Override
protected Object getMalformedValue() {
return randomBoolean() ? RandomStrings.randomAsciiOfLength(random(), 8) : Double.POSITIVE_INFINITY;
}
}
Loading