Skip to content

Commit 0acec52

Browse files
authored
Use FallbackSyntheticSourceBlockLoader for unsigned_long and scaled_float fields (#122637) (#122877)
1 parent e469f84 commit 0acec52

File tree

17 files changed

+296
-29
lines changed

17 files changed

+296
-29
lines changed

docs/changelog/122637.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 122637
2+
summary: Use `FallbackSyntheticSourceBlockLoader` for `unsigned_long` and `scaled_float`
3+
fields
4+
area: Mapping
5+
type: enhancement
6+
issues: []

modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.index.mapper.BlockLoader;
3434
import org.elasticsearch.index.mapper.BlockSourceReader;
3535
import org.elasticsearch.index.mapper.DocumentParserContext;
36+
import org.elasticsearch.index.mapper.FallbackSyntheticSourceBlockLoader;
3637
import org.elasticsearch.index.mapper.FieldMapper;
3738
import org.elasticsearch.index.mapper.IgnoreMalformedStoredValues;
3839
import org.elasticsearch.index.mapper.MapperBuilderContext;
@@ -195,7 +196,9 @@ public ScaledFloatFieldMapper build(MapperBuilderContext context) {
195196
scalingFactor.getValue(),
196197
nullValue.getValue(),
197198
metric.getValue(),
198-
indexMode
199+
indexMode,
200+
coerce.getValue().value(),
201+
context.isSourceSynthetic()
199202
);
200203
return new ScaledFloatFieldMapper(leafName(), type, builderParams(this, context), context.isSourceSynthetic(), this);
201204
}
@@ -209,6 +212,8 @@ public static final class ScaledFloatFieldType extends SimpleMappedFieldType {
209212
private final Double nullValue;
210213
private final TimeSeriesParams.MetricType metricType;
211214
private final IndexMode indexMode;
215+
private final boolean coerce;
216+
private final boolean isSyntheticSource;
212217

213218
public ScaledFloatFieldType(
214219
String name,
@@ -219,21 +224,25 @@ public ScaledFloatFieldType(
219224
double scalingFactor,
220225
Double nullValue,
221226
TimeSeriesParams.MetricType metricType,
222-
IndexMode indexMode
227+
IndexMode indexMode,
228+
boolean coerce,
229+
boolean isSyntheticSource
223230
) {
224231
super(name, indexed, stored, hasDocValues, TextSearchInfo.SIMPLE_MATCH_WITHOUT_TERMS, meta);
225232
this.scalingFactor = scalingFactor;
226233
this.nullValue = nullValue;
227234
this.metricType = metricType;
228235
this.indexMode = indexMode;
236+
this.coerce = coerce;
237+
this.isSyntheticSource = isSyntheticSource;
229238
}
230239

231240
public ScaledFloatFieldType(String name, double scalingFactor) {
232241
this(name, scalingFactor, true);
233242
}
234243

235244
public ScaledFloatFieldType(String name, double scalingFactor, boolean indexed) {
236-
this(name, indexed, false, true, Collections.emptyMap(), scalingFactor, null, null, null);
245+
this(name, indexed, false, true, Collections.emptyMap(), scalingFactor, null, null, null, false, false);
237246
}
238247

239248
public double getScalingFactor() {
@@ -315,13 +324,73 @@ public BlockLoader blockLoader(BlockLoaderContext blContext) {
315324
double scalingFactorInverse = 1d / scalingFactor;
316325
return new BlockDocValuesReader.DoublesBlockLoader(name(), l -> l * scalingFactorInverse);
317326
}
327+
if (isSyntheticSource) {
328+
return new FallbackSyntheticSourceBlockLoader(fallbackSyntheticSourceBlockLoaderReader(), name()) {
329+
@Override
330+
public Builder builder(BlockFactory factory, int expectedCount) {
331+
return factory.doubles(expectedCount);
332+
}
333+
};
334+
}
335+
318336
ValueFetcher valueFetcher = sourceValueFetcher(blContext.sourcePaths(name()));
319337
BlockSourceReader.LeafIteratorLookup lookup = isStored() || isIndexed()
320338
? BlockSourceReader.lookupFromFieldNames(blContext.fieldNames(), name())
321339
: BlockSourceReader.lookupMatchingAll();
322340
return new BlockSourceReader.DoublesBlockLoader(valueFetcher, lookup);
323341
}
324342

343+
private FallbackSyntheticSourceBlockLoader.Reader<?> fallbackSyntheticSourceBlockLoaderReader() {
344+
var nullValueAdjusted = nullValue != null ? adjustSourceValue(nullValue, scalingFactor) : null;
345+
346+
return new FallbackSyntheticSourceBlockLoader.ReaderWithNullValueSupport<>(nullValue) {
347+
@Override
348+
public void convertValue(Object value, List<Double> accumulator) {
349+
if (coerce && value.equals("")) {
350+
if (nullValueAdjusted != null) {
351+
accumulator.add(nullValueAdjusted);
352+
}
353+
}
354+
355+
try {
356+
// Convert to doc_values format
357+
var converted = adjustSourceValue(NumberFieldMapper.NumberType.objectToDouble(value), scalingFactor);
358+
accumulator.add(converted);
359+
} catch (Exception e) {
360+
// Malformed value, skip it
361+
}
362+
}
363+
364+
@Override
365+
protected void parseNonNullValue(XContentParser parser, List<Double> accumulator) throws IOException {
366+
// Aligned with implementation of `parseCreateField(XContentParser)`
367+
if (coerce && parser.currentToken() == XContentParser.Token.VALUE_STRING && parser.textLength() == 0) {
368+
if (nullValueAdjusted != null) {
369+
accumulator.add(nullValueAdjusted);
370+
}
371+
}
372+
373+
try {
374+
double value = parser.doubleValue(coerce);
375+
// Convert to doc_values format
376+
var converted = adjustSourceValue(value, scalingFactor);
377+
accumulator.add(converted);
378+
} catch (Exception e) {
379+
// Malformed value, skip it
380+
}
381+
}
382+
383+
@Override
384+
public void writeToBlock(List<Double> values, BlockLoader.Builder blockBuilder) {
385+
var longBuilder = (BlockLoader.DoubleBuilder) blockBuilder;
386+
387+
for (var value : values) {
388+
longBuilder.appendDouble(value);
389+
}
390+
}
391+
};
392+
}
393+
325394
@Override
326395
public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) {
327396
FielddataOperation operation = fieldDataContext.fielddataOperation();
@@ -386,12 +455,16 @@ protected Double parseSourceValue(Object value) {
386455
doubleValue = NumberFieldMapper.NumberType.objectToDouble(value);
387456
}
388457

389-
double factor = getScalingFactor();
390-
return Math.round(doubleValue * factor) / factor;
458+
return adjustSourceValue(doubleValue, getScalingFactor());
391459
}
392460
};
393461
}
394462

463+
// Adjusts precision of a double value so that it looks like it came from doc_values.
464+
private static Double adjustSourceValue(double value, double scalingFactor) {
465+
return Math.round(value * scalingFactor) / scalingFactor;
466+
}
467+
395468
@Override
396469
public Object valueForDisplay(Object value) {
397470
if (value == null) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index.mapper.extras;
11+
12+
import org.elasticsearch.index.mapper.NumberFieldBlockLoaderTestCase;
13+
import org.elasticsearch.logsdb.datageneration.FieldType;
14+
import org.elasticsearch.plugins.Plugin;
15+
16+
import java.util.Collection;
17+
import java.util.List;
18+
import java.util.Map;
19+
20+
public class ScaledFloatFieldBlockLoaderTests extends NumberFieldBlockLoaderTestCase<Double> {
21+
public ScaledFloatFieldBlockLoaderTests() {
22+
super(FieldType.SCALED_FLOAT);
23+
}
24+
25+
@Override
26+
protected Double convert(Number value, Map<String, Object> fieldMapping) {
27+
var scalingFactor = ((Number) fieldMapping.get("scaling_factor")).doubleValue();
28+
29+
var docValues = (boolean) fieldMapping.getOrDefault("doc_values", false);
30+
31+
// There is a slight inconsistency between values that are read from doc_values and from source.
32+
// Due to how precision reduction is applied to source values so that they are consistent with doc_values.
33+
// See #122547.
34+
if (docValues) {
35+
var reverseScalingFactor = 1d / scalingFactor;
36+
return Math.round(value.doubleValue() * scalingFactor) * reverseScalingFactor;
37+
}
38+
39+
// Adjust values coming from source to the way they are stored in doc_values.
40+
// See mapper implementation.
41+
return Math.round(value.doubleValue() * scalingFactor) / scalingFactor;
42+
}
43+
44+
@Override
45+
protected Collection<? extends Plugin> getPlugins() {
46+
return List.of(new MapperExtrasPlugin());
47+
}
48+
}

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldTypeTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ public void testRangeQuery() throws IOException {
9595
0.1 + randomDouble() * 100,
9696
null,
9797
null,
98-
null
98+
null,
99+
false,
100+
false
99101
);
100102
Directory dir = newDirectory();
101103
IndexWriter w = new IndexWriter(dir, new IndexWriterConfig(null));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,6 @@ public void parse(XContentParser parser, List<T> accumulator) throws IOException
293293
parseNonNullValue(parser, accumulator);
294294
}
295295

296-
abstract void parseNonNullValue(XContentParser parser, List<T> accumulator) throws IOException;
296+
protected abstract void parseNonNullValue(XContentParser parser, List<T> accumulator) throws IOException;
297297
}
298298
}

server/src/test/java/org/elasticsearch/index/mapper/blockloader/ByteFieldBlockLoaderTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@
99

1010
package org.elasticsearch.index.mapper.blockloader;
1111

12+
import org.elasticsearch.index.mapper.NumberFieldBlockLoaderTestCase;
1213
import org.elasticsearch.logsdb.datageneration.FieldType;
1314

15+
import java.util.Map;
16+
1417
public class ByteFieldBlockLoaderTests extends NumberFieldBlockLoaderTestCase<Integer> {
1518
public ByteFieldBlockLoaderTests() {
1619
super(FieldType.BYTE);
1720
}
1821

1922
@Override
20-
protected Integer convert(Number value) {
23+
protected Integer convert(Number value, Map<String, Object> fieldMapping) {
2124
// All values that fit into int are represented as ints
2225
return value.intValue();
2326
}

server/src/test/java/org/elasticsearch/index/mapper/blockloader/DoubleFieldBlockLoaderTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@
99

1010
package org.elasticsearch.index.mapper.blockloader;
1111

12+
import org.elasticsearch.index.mapper.NumberFieldBlockLoaderTestCase;
1213
import org.elasticsearch.logsdb.datageneration.FieldType;
1314

15+
import java.util.Map;
16+
1417
public class DoubleFieldBlockLoaderTests extends NumberFieldBlockLoaderTestCase<Double> {
1518
public DoubleFieldBlockLoaderTests() {
1619
super(FieldType.DOUBLE);
1720
}
1821

1922
@Override
20-
protected Double convert(Number value) {
23+
protected Double convert(Number value, Map<String, Object> fieldMapping) {
2124
return value.doubleValue();
2225
}
2326
}

server/src/test/java/org/elasticsearch/index/mapper/blockloader/FloatFieldBlockLoaderTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@
99

1010
package org.elasticsearch.index.mapper.blockloader;
1111

12+
import org.elasticsearch.index.mapper.NumberFieldBlockLoaderTestCase;
1213
import org.elasticsearch.logsdb.datageneration.FieldType;
1314

15+
import java.util.Map;
16+
1417
public class FloatFieldBlockLoaderTests extends NumberFieldBlockLoaderTestCase<Double> {
1518
public FloatFieldBlockLoaderTests() {
1619
super(FieldType.FLOAT);
1720
}
1821

1922
@Override
20-
protected Double convert(Number value) {
23+
protected Double convert(Number value, Map<String, Object> fieldMapping) {
2124
// All float values are represented as double
2225
return value.doubleValue();
2326
}

server/src/test/java/org/elasticsearch/index/mapper/blockloader/HalfFloatFieldBlockLoaderTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@
1010
package org.elasticsearch.index.mapper.blockloader;
1111

1212
import org.apache.lucene.sandbox.document.HalfFloatPoint;
13+
import org.elasticsearch.index.mapper.NumberFieldBlockLoaderTestCase;
1314
import org.elasticsearch.logsdb.datageneration.FieldType;
1415

16+
import java.util.Map;
17+
1518
public class HalfFloatFieldBlockLoaderTests extends NumberFieldBlockLoaderTestCase<Double> {
1619
public HalfFloatFieldBlockLoaderTests() {
1720
super(FieldType.HALF_FLOAT);
1821
}
1922

2023
@Override
21-
protected Double convert(Number value) {
24+
protected Double convert(Number value, Map<String, Object> fieldMapping) {
2225
// All float values are represented as double
2326
return (double) HalfFloatPoint.sortableShortToHalfFloat(HalfFloatPoint.halfFloatToSortableShort(value.floatValue()));
2427
}

server/src/test/java/org/elasticsearch/index/mapper/blockloader/IntegerFieldBlockLoaderTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@
99

1010
package org.elasticsearch.index.mapper.blockloader;
1111

12+
import org.elasticsearch.index.mapper.NumberFieldBlockLoaderTestCase;
1213
import org.elasticsearch.logsdb.datageneration.FieldType;
1314

15+
import java.util.Map;
16+
1417
public class IntegerFieldBlockLoaderTests extends NumberFieldBlockLoaderTestCase<Integer> {
1518
public IntegerFieldBlockLoaderTests() {
1619
super(FieldType.INTEGER);
1720
}
1821

1922
@Override
20-
protected Integer convert(Number value) {
23+
protected Integer convert(Number value, Map<String, Object> fieldMapping) {
2124
return value.intValue();
2225
}
2326
}

0 commit comments

Comments
 (0)