
org.elasticsearch.cluster.metadata.DataStreamAlias Maven / Gradle / Ivy
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.cluster.metadata;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.AbstractDiffable;
import org.elasticsearch.cluster.Diff;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.xcontent.ConstructingObjectParser;
import org.elasticsearch.xcontent.ObjectParser;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.ToXContentFragment;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class DataStreamAlias extends AbstractDiffable implements ToXContentFragment {
public static final ParseField DATA_STREAMS_FIELD = new ParseField("data_streams");
public static final ParseField WRITE_DATA_STREAM_FIELD = new ParseField("write_data_stream");
public static final ParseField FILTER_FIELD = new ParseField("filter");
@SuppressWarnings("unchecked")
private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(
"data_stream_alias",
false,
(args, name) -> new DataStreamAlias(name, (List) args[0], (String) args[1], (CompressedXContent) args[2])
);
static {
PARSER.declareStringArray(ConstructingObjectParser.constructorArg(), DATA_STREAMS_FIELD);
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), WRITE_DATA_STREAM_FIELD);
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> {
if (p.currentToken() == XContentParser.Token.VALUE_EMBEDDED_OBJECT || p.currentToken() == XContentParser.Token.VALUE_STRING) {
return new CompressedXContent(p.binaryValue());
} else if (p.currentToken() == XContentParser.Token.START_OBJECT) {
XContentBuilder builder = XContentFactory.jsonBuilder().map(p.mapOrdered());
return new CompressedXContent(BytesReference.bytes(builder));
} else {
assert false : "unexpected token [" + p.currentToken() + " ]";
return null;
}
}, FILTER_FIELD, ObjectParser.ValueType.VALUE_OBJECT_ARRAY);
}
private final String name;
private final List dataStreams;
private final String writeDataStream;
private final CompressedXContent filter;
private DataStreamAlias(String name, Collection dataStreams, String writeDataStream, CompressedXContent filter) {
this.name = Objects.requireNonNull(name);
this.dataStreams = Collections.unmodifiableList(new ArrayList<>(dataStreams));
this.writeDataStream = writeDataStream;
this.filter = filter;
assert writeDataStream == null || dataStreams.contains(writeDataStream);
}
public DataStreamAlias(String name, List dataStreams, String writeDataStream, Map filter) {
this(name, dataStreams, writeDataStream, compress(filter));
}
private static CompressedXContent compress(Map filterAsMap) {
if (filterAsMap == null) {
return null;
}
try {
XContentBuilder builder = XContentFactory.jsonBuilder().map(filterAsMap);
return new CompressedXContent(BytesReference.bytes(builder));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static Map decompress(CompressedXContent filter) {
String filterAsString = filter.string();
return XContentHelper.convertToMap(XContentFactory.xContent(filterAsString), filterAsString, true);
}
public DataStreamAlias(StreamInput in) throws IOException {
this.name = in.readString();
this.dataStreams = in.readStringList();
this.writeDataStream = in.readOptionalString();
this.filter = in.getVersion().onOrAfter(Version.V_7_15_0) && in.readBoolean() ? CompressedXContent.readCompressedString(in) : null;
}
/**
* Returns the name of this data stream alias.
*/
public String getName() {
return name;
}
/**
* Returns the data streams that are referenced
*/
public List getDataStreams() {
return dataStreams;
}
/**
* Returns the write data stream this data stream alias is referring to.
* Write requests targeting this instance will resolve the write index
* of the write data stream this alias is referring to.
*
* Note that the write data stream is also included in {@link #getDataStreams()}.
*/
public String getWriteDataStream() {
return writeDataStream;
}
public CompressedXContent getFilter() {
return filter;
}
public boolean filteringRequired() {
return filter != null;
}
/**
* Returns a new {@link DataStreamAlias} instance with the provided data stream name added to it as a new member.
* If the provided isWriteDataStream is set to true
then the provided data stream is also set as write data stream.
* If the provided isWriteDataStream is set to false
and the provided data stream is also the write data stream of
* this instance then the returned data stream alias instance's write data stream is unset.
* If the provided filter is the same as the filter of this alias then this instance isn't updated, otherwise it is updated.
*
* The same instance is returned if the attempted addition of the provided data stream didn't change this instance.
*/
public DataStreamAlias update(String dataStream, Boolean isWriteDataStream, Map filterAsMap) {
String writeDataStream = this.writeDataStream;
if (isWriteDataStream != null) {
if (isWriteDataStream) {
writeDataStream = dataStream;
} else {
if (dataStream.equals(writeDataStream)) {
writeDataStream = null;
}
}
}
boolean filterUpdated;
CompressedXContent filter;
if (filterAsMap != null) {
filter = compress(filterAsMap);
if (this.filter == null) {
filterUpdated = true;
} else {
filterUpdated = filterAsMap.equals(decompress(this.filter)) == false;
}
} else {
filter = this.filter;
filterUpdated = false;
}
Set dataStreams = new HashSet<>(this.dataStreams);
boolean added = dataStreams.add(dataStream);
if (added || Objects.equals(this.writeDataStream, writeDataStream) == false || filterUpdated) {
return new DataStreamAlias(name, dataStreams, writeDataStream, filter);
} else {
return this;
}
}
/**
* Returns a {@link DataStreamAlias} instance based on this instance but with the specified data stream no longer referenced.
* Returns null
if because of the removal of the provided data stream name a new instance wouldn't reference to
* any data stream. The same instance is returned if the attempted removal of the provided data stream didn't change this instance.
*/
public DataStreamAlias removeDataStream(String dataStream) {
Set dataStreams = new HashSet<>(this.dataStreams);
boolean removed = dataStreams.remove(dataStream);
if (removed == false) {
return this;
}
if (dataStreams.isEmpty()) {
return null;
} else {
String writeDataStream = this.writeDataStream;
if (dataStream.equals(writeDataStream)) {
writeDataStream = null;
}
return new DataStreamAlias(name, dataStreams, writeDataStream, filter);
}
}
/**
* Returns a new {@link DataStreamAlias} instance that contains a new intersection
* of data streams from this instance and the provided filter.
*
* The write data stream gets set to null in the returned instance if the write
* data stream no longer appears in the intersection.
*/
public DataStreamAlias intersect(Predicate filter) {
List intersectingDataStreams = this.dataStreams.stream().filter(filter).collect(Collectors.toList());
String writeDataStream = this.writeDataStream;
if (intersectingDataStreams.contains(writeDataStream) == false) {
writeDataStream = null;
}
return new DataStreamAlias(this.name, intersectingDataStreams, writeDataStream, this.filter);
}
/**
* Returns a new {@link DataStreamAlias} instance containing data streams referenced in this instance
* and the other instance. If this instance doesn't have a write data stream then the write index of
* the other data stream becomes the write data stream of the returned instance.
*/
public DataStreamAlias merge(DataStreamAlias other) {
Set mergedDataStreams = new HashSet<>(other.getDataStreams());
mergedDataStreams.addAll(this.getDataStreams());
String writeDataStream = this.writeDataStream;
if (writeDataStream == null) {
if (other.getWriteDataStream() != null && mergedDataStreams.contains(other.getWriteDataStream())) {
writeDataStream = other.getWriteDataStream();
}
}
return new DataStreamAlias(this.name, mergedDataStreams, writeDataStream, filter);
}
/**
* Returns a new instance with potentially renamed data stream names and write data stream name.
* If a data stream name matches with the provided rename pattern then it is renamed according
* to the provided rename replacement.
*/
public DataStreamAlias renameDataStreams(String renamePattern, String renameReplacement) {
List renamedDataStreams = this.dataStreams.stream()
.map(s -> s.replaceAll(renamePattern, renameReplacement))
.collect(Collectors.toList());
String writeDataStream = this.writeDataStream;
if (writeDataStream != null) {
writeDataStream = writeDataStream.replaceAll(renamePattern, renameReplacement);
}
return new DataStreamAlias(this.name, renamedDataStreams, writeDataStream, filter);
}
public static Diff readDiffFrom(StreamInput in) throws IOException {
return readDiffFrom(DataStreamAlias::new, in);
}
public static DataStreamAlias fromXContent(XContentParser parser) throws IOException {
XContentParser.Token token = parser.currentToken();
if (token != XContentParser.Token.FIELD_NAME) {
throw new ParsingException(parser.getTokenLocation(), "unexpected token");
}
String name = parser.currentName();
return PARSER.parse(parser, name);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(name);
builder.stringListField(DATA_STREAMS_FIELD.getPreferredName(), dataStreams);
if (writeDataStream != null) {
builder.field(WRITE_DATA_STREAM_FIELD.getPreferredName(), writeDataStream);
}
if (filter != null) {
boolean binary = params.paramAsBoolean("binary", false);
if (binary) {
builder.field("filter", filter.compressed());
} else {
builder.field("filter", XContentHelper.convertToMap(filter.uncompressed(), true).v2());
}
}
builder.endObject();
return builder;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
out.writeStringCollection(dataStreams);
out.writeOptionalString(writeDataStream);
if (out.getVersion().onOrAfter(Version.V_7_15_0)) {
if (filter != null) {
out.writeBoolean(true);
filter.writeTo(out);
} else {
out.writeBoolean(false);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DataStreamAlias that = (DataStreamAlias) o;
return Objects.equals(name, that.name)
&& Objects.equals(dataStreams, that.dataStreams)
&& Objects.equals(writeDataStream, that.writeDataStream)
&& Objects.equals(filter, that.filter);
}
@Override
public int hashCode() {
return Objects.hash(name, dataStreams, writeDataStream, filter);
}
@Override
public String toString() {
return "DataStreamAlias{"
+ "name='"
+ name
+ '\''
+ ", dataStreams="
+ dataStreams
+ ", writeDataStream='"
+ writeDataStream
+ '\''
+ ", filter="
+ (filter != null ? filter.string() : "null")
+ '}';
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy