org.opensearch.index.mapper.DataStreamFieldMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opensearch Show documentation
Show all versions of opensearch Show documentation
OpenSearch subproject :server
/*
* 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"
);
}
}
}