Skip to content

Commit dfe9cf6

Browse files
Store arrays offsets for unsigned long fields natively with synthetic source (elastic#125709) (elastic#125746)
This patch builds on the work in elastic#113757, elastic#122999, elastic#124594, and elastic#125529 to natively store array offsets for unsigned long fields instead of falling back to ignored source when synthetic_source_keep: arrays. (cherry picked from commit 689eaf2) # Conflicts: # server/src/main/java/org/elasticsearch/index/IndexVersions.java # x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java
1 parent c220dc5 commit dfe9cf6

File tree

14 files changed

+229
-48
lines changed

14 files changed

+229
-48
lines changed

docs/changelog/125709.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 125709
2+
summary: Store arrays offsets for unsigned long fields natively with synthetic source
3+
area: Mapping
4+
type: enhancement
5+
issues: []

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ protected String contentType() {
665665

666666
private SourceLoader.SyntheticFieldLoader docValuesSyntheticFieldLoader() {
667667
if (offsetsFieldName != null) {
668-
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>();
668+
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>(2);
669669
layers.add(
670670
new SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(
671671
fullPath(),

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ public class FieldArrayContext {
2727
private static final String OFFSETS_FIELD_NAME_SUFFIX = ".offsets";
2828
private final Map<String, Offsets> offsetsPerField = new HashMap<>();
2929

30-
void recordOffset(String field, Comparable<?> value) {
30+
public void recordOffset(String field, Comparable<?> value) {
3131
Offsets arrayOffsets = offsetsPerField.computeIfAbsent(field, k -> new Offsets());
3232
int nextOffset = arrayOffsets.currentOffset++;
3333
var offsets = arrayOffsets.valueToOffsets.computeIfAbsent(value, s -> new ArrayList<>(2));
3434
offsets.add(nextOffset);
3535
}
3636

37-
void recordNull(String field) {
37+
public void recordNull(String field) {
3838
Offsets arrayOffsets = offsetsPerField.computeIfAbsent(field, k -> new Offsets());
3939
int nextOffset = arrayOffsets.currentOffset++;
4040
arrayOffsets.nullValueOffsets.add(nextOffset);
@@ -82,7 +82,7 @@ static int[] parseOffsetArray(StreamInput in) throws IOException {
8282
return offsetToOrd;
8383
}
8484

85-
static String getOffsetsFieldName(
85+
public static String getOffsetsFieldName(
8686
MapperBuilderContext context,
8787
Mapper.SourceKeepMode indexSourceKeepMode,
8888
boolean hasDocValues,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ public void doValidate(MappingLookup lookup) {
646646
protected SyntheticSourceSupport syntheticSourceSupport() {
647647
if (hasDocValues) {
648648
return new SyntheticSourceSupport.Native(() -> {
649-
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>();
649+
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>(2);
650650
if (offsetsFieldName != null) {
651651
layers.add(
652652
new SortedSetWithOffsetsDocValuesSyntheticFieldLoaderLayer(fullPath(), offsetsFieldName, IpFieldMapper::convert)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ protected SyntheticSourceSupport syntheticSourceSupport() {
11541154
public SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fullFieldName, String leafFieldName) {
11551155
assert fieldType.stored() || hasDocValues;
11561156

1157-
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>();
1157+
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>(2);
11581158
if (fieldType.stored()) {
11591159
layers.add(new CompositeSyntheticFieldLoader.StoredFieldLayer(fullPath()) {
11601160
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2275,7 +2275,7 @@ public void doValidate(MappingLookup lookup) {
22752275

22762276
private SourceLoader.SyntheticFieldLoader docValuesSyntheticFieldLoader() {
22772277
if (offsetsFieldName != null) {
2278-
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>();
2278+
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>(2);
22792279
layers.add(new SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(fullPath(), offsetsFieldName, type::writeValue));
22802280
if (ignoreMalformed.value()) {
22812281
layers.add(new CompositeSyntheticFieldLoader.MalformedValuesLayer(fullPath()));

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
import java.io.IOException;
2020

21-
class SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer implements CompositeSyntheticFieldLoader.DocValuesLayer {
21+
public class SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer implements CompositeSyntheticFieldLoader.DocValuesLayer {
2222
@FunctionalInterface
23-
interface NumericValueWriter {
23+
public interface NumericValueWriter {
2424
void writeLongValue(XContentBuilder b, long value) throws IOException;
2525
}
2626

@@ -29,7 +29,11 @@ interface NumericValueWriter {
2929
private final NumericValueWriter valueWriter;
3030
private NumericDocValuesWithOffsetsLoader docValuesLoader;
3131

32-
SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(String fullPath, String offsetsFieldName, NumericValueWriter valueWriter) {
32+
public SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(
33+
String fullPath,
34+
String offsetsFieldName,
35+
NumericValueWriter valueWriter
36+
) {
3337
this.fullPath = fullPath;
3438
this.offsetsFieldName = offsetsFieldName;
3539
this.valueWriter = valueWriter;

x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java

Lines changed: 118 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
import org.elasticsearch.common.Explicit;
2121
import org.elasticsearch.common.settings.Settings;
2222
import org.elasticsearch.index.IndexMode;
23+
import org.elasticsearch.index.IndexVersion;
24+
import org.elasticsearch.index.IndexVersions;
2325
import org.elasticsearch.index.fielddata.FieldDataContext;
2426
import org.elasticsearch.index.fielddata.IndexFieldData;
2527
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
2628
import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
2729
import org.elasticsearch.index.mapper.BlockDocValuesReader;
2830
import org.elasticsearch.index.mapper.BlockLoader;
2931
import org.elasticsearch.index.mapper.BlockSourceReader;
32+
import org.elasticsearch.index.mapper.CompositeSyntheticFieldLoader;
3033
import org.elasticsearch.index.mapper.DocumentParserContext;
3134
import org.elasticsearch.index.mapper.FallbackSyntheticSourceBlockLoader;
3235
import org.elasticsearch.index.mapper.FieldMapper;
@@ -37,6 +40,8 @@
3740
import org.elasticsearch.index.mapper.MappingLookup;
3841
import org.elasticsearch.index.mapper.SimpleMappedFieldType;
3942
import org.elasticsearch.index.mapper.SortedNumericDocValuesSyntheticFieldLoader;
43+
import org.elasticsearch.index.mapper.SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer;
44+
import org.elasticsearch.index.mapper.SourceLoader;
4045
import org.elasticsearch.index.mapper.SourceValueFetcher;
4146
import org.elasticsearch.index.mapper.TextSearchInfo;
4247
import org.elasticsearch.index.mapper.TimeSeriesParams;
@@ -64,6 +69,7 @@
6469
import java.util.Set;
6570
import java.util.function.Function;
6671

72+
import static org.elasticsearch.index.mapper.FieldArrayContext.getOffsetsFieldName;
6773
import static org.elasticsearch.xpack.unsignedlong.UnsignedLongLeafFieldData.convertUnsignedLongToDouble;
6874

6975
public class UnsignedLongFieldMapper extends FieldMapper {
@@ -97,13 +103,27 @@ public static final class Builder extends FieldMapper.DimensionBuilder {
97103
*/
98104
private final Parameter<MetricType> metric;
99105

106+
private final IndexVersion indexCreatedVersion;
100107
private final IndexMode indexMode;
108+
private final SourceKeepMode indexSourceKeepMode;
101109

102-
public Builder(String name, Settings settings, IndexMode mode) {
103-
this(name, IGNORE_MALFORMED_SETTING.get(settings), mode);
110+
public Builder(
111+
String name,
112+
Settings settings,
113+
IndexVersion indexCreatedVersion,
114+
IndexMode mode,
115+
SourceKeepMode indexSourceKeepMode
116+
) {
117+
this(name, IGNORE_MALFORMED_SETTING.get(settings), indexCreatedVersion, mode, indexSourceKeepMode);
104118
}
105119

106-
public Builder(String name, boolean ignoreMalformedByDefault, IndexMode mode) {
120+
public Builder(
121+
String name,
122+
boolean ignoreMalformedByDefault,
123+
IndexVersion indexCreatedVersion,
124+
IndexMode mode,
125+
SourceKeepMode indexSourceKeepMode
126+
) {
107127
super(name);
108128
this.ignoreMalformed = Parameter.explicitBoolParam(
109129
"ignore_malformed",
@@ -150,6 +170,9 @@ public Builder(String name, boolean ignoreMalformedByDefault, IndexMode mode) {
150170
);
151171
}
152172
}).precludesParameters(dimension);
173+
174+
this.indexCreatedVersion = indexCreatedVersion;
175+
this.indexSourceKeepMode = indexSourceKeepMode;
153176
}
154177

155178
private String parseNullValueAsString(Object o) {
@@ -211,11 +234,35 @@ public UnsignedLongFieldMapper build(MapperBuilderContext context) {
211234
indexMode,
212235
context.isSourceSynthetic()
213236
);
214-
return new UnsignedLongFieldMapper(leafName(), fieldType, builderParams(this, context), context.isSourceSynthetic(), this);
237+
String offsetsFieldName = getOffsetsFieldName(
238+
context,
239+
indexSourceKeepMode,
240+
hasDocValues.getValue(),
241+
stored.getValue(),
242+
this,
243+
indexCreatedVersion,
244+
IndexVersions.SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY
245+
);
246+
return new UnsignedLongFieldMapper(
247+
leafName(),
248+
fieldType,
249+
builderParams(this, context),
250+
context.isSourceSynthetic(),
251+
this,
252+
offsetsFieldName
253+
);
215254
}
216255
}
217256

218-
public static final TypeParser PARSER = new TypeParser((n, c) -> new Builder(n, c.getSettings(), c.getIndexSettings().getMode()));
257+
public static final TypeParser PARSER = new TypeParser(
258+
(n, c) -> new Builder(
259+
n,
260+
c.getSettings(),
261+
c.indexVersionCreated(),
262+
c.getIndexSettings().getMode(),
263+
c.getIndexSettings().sourceKeepMode()
264+
)
265+
);
219266

220267
public static final class UnsignedLongFieldType extends SimpleMappedFieldType {
221268

@@ -640,14 +687,18 @@ public MetricType getMetricType() {
640687
private final Long nullValueIndexed; // null value to use for indexing, represented as shifted to signed long range
641688
private final boolean dimension;
642689
private final MetricType metricType;
690+
private final IndexVersion indexCreatedVersion;
643691
private final IndexMode indexMode;
692+
private final String offsetsFieldName;
693+
private final SourceKeepMode indexSourceKeepMode;
644694

645695
private UnsignedLongFieldMapper(
646696
String simpleName,
647697
MappedFieldType mappedFieldType,
648698
BuilderParams builderParams,
649699
boolean isSourceSynthetic,
650-
Builder builder
700+
Builder builder,
701+
String offsetsFieldName
651702
) {
652703
super(simpleName, mappedFieldType, builderParams);
653704
this.isSourceSynthetic = isSourceSynthetic;
@@ -666,6 +717,9 @@ private UnsignedLongFieldMapper(
666717
this.dimension = builder.dimension.getValue();
667718
this.metricType = builder.metric.getValue();
668719
this.indexMode = builder.indexMode;
720+
this.indexCreatedVersion = builder.indexCreatedVersion;
721+
this.offsetsFieldName = offsetsFieldName;
722+
this.indexSourceKeepMode = builder.indexSourceKeepMode;
669723
}
670724

671725
@Override
@@ -678,6 +732,11 @@ public UnsignedLongFieldType fieldType() {
678732
return (UnsignedLongFieldType) super.fieldType();
679733
}
680734

735+
@Override
736+
public String getOffsetFieldName() {
737+
return offsetsFieldName;
738+
}
739+
681740
@Override
682741
protected String contentType() {
683742
return CONTENT_TYPE;
@@ -714,7 +773,6 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
714773
boolean isNullValue = false;
715774
if (numericValue == null) {
716775
numericValue = nullValueIndexed;
717-
if (numericValue == null) return;
718776
isNullValue = true;
719777
} else {
720778
numericValue = unsignedToSortableSignedLong(numericValue);
@@ -724,29 +782,41 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
724782
context.getRoutingFields().addUnsignedLong(fieldType().name(), numericValue);
725783
}
726784

727-
List<Field> fields = new ArrayList<>();
728-
if (indexed && hasDocValues) {
729-
fields.add(new LongField(fieldType().name(), numericValue));
730-
} else if (hasDocValues) {
731-
fields.add(new SortedNumericDocValuesField(fieldType().name(), numericValue));
732-
} else if (indexed) {
733-
fields.add(new LongPoint(fieldType().name(), numericValue));
734-
}
735-
if (stored) {
736-
// for stored field, keeping original unsigned_long value in the String form
737-
String storedValued = isNullValue ? nullValue : Long.toUnsignedString(unsignedToSortableSignedLong(numericValue));
738-
fields.add(new StoredField(fieldType().name(), storedValued));
785+
if (numericValue != null) {
786+
List<Field> fields = new ArrayList<>();
787+
if (indexed && hasDocValues) {
788+
fields.add(new LongField(fieldType().name(), numericValue));
789+
} else if (hasDocValues) {
790+
fields.add(new SortedNumericDocValuesField(fieldType().name(), numericValue));
791+
} else if (indexed) {
792+
fields.add(new LongPoint(fieldType().name(), numericValue));
793+
}
794+
if (stored) {
795+
// for stored field, keeping original unsigned_long value in the String form
796+
String storedValued = isNullValue ? nullValue : Long.toUnsignedString(unsignedToSortableSignedLong(numericValue));
797+
fields.add(new StoredField(fieldType().name(), storedValued));
798+
}
799+
context.doc().addAll(fields);
800+
801+
if (hasDocValues == false && (stored || indexed)) {
802+
context.addToFieldNames(fieldType().name());
803+
}
739804
}
740-
context.doc().addAll(fields);
741805

742-
if (hasDocValues == false && (stored || indexed)) {
743-
context.addToFieldNames(fieldType().name());
806+
if (offsetsFieldName != null && context.isImmediateParentAnArray() && context.canAddIgnoredField()) {
807+
if (numericValue != null) {
808+
context.getOffSetContext().recordOffset(offsetsFieldName, numericValue);
809+
} else {
810+
context.getOffSetContext().recordNull(offsetsFieldName);
811+
}
744812
}
745813
}
746814

747815
@Override
748816
public FieldMapper.Builder getMergeBuilder() {
749-
return new Builder(leafName(), ignoreMalformedByDefault, indexMode).dimension(dimension).metric(metricType).init(this);
817+
return new Builder(leafName(), ignoreMalformedByDefault, indexCreatedVersion, indexMode, indexSourceKeepMode).dimension(dimension)
818+
.metric(metricType)
819+
.init(this);
750820
}
751821

752822
/**
@@ -827,17 +897,34 @@ public void doValidate(MappingLookup lookup) {
827897
}
828898
}
829899

900+
private SourceLoader.SyntheticFieldLoader docValuesSyntheticFieldLoader() {
901+
if (offsetsFieldName != null) {
902+
var layers = new ArrayList<CompositeSyntheticFieldLoader.Layer>(2);
903+
layers.add(
904+
new SortedNumericWithOffsetsDocValuesSyntheticFieldLoaderLayer(
905+
fullPath(),
906+
offsetsFieldName,
907+
(b, value) -> b.value(DocValueFormat.UNSIGNED_LONG_SHIFTED.format(value))
908+
)
909+
);
910+
if (ignoreMalformed.value()) {
911+
layers.add(new CompositeSyntheticFieldLoader.MalformedValuesLayer(fullPath()));
912+
}
913+
return new CompositeSyntheticFieldLoader(leafName(), fullPath(), layers);
914+
} else {
915+
return new SortedNumericDocValuesSyntheticFieldLoader(fullPath(), leafName(), ignoreMalformed()) {
916+
@Override
917+
protected void writeValue(XContentBuilder b, long value) throws IOException {
918+
b.value(DocValueFormat.UNSIGNED_LONG_SHIFTED.format(value));
919+
}
920+
};
921+
}
922+
}
923+
830924
@Override
831925
protected SyntheticSourceSupport syntheticSourceSupport() {
832926
if (hasDocValues) {
833-
return new SyntheticSourceSupport.Native(
834-
() -> new SortedNumericDocValuesSyntheticFieldLoader(fullPath(), leafName(), ignoreMalformed()) {
835-
@Override
836-
protected void writeValue(XContentBuilder b, long value) throws IOException {
837-
b.value(DocValueFormat.UNSIGNED_LONG_SHIFTED.format(value));
838-
}
839-
}
840-
);
927+
return new SyntheticSourceSupport.Native(this::docValuesSyntheticFieldLoader);
841928
}
842929

843930
return super.syntheticSourceSupport();

0 commit comments

Comments
 (0)