Skip to content

Commit 6d3637f

Browse files
committed
Fixed a bug where text fields in LogsDB indices did not use their keyword multi fields for block loading (elastic#134253)
* Added keyword multi field with ignore_above to match only text bwc tests * Rework ignore_above * Added unit tests * Undo changed to match only text bwc test * formatting * Removed indexMode from field type * Added another test case * Fixed failing bwc tests * Improved msg * Added additional tests * Added IgnoreAbove record, addressed index-level ignore above * Fixed test typos * Added IgnoreAboveTest * Enforce at least one value or defaultValue to always be non-null when IgnoreAbove is initialized * When initializing IgnoreAbove, dont use defaultValue from builder - this fixes failing BWC test * Fixed typo * Switched IgnoreAbove to constructor only, removed the ability to set default directly * Update docs/changelog/134253.yaml * Update 134253.yaml * Move IgnoreAbove into Mapper and make it final, move everything new out of IndexSettings and into IgnoreAbove * Fixed typo * Added helpful constructor to IgnoreAbove * Added helpful constructor to IgnoreAbove (cherry picked from commit 2c3bee7) # Conflicts: # modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java # qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/TextRollingUpgradeIT.java # server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java # server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java # server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java # server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java # server/src/test/java/org/elasticsearch/index/mapper/MultiFieldsTests.java # x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java
1 parent c6d8fb3 commit 6d3637f

File tree

19 files changed

+908
-137
lines changed

19 files changed

+908
-137
lines changed

docs/changelog/134253.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 134253
2+
summary: Fixed a bug where text fields in LogsDB indices did not use their keyword multi fields for block loading
3+
area: Mapping
4+
type: bug
5+
issues: []

plugins/analysis-icu/src/main/java/org/elasticsearch/plugin/analysis/icu/ICUCollationKeywordFieldMapper.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,10 @@ public static class Builder extends FieldMapper.Builder {
250250
false
251251
).acceptsNull();
252252

253-
final Parameter<Integer> ignoreAbove = Parameter.intParam("ignore_above", true, m -> toType(m).ignoreAbove, Integer.MAX_VALUE)
254-
.addValidator(v -> {
255-
if (v < 0) {
256-
throw new IllegalArgumentException("[ignore_above] must be positive, got [" + v + "]");
257-
}
258-
});
253+
final Parameter<Integer> ignoreAbove = Parameter.ignoreAboveParam(
254+
m -> toType(m).ignoreAbove,
255+
IgnoreAbove.IGNORE_ABOVE_DEFAULT_VALUE
256+
);
259257
final Parameter<String> nullValue = Parameter.stringParam("null_value", false, m -> toType(m).nullValue, null).acceptsNull();
260258

261259
public Builder(String name) {

server/src/main/java/org/elasticsearch/index/IndexSettings.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -790,20 +790,21 @@ public Iterator<Setting<?>> settings() {
790790

791791
public static final Setting<Integer> IGNORE_ABOVE_SETTING = Setting.intSetting(
792792
"index.mapping.ignore_above",
793-
IndexSettings::getIgnoreAboveDefaultValue,
793+
settings -> String.valueOf(getIgnoreAboveDefaultValue(settings)),
794794
0,
795795
Integer.MAX_VALUE,
796796
Property.IndexScope,
797797
Property.ServerlessPublic
798798
);
799799

800-
private static String getIgnoreAboveDefaultValue(final Settings settings) {
801-
if (IndexSettings.MODE.get(settings) == IndexMode.LOGSDB
802-
&& IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(settings).onOrAfter(IndexVersions.ENABLE_IGNORE_ABOVE_LOGSDB)) {
803-
return "8191";
804-
} else {
805-
return String.valueOf(Integer.MAX_VALUE);
800+
private static int getIgnoreAboveDefaultValue(final Settings settings) {
801+
if (settings == null) {
802+
return Mapper.IgnoreAbove.IGNORE_ABOVE_DEFAULT_VALUE;
806803
}
804+
return Mapper.IgnoreAbove.getIgnoreAboveDefaultValue(
805+
IndexSettings.MODE.get(settings),
806+
IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(settings)
807+
);
807808
}
808809

809810
private final Index index;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,14 @@ public static Parameter<Boolean> docValuesParam(Function<FieldMapper, Boolean> i
13111311
return Parameter.boolParam("doc_values", false, initializer, defaultValue);
13121312
}
13131313

1314+
public static Parameter<Integer> ignoreAboveParam(Function<FieldMapper, Integer> initializer, int defaultValue) {
1315+
return Parameter.intParam("ignore_above", true, initializer, defaultValue).addValidator(v -> {
1316+
if (v < 0) {
1317+
throw new IllegalArgumentException("[ignore_above] must be positive, got [" + v + "]");
1318+
}
1319+
});
1320+
}
1321+
13141322
/**
13151323
* Defines a script parameter
13161324
* @param initializer retrieves the equivalent parameter from an existing FieldMapper for use in merges

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

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.elasticsearch.common.lucene.search.AutomatonQueries;
3939
import org.elasticsearch.common.unit.Fuzziness;
4040
import org.elasticsearch.core.Nullable;
41+
import org.elasticsearch.index.IndexMode;
4142
import org.elasticsearch.index.IndexVersion;
4243
import org.elasticsearch.index.analysis.IndexAnalyzers;
4344
import org.elasticsearch.index.analysis.NamedAnalyzer;
@@ -78,6 +79,7 @@
7879
import static org.apache.lucene.index.IndexWriter.MAX_TERM_LENGTH;
7980
import static org.elasticsearch.core.Strings.format;
8081
import static org.elasticsearch.index.IndexSettings.IGNORE_ABOVE_SETTING;
82+
import static org.elasticsearch.index.mapper.Mapper.IgnoreAbove.getIgnoreAboveDefaultValue;
8183

8284
/**
8385
* A field mapper for keywords. This mapper accepts strings and indexes them as-is.
@@ -155,6 +157,7 @@ public static final class Builder extends FieldMapper.DimensionBuilder {
155157
private final Parameter<Integer> ignoreAbove;
156158
private final int ignoreAboveDefault;
157159

160+
private final IndexMode indexMode;
158161
private final Parameter<String> indexOptions = TextParams.keywordIndexOptions(m -> toType(m).indexOptions);
159162
private final Parameter<Boolean> hasNorms = TextParams.norms(false, m -> toType(m).fieldType.omitNorms() == false);
160163
private final Parameter<SimilarityProvider> similarity = TextParams.similarity(
@@ -189,16 +192,34 @@ public Builder(final String name, final MappingParserContext mappingParserContex
189192
mappingParserContext.getIndexAnalyzers(),
190193
mappingParserContext.scriptCompiler(),
191194
IGNORE_ABOVE_SETTING.get(mappingParserContext.getSettings()),
192-
mappingParserContext.getIndexSettings().getIndexVersionCreated()
195+
mappingParserContext.getIndexSettings().getIndexVersionCreated(),
196+
mappingParserContext.getIndexSettings().getMode()
193197
);
194198
}
195199

196200
Builder(
197201
String name,
198202
IndexAnalyzers indexAnalyzers,
199203
ScriptCompiler scriptCompiler,
200-
int ignoreAboveDefault,
201204
IndexVersion indexCreatedVersion
205+
) {
206+
this(
207+
name,
208+
indexAnalyzers,
209+
scriptCompiler,
210+
getIgnoreAboveDefaultValue(IndexMode.STANDARD, indexCreatedVersion),
211+
indexCreatedVersion,
212+
IndexMode.STANDARD
213+
);
214+
}
215+
216+
private Builder(
217+
String name,
218+
IndexAnalyzers indexAnalyzers,
219+
ScriptCompiler scriptCompiler,
220+
int ignoreAboveDefault,
221+
IndexVersion indexCreatedVersion,
222+
IndexMode indexMode
202223
) {
203224
super(name);
204225
this.indexAnalyzers = indexAnalyzers;
@@ -227,16 +248,12 @@ public Builder(final String name, final MappingParserContext mappingParserContex
227248
}
228249
}).precludesParameters(normalizer);
229250
this.ignoreAboveDefault = ignoreAboveDefault;
230-
this.ignoreAbove = Parameter.intParam("ignore_above", true, m -> toType(m).fieldType().ignoreAbove(), ignoreAboveDefault)
231-
.addValidator(v -> {
232-
if (v < 0) {
233-
throw new IllegalArgumentException("[ignore_above] must be positive, got [" + v + "]");
234-
}
235-
});
251+
this.ignoreAbove = Parameter.ignoreAboveParam(m -> toType(m).fieldType().ignoreAbove().get(), ignoreAboveDefault);
252+
this.indexMode = indexMode;
236253
}
237254

238255
public Builder(String name, IndexVersion indexCreatedVersion) {
239-
this(name, null, ScriptCompiler.NONE, Integer.MAX_VALUE, indexCreatedVersion);
256+
this(name, null, ScriptCompiler.NONE, indexCreatedVersion);
240257
}
241258

242259
public Builder ignoreAbove(int ignoreAbove) {
@@ -385,13 +402,16 @@ public KeywordFieldMapper build(MapperBuilderContext context) {
385402

386403
public static final class KeywordFieldType extends StringFieldType {
387404

388-
private final int ignoreAbove;
405+
private static final IgnoreAbove IGNORE_ABOVE_DEFAULT = new IgnoreAbove(null, IndexMode.STANDARD);
406+
407+
private final IgnoreAbove ignoreAbove;
389408
private final String nullValue;
390409
private final NamedAnalyzer normalizer;
391410
private final boolean eagerGlobalOrdinals;
392411
private final FieldValues<String> scriptValues;
393412
private final boolean isDimension;
394413
private final boolean isSyntheticSource;
414+
private final String originalName;
395415

396416
public KeywordFieldType(
397417
String name,
@@ -412,26 +432,28 @@ public KeywordFieldType(
412432
);
413433
this.eagerGlobalOrdinals = builder.eagerGlobalOrdinals.getValue();
414434
this.normalizer = normalizer;
415-
this.ignoreAbove = builder.ignoreAbove.getValue();
435+
this.ignoreAbove = new IgnoreAbove(builder.ignoreAbove.getValue(), builder.indexMode, builder.indexCreatedVersion);
416436
this.nullValue = builder.nullValue.getValue();
417437
this.scriptValues = builder.scriptValues();
418438
this.isDimension = builder.dimension.getValue();
419439
this.isSyntheticSource = isSyntheticSource;
440+
this.originalName = isSyntheticSource ? name + "._original" : null;
441+
}
442+
443+
public KeywordFieldType(String name) {
444+
this(name, true, true, Collections.emptyMap());
420445
}
421446

422447
public KeywordFieldType(String name, boolean isIndexed, boolean hasDocValues, Map<String, String> meta) {
423448
super(name, isIndexed, false, hasDocValues, TextSearchInfo.SIMPLE_MATCH_ONLY, meta);
424449
this.normalizer = Lucene.KEYWORD_ANALYZER;
425-
this.ignoreAbove = Integer.MAX_VALUE;
450+
this.ignoreAbove = IGNORE_ABOVE_DEFAULT;
426451
this.nullValue = null;
427452
this.eagerGlobalOrdinals = false;
428453
this.scriptValues = null;
429454
this.isDimension = false;
430455
this.isSyntheticSource = false;
431-
}
432-
433-
public KeywordFieldType(String name) {
434-
this(name, true, true, Collections.emptyMap());
456+
this.originalName = null;
435457
}
436458

437459
public KeywordFieldType(String name, FieldType fieldType) {
@@ -444,23 +466,25 @@ public KeywordFieldType(String name, FieldType fieldType) {
444466
Collections.emptyMap()
445467
);
446468
this.normalizer = Lucene.KEYWORD_ANALYZER;
447-
this.ignoreAbove = Integer.MAX_VALUE;
469+
this.ignoreAbove = IGNORE_ABOVE_DEFAULT;
448470
this.nullValue = null;
449471
this.eagerGlobalOrdinals = false;
450472
this.scriptValues = null;
451473
this.isDimension = false;
452474
this.isSyntheticSource = false;
475+
this.originalName = null;
453476
}
454477

455478
public KeywordFieldType(String name, NamedAnalyzer analyzer) {
456479
super(name, true, false, true, textSearchInfo(Defaults.FIELD_TYPE, null, analyzer, analyzer), Collections.emptyMap());
457480
this.normalizer = Lucene.KEYWORD_ANALYZER;
458-
this.ignoreAbove = Integer.MAX_VALUE;
481+
this.ignoreAbove = IGNORE_ABOVE_DEFAULT;
459482
this.nullValue = null;
460483
this.eagerGlobalOrdinals = false;
461484
this.scriptValues = null;
462485
this.isDimension = false;
463486
this.isSyntheticSource = false;
487+
this.originalName = null;
464488
}
465489

466490
@Override
@@ -710,15 +734,16 @@ private SourceValueFetcher sourceValueFetcher(Set<String> sourcePaths) {
710734
@Override
711735
protected String parseSourceValue(Object value) {
712736
String keywordValue = value.toString();
713-
if (keywordValue.length() > ignoreAbove) {
714-
return null;
715-
}
716-
717-
return normalizeValue(normalizer(), name(), keywordValue);
737+
return applyIgnoreAboveAndNormalizer(keywordValue);
718738
}
719739
};
720740
}
721741

742+
private String applyIgnoreAboveAndNormalizer(String value) {
743+
if (ignoreAbove.isIgnored(value)) return null;
744+
return normalizeValue(normalizer(), name(), value);
745+
}
746+
722747
@Override
723748
public Object valueForDisplay(Object value) {
724749
if (value == null) {
@@ -834,7 +859,7 @@ public CollapseType collapseType() {
834859

835860
/** Values that have more chars than the return value of this method will
836861
* be skipped at parsing time. */
837-
public int ignoreAbove() {
862+
public IgnoreAbove ignoreAbove() {
838863
return ignoreAbove;
839864
}
840865

@@ -851,6 +876,15 @@ public boolean hasScriptValues() {
851876
public boolean hasNormalizer() {
852877
return normalizer != Lucene.KEYWORD_ANALYZER;
853878
}
879+
880+
/**
881+
* The name used to store "original" that have been ignored
882+
* by {@link KeywordFieldType#ignoreAbove()} so that they can be rebuilt
883+
* for synthetic source.
884+
*/
885+
public String originalName() {
886+
return originalName;
887+
}
854888
}
855889

856890
private final boolean indexed;
@@ -862,11 +896,11 @@ public boolean hasNormalizer() {
862896
private final Script script;
863897
private final ScriptCompiler scriptCompiler;
864898
private final IndexVersion indexCreatedVersion;
899+
private final IndexMode indexMode;
865900
private final boolean isSyntheticSource;
866901

867902
private final IndexAnalyzers indexAnalyzers;
868903
private final int ignoreAboveDefault;
869-
private final int ignoreAbove;
870904

871905
private KeywordFieldMapper(
872906
String simpleName,
@@ -888,9 +922,9 @@ private KeywordFieldMapper(
888922
this.indexAnalyzers = builder.indexAnalyzers;
889923
this.scriptCompiler = builder.scriptCompiler;
890924
this.indexCreatedVersion = builder.indexCreatedVersion;
925+
this.indexMode = builder.indexMode;
891926
this.isSyntheticSource = isSyntheticSource;
892927
this.ignoreAboveDefault = builder.ignoreAboveDefault;
893-
this.ignoreAbove = builder.ignoreAbove.getValue();
894928
}
895929

896930
@Override
@@ -923,7 +957,7 @@ private void indexValue(DocumentParserContext context, String value) {
923957
return;
924958
}
925959

926-
if (value.length() > fieldType().ignoreAbove()) {
960+
if (fieldType().ignoreAbove().isIgnored(value)) {
927961
context.addIgnoredField(fullPath());
928962
if (isSyntheticSource) {
929963
// Save a copy of the field so synthetic source can load it
@@ -1008,7 +1042,7 @@ public Map<String, NamedAnalyzer> indexAnalyzers() {
10081042

10091043
@Override
10101044
public FieldMapper.Builder getMergeBuilder() {
1011-
return new Builder(leafName(), indexAnalyzers, scriptCompiler, ignoreAboveDefault, indexCreatedVersion).dimension(
1045+
return new Builder(leafName(), indexAnalyzers, scriptCompiler, ignoreAboveDefault, indexCreatedVersion, indexMode).dimension(
10121046
fieldType().isDimension()
10131047
).init(this);
10141048
}
@@ -1078,7 +1112,7 @@ protected BytesRef preserve(BytesRef value) {
10781112
});
10791113
}
10801114

1081-
if (fieldType().ignoreAbove != Integer.MAX_VALUE) {
1115+
if (fieldType().ignoreAbove.isSet()) {
10821116
layers.add(new CompositeSyntheticFieldLoader.StoredFieldLayer(originalName()) {
10831117
@Override
10841118
protected void writeValue(Object value, XContentBuilder b) throws IOException {

0 commit comments

Comments
 (0)