
org.opensearch.index.mapper.DataStreamFieldMapper Maven / Gradle / Ivy
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
package org.opensearch.index.mapper;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.Query;
import org.opensearch.cluster.metadata.DataStream.TimestampField;
import org.opensearch.index.mapper.ParseContext.Document;
import org.opensearch.index.query.QueryShardContext;
import org.opensearch.search.lookup.SearchLookup;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Field mapper for a datastream field
*
* @opensearch.internal
*/
public class DataStreamFieldMapper extends MetadataFieldMapper {
public static final String NAME = "_data_stream_timestamp";
public static final String CONTENT_TYPE = "_data_stream_timestamp";
/**
* Default parameters
*
* @opensearch.internal
*/
public static final class Defaults {
public static final boolean ENABLED = false;
public static final TimestampField TIMESTAMP_FIELD = new TimestampField("@timestamp");
}
/**
* Builder for the field mapper
*
* @opensearch.internal
*/
public static final class Builder extends MetadataFieldMapper.Builder {
final Parameter enabledParam = Parameter.boolParam("enabled", false, mapper -> toType(mapper).enabled, Defaults.ENABLED);
final Parameter timestampFieldParam = new Parameter<>(
"timestamp_field",
false,
() -> Defaults.TIMESTAMP_FIELD,
(n, c, o) -> new TimestampField((String) ((Map, ?>) o).get("name")),
mapper -> toType(mapper).timestampField
);
protected Builder() {
super(NAME);
}
@Override
protected List> getParameters() {
return Collections.unmodifiableList(Arrays.asList(enabledParam, timestampFieldParam));
}
@Override
public MetadataFieldMapper build(BuilderContext context) {
return new DataStreamFieldMapper(enabledParam.getValue(), timestampFieldParam.getValue());
}
}
/**
* Field type for data stream field mapper
*
* @opensearch.internal
*/
public static final class DataStreamFieldType extends MappedFieldType {
public static final DataStreamFieldType INSTANCE = new DataStreamFieldType();
private DataStreamFieldType() {
super(NAME, false, false, false, TextSearchInfo.NONE, Collections.emptyMap());
}
@Override
public ValueFetcher valueFetcher(QueryShardContext context, SearchLookup searchLookup, String format) {
throw new UnsupportedOperationException("Cannot fetch values for internal field [" + typeName() + "]");
}
@Override
public String typeName() {
return CONTENT_TYPE;
}
@Override
public Query termQuery(Object value, QueryShardContext context) {
throw new UnsupportedOperationException("Cannot run term query on internal field [" + typeName() + "]");
}
@Override
public Query existsQuery(QueryShardContext context) {
throw new UnsupportedOperationException("Cannot run exists query on internal field [" + typeName() + "]");
}
}
public static final TypeParser PARSER = new ConfigurableTypeParser(
context -> new DataStreamFieldMapper(Defaults.ENABLED, Defaults.TIMESTAMP_FIELD),
context -> new Builder()
);
@Override
public ParametrizedFieldMapper.Builder getMergeBuilder() {
return new Builder().init(this);
}
private static DataStreamFieldMapper toType(FieldMapper in) {
return (DataStreamFieldMapper) in;
}
private final boolean enabled;
private final TimestampField timestampField;
protected DataStreamFieldMapper(boolean enabled, TimestampField timestampField) {
super(DataStreamFieldType.INSTANCE);
this.enabled = enabled;
this.timestampField = timestampField;
}
@Override
protected String contentType() {
return CONTENT_TYPE;
}
@Override
public void postParse(ParseContext context) throws IOException {
// If _data_stream_timestamp metadata mapping is disabled, then skip all the remaining checks.
if (enabled == false) {
return;
}
// It is expected that the timestamp field will be parsed by the DateFieldMapper during the parseCreateField step.
// The parsed field will be added to the document as:
// 1. LongPoint (indexed = true; an indexed long field to allow fast range filters on the timestamp field value)
// 2. SortedNumericDocValuesField (hasDocValues = true; allows sorting, aggregations and access to the timestamp field value)
Document document = context.doc();
IndexableField[] fields = document.getFields(timestampField.getName());
// Documents must contain exactly one value for the timestamp field.
long numTimestampValues = Arrays.stream(fields)
.filter(field -> field.fieldType().docValuesType() == DocValuesType.SORTED_NUMERIC)
.count();
if (numTimestampValues != 1) {
throw new IllegalArgumentException(
"documents must contain a single-valued timestamp field '" + timestampField.getName() + "' of date type"
);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy