-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add declarative parameters to FieldMappers #58663
Changes from all commits
9dda2f3
6c118f3
56ad229
da0aca5
6e99626
7326c49
ae14ac5
5bb39b2
a31b1a6
928a15f
47c17e4
4bf9327
3011064
bb35eda
b9dd287
8f1853f
37ea8d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -332,6 +332,16 @@ public static String nodeStringValue(Object node, String defaultValue) { | |
return node.toString(); | ||
} | ||
|
||
/** | ||
* Returns the {@link Object#toString} value of its input, or {@code null} if the input is null | ||
*/ | ||
public static String nodeStringValue(Object node) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its probably worth adding javadoc about how this is different from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ++ |
||
if (node == null) { | ||
return null; | ||
} | ||
return node.toString(); | ||
} | ||
|
||
public static float nodeFloatValue(Object node, float defaultValue) { | ||
if (node == null) { | ||
return defaultValue; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,9 +20,7 @@ | |
package org.elasticsearch.index.mapper; | ||
|
||
import com.carrotsearch.hppc.ObjectArrayList; | ||
import org.apache.lucene.document.FieldType; | ||
import org.apache.lucene.document.StoredField; | ||
import org.apache.lucene.index.IndexOptions; | ||
import org.apache.lucene.index.Term; | ||
import org.apache.lucene.search.DocValuesFieldExistsQuery; | ||
import org.apache.lucene.search.Query; | ||
|
@@ -48,42 +46,39 @@ | |
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static org.elasticsearch.index.mapper.TypeParsers.parseField; | ||
|
||
public class BinaryFieldMapper extends FieldMapper { | ||
public class BinaryFieldMapper extends ParametrizedFieldMapper { | ||
|
||
public static final String CONTENT_TYPE = "binary"; | ||
|
||
public static class Defaults { | ||
public static final FieldType FIELD_TYPE = new FieldType(); | ||
|
||
static { | ||
FIELD_TYPE.setIndexOptions(IndexOptions.NONE); | ||
FIELD_TYPE.setOmitNorms(true); | ||
FIELD_TYPE.freeze(); | ||
} | ||
private static BinaryFieldMapper toType(FieldMapper in) { | ||
return (BinaryFieldMapper) in; | ||
} | ||
|
||
public static class Builder extends FieldMapper.Builder<Builder> { | ||
public static class Builder extends ParametrizedFieldMapper.Builder { | ||
|
||
private final Parameter<Boolean> stored = Parameter.boolParam("store", false, m -> toType(m).stored, false); | ||
private final Parameter<Boolean> hasDocValues = Parameter.boolParam("doc_values", false, m -> toType(m).hasDocValues, false); | ||
private final Parameter<Map<String, String>> meta | ||
= new Parameter<>("meta", true, Collections.emptyMap(), TypeParsers::parseMeta, m -> m.fieldType().meta()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we plan to eventually move There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
public Builder(String name) { | ||
super(name, Defaults.FIELD_TYPE); | ||
hasDocValues = false; | ||
builder = this; | ||
this(name, false); | ||
} | ||
|
||
public Builder(String name, boolean hasDocValues) { | ||
super(name); | ||
this.hasDocValues.setValue(hasDocValues); | ||
} | ||
|
||
@Override | ||
public BinaryFieldMapper build(BuilderContext context) { | ||
return new BinaryFieldMapper(name, fieldType, new BinaryFieldType(buildFullName(context), hasDocValues, meta), | ||
multiFieldsBuilder.build(this, context), copyTo); | ||
public List<Parameter<?>> getParameters() { | ||
return List.of(meta, stored, hasDocValues); | ||
} | ||
|
||
@Override | ||
public Builder index(boolean index) { | ||
if (index) { | ||
throw new MapperParsingException("Binary field [" + name() + "] cannot be indexed"); | ||
} | ||
return builder; | ||
public BinaryFieldMapper build(BuilderContext context) { | ||
return new BinaryFieldMapper(name, new BinaryFieldType(buildFullName(context), hasDocValues.getValue(), meta.getValue()), | ||
multiFieldsBuilder.build(this, context), copyTo.build(), this); | ||
} | ||
} | ||
|
||
|
@@ -92,7 +87,7 @@ public static class TypeParser implements Mapper.TypeParser { | |
public BinaryFieldMapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) | ||
throws MapperParsingException { | ||
BinaryFieldMapper.Builder builder = new BinaryFieldMapper.Builder(name); | ||
parseField(builder, name, node, parserContext); | ||
builder.parse(name, parserContext, node); | ||
return builder; | ||
} | ||
} | ||
|
@@ -167,14 +162,19 @@ public Query termQuery(Object value, QueryShardContext context) { | |
} | ||
} | ||
|
||
protected BinaryFieldMapper(String simpleName, FieldType fieldType, MappedFieldType mappedFieldType, | ||
MultiFields multiFields, CopyTo copyTo) { | ||
super(simpleName, fieldType, mappedFieldType, multiFields, copyTo); | ||
private final boolean stored; | ||
private final boolean hasDocValues; | ||
|
||
protected BinaryFieldMapper(String simpleName, MappedFieldType mappedFieldType, | ||
MultiFields multiFields, CopyTo copyTo, Builder builder) { | ||
super(simpleName, mappedFieldType, multiFields, copyTo); | ||
this.stored = builder.stored.getValue(); | ||
this.hasDocValues = builder.hasDocValues.getValue(); | ||
} | ||
|
||
@Override | ||
protected void parseCreateField(ParseContext context) throws IOException { | ||
if (!fieldType.stored() && !fieldType().hasDocValues()) { | ||
if (stored == false && hasDocValues == false) { | ||
return; | ||
} | ||
byte[] value = context.parseExternalValue(byte[].class); | ||
|
@@ -188,11 +188,11 @@ protected void parseCreateField(ParseContext context) throws IOException { | |
if (value == null) { | ||
return; | ||
} | ||
if (fieldType.stored()) { | ||
if (stored) { | ||
context.doc().add(new StoredField(fieldType().name(), value)); | ||
} | ||
|
||
if (fieldType().hasDocValues()) { | ||
if (hasDocValues) { | ||
CustomBinaryDocValuesField field = (CustomBinaryDocValuesField) context.doc().getByKey(fieldType().name()); | ||
if (field == null) { | ||
field = new CustomBinaryDocValuesField(fieldType().name(), value); | ||
|
@@ -210,18 +210,8 @@ protected void parseCreateField(ParseContext context) throws IOException { | |
} | ||
|
||
@Override | ||
protected boolean indexedByDefault() { | ||
return false; | ||
} | ||
|
||
@Override | ||
protected boolean docValuesByDefault() { | ||
return false; | ||
} | ||
|
||
@Override | ||
protected void mergeOptions(FieldMapper other, List<String> conflicts) { | ||
|
||
public ParametrizedFieldMapper.Builder getMergeBuilder() { | ||
return new BinaryFieldMapper.Builder(simpleName()).init(this); | ||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
import org.apache.lucene.document.Field; | ||
import org.apache.lucene.document.FieldType; | ||
import org.apache.lucene.document.SortedNumericDocValuesField; | ||
import org.apache.lucene.document.StoredField; | ||
import org.apache.lucene.index.IndexOptions; | ||
import org.apache.lucene.index.Term; | ||
import org.apache.lucene.search.DocValuesFieldExistsQuery; | ||
|
@@ -30,7 +31,6 @@ | |
import org.apache.lucene.search.TermRangeQuery; | ||
import org.apache.lucene.util.BytesRef; | ||
import org.elasticsearch.common.Nullable; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
import org.elasticsearch.common.xcontent.XContentParser; | ||
import org.elasticsearch.common.xcontent.support.XContentMapValues; | ||
import org.elasticsearch.index.fielddata.IndexFieldData; | ||
|
@@ -42,16 +42,13 @@ | |
import java.io.IOException; | ||
import java.time.ZoneId; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static org.elasticsearch.index.mapper.TypeParsers.parseField; | ||
|
||
/** | ||
* A field mapper for boolean fields. | ||
*/ | ||
public class BooleanFieldMapper extends FieldMapper { | ||
public class BooleanFieldMapper extends ParametrizedFieldMapper { | ||
|
||
public static final String CONTENT_TYPE = "boolean"; | ||
|
||
|
@@ -71,25 +68,37 @@ public static class Values { | |
public static final BytesRef FALSE = new BytesRef("F"); | ||
} | ||
|
||
public static class Builder extends FieldMapper.Builder<Builder> { | ||
private static BooleanFieldMapper toType(FieldMapper in) { | ||
return (BooleanFieldMapper) in; | ||
} | ||
|
||
public static class Builder extends ParametrizedFieldMapper.Builder { | ||
|
||
private final Parameter<Boolean> docValues = Parameter.boolParam("doc_values", false, m -> toType(m).hasDocValues, true); | ||
private final Parameter<Boolean> indexed = Parameter.boolParam("index", false, m -> toType(m).indexed, true); | ||
private final Parameter<Boolean> stored = Parameter.boolParam("store", false, m -> toType(m).stored, false); | ||
|
||
private Boolean nullValue; | ||
private final Parameter<Boolean> nullValue | ||
= new Parameter<>("null_value", false, null, (n, o) -> XContentMapValues.nodeBooleanValue(o), m -> toType(m).nullValue); | ||
|
||
private final Parameter<Float> boost = Parameter.floatParam("boost", true, m -> m.fieldType().boost(), 1.0f); | ||
private final Parameter<Map<String, String>> meta | ||
= new Parameter<>("meta", true, Collections.emptyMap(), TypeParsers::parseMeta, m -> m.fieldType().meta()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get confused here that stored seems to be treated differently from e.g. doc_values. Why is that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Convenience, mainly - There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I am trying to see what the difference is between stored and indexed/doc_values. I would expect them to be treated in the same way/same place. Why can't stored be derived from the field type values? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As it stands, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You made a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add |
||
|
||
public Builder(String name) { | ||
super(name, Defaults.FIELD_TYPE); | ||
this.builder = this; | ||
super(name); | ||
} | ||
|
||
public Builder nullValue(Boolean nullValue) { | ||
this.nullValue = nullValue; | ||
return builder; | ||
@Override | ||
protected List<Parameter<?>> getParameters() { | ||
return List.of(meta, boost, docValues, indexed, nullValue, stored); | ||
} | ||
|
||
@Override | ||
public BooleanFieldMapper build(BuilderContext context) { | ||
return new BooleanFieldMapper(name, fieldType, | ||
new BooleanFieldType(buildFullName(context), indexed, hasDocValues, meta), | ||
multiFieldsBuilder.build(this, context), copyTo, nullValue); | ||
MappedFieldType ft = new BooleanFieldType(buildFullName(context), indexed.getValue(), docValues.getValue(), meta.getValue()); | ||
ft.setBoost(boost.getValue()); | ||
return new BooleanFieldMapper(name, ft, multiFieldsBuilder.build(this, context), copyTo.build(), this); | ||
} | ||
} | ||
|
||
|
@@ -98,19 +107,7 @@ public static class TypeParser implements Mapper.TypeParser { | |
public BooleanFieldMapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) | ||
throws MapperParsingException { | ||
BooleanFieldMapper.Builder builder = new BooleanFieldMapper.Builder(name); | ||
parseField(builder, name, node, parserContext); | ||
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) { | ||
Map.Entry<String, Object> entry = iterator.next(); | ||
String propName = entry.getKey(); | ||
Object propNode = entry.getValue(); | ||
if (propName.equals("null_value")) { | ||
if (propNode == null) { | ||
throw new MapperParsingException("Property [null_value] cannot be null."); | ||
} | ||
builder.nullValue(XContentMapValues.nodeBooleanValue(propNode, name + ".null_value")); | ||
iterator.remove(); | ||
} | ||
} | ||
builder.parse(name, parserContext, node); | ||
return builder; | ||
} | ||
} | ||
|
@@ -217,11 +214,17 @@ public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower | |
} | ||
|
||
private final Boolean nullValue; | ||
|
||
protected BooleanFieldMapper(String simpleName, FieldType fieldType, MappedFieldType mappedFieldType, | ||
MultiFields multiFields, CopyTo copyTo, Boolean nullValue) { | ||
super(simpleName, fieldType, mappedFieldType, multiFields, copyTo); | ||
this.nullValue = nullValue; | ||
private final boolean indexed; | ||
private final boolean hasDocValues; | ||
private final boolean stored; | ||
|
||
protected BooleanFieldMapper(String simpleName, MappedFieldType mappedFieldType, | ||
MultiFields multiFields, CopyTo copyTo, Builder builder) { | ||
super(simpleName, mappedFieldType, multiFields, copyTo); | ||
this.nullValue = builder.nullValue.getValue(); | ||
this.stored = builder.stored.getValue(); | ||
this.indexed = builder.indexed.getValue(); | ||
this.hasDocValues = builder.docValues.getValue(); | ||
} | ||
|
||
@Override | ||
|
@@ -231,7 +234,7 @@ public BooleanFieldType fieldType() { | |
|
||
@Override | ||
protected void parseCreateField(ParseContext context) throws IOException { | ||
if (fieldType().isSearchable() == false && !fieldType.stored() && !fieldType().hasDocValues()) { | ||
if (indexed == false && stored == false && hasDocValues == false) { | ||
return; | ||
} | ||
|
||
|
@@ -250,31 +253,27 @@ protected void parseCreateField(ParseContext context) throws IOException { | |
if (value == null) { | ||
return; | ||
} | ||
if (fieldType().isSearchable() || fieldType.stored()) { | ||
context.doc().add(new Field(fieldType().name(), value ? "T" : "F", fieldType)); | ||
if (indexed) { | ||
context.doc().add(new Field(fieldType().name(), value ? "T" : "F", Defaults.FIELD_TYPE)); | ||
} | ||
if (stored) { | ||
context.doc().add(new StoredField(fieldType().name(), value ? "T" : "F")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like if the field is searchable and stored too, we end up adding two fields, while before we would only add one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding a field with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was odd that we were using |
||
} | ||
if (fieldType().hasDocValues()) { | ||
if (hasDocValues) { | ||
context.doc().add(new SortedNumericDocValuesField(fieldType().name(), value ? 1 : 0)); | ||
} else { | ||
createFieldNamesField(context); | ||
} | ||
} | ||
|
||
@Override | ||
protected void mergeOptions(FieldMapper other, List<String> conflicts) { | ||
// TODO ban updating null values | ||
public ParametrizedFieldMapper.Builder getMergeBuilder() { | ||
return new Builder(simpleName()).init(this); | ||
} | ||
|
||
@Override | ||
protected String contentType() { | ||
return CONTENT_TYPE; | ||
} | ||
|
||
@Override | ||
protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { | ||
super.doXContentBody(builder, includeDefaults, params); | ||
if (includeDefaults || nullValue != null) { | ||
builder.field("null_value", nullValue); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self - these were the defaults so they are safe to drop.