Merge branch 'master' into deprecate
This commit is contained in:
commit
c8f57df5cf
|
@ -248,8 +248,6 @@
|
|||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MappingMetaData.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaData.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataCreateIndexService.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataDeleteIndexService.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexAliasesService.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexStateService.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexTemplateService.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexUpgradeService.java" checks="LineLength" />
|
||||
|
|
|
@ -21,29 +21,22 @@ package org.elasticsearch.action.admin.indices.alias;
|
|||
import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Cluster state update request that allows to add or remove aliases
|
||||
*/
|
||||
public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest<IndicesAliasesClusterStateUpdateRequest> {
|
||||
private final List<AliasAction> actions;
|
||||
|
||||
AliasAction[] actions;
|
||||
|
||||
public IndicesAliasesClusterStateUpdateRequest() {
|
||||
|
||||
public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions) {
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alias actions to be performed
|
||||
*/
|
||||
public AliasAction[] actions() {
|
||||
public List<AliasAction> actions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alias actions to be executed
|
||||
*/
|
||||
public IndicesAliasesClusterStateUpdateRequest actions(AliasAction[] actions) {
|
||||
this.actions = actions;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
package org.elasticsearch.action.admin.indices.alias;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
|
||||
import org.elasticsearch.ElasticsearchGenerationException;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.AliasesRequest;
|
||||
import org.elasticsearch.action.CompositeIndicesRequest;
|
||||
|
@ -27,30 +29,41 @@ import org.elasticsearch.action.IndicesRequest;
|
|||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction.Type;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcherSupplier;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.util.CollectionUtils;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.elasticsearch.action.ValidateActions.addValidationError;
|
||||
import static org.elasticsearch.cluster.metadata.AliasAction.readAliasAction;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
|
||||
|
||||
/**
|
||||
* A request to add/remove aliases for one or more indices.
|
||||
*/
|
||||
public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesRequest> implements CompositeIndicesRequest {
|
||||
|
||||
private List<AliasActions> allAliasActions = new ArrayList<>();
|
||||
|
||||
//indices options that require every specified index to exist, expand wildcards only to open indices and
|
||||
|
@ -61,94 +74,317 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Aliases can be added by passing multiple indices to the Request and
|
||||
* deleted by passing multiple indices and aliases. They are expanded into
|
||||
* distinct AliasAction instances when the request is processed. This class
|
||||
* holds the AliasAction and in addition the arrays or alias names and
|
||||
* indices that is later used to create the final AliasAction instances.
|
||||
/**
|
||||
* Request to take one or more actions on one or more indexes and alias combinations.
|
||||
*/
|
||||
public static class AliasActions implements AliasesRequest {
|
||||
private String[] indices = Strings.EMPTY_ARRAY;
|
||||
public static class AliasActions implements AliasesRequest, Writeable {
|
||||
public enum Type {
|
||||
ADD((byte) 0),
|
||||
REMOVE((byte) 1),
|
||||
REMOVE_INDEX((byte) 2);
|
||||
|
||||
private final byte value;
|
||||
|
||||
Type(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public byte value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Type fromValue(byte value) {
|
||||
switch (value) {
|
||||
case 0: return ADD;
|
||||
case 1: return REMOVE;
|
||||
case 2: return REMOVE_INDEX;
|
||||
default: throw new IllegalArgumentException("No type for action [" + value + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link AliasAction} to add aliases.
|
||||
*/
|
||||
public static AliasActions add() {
|
||||
return new AliasActions(AliasActions.Type.ADD);
|
||||
}
|
||||
/**
|
||||
* Build a new {@link AliasAction} to remove aliases.
|
||||
*/
|
||||
public static AliasActions remove() {
|
||||
return new AliasActions(AliasActions.Type.REMOVE);
|
||||
}
|
||||
/**
|
||||
* Build a new {@link AliasAction} to remove aliases.
|
||||
*/
|
||||
public static AliasActions removeIndex() {
|
||||
return new AliasActions(AliasActions.Type.REMOVE_INDEX);
|
||||
}
|
||||
private static ObjectParser<AliasActions, ParseFieldMatcherSupplier> parser(String name, Supplier<AliasActions> supplier) {
|
||||
ObjectParser<AliasActions, ParseFieldMatcherSupplier> parser = new ObjectParser<>(name, supplier);
|
||||
parser.declareString((action, index) -> {
|
||||
if (action.indices() != null) {
|
||||
throw new IllegalArgumentException("Only one of [index] and [indices] is supported");
|
||||
}
|
||||
action.index(index);
|
||||
}, new ParseField("index"));
|
||||
parser.declareStringArray(fromList(String.class, (action, indices) -> {
|
||||
if (action.indices() != null) {
|
||||
throw new IllegalArgumentException("Only one of [index] and [indices] is supported");
|
||||
}
|
||||
action.indices(indices);
|
||||
}), new ParseField("indices"));
|
||||
parser.declareString((action, alias) -> {
|
||||
if (action.aliases() != null && action.aliases().length != 0) {
|
||||
throw new IllegalArgumentException("Only one of [alias] and [aliases] is supported");
|
||||
}
|
||||
action.alias(alias);
|
||||
}, new ParseField("alias"));
|
||||
parser.declareStringArray(fromList(String.class, (action, aliases) -> {
|
||||
if (action.aliases() != null && action.aliases().length != 0) {
|
||||
throw new IllegalArgumentException("Only one of [alias] and [aliases] is supported");
|
||||
}
|
||||
action.aliases(aliases);
|
||||
}), new ParseField("aliases"));
|
||||
return parser;
|
||||
}
|
||||
|
||||
private static final ObjectParser<AliasActions, ParseFieldMatcherSupplier> ADD_PARSER = parser("add", AliasActions::add);
|
||||
static {
|
||||
ADD_PARSER.declareObject(AliasActions::filter, (parser, m) -> {
|
||||
try {
|
||||
return parser.mapOrdered();
|
||||
} catch (IOException e) {
|
||||
throw new ParsingException(parser.getTokenLocation(), "Problems parsing [filter]", e);
|
||||
}
|
||||
}, new ParseField("filter"));
|
||||
// Since we need to support numbers AND strings here we have to use ValueType.INT.
|
||||
ADD_PARSER.declareField(AliasActions::routing, p -> p.text(), new ParseField("routing"), ValueType.INT);
|
||||
ADD_PARSER.declareField(AliasActions::indexRouting, p -> p.text(), new ParseField("index_routing"), ValueType.INT);
|
||||
ADD_PARSER.declareField(AliasActions::searchRouting, p -> p.text(), new ParseField("search_routing"), ValueType.INT);
|
||||
}
|
||||
private static final ObjectParser<AliasActions, ParseFieldMatcherSupplier> REMOVE_PARSER = parser("remove", AliasActions::remove);
|
||||
private static final ObjectParser<AliasActions, ParseFieldMatcherSupplier> REMOVE_INDEX_PARSER = parser("remove_index",
|
||||
AliasActions::removeIndex);
|
||||
|
||||
/**
|
||||
* Parser for any one {@link AliasAction}.
|
||||
*/
|
||||
public static final ConstructingObjectParser<AliasActions, ParseFieldMatcherSupplier> PARSER = new ConstructingObjectParser<>(
|
||||
"alias_action", a -> {
|
||||
// Take the first action and complain if there are more than one actions
|
||||
AliasActions action = null;
|
||||
for (Object o : a) {
|
||||
if (o != null) {
|
||||
if (action == null) {
|
||||
action = (AliasActions) o;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Too many operations declared in on opeation entry");
|
||||
}
|
||||
}
|
||||
}
|
||||
return action;
|
||||
});
|
||||
static {
|
||||
PARSER.declareObject(optionalConstructorArg(), ADD_PARSER, new ParseField("add"));
|
||||
PARSER.declareObject(optionalConstructorArg(), REMOVE_PARSER, new ParseField("remove"));
|
||||
PARSER.declareObject(optionalConstructorArg(), REMOVE_INDEX_PARSER, new ParseField("remove_index"));
|
||||
}
|
||||
|
||||
private final AliasActions.Type type;
|
||||
private String[] indices;
|
||||
private String[] aliases = Strings.EMPTY_ARRAY;
|
||||
private AliasAction aliasAction;
|
||||
private String filter;
|
||||
private String routing;
|
||||
private String indexRouting;
|
||||
private String searchRouting;
|
||||
|
||||
public AliasActions(AliasAction.Type type, String[] indices, String[] aliases) {
|
||||
aliasAction = new AliasAction(type);
|
||||
indices(indices);
|
||||
aliases(aliases);
|
||||
AliasActions(AliasActions.Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public AliasActions(AliasAction.Type type, String index, String alias) {
|
||||
aliasAction = new AliasAction(type);
|
||||
indices(index);
|
||||
aliases(alias);
|
||||
/**
|
||||
* Read from a stream.
|
||||
*/
|
||||
public AliasActions(StreamInput in) throws IOException {
|
||||
type = AliasActions.Type.fromValue(in.readByte());
|
||||
indices = in.readStringArray();
|
||||
aliases = in.readStringArray();
|
||||
filter = in.readOptionalString();
|
||||
routing = in.readOptionalString();
|
||||
searchRouting = in.readOptionalString();
|
||||
indexRouting = in.readOptionalString();
|
||||
}
|
||||
|
||||
AliasActions(AliasAction.Type type, String[] index, String alias) {
|
||||
aliasAction = new AliasAction(type);
|
||||
indices(index);
|
||||
aliases(alias);
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeByte(type.value());
|
||||
out.writeStringArray(indices);
|
||||
out.writeStringArray(aliases);
|
||||
out.writeOptionalString(filter);
|
||||
out.writeOptionalString(routing);
|
||||
out.writeOptionalString(searchRouting);
|
||||
out.writeOptionalString(indexRouting);
|
||||
}
|
||||
|
||||
public AliasActions(AliasAction action) {
|
||||
this.aliasAction = action;
|
||||
indices(action.index());
|
||||
aliases(action.alias());
|
||||
/**
|
||||
* Validate that the action is sane. Called when the action is added to the request because actions can be invalid while being
|
||||
* built.
|
||||
*/
|
||||
void validate() {
|
||||
if (indices == null) {
|
||||
throw new IllegalArgumentException("One of [index] or [indices] is required");
|
||||
}
|
||||
if (type != AliasActions.Type.REMOVE_INDEX && (aliases == null || aliases.length == 0)) {
|
||||
throw new IllegalArgumentException("One of [alias] or [aliases] is required");
|
||||
}
|
||||
}
|
||||
|
||||
public AliasActions(Type type, String index, String[] aliases) {
|
||||
aliasAction = new AliasAction(type);
|
||||
indices(index);
|
||||
aliases(aliases);
|
||||
}
|
||||
|
||||
public AliasActions() {
|
||||
}
|
||||
|
||||
public AliasActions filter(Map<String, Object> filter) {
|
||||
aliasAction.filter(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AliasActions filter(QueryBuilder filter) {
|
||||
aliasAction.filter(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Type actionType() {
|
||||
return aliasAction.actionType();
|
||||
}
|
||||
|
||||
public void routing(String routing) {
|
||||
aliasAction.routing(routing);
|
||||
}
|
||||
|
||||
public void searchRouting(String searchRouting) {
|
||||
aliasAction.searchRouting(searchRouting);
|
||||
}
|
||||
|
||||
public void indexRouting(String indexRouting) {
|
||||
aliasAction.indexRouting(indexRouting);
|
||||
}
|
||||
|
||||
public AliasActions filter(String filter) {
|
||||
aliasAction.filter(filter);
|
||||
return this;
|
||||
/**
|
||||
* Type of the action to perform.
|
||||
*/
|
||||
public AliasActions.Type actionType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AliasActions indices(String... indices) {
|
||||
if (indices == null || indices.length == 0) {
|
||||
throw new IllegalArgumentException("[indices] can't be empty");
|
||||
}
|
||||
for (String index : indices) {
|
||||
if (false == Strings.hasLength(index)) {
|
||||
throw new IllegalArgumentException("[indices] can't contain empty string");
|
||||
}
|
||||
}
|
||||
this.indices = indices;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index this action is operating on.
|
||||
*/
|
||||
public AliasActions index(String index) {
|
||||
if (false == Strings.hasLength(index)) {
|
||||
throw new IllegalArgumentException("[index] can't be empty string");
|
||||
}
|
||||
this.indices = new String[] {index};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aliases to use with this action.
|
||||
*/
|
||||
@Override
|
||||
public AliasActions aliases(String... aliases) {
|
||||
if (type == AliasActions.Type.REMOVE_INDEX) {
|
||||
throw new IllegalArgumentException("[aliases] is unsupported for [" + type + "]");
|
||||
}
|
||||
if (aliases == null || aliases.length == 0) {
|
||||
throw new IllegalArgumentException("[aliases] can't be empty");
|
||||
}
|
||||
for (String alias : aliases) {
|
||||
if (false == Strings.hasLength(alias)) {
|
||||
throw new IllegalArgumentException("[aliases] can't contain empty string");
|
||||
}
|
||||
}
|
||||
this.aliases = aliases;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alias this action is operating on.
|
||||
*/
|
||||
public AliasActions alias(String alias) {
|
||||
if (type == AliasActions.Type.REMOVE_INDEX) {
|
||||
throw new IllegalArgumentException("[alias] is unsupported for [" + type + "]");
|
||||
}
|
||||
if (false == Strings.hasLength(alias)) {
|
||||
throw new IllegalArgumentException("[alias] can't be empty string");
|
||||
}
|
||||
this.aliases = new String[] {alias};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default routing.
|
||||
*/
|
||||
public AliasActions routing(String routing) {
|
||||
if (type != AliasActions.Type.ADD) {
|
||||
throw new IllegalArgumentException("[routing] is unsupported for [" + type + "]");
|
||||
}
|
||||
this.routing = routing;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String searchRouting() {
|
||||
return searchRouting == null ? routing : searchRouting;
|
||||
}
|
||||
|
||||
public AliasActions searchRouting(String searchRouting) {
|
||||
if (type != AliasActions.Type.ADD) {
|
||||
throw new IllegalArgumentException("[search_routing] is unsupported for [" + type + "]");
|
||||
}
|
||||
this.searchRouting = searchRouting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String indexRouting() {
|
||||
return indexRouting == null ? routing : indexRouting;
|
||||
}
|
||||
|
||||
public AliasActions indexRouting(String indexRouting) {
|
||||
if (type != AliasActions.Type.ADD) {
|
||||
throw new IllegalArgumentException("[index_routing] is unsupported for [" + type + "]");
|
||||
}
|
||||
this.indexRouting = indexRouting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String filter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public AliasActions filter(String filter) {
|
||||
if (type != AliasActions.Type.ADD) {
|
||||
throw new IllegalArgumentException("[filter] is unsupported for [" + type + "]");
|
||||
}
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AliasActions filter(Map<String, Object> filter) {
|
||||
if (filter == null || filter.isEmpty()) {
|
||||
this.filter = null;
|
||||
return this;
|
||||
}
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
builder.map(filter);
|
||||
this.filter = builder.string();
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchGenerationException("Failed to generate [" + filter + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public AliasActions filter(QueryBuilder filter) {
|
||||
if (filter == null) {
|
||||
this.filter = null;
|
||||
return this;
|
||||
}
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
filter.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.close();
|
||||
this.filter = builder.string();
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchGenerationException("Failed to build json for alias request", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return aliases;
|
||||
|
@ -157,7 +393,7 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
@Override
|
||||
public boolean expandAliasesWildcards() {
|
||||
//remove operations support wildcards among aliases, add operations don't
|
||||
return aliasAction.actionType() == Type.REMOVE;
|
||||
return type == Type.REMOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,10 +406,6 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
return INDICES_OPTIONS;
|
||||
}
|
||||
|
||||
public AliasAction aliasAction() {
|
||||
return aliasAction;
|
||||
}
|
||||
|
||||
public String[] concreteAliases(MetaData metaData, String concreteIndex) {
|
||||
if (expandAliasesWildcards()) {
|
||||
//for DELETE we expand the aliases
|
||||
|
@ -191,83 +423,48 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
return aliases;
|
||||
}
|
||||
}
|
||||
public AliasActions readFrom(StreamInput in) throws IOException {
|
||||
indices = in.readStringArray();
|
||||
aliases = in.readStringArray();
|
||||
aliasAction = readAliasAction(in);
|
||||
return this;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AliasActions["
|
||||
+ "type=" + type
|
||||
+ ",indices=" + Arrays.toString(indices)
|
||||
+ ",aliases=" + Arrays.deepToString(aliases)
|
||||
+ ",filter=" + filter
|
||||
+ ",routing=" + routing
|
||||
+ ",indexRouting=" + indexRouting
|
||||
+ ",searchRouting=" + searchRouting
|
||||
+ "]";
|
||||
}
|
||||
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeStringArray(indices);
|
||||
out.writeStringArray(aliases);
|
||||
this.aliasAction.writeTo(out);
|
||||
// equals, and hashCode implemented for easy testing of round trip
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
AliasActions other = (AliasActions) obj;
|
||||
return Objects.equals(type, other.type)
|
||||
&& Arrays.equals(indices, other.indices)
|
||||
&& Arrays.equals(aliases, other.aliases)
|
||||
&& Objects.equals(filter, other.filter)
|
||||
&& Objects.equals(routing, other.routing)
|
||||
&& Objects.equals(indexRouting, other.indexRouting)
|
||||
&& Objects.equals(searchRouting, other.searchRouting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, indices, aliases, filter, routing, indexRouting, searchRouting);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias to the index.
|
||||
* @param alias The alias
|
||||
* @param indices The indices
|
||||
* Add the action to this request and validate it.
|
||||
*/
|
||||
public IndicesAliasesRequest addAlias(String alias, String... indices) {
|
||||
addAliasAction(new AliasActions(AliasAction.Type.ADD, indices, alias));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public void addAliasAction(AliasActions aliasAction) {
|
||||
public IndicesAliasesRequest addAliasAction(AliasActions aliasAction) {
|
||||
aliasAction.validate();
|
||||
allAliasActions.add(aliasAction);
|
||||
}
|
||||
|
||||
|
||||
public IndicesAliasesRequest addAliasAction(AliasAction action) {
|
||||
addAliasAction(new AliasActions(action));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias to the index.
|
||||
* @param alias The alias
|
||||
* @param filter The filter
|
||||
* @param indices The indices
|
||||
*/
|
||||
public IndicesAliasesRequest addAlias(String alias, Map<String, Object> filter, String... indices) {
|
||||
addAliasAction(new AliasActions(AliasAction.Type.ADD, indices, alias).filter(filter));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias to the index.
|
||||
* @param alias The alias
|
||||
* @param filterBuilder The filter
|
||||
* @param indices The indices
|
||||
*/
|
||||
public IndicesAliasesRequest addAlias(String alias, QueryBuilder filterBuilder, String... indices) {
|
||||
addAliasAction(new AliasActions(AliasAction.Type.ADD, indices, alias).filter(filterBuilder));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes an alias to the index.
|
||||
*
|
||||
* @param indices The indices
|
||||
* @param aliases The aliases
|
||||
*/
|
||||
public IndicesAliasesRequest removeAlias(String[] indices, String... aliases) {
|
||||
addAliasAction(new AliasActions(AliasAction.Type.REMOVE, indices, aliases));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an alias to the index.
|
||||
*
|
||||
* @param index The index
|
||||
* @param aliases The aliases
|
||||
*/
|
||||
public IndicesAliasesRequest removeAlias(String index, String... aliases) {
|
||||
addAliasAction(new AliasActions(AliasAction.Type.REMOVE, index, aliases));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -285,50 +482,20 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
if (allAliasActions.isEmpty()) {
|
||||
return addValidationError("Must specify at least one alias action", validationException);
|
||||
}
|
||||
for (AliasActions aliasAction : allAliasActions) {
|
||||
if (CollectionUtils.isEmpty(aliasAction.aliases)) {
|
||||
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
|
||||
+ "]: Property [alias/aliases] is either missing or null", validationException);
|
||||
} else {
|
||||
for (String alias : aliasAction.aliases) {
|
||||
if (!Strings.hasText(alias)) {
|
||||
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
|
||||
+ "]: [alias/aliases] may not be empty string", validationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isEmpty(aliasAction.indices)) {
|
||||
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
|
||||
+ "]: Property [index/indices] is either missing or null", validationException);
|
||||
} else {
|
||||
for (String index : aliasAction.indices) {
|
||||
if (!Strings.hasText(index)) {
|
||||
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
|
||||
+ "]: [index/indices] may not be empty string", validationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
int size = in.readVInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
allAliasActions.add(readAliasActions(in));
|
||||
}
|
||||
allAliasActions = in.readList(AliasActions::new);
|
||||
readTimeout(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeVInt(allAliasActions.size());
|
||||
for (AliasActions aliasAction : allAliasActions) {
|
||||
aliasAction.writeTo(out);
|
||||
}
|
||||
out.writeList(allAliasActions);
|
||||
writeTimeout(out);
|
||||
}
|
||||
|
||||
|
@ -336,11 +503,6 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
return INDICES_OPTIONS;
|
||||
}
|
||||
|
||||
private static AliasActions readAliasActions(StreamInput in) throws IOException {
|
||||
AliasActions actions = new AliasActions();
|
||||
return actions.readFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IndicesRequest> subRequests() {
|
||||
return allAliasActions;
|
||||
|
|
|
@ -22,15 +22,15 @@ package org.elasticsearch.action.admin.indices.alias;
|
|||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* Builder for request to modify many aliases at once.
|
||||
*/
|
||||
public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<IndicesAliasesRequest, IndicesAliasesResponse, IndicesAliasesRequestBuilder> {
|
||||
public class IndicesAliasesRequestBuilder
|
||||
extends AcknowledgedRequestBuilder<IndicesAliasesRequest, IndicesAliasesResponse, IndicesAliasesRequestBuilder> {
|
||||
|
||||
public IndicesAliasesRequestBuilder(ElasticsearchClient client, IndicesAliasesAction action) {
|
||||
super(client, action, new IndicesAliasesRequest());
|
||||
|
@ -43,7 +43,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param alias The alias
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String index, String alias) {
|
||||
request.addAlias(alias, index);
|
||||
request.addAliasAction(AliasActions.add().index(index).alias(alias));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param alias The alias
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String[] indices, String alias) {
|
||||
request.addAlias(alias, indices);
|
||||
request.addAliasAction(AliasActions.add().indices(indices).alias(alias));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -66,8 +66,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param filter The filter
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String index, String alias, String filter) {
|
||||
AliasActions action = new AliasActions(AliasAction.Type.ADD, index, alias).filter(filter);
|
||||
request.addAliasAction(action);
|
||||
request.addAliasAction(AliasActions.add().index(index).alias(alias).filter(filter));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -79,8 +78,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param filter The filter
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String indices[], String alias, String filter) {
|
||||
AliasActions action = new AliasActions(AliasAction.Type.ADD, indices, alias).filter(filter);
|
||||
request.addAliasAction(action);
|
||||
request.addAliasAction(AliasActions.add().indices(indices).alias(alias).filter(filter));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -92,7 +90,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param filter The filter
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String[] indices, String alias, Map<String, Object> filter) {
|
||||
request.addAlias(alias, filter, indices);
|
||||
request.addAliasAction(AliasActions.add().indices(indices).alias(alias).filter(filter));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -104,7 +102,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param filter The filter
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String index, String alias, Map<String, Object> filter) {
|
||||
request.addAlias(alias, filter, index);
|
||||
request.addAliasAction(AliasActions.add().index(index).alias(alias).filter(filter));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -116,7 +114,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param filterBuilder The filter
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String indices[], String alias, QueryBuilder filterBuilder) {
|
||||
request.addAlias(alias, filterBuilder, indices);
|
||||
request.addAliasAction(AliasActions.add().indices(indices).alias(alias).filter(filterBuilder));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -128,7 +126,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param filterBuilder The filter
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAlias(String index, String alias, QueryBuilder filterBuilder) {
|
||||
request.addAlias(alias, filterBuilder, index);
|
||||
request.addAliasAction(AliasActions.add().index(index).alias(alias).filter(filterBuilder));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -139,7 +137,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param alias The alias
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder removeAlias(String index, String alias) {
|
||||
request.removeAlias(index, alias);
|
||||
request.addAliasAction(AliasActions.remove().index(index).alias(alias));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -150,7 +148,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param aliases The aliases
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder removeAlias(String[] indices, String... aliases) {
|
||||
request.removeAlias(indices, aliases);
|
||||
request.addAliasAction(AliasActions.remove().indices(indices).aliases(aliases));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -161,17 +159,12 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
|
|||
* @param aliases The aliases
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder removeAlias(String index, String[] aliases) {
|
||||
request.removeAlias(index, aliases);
|
||||
request.addAliasAction(AliasActions.remove().index(index).aliases(aliases));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias action to the request.
|
||||
*
|
||||
* @param aliasAction The alias action
|
||||
*/
|
||||
public IndicesAliasesRequestBuilder addAliasAction(AliasAction aliasAction) {
|
||||
request.addAliasAction(aliasAction);
|
||||
public IndicesAliasesRequestBuilder removeIndex(String index) {
|
||||
request.addAliasAction(AliasActions.removeIndex().index(index));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
/**
|
||||
* Add/remove aliases action
|
||||
*/
|
||||
|
@ -86,31 +88,38 @@ public class TransportIndicesAliasesAction extends TransportMasterNodeAction<Ind
|
|||
//Expand the indices names
|
||||
List<AliasActions> actions = request.aliasActions();
|
||||
List<AliasAction> finalActions = new ArrayList<>();
|
||||
boolean hasOnlyDeletesButNoneCanBeDone = true;
|
||||
|
||||
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
||||
Set<String> aliases = new HashSet<>();
|
||||
for (AliasActions action : actions) {
|
||||
//expand indices
|
||||
String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request.indicesOptions(), action.indices());
|
||||
//collect the aliases
|
||||
Collections.addAll(aliases, action.aliases());
|
||||
for (String index : concreteIndices) {
|
||||
for (String alias : action.concreteAliases(state.metaData(), index)) {
|
||||
AliasAction finalAction = new AliasAction(action.aliasAction());
|
||||
finalAction.index(index);
|
||||
finalAction.alias(alias);
|
||||
finalActions.add(finalAction);
|
||||
//if there is only delete requests, none will be added if the types do not map to any existing type
|
||||
hasOnlyDeletesButNoneCanBeDone = false;
|
||||
switch (action.actionType()) {
|
||||
case ADD:
|
||||
for (String alias : action.concreteAliases(state.metaData(), index)) {
|
||||
finalActions.add(new AliasAction.Add(index, alias, action.filter(), action.indexRouting(), action.searchRouting()));
|
||||
}
|
||||
break;
|
||||
case REMOVE:
|
||||
for (String alias : action.concreteAliases(state.metaData(), index)) {
|
||||
finalActions.add(new AliasAction.Remove(index, alias));
|
||||
}
|
||||
break;
|
||||
case REMOVE_INDEX:
|
||||
finalActions.add(new AliasAction.RemoveIndex(index));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported action [" + action.actionType() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasOnlyDeletesButNoneCanBeDone && actions.size() != 0) {
|
||||
if (finalActions.isEmpty() && false == actions.isEmpty()) {
|
||||
throw new AliasesNotFoundException(aliases.toArray(new String[aliases.size()]));
|
||||
}
|
||||
request.aliasActions().clear();
|
||||
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest()
|
||||
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
|
||||
.actions(finalActions.toArray(new AliasAction[finalActions.size()]));
|
||||
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(unmodifiableList(finalActions))
|
||||
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout());
|
||||
|
||||
indexAliasesService.indicesAliases(updateRequest, new ActionListener<ClusterStateUpdateResponse>() {
|
||||
@Override
|
||||
|
|
|
@ -47,11 +47,15 @@ import org.elasticsearch.index.shard.DocsStats;
|
|||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
||||
/**
|
||||
* Main class to swap the index pointed to by an alias, given some conditions
|
||||
*/
|
||||
|
@ -156,13 +160,12 @@ public class TransportRolloverAction extends TransportMasterNodeAction<RolloverR
|
|||
|
||||
static IndicesAliasesClusterStateUpdateRequest prepareRolloverAliasesUpdateRequest(String oldIndex, String newIndex,
|
||||
RolloverRequest request) {
|
||||
final IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest()
|
||||
List<AliasAction> actions = unmodifiableList(Arrays.asList(
|
||||
new AliasAction.Add(newIndex, request.getAlias(), null, null, null),
|
||||
new AliasAction.Remove(oldIndex, request.getAlias())));
|
||||
final IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(actions)
|
||||
.ackTimeout(request.ackTimeout())
|
||||
.masterNodeTimeout(request.masterNodeTimeout());
|
||||
AliasAction[] actions = new AliasAction[2];
|
||||
actions[0] = new AliasAction(AliasAction.Type.ADD, newIndex, request.getAlias());
|
||||
actions[1] = new AliasAction(AliasAction.Type.REMOVE, oldIndex, request.getAlias());
|
||||
updateRequest.actions(actions);
|
||||
return updateRequest;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,213 +19,167 @@
|
|||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.ElasticsearchGenerationException;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Streamable;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import org.elasticsearch.common.Strings;
|
||||
|
||||
/**
|
||||
*
|
||||
* Individual operation to perform on the cluster state as part of an {@link IndicesAliasesRequest}.
|
||||
*/
|
||||
public class AliasAction implements Streamable {
|
||||
public abstract class AliasAction {
|
||||
private final String index;
|
||||
|
||||
public static enum Type {
|
||||
ADD((byte) 0),
|
||||
REMOVE((byte) 1);
|
||||
|
||||
private final byte value;
|
||||
|
||||
Type(byte value) {
|
||||
this.value = value;
|
||||
private AliasAction(String index) {
|
||||
if (false == Strings.hasText(index)) {
|
||||
throw new IllegalArgumentException("[index] is required");
|
||||
}
|
||||
|
||||
public byte value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Type fromValue(byte value) {
|
||||
if (value == 0) {
|
||||
return ADD;
|
||||
} else if (value == 1) {
|
||||
return REMOVE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("No type for action [" + value + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Type actionType;
|
||||
|
||||
private String index;
|
||||
|
||||
private String alias;
|
||||
|
||||
@Nullable
|
||||
private String filter;
|
||||
|
||||
@Nullable
|
||||
private String indexRouting;
|
||||
|
||||
@Nullable
|
||||
private String searchRouting;
|
||||
|
||||
private AliasAction() {
|
||||
|
||||
}
|
||||
|
||||
public AliasAction(AliasAction other) {
|
||||
this.actionType = other.actionType;
|
||||
this.index = other.index;
|
||||
this.alias = other.alias;
|
||||
this.filter = other.filter;
|
||||
this.indexRouting = other.indexRouting;
|
||||
this.searchRouting = other.searchRouting;
|
||||
}
|
||||
|
||||
public AliasAction(Type actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public AliasAction(Type actionType, String index, String alias) {
|
||||
this.actionType = actionType;
|
||||
this.index = index;
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public AliasAction(Type actionType, String index, String alias, String filter) {
|
||||
this.actionType = actionType;
|
||||
this.index = index;
|
||||
this.alias = alias;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public Type actionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public AliasAction index(String index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String index() {
|
||||
/**
|
||||
* Get the index on which the operation should act.
|
||||
*/
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public AliasAction alias(String alias) {
|
||||
this.alias = alias;
|
||||
return this;
|
||||
|
||||
/**
|
||||
* Should this action remove the index? Actions that return true from this will never execute
|
||||
* {@link #apply(NewAliasValidator, MetaData.Builder, IndexMetaData)}.
|
||||
*/
|
||||
abstract boolean removeIndex();
|
||||
|
||||
/**
|
||||
* Apply the action.
|
||||
*
|
||||
* @param aliasValidator call to validate a new alias before adding it to the builder
|
||||
* @param metadata metadata builder for the changes made by all actions as part of this request
|
||||
* @param index metadata for the index being changed
|
||||
* @return did this action make any changes?
|
||||
*/
|
||||
abstract boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index);
|
||||
|
||||
/**
|
||||
* Validate a new alias.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface NewAliasValidator {
|
||||
void validate(String alias, @Nullable String indexRouting, @Nullable String filter);
|
||||
}
|
||||
|
||||
public String alias() {
|
||||
return alias;
|
||||
}
|
||||
/**
|
||||
* Operation to add an alias to an index.
|
||||
*/
|
||||
public static class Add extends AliasAction {
|
||||
private final String alias;
|
||||
|
||||
public String filter() {
|
||||
return filter;
|
||||
}
|
||||
@Nullable
|
||||
private final String filter;
|
||||
|
||||
public AliasAction filter(String filter) {
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
@Nullable
|
||||
private final String indexRouting;
|
||||
|
||||
public AliasAction filter(Map<String, Object> filter) {
|
||||
if (filter == null || filter.isEmpty()) {
|
||||
this.filter = null;
|
||||
return this;
|
||||
@Nullable
|
||||
private final String searchRouting;
|
||||
|
||||
/**
|
||||
* Build the operation.
|
||||
*/
|
||||
public Add(String index, String alias, @Nullable String filter, @Nullable String indexRouting, @Nullable String searchRouting) {
|
||||
super(index);
|
||||
if (false == Strings.hasText(alias)) {
|
||||
throw new IllegalArgumentException("[alias] is required");
|
||||
}
|
||||
this.alias = alias;
|
||||
this.filter = filter;
|
||||
this.indexRouting = indexRouting;
|
||||
this.searchRouting = searchRouting;
|
||||
}
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
builder.map(filter);
|
||||
this.filter = builder.string();
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchGenerationException("Failed to generate [" + filter + "]", e);
|
||||
|
||||
/**
|
||||
* Alias to add to the index.
|
||||
*/
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeIndex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index) {
|
||||
aliasValidator.validate(alias, indexRouting, filter);
|
||||
AliasMetaData newAliasMd = AliasMetaData.newAliasMetaDataBuilder(alias).filter(filter).indexRouting(indexRouting)
|
||||
.searchRouting(searchRouting).build();
|
||||
// Check if this alias already exists
|
||||
AliasMetaData currentAliasMd = index.getAliases().get(alias);
|
||||
if (currentAliasMd != null && currentAliasMd.equals(newAliasMd)) {
|
||||
// It already exists, ignore it
|
||||
return false;
|
||||
}
|
||||
metadata.put(IndexMetaData.builder(index).putAlias(newAliasMd));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public AliasAction filter(QueryBuilder queryBuilder) {
|
||||
if (queryBuilder == null) {
|
||||
this.filter = null;
|
||||
return this;
|
||||
/**
|
||||
* Operation to remove an alias from an index.
|
||||
*/
|
||||
public static class Remove extends AliasAction {
|
||||
private final String alias;
|
||||
|
||||
/**
|
||||
* Build the operation.
|
||||
*/
|
||||
public Remove(String index, String alias) {
|
||||
super(index);
|
||||
if (false == Strings.hasText(alias)) {
|
||||
throw new IllegalArgumentException("[alias] is required");
|
||||
}
|
||||
this.alias = alias;
|
||||
}
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
queryBuilder.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.close();
|
||||
this.filter = builder.string();
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchGenerationException("Failed to build json for alias request", e);
|
||||
|
||||
/**
|
||||
* Alias to remove from the index.
|
||||
*/
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeIndex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index) {
|
||||
if (false == index.getAliases().containsKey(alias)) {
|
||||
return false;
|
||||
}
|
||||
metadata.put(IndexMetaData.builder(index).removeAlias(alias));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public AliasAction routing(String routing) {
|
||||
this.indexRouting = routing;
|
||||
this.searchRouting = routing;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Operation to remove an index. This is an "alias action" because it allows us to remove an index at the same time as we remove add an
|
||||
* alias to replace it.
|
||||
*/
|
||||
public static class RemoveIndex extends AliasAction {
|
||||
public RemoveIndex(String index) {
|
||||
super(index);
|
||||
}
|
||||
|
||||
public String indexRouting() {
|
||||
return indexRouting;
|
||||
}
|
||||
@Override
|
||||
boolean removeIndex() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public AliasAction indexRouting(String indexRouting) {
|
||||
this.indexRouting = indexRouting;
|
||||
return this;
|
||||
@Override
|
||||
boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public String searchRouting() {
|
||||
return searchRouting;
|
||||
}
|
||||
|
||||
public AliasAction searchRouting(String searchRouting) {
|
||||
this.searchRouting = searchRouting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static AliasAction readAliasAction(StreamInput in) throws IOException {
|
||||
AliasAction aliasAction = new AliasAction();
|
||||
aliasAction.readFrom(in);
|
||||
return aliasAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
actionType = Type.fromValue(in.readByte());
|
||||
index = in.readOptionalString();
|
||||
alias = in.readOptionalString();
|
||||
filter = in.readOptionalString();
|
||||
indexRouting = in.readOptionalString();
|
||||
searchRouting = in.readOptionalString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeByte(actionType.value());
|
||||
out.writeOptionalString(index);
|
||||
out.writeOptionalString(alias);
|
||||
out.writeOptionalString(filter);
|
||||
out.writeOptionalString(indexRouting);
|
||||
out.writeOptionalString(searchRouting);
|
||||
}
|
||||
|
||||
public static AliasAction newAddAliasAction(String index, String alias) {
|
||||
return new AliasAction(Type.ADD, index, alias);
|
||||
}
|
||||
|
||||
public static AliasAction newRemoveAliasAction(String index, String alias) {
|
||||
return new AliasAction(Type.REMOVE, index, alias);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
|
@ -33,6 +34,7 @@ import org.elasticsearch.indices.InvalidAliasNameException;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Validator for an alias, to be used before adding an alias to the index metadata
|
||||
|
@ -45,22 +47,13 @@ public class AliasValidator extends AbstractComponent {
|
|||
super(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to validate an {@link org.elasticsearch.cluster.metadata.AliasAction} and make sure
|
||||
* it's valid before it gets added to the index metadata. Doesn't validate the alias filter.
|
||||
* @throws IllegalArgumentException if the alias is not valid
|
||||
*/
|
||||
public void validateAliasAction(AliasAction aliasAction, MetaData metaData) {
|
||||
validateAlias(aliasAction.alias(), aliasAction.index(), aliasAction.indexRouting(), metaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to validate an {@link org.elasticsearch.action.admin.indices.alias.Alias} and make sure
|
||||
* it's valid before it gets added to the index metadata. Doesn't validate the alias filter.
|
||||
* @throws IllegalArgumentException if the alias is not valid
|
||||
*/
|
||||
public void validateAlias(Alias alias, String index, MetaData metaData) {
|
||||
validateAlias(alias.name(), index, alias.indexRouting(), metaData);
|
||||
validateAlias(alias.name(), index, alias.indexRouting(), name -> metaData.index(name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +62,7 @@ public class AliasValidator extends AbstractComponent {
|
|||
* @throws IllegalArgumentException if the alias is not valid
|
||||
*/
|
||||
public void validateAliasMetaData(AliasMetaData aliasMetaData, String index, MetaData metaData) {
|
||||
validateAlias(aliasMetaData.alias(), index, aliasMetaData.indexRouting(), metaData);
|
||||
validateAlias(aliasMetaData.alias(), index, aliasMetaData.indexRouting(), name -> metaData.index(name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,16 +83,19 @@ public class AliasValidator extends AbstractComponent {
|
|||
}
|
||||
}
|
||||
|
||||
private void validateAlias(String alias, String index, String indexRouting, MetaData metaData) {
|
||||
/**
|
||||
* Validate a proposed alias.
|
||||
*/
|
||||
public void validateAlias(String alias, String index, @Nullable String indexRouting, Function<String, IndexMetaData> indexLookup) {
|
||||
validateAliasStandalone(alias, indexRouting);
|
||||
|
||||
if (!Strings.hasText(index)) {
|
||||
throw new IllegalArgumentException("index name is required");
|
||||
}
|
||||
|
||||
assert metaData != null;
|
||||
if (metaData.hasIndex(alias)) {
|
||||
throw new InvalidAliasNameException(metaData.index(alias).getIndex(), alias, "an index exists with the same name as the alias");
|
||||
IndexMetaData indexNamedSameAsAlias = indexLookup.apply(alias);
|
||||
if (indexNamedSameAsAlias != null) {
|
||||
throw new InvalidAliasNameException(indexNamedSameAsAlias.getIndex(), alias, "an index exists with the same name as the alias");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,11 +37,11 @@ import org.elasticsearch.index.Index;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
|
@ -219,7 +219,7 @@ public final class IndexGraveyard implements MetaData.Custom {
|
|||
/**
|
||||
* Add a set of deleted indexes to the list of tombstones in the cluster state.
|
||||
*/
|
||||
public Builder addTombstones(final Index[] indices) {
|
||||
public Builder addTombstones(final Collection<Index> indices) {
|
||||
for (Index index : indices) {
|
||||
addTombstone(index);
|
||||
}
|
||||
|
|
|
@ -37,11 +37,13 @@ import org.elasticsearch.index.Index;
|
|||
import org.elasticsearch.snapshots.SnapshotsService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* Deletes indices.
|
||||
*/
|
||||
public class MetaDataDeleteIndexService extends AbstractComponent {
|
||||
|
||||
|
@ -56,7 +58,8 @@ public class MetaDataDeleteIndexService extends AbstractComponent {
|
|||
this.allocationService = allocationService;
|
||||
}
|
||||
|
||||
public void deleteIndices(final DeleteIndexClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
public void deleteIndices(final DeleteIndexClusterStateUpdateRequest request,
|
||||
final ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
if (request.indices() == null || request.indices().length == 0) {
|
||||
throw new IllegalArgumentException("Index name is required");
|
||||
}
|
||||
|
@ -71,37 +74,43 @@ public class MetaDataDeleteIndexService extends AbstractComponent {
|
|||
|
||||
@Override
|
||||
public ClusterState execute(final ClusterState currentState) {
|
||||
final MetaData meta = currentState.metaData();
|
||||
final Index[] indices = request.indices();
|
||||
final Set<IndexMetaData> metaDatas = Arrays.asList(indices).stream().map(i -> meta.getIndexSafe(i)).collect(Collectors.toSet());
|
||||
// Check if index deletion conflicts with any running snapshots
|
||||
SnapshotsService.checkIndexDeletion(currentState, metaDatas);
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder(meta);
|
||||
ClusterBlocks.Builder clusterBlocksBuilder = ClusterBlocks.builder().blocks(currentState.blocks());
|
||||
|
||||
final IndexGraveyard.Builder graveyardBuilder = IndexGraveyard.builder(metaDataBuilder.indexGraveyard());
|
||||
final int previousGraveyardSize = graveyardBuilder.tombstones().size();
|
||||
for (final Index index : indices) {
|
||||
String indexName = index.getName();
|
||||
logger.debug("[{}] deleting index", index);
|
||||
routingTableBuilder.remove(indexName);
|
||||
clusterBlocksBuilder.removeIndexBlocks(indexName);
|
||||
metaDataBuilder.remove(indexName);
|
||||
}
|
||||
// add tombstones to the cluster state for each deleted index
|
||||
final IndexGraveyard currentGraveyard = graveyardBuilder.addTombstones(indices).build(settings);
|
||||
metaDataBuilder.indexGraveyard(currentGraveyard); // the new graveyard set on the metadata
|
||||
logger.trace("{} tombstones purged from the cluster state. Previous tombstone size: {}. Current tombstone size: {}.",
|
||||
graveyardBuilder.getNumPurged(), previousGraveyardSize, currentGraveyard.getTombstones().size());
|
||||
|
||||
MetaData newMetaData = metaDataBuilder.build();
|
||||
ClusterBlocks blocks = clusterBlocksBuilder.build();
|
||||
RoutingAllocation.Result routingResult = allocationService.reroute(
|
||||
ClusterState.builder(currentState).routingTable(routingTableBuilder.build()).metaData(newMetaData).build(),
|
||||
"deleted indices [" + indices + "]");
|
||||
return ClusterState.builder(currentState).routingResult(routingResult).metaData(newMetaData).blocks(blocks).build();
|
||||
return deleteIndices(currentState, Arrays.asList(request.indices()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete some indices from the cluster state.
|
||||
*/
|
||||
public ClusterState deleteIndices(ClusterState currentState, Collection<Index> indices) {
|
||||
final MetaData meta = currentState.metaData();
|
||||
final Set<IndexMetaData> metaDatas = indices.stream().map(i -> meta.getIndexSafe(i)).collect(toSet());
|
||||
// Check if index deletion conflicts with any running snapshots
|
||||
SnapshotsService.checkIndexDeletion(currentState, metaDatas);
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder(meta);
|
||||
ClusterBlocks.Builder clusterBlocksBuilder = ClusterBlocks.builder().blocks(currentState.blocks());
|
||||
|
||||
final IndexGraveyard.Builder graveyardBuilder = IndexGraveyard.builder(metaDataBuilder.indexGraveyard());
|
||||
final int previousGraveyardSize = graveyardBuilder.tombstones().size();
|
||||
for (final Index index : indices) {
|
||||
String indexName = index.getName();
|
||||
logger.debug("[{}] deleting index", index);
|
||||
routingTableBuilder.remove(indexName);
|
||||
clusterBlocksBuilder.removeIndexBlocks(indexName);
|
||||
metaDataBuilder.remove(indexName);
|
||||
}
|
||||
// add tombstones to the cluster state for each deleted index
|
||||
final IndexGraveyard currentGraveyard = graveyardBuilder.addTombstones(indices).build(settings);
|
||||
metaDataBuilder.indexGraveyard(currentGraveyard); // the new graveyard set on the metadata
|
||||
logger.trace("{} tombstones purged from the cluster state. Previous tombstone size: {}. Current tombstone size: {}.",
|
||||
graveyardBuilder.getNumPurged(), previousGraveyardSize, currentGraveyard.getTombstones().size());
|
||||
|
||||
MetaData newMetaData = metaDataBuilder.build();
|
||||
ClusterBlocks blocks = clusterBlocksBuilder.build();
|
||||
RoutingAllocation.Result routingResult = allocationService.reroute(
|
||||
ClusterState.builder(currentState).routingTable(routingTableBuilder.build()).metaData(newMetaData).build(),
|
||||
"deleted indices [" + indices + "]");
|
||||
return ClusterState.builder(currentState).routingResult(routingResult).metaData(newMetaData).blocks(blocks).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,14 @@
|
|||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
|
||||
import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction.NewAliasValidator;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.common.Strings;
|
||||
|
@ -38,11 +41,16 @@ import org.elasticsearch.index.NodeServicesProvider;
|
|||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.indices.IndicesService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* Service responsible for submitting add and remove aliases requests
|
||||
|
@ -56,109 +64,114 @@ public class MetaDataIndexAliasesService extends AbstractComponent {
|
|||
private final AliasValidator aliasValidator;
|
||||
|
||||
private final NodeServicesProvider nodeServicesProvider;
|
||||
|
||||
private final MetaDataDeleteIndexService deleteIndexService;
|
||||
|
||||
@Inject
|
||||
public MetaDataIndexAliasesService(Settings settings, ClusterService clusterService, IndicesService indicesService, AliasValidator aliasValidator, NodeServicesProvider nodeServicesProvider) {
|
||||
public MetaDataIndexAliasesService(Settings settings, ClusterService clusterService, IndicesService indicesService,
|
||||
AliasValidator aliasValidator, NodeServicesProvider nodeServicesProvider, MetaDataDeleteIndexService deleteIndexService) {
|
||||
super(settings);
|
||||
this.clusterService = clusterService;
|
||||
this.indicesService = indicesService;
|
||||
this.aliasValidator = aliasValidator;
|
||||
this.nodeServicesProvider = nodeServicesProvider;
|
||||
this.deleteIndexService = deleteIndexService;
|
||||
}
|
||||
|
||||
public void indicesAliases(final IndicesAliasesClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
clusterService.submitStateUpdateTask("index-aliases", new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, listener) {
|
||||
public void indicesAliases(final IndicesAliasesClusterStateUpdateRequest request,
|
||||
final ActionListener<ClusterStateUpdateResponse> listener) {
|
||||
clusterService.submitStateUpdateTask("index-aliases",
|
||||
new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, listener) {
|
||||
@Override
|
||||
protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
|
||||
return new ClusterStateUpdateResponse(acknowledged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterState execute(final ClusterState currentState) {
|
||||
List<Index> indicesToClose = new ArrayList<>();
|
||||
Map<String, IndexService> indices = new HashMap<>();
|
||||
try {
|
||||
for (AliasAction aliasAction : request.actions()) {
|
||||
aliasValidator.validateAliasAction(aliasAction, currentState.metaData());
|
||||
if (!currentState.metaData().hasIndex(aliasAction.index())) {
|
||||
throw new IndexNotFoundException(aliasAction.index());
|
||||
}
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
MetaData.Builder builder = MetaData.builder(currentState.metaData());
|
||||
for (AliasAction aliasAction : request.actions()) {
|
||||
IndexMetaData indexMetaData = builder.get(aliasAction.index());
|
||||
if (indexMetaData == null) {
|
||||
throw new IndexNotFoundException(aliasAction.index());
|
||||
}
|
||||
// TODO: not copy (putAll)
|
||||
IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder(indexMetaData);
|
||||
if (aliasAction.actionType() == AliasAction.Type.ADD) {
|
||||
String filter = aliasAction.filter();
|
||||
if (Strings.hasLength(filter)) {
|
||||
// parse the filter, in order to validate it
|
||||
IndexService indexService = indices.get(indexMetaData.getIndex());
|
||||
if (indexService == null) {
|
||||
indexService = indicesService.indexService(indexMetaData.getIndex());
|
||||
if (indexService == null) {
|
||||
// temporarily create the index and add mappings so we can parse the filter
|
||||
try {
|
||||
indexService = indicesService.createIndex(nodeServicesProvider, indexMetaData, Collections.emptyList());
|
||||
for (ObjectCursor<MappingMetaData> cursor : indexMetaData.getMappings().values()) {
|
||||
MappingMetaData mappingMetaData = cursor.value;
|
||||
indexService.mapperService().merge(mappingMetaData.type(), mappingMetaData.source(), MapperService.MergeReason.MAPPING_RECOVERY, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("[{}] failed to temporary create in order to apply alias action", e, indexMetaData.getIndex());
|
||||
continue;
|
||||
}
|
||||
indicesToClose.add(indexMetaData.getIndex());
|
||||
}
|
||||
indices.put(indexMetaData.getIndex().getName(), indexService);
|
||||
}
|
||||
|
||||
aliasValidator.validateAliasFilter(aliasAction.alias(), filter, indexService.newQueryShardContext());
|
||||
}
|
||||
AliasMetaData newAliasMd = AliasMetaData.newAliasMetaDataBuilder(
|
||||
aliasAction.alias())
|
||||
.filter(filter)
|
||||
.indexRouting(aliasAction.indexRouting())
|
||||
.searchRouting(aliasAction.searchRouting())
|
||||
.build();
|
||||
// Check if this alias already exists
|
||||
AliasMetaData aliasMd = indexMetaData.getAliases().get(aliasAction.alias());
|
||||
if (aliasMd != null && aliasMd.equals(newAliasMd)) {
|
||||
// It's the same alias - ignore it
|
||||
continue;
|
||||
}
|
||||
indexMetaDataBuilder.putAlias(newAliasMd);
|
||||
} else if (aliasAction.actionType() == AliasAction.Type.REMOVE) {
|
||||
if (!indexMetaData.getAliases().containsKey(aliasAction.alias())) {
|
||||
// This alias doesn't exist - ignore
|
||||
continue;
|
||||
}
|
||||
indexMetaDataBuilder.removeAlias(aliasAction.alias());
|
||||
}
|
||||
changed = true;
|
||||
builder.put(indexMetaDataBuilder);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ClusterState updatedState = ClusterState.builder(currentState).metaData(builder).build();
|
||||
// even though changes happened, they resulted in 0 actual changes to metadata
|
||||
// i.e. remove and add the same alias to the same index
|
||||
if (!updatedState.metaData().equalsAliases(currentState.metaData())) {
|
||||
return updatedState;
|
||||
}
|
||||
}
|
||||
return currentState;
|
||||
} finally {
|
||||
for (Index index : indicesToClose) {
|
||||
indicesService.removeIndex(index, "created for alias processing");
|
||||
}
|
||||
}
|
||||
public ClusterState execute(ClusterState currentState) {
|
||||
return innerExecute(currentState, request.actions());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ClusterState innerExecute(ClusterState currentState, Iterable<AliasAction> actions) {
|
||||
List<Index> indicesToClose = new ArrayList<>();
|
||||
Map<String, IndexService> indices = new HashMap<>();
|
||||
try {
|
||||
boolean changed = false;
|
||||
// Gather all the indexes that must be removed first so:
|
||||
// 1. We don't cause error when attempting to replace an index with a alias of the same name.
|
||||
// 2. We don't allow removal of aliases from indexes that we're just going to delete anyway. That'd be silly.
|
||||
Set<Index> indicesToDelete = new HashSet<>();
|
||||
for (AliasAction action : actions) {
|
||||
if (action.removeIndex()) {
|
||||
IndexMetaData index = currentState.metaData().getIndices().get(action.getIndex());
|
||||
if (index == null) {
|
||||
throw new IndexNotFoundException(action.getIndex());
|
||||
}
|
||||
indicesToDelete.add(index.getIndex());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// Remove the indexes if there are any to remove
|
||||
if (changed) {
|
||||
currentState = deleteIndexService.deleteIndices(currentState, indicesToDelete);
|
||||
}
|
||||
MetaData.Builder metadata = MetaData.builder(currentState.metaData());
|
||||
// Run the remaining alias actions
|
||||
for (AliasAction action : actions) {
|
||||
if (action.removeIndex()) {
|
||||
// Handled above
|
||||
continue;
|
||||
}
|
||||
IndexMetaData index = metadata.get(action.getIndex());
|
||||
if (index == null) {
|
||||
throw new IndexNotFoundException(action.getIndex());
|
||||
}
|
||||
NewAliasValidator newAliasValidator = (alias, indexRouting, filter) -> {
|
||||
/* It is important that we look up the index using the metadata builder we are modifying so we can remove an
|
||||
* index and replace it with an alias. */
|
||||
Function<String, IndexMetaData> indexLookup = name -> metadata.get(name);
|
||||
aliasValidator.validateAlias(alias, action.getIndex(), indexRouting, indexLookup);
|
||||
if (Strings.hasLength(filter)) {
|
||||
IndexService indexService = indices.get(index.getIndex());
|
||||
if (indexService == null) {
|
||||
indexService = indicesService.indexService(index.getIndex());
|
||||
if (indexService == null) {
|
||||
// temporarily create the index and add mappings so we can parse the filter
|
||||
try {
|
||||
indexService = indicesService.createIndex(nodeServicesProvider, index, emptyList());
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Failed to create temporary index for parsing the alias", e);
|
||||
}
|
||||
for (ObjectCursor<MappingMetaData> cursor : index.getMappings().values()) {
|
||||
MappingMetaData mappingMetaData = cursor.value;
|
||||
indexService.mapperService().merge(mappingMetaData.type(), mappingMetaData.source(),
|
||||
MapperService.MergeReason.MAPPING_RECOVERY, false);
|
||||
}
|
||||
indicesToClose.add(index.getIndex());
|
||||
}
|
||||
indices.put(action.getIndex(), indexService);
|
||||
}
|
||||
aliasValidator.validateAliasFilter(alias, filter, indexService.newQueryShardContext());
|
||||
}
|
||||
};
|
||||
changed |= action.apply(newAliasValidator, metadata, index);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ClusterState updatedState = ClusterState.builder(currentState).metaData(metadata).build();
|
||||
// even though changes happened, they resulted in 0 actual changes to metadata
|
||||
// i.e. remove and add the same alias to the same index
|
||||
if (!updatedState.metaData().equalsAliases(currentState.metaData())) {
|
||||
return updatedState;
|
||||
}
|
||||
}
|
||||
return currentState;
|
||||
} finally {
|
||||
for (Index index : indicesToClose) {
|
||||
indicesService.removeIndex(index, "created for alias processing");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
|
||||
package org.elasticsearch.common.xcontent;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A generic abstraction on top of handling content, inspired by JSON and pull parsing.
|
||||
|
@ -42,27 +45,20 @@ public interface XContent {
|
|||
* Creates a new generator using the provided output stream.
|
||||
*/
|
||||
default XContentGenerator createGenerator(OutputStream os) throws IOException {
|
||||
return createGenerator(os, null, true);
|
||||
return createGenerator(os, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new generator using the provided output stream and some
|
||||
* inclusive filters. Same as createGenerator(os, filters, true).
|
||||
*/
|
||||
default XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException {
|
||||
return createGenerator(os, filters, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new generator using the provided output stream and some
|
||||
* filters.
|
||||
* Creates a new generator using the provided output stream and some inclusive and/or exclusive filters. When both exclusive and
|
||||
* inclusive filters are provided, the underlying generator will first use exclusion filters to remove fields and then will check the
|
||||
* remaining fields against the inclusive filters.
|
||||
*
|
||||
* @param inclusive
|
||||
* If true only paths matching a filter will be included in
|
||||
* output. If false no path matching a filter will be included in
|
||||
* output
|
||||
* @param os the output stream
|
||||
* @param includes the inclusive filters: only fields and objects that match the inclusive filters will be written to the output.
|
||||
* @param excludes the exclusive filters: only fields and objects that don't match the exclusive filters will be written to the output.
|
||||
*/
|
||||
XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException;
|
||||
XContentGenerator createGenerator(OutputStream os, Set<String> includes, Set<String> excludes) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a parser over the provided string content.
|
||||
*/
|
||||
|
|
|
@ -19,21 +19,8 @@
|
|||
|
||||
package org.elasticsearch.common.xcontent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.io.BytesStream;
|
||||
|
@ -47,6 +34,21 @@ import org.joda.time.ReadableInstant;
|
|||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A utility to build XContent (ie json).
|
||||
*/
|
||||
|
@ -58,12 +60,8 @@ public final class XContentBuilder implements BytesStream, Releasable {
|
|||
return new XContentBuilder(xContent, new BytesStreamOutput());
|
||||
}
|
||||
|
||||
public static XContentBuilder builder(XContent xContent, String[] filters) throws IOException {
|
||||
return new XContentBuilder(xContent, new BytesStreamOutput(), filters);
|
||||
}
|
||||
|
||||
public static XContentBuilder builder(XContent xContent, String[] filters, boolean inclusive) throws IOException {
|
||||
return new XContentBuilder(xContent, new BytesStreamOutput(), filters, inclusive);
|
||||
public static XContentBuilder builder(XContent xContent, Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return new XContentBuilder(xContent, new BytesStreamOutput(), includes, excludes);
|
||||
}
|
||||
|
||||
private XContentGenerator generator;
|
||||
|
@ -77,7 +75,7 @@ public final class XContentBuilder implements BytesStream, Releasable {
|
|||
* to call {@link #close()} when the builder is done with.
|
||||
*/
|
||||
public XContentBuilder(XContent xContent, OutputStream bos) throws IOException {
|
||||
this(xContent, bos, null);
|
||||
this(xContent, bos, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,20 +84,24 @@ public final class XContentBuilder implements BytesStream, Releasable {
|
|||
* filter will be written to the output stream. Make sure to call
|
||||
* {@link #close()} when the builder is done with.
|
||||
*/
|
||||
public XContentBuilder(XContent xContent, OutputStream bos, String[] filters) throws IOException {
|
||||
this(xContent, bos, filters, true);
|
||||
public XContentBuilder(XContent xContent, OutputStream bos, Set<String> includes) throws IOException {
|
||||
this(xContent, bos, includes, Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new builder using the provided xcontent, an OutputStream and
|
||||
* some filters. If {@code filters} are specified and {@code inclusive} is
|
||||
* true, only those values matching a filter will be written to the output
|
||||
* stream. If {@code inclusive} is false, those matching will be excluded.
|
||||
* Creates a new builder using the provided XContent, output stream and some inclusive and/or exclusive filters. When both exclusive and
|
||||
* inclusive filters are provided, the underlying builder will first use exclusion filters to remove fields and then will check the
|
||||
* remaining fields against the inclusive filters.
|
||||
* <p>
|
||||
* Make sure to call {@link #close()} when the builder is done with.
|
||||
*
|
||||
* @param os the output stream
|
||||
* @param includes the inclusive filters: only fields and objects that match the inclusive filters will be written to the output.
|
||||
* @param excludes the exclusive filters: only fields and objects that don't match the exclusive filters will be written to the output.
|
||||
*/
|
||||
public XContentBuilder(XContent xContent, OutputStream bos, String[] filters, boolean inclusive) throws IOException {
|
||||
this.bos = bos;
|
||||
this.generator = xContent.createGenerator(bos, filters, inclusive);
|
||||
public XContentBuilder(XContent xContent, OutputStream os, Set<String> includes, Set<String> excludes) throws IOException {
|
||||
this.bos = os;
|
||||
this.generator = xContent.createGenerator(bos, includes, excludes);
|
||||
}
|
||||
|
||||
public XContentType contentType() {
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A CBOR based content implementation using Jackson.
|
||||
|
@ -70,8 +71,8 @@ public class CborXContent implements XContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException {
|
||||
return new CborXContentGenerator(cborFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive);
|
||||
public XContentGenerator createGenerator(OutputStream os, Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return new CborXContentGenerator(cborFactory.createGenerator(os, JsonEncoding.UTF8), os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,23 +20,22 @@
|
|||
package org.elasticsearch.common.xcontent.cbor;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContentGenerator;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CborXContentGenerator extends JsonXContentGenerator {
|
||||
|
||||
public CborXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) {
|
||||
this(jsonGenerator, os, filters, true);
|
||||
public CborXContentGenerator(JsonGenerator jsonGenerator, OutputStream os) {
|
||||
this(jsonGenerator, os, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
public CborXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) {
|
||||
super(jsonGenerator, os, filters, inclusive);
|
||||
public CborXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, Set<String> includes, Set<String> excludes) {
|
||||
super(jsonGenerator, os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A JSON based content implementation using Jackson.
|
||||
|
@ -92,8 +93,8 @@ public class JsonXContent implements XContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException {
|
||||
return new JsonXContentGenerator(jsonFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive);
|
||||
public XContentGenerator createGenerator(OutputStream os, Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return new JsonXContentGenerator(jsonFactory.createGenerator(os, JsonEncoding.UTF8), os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,10 +27,10 @@ import com.fasterxml.jackson.core.io.SerializedString;
|
|||
import com.fasterxml.jackson.core.json.JsonWriteContext;
|
||||
import com.fasterxml.jackson.core.util.DefaultIndenter;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
import com.fasterxml.jackson.core.util.JsonGeneratorDelegate;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.util.CollectionUtils;
|
||||
import org.elasticsearch.common.xcontent.XContent;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentGenerator;
|
||||
|
@ -43,6 +43,9 @@ import java.io.BufferedInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -72,23 +75,38 @@ public class JsonXContentGenerator implements XContentGenerator {
|
|||
private static final DefaultPrettyPrinter.Indenter INDENTER = new DefaultIndenter(" ", LF.getValue());
|
||||
private boolean prettyPrint = false;
|
||||
|
||||
public JsonXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) {
|
||||
public JsonXContentGenerator(JsonGenerator jsonGenerator, OutputStream os) {
|
||||
this(jsonGenerator, os, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
public JsonXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, Set<String> includes, Set<String> excludes) {
|
||||
Objects.requireNonNull(includes, "Including filters must not be null");
|
||||
Objects.requireNonNull(excludes, "Excluding filters must not be null");
|
||||
this.os = os;
|
||||
if (jsonGenerator instanceof GeneratorBase) {
|
||||
this.base = (GeneratorBase) jsonGenerator;
|
||||
} else {
|
||||
this.base = null;
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(filters)) {
|
||||
this.generator = jsonGenerator;
|
||||
this.filter = null;
|
||||
} else {
|
||||
this.filter = new FilteringGeneratorDelegate(jsonGenerator,
|
||||
new FilterPathBasedFilter(filters, inclusive), true, true);
|
||||
this.generator = this.filter;
|
||||
JsonGenerator generator = jsonGenerator;
|
||||
|
||||
boolean hasExcludes = excludes.isEmpty() == false;
|
||||
if (hasExcludes) {
|
||||
generator = new FilteringGeneratorDelegate(generator, new FilterPathBasedFilter(excludes, false), true, true);
|
||||
}
|
||||
|
||||
this.os = os;
|
||||
boolean hasIncludes = includes.isEmpty() == false;
|
||||
if (hasIncludes) {
|
||||
generator = new FilteringGeneratorDelegate(generator, new FilterPathBasedFilter(includes, true), true, true);
|
||||
}
|
||||
|
||||
if (hasExcludes || hasIncludes) {
|
||||
this.filter = (FilteringGeneratorDelegate) generator;
|
||||
} else {
|
||||
this.filter = null;
|
||||
}
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,23 +140,34 @@ public class JsonXContentGenerator implements XContentGenerator {
|
|||
generator.writeEndArray();
|
||||
}
|
||||
|
||||
protected boolean isFiltered() {
|
||||
private boolean isFiltered() {
|
||||
return filter != null;
|
||||
}
|
||||
|
||||
protected boolean inRoot() {
|
||||
private JsonGenerator getLowLevelGenerator() {
|
||||
if (isFiltered()) {
|
||||
JsonStreamContext context = filter.getFilterContext();
|
||||
return ((context != null) && (context.inRoot() && context.getCurrentName() == null));
|
||||
JsonGenerator delegate = filter.getDelegate();
|
||||
if (delegate instanceof JsonGeneratorDelegate) {
|
||||
// In case of combined inclusion and exclusion filters, we have one and only one another delegating level
|
||||
delegate = ((JsonGeneratorDelegate) delegate).getDelegate();
|
||||
assert delegate instanceof JsonGeneratorDelegate == false;
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
return false;
|
||||
return generator;
|
||||
}
|
||||
|
||||
private boolean inRoot() {
|
||||
JsonStreamContext context = generator.getOutputContext();
|
||||
return ((context != null) && (context.inRoot() && context.getCurrentName() == null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStartObject() throws IOException {
|
||||
if (isFiltered() && inRoot()) {
|
||||
// Bypass generator to always write the root start object
|
||||
filter.getDelegate().writeStartObject();
|
||||
if (inRoot()) {
|
||||
// Use the low level generator to write the startObject so that the root
|
||||
// start object is always written even if a filtered generator is used
|
||||
getLowLevelGenerator().writeStartObject();
|
||||
return;
|
||||
}
|
||||
generator.writeStartObject();
|
||||
|
@ -146,9 +175,10 @@ public class JsonXContentGenerator implements XContentGenerator {
|
|||
|
||||
@Override
|
||||
public void writeEndObject() throws IOException {
|
||||
if (isFiltered() && inRoot()) {
|
||||
// Bypass generator to always write the root end object
|
||||
filter.getDelegate().writeEndObject();
|
||||
if (inRoot()) {
|
||||
// Use the low level generator to write the startObject so that the root
|
||||
// start object is always written even if a filtered generator is used
|
||||
getLowLevelGenerator().writeEndObject();
|
||||
return;
|
||||
}
|
||||
generator.writeEndObject();
|
||||
|
@ -390,7 +420,8 @@ public class JsonXContentGenerator implements XContentGenerator {
|
|||
}
|
||||
if (writeLineFeedAtEnd) {
|
||||
flush();
|
||||
generator.writeRaw(LF);
|
||||
// Bypass generator to always write the line feed
|
||||
getLowLevelGenerator().writeRaw(LF);
|
||||
}
|
||||
generator.close();
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A Smile based content implementation using Jackson.
|
||||
|
@ -71,8 +72,8 @@ public class SmileXContent implements XContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException {
|
||||
return new SmileXContentGenerator(smileFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive);
|
||||
public XContentGenerator createGenerator(OutputStream os, Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return new SmileXContentGenerator(smileFactory.createGenerator(os, JsonEncoding.UTF8), os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,23 +20,22 @@
|
|||
package org.elasticsearch.common.xcontent.smile;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContentGenerator;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SmileXContentGenerator extends JsonXContentGenerator {
|
||||
|
||||
public SmileXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) {
|
||||
this(jsonGenerator, os, filters, true);
|
||||
public SmileXContentGenerator(JsonGenerator jsonGenerator, OutputStream os) {
|
||||
this(jsonGenerator, os, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
public SmileXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) {
|
||||
super(jsonGenerator, os, filters, inclusive);
|
||||
public SmileXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, Set<String> includes, Set<String> excludes) {
|
||||
super(jsonGenerator, os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
package org.elasticsearch.common.xcontent.support.filtering;
|
||||
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class FilterPath {
|
||||
|
||||
|
@ -75,8 +75,8 @@ public class FilterPath {
|
|||
return next;
|
||||
}
|
||||
|
||||
public static FilterPath[] compile(String... filters) {
|
||||
if (CollectionUtils.isEmpty(filters)) {
|
||||
public static FilterPath[] compile(Set<String> filters) {
|
||||
if (filters == null || filters.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.common.util.CollectionUtils;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class FilterPathBasedFilter extends TokenFilter {
|
||||
|
||||
|
@ -53,7 +54,7 @@ public class FilterPathBasedFilter extends TokenFilter {
|
|||
this.filters = filters;
|
||||
}
|
||||
|
||||
public FilterPathBasedFilter(String[] filters, boolean inclusive) {
|
||||
public FilterPathBasedFilter(Set<String> filters, boolean inclusive) {
|
||||
this(FilterPath.compile(filters), inclusive);
|
||||
}
|
||||
|
||||
|
@ -103,11 +104,6 @@ public class FilterPathBasedFilter extends TokenFilter {
|
|||
|
||||
@Override
|
||||
protected boolean _includeScalar() {
|
||||
for (FilterPath filter : filters) {
|
||||
if (filter.matches()) {
|
||||
return inclusive;
|
||||
}
|
||||
}
|
||||
return !inclusive;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A YAML based content implementation using Jackson.
|
||||
|
@ -66,8 +67,8 @@ public class YamlXContent implements XContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException {
|
||||
return new YamlXContentGenerator(yamlFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive);
|
||||
public XContentGenerator createGenerator(OutputStream os, Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return new YamlXContentGenerator(yamlFactory.createGenerator(os, JsonEncoding.UTF8), os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,23 +20,22 @@
|
|||
package org.elasticsearch.common.xcontent.yaml;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContentGenerator;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class YamlXContentGenerator extends JsonXContentGenerator {
|
||||
|
||||
public YamlXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) {
|
||||
this(jsonGenerator, os, filters, true);
|
||||
public YamlXContentGenerator(JsonGenerator jsonGenerator, OutputStream os) {
|
||||
this(jsonGenerator, os, Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
|
||||
public YamlXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) {
|
||||
super(jsonGenerator, os, filters, inclusive);
|
||||
public YamlXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, Set<String> includes, Set<String> excludes) {
|
||||
super(jsonGenerator, os, includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -166,6 +166,17 @@ public final class IngestDocument {
|
|||
* @throws IllegalArgumentException if the path is null, empty or invalid.
|
||||
*/
|
||||
public boolean hasField(String path) {
|
||||
return hasField(path, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the document contains a value for the provided path
|
||||
* @param path The path within the document in dot-notation
|
||||
* @param failOutOfRange Whether to throw an IllegalArgumentException if array is accessed outside of its range
|
||||
* @return true if the document contains a value for the field, false otherwise
|
||||
* @throws IllegalArgumentException if the path is null, empty or invalid.
|
||||
*/
|
||||
public boolean hasField(String path, boolean failOutOfRange) {
|
||||
FieldPath fieldPath = new FieldPath(path);
|
||||
Object context = fieldPath.initialContext;
|
||||
for (int i = 0; i < fieldPath.pathElements.length - 1; i++) {
|
||||
|
@ -183,7 +194,12 @@ public final class IngestDocument {
|
|||
try {
|
||||
int index = Integer.parseInt(pathElement);
|
||||
if (index < 0 || index >= list.size()) {
|
||||
return false;
|
||||
if (failOutOfRange) {
|
||||
throw new IllegalArgumentException("[" + index + "] is out of bounds for array with length [" +
|
||||
list.size() + "] as part of path [" + path +"]");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
context = list.get(index);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -206,7 +222,16 @@ public final class IngestDocument {
|
|||
List<Object> list = (List<Object>) context;
|
||||
try {
|
||||
int index = Integer.parseInt(leafKey);
|
||||
return index >= 0 && index < list.size();
|
||||
if (index >= 0 && index < list.size()) {
|
||||
return true;
|
||||
} else {
|
||||
if (failOutOfRange) {
|
||||
throw new IllegalArgumentException("[" + index + "] is out of bounds for array with length [" +
|
||||
list.size() + "] as part of path [" + path +"]");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.elasticsearch.rest;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -26,9 +27,17 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
public abstract class AbstractRestChannel implements RestChannel {
|
||||
|
||||
private static final Predicate<String> INCLUDE_FILTER = f -> f.charAt(0) != '-';
|
||||
private static final Predicate<String> EXCLUDE_FILTER = INCLUDE_FILTER.negate();
|
||||
|
||||
protected final RestRequest request;
|
||||
protected final boolean detailedErrorsEnabled;
|
||||
|
||||
|
@ -41,7 +50,7 @@ public abstract class AbstractRestChannel implements RestChannel {
|
|||
|
||||
@Override
|
||||
public XContentBuilder newBuilder() throws IOException {
|
||||
return newBuilder(request.hasContent() ? request.content() : null, request.hasParam("filter_path"));
|
||||
return newBuilder(request.hasContent() ? request.content() : null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,8 +73,15 @@ public abstract class AbstractRestChannel implements RestChannel {
|
|||
contentType = XContentType.JSON;
|
||||
}
|
||||
|
||||
String[] filters = useFiltering ? request.paramAsStringArrayOrEmptyIfAll("filter_path") : null;
|
||||
XContentBuilder builder = new XContentBuilder(XContentFactory.xContent(contentType), bytesOutput(), filters);
|
||||
Set<String> includes = Collections.emptySet();
|
||||
Set<String> excludes = Collections.emptySet();
|
||||
if (useFiltering) {
|
||||
Set<String> filters = Strings.splitStringByCommaToSet(request.param("filter_path", null));
|
||||
includes = filters.stream().filter(INCLUDE_FILTER).collect(toSet());
|
||||
excludes = filters.stream().filter(EXCLUDE_FILTER).map(f -> f.substring(1)).collect(toSet());
|
||||
}
|
||||
|
||||
XContentBuilder builder = new XContentBuilder(XContentFactory.xContent(contentType), bytesOutput(), includes, excludes);
|
||||
if (request.paramAsBoolean("pretty", false)) {
|
||||
builder.prettyPrint().lfAtEnd();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.elasticsearch.rest.action.admin.indices;
|
|||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
|
@ -49,7 +50,7 @@ public class RestIndexDeleteAliasesAction extends BaseRestHandler {
|
|||
final String[] aliases = Strings.splitStringByCommaToArray(request.param("name"));
|
||||
IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
|
||||
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
|
||||
indicesAliasesRequest.removeAlias(indices, aliases);
|
||||
indicesAliasesRequest.addAliasAction(AliasActions.remove().indices(indices).aliases(aliases));
|
||||
indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
|
||||
|
||||
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<IndicesAliasesResponse>(channel));
|
||||
|
|
|
@ -20,9 +20,7 @@ package org.elasticsearch.rest.action.admin.indices;
|
|||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -103,12 +101,9 @@ public class RestIndexPutAliasAction extends BaseRestHandler {
|
|||
|
||||
IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
|
||||
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
|
||||
String[] aliases = new String[]{alias};
|
||||
IndicesAliasesRequest.AliasActions aliasAction = new AliasActions(AliasAction.Type.ADD, indices, aliases);
|
||||
indicesAliasesRequest.addAliasAction(aliasAction);
|
||||
indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
|
||||
|
||||
|
||||
IndicesAliasesRequest.AliasActions aliasAction = AliasActions.add().indices(indices).alias(alias);
|
||||
if (routing != null) {
|
||||
aliasAction.routing(routing);
|
||||
}
|
||||
|
@ -121,6 +116,7 @@ public class RestIndexPutAliasAction extends BaseRestHandler {
|
|||
if (filter != null) {
|
||||
aliasAction.filter(filter);
|
||||
}
|
||||
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<IndicesAliasesResponse>(channel));
|
||||
indicesAliasesRequest.addAliasAction(aliasAction);
|
||||
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<>(channel));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,12 @@ import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
|||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.ParseFieldMatcherSupplier;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
|
@ -34,13 +37,17 @@ import org.elasticsearch.rest.RestController;
|
|||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.AcknowledgedRestListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
|
||||
public class RestIndicesAliasesAction extends BaseRestHandler {
|
||||
static final ObjectParser<IndicesAliasesRequest, ParseFieldMatcherSupplier> PARSER = new ObjectParser<>("aliases");
|
||||
static {
|
||||
PARSER.declareObjectArray((request, actions) -> {
|
||||
for (AliasActions action: actions) {
|
||||
request.addAliasAction(action);
|
||||
}
|
||||
}, AliasActions.PARSER, new ParseField("actions"));
|
||||
}
|
||||
|
||||
@Inject
|
||||
public RestIndicesAliasesAction(Settings settings, RestController controller) {
|
||||
|
@ -52,104 +59,12 @@ public class RestIndicesAliasesAction extends BaseRestHandler {
|
|||
public void handleRequest(final RestRequest request, final RestChannel channel, final NodeClient client) throws Exception {
|
||||
IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
|
||||
indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
|
||||
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
|
||||
try (XContentParser parser = XContentFactory.xContent(request.content()).createParser(request.content())) {
|
||||
// {
|
||||
// actions : [
|
||||
// { add : { index : "test1", alias : "alias1", filter : {"user" : "kimchy"} } }
|
||||
// { remove : { index : "test1", alias : "alias1" } }
|
||||
// ]
|
||||
// }
|
||||
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token == null) {
|
||||
throw new IllegalArgumentException("No action is specified");
|
||||
}
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.START_ARRAY) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
String action = parser.currentName();
|
||||
AliasAction.Type type;
|
||||
if ("add".equals(action)) {
|
||||
type = AliasAction.Type.ADD;
|
||||
} else if ("remove".equals(action)) {
|
||||
type = AliasAction.Type.REMOVE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Alias action [" + action + "] not supported");
|
||||
}
|
||||
String[] indices = null;
|
||||
String[] aliases = null;
|
||||
Map<String, Object> filter = null;
|
||||
String routing = null;
|
||||
boolean routingSet = false;
|
||||
String indexRouting = null;
|
||||
boolean indexRoutingSet = false;
|
||||
String searchRouting = null;
|
||||
boolean searchRoutingSet = false;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if ("index".equals(currentFieldName)) {
|
||||
indices = new String[] { parser.text() };
|
||||
} else if ("alias".equals(currentFieldName)) {
|
||||
aliases = new String[] { parser.text() };
|
||||
} else if ("routing".equals(currentFieldName)) {
|
||||
routing = parser.textOrNull();
|
||||
routingSet = true;
|
||||
} else if ("indexRouting".equals(currentFieldName)
|
||||
|| "index-routing".equals(currentFieldName) || "index_routing".equals(currentFieldName)) {
|
||||
indexRouting = parser.textOrNull();
|
||||
indexRoutingSet = true;
|
||||
} else if ("searchRouting".equals(currentFieldName)
|
||||
|| "search-routing".equals(currentFieldName) || "search_routing".equals(currentFieldName)) {
|
||||
searchRouting = parser.textOrNull();
|
||||
searchRoutingSet = true;
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
if ("indices".equals(currentFieldName)) {
|
||||
List<String> indexNames = new ArrayList<>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
String index = parser.text();
|
||||
indexNames.add(index);
|
||||
}
|
||||
indices = indexNames.toArray(new String[indexNames.size()]);
|
||||
}
|
||||
if ("aliases".equals(currentFieldName)) {
|
||||
List<String> aliasNames = new ArrayList<>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
String alias = parser.text();
|
||||
aliasNames.add(alias);
|
||||
}
|
||||
aliases = aliasNames.toArray(new String[aliasNames.size()]);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if ("filter".equals(currentFieldName)) {
|
||||
filter = parser.mapOrdered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == AliasAction.Type.ADD) {
|
||||
AliasActions aliasActions = new AliasActions(type, indices, aliases).filter(filter);
|
||||
if (routingSet) {
|
||||
aliasActions.routing(routing);
|
||||
}
|
||||
if (indexRoutingSet) {
|
||||
aliasActions.indexRouting(indexRouting);
|
||||
}
|
||||
if (searchRoutingSet) {
|
||||
aliasActions.searchRouting(searchRouting);
|
||||
}
|
||||
indicesAliasesRequest.addAliasAction(aliasActions);
|
||||
} else if (type == AliasAction.Type.REMOVE) {
|
||||
indicesAliasesRequest.removeAlias(indices, aliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PARSER.parse(parser, indicesAliasesRequest, () -> ParseFieldMatcher.STRICT);
|
||||
}
|
||||
if (indicesAliasesRequest.getAliasActions().isEmpty()) {
|
||||
throw new IllegalArgumentException("No action specified");
|
||||
}
|
||||
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<IndicesAliasesResponse>(channel));
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ import java.util.Comparator;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
|
@ -76,15 +75,12 @@ import java.util.stream.StreamSupport;
|
|||
*/
|
||||
public class SearchPhaseController extends AbstractComponent {
|
||||
|
||||
public static final Comparator<AtomicArray.Entry<? extends QuerySearchResultProvider>> QUERY_RESULT_ORDERING = new Comparator<AtomicArray.Entry<? extends QuerySearchResultProvider>>() {
|
||||
@Override
|
||||
public int compare(AtomicArray.Entry<? extends QuerySearchResultProvider> o1, AtomicArray.Entry<? extends QuerySearchResultProvider> o2) {
|
||||
int i = o1.value.shardTarget().index().compareTo(o2.value.shardTarget().index());
|
||||
if (i == 0) {
|
||||
i = o1.value.shardTarget().shardId().id() - o2.value.shardTarget().shardId().id();
|
||||
}
|
||||
return i;
|
||||
public static final Comparator<AtomicArray.Entry<? extends QuerySearchResultProvider>> QUERY_RESULT_ORDERING = (o1, o2) -> {
|
||||
int i = o1.value.shardTarget().index().compareTo(o2.value.shardTarget().index());
|
||||
if (i == 0) {
|
||||
i = o1.value.shardTarget().shardId().id() - o2.value.shardTarget().shardId().id();
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
public static final ScoreDoc[] EMPTY_DOCS = new ScoreDoc[0];
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.action.admin.indices.alias;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import static org.hamcrest.Matchers.arrayWithSize;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
public class AliasActionsTests extends ESTestCase {
|
||||
public void testValidate() {
|
||||
AliasActions.Type type = randomFrom(AliasActions.Type.values());
|
||||
if (type == AliasActions.Type.REMOVE_INDEX) {
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).validate());
|
||||
assertEquals("One of [index] or [indices] is required", e.getMessage());
|
||||
} else {
|
||||
Exception e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(type).alias(randomAsciiOfLength(5)).validate());
|
||||
assertEquals("One of [index] or [indices] is required", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).index(randomAsciiOfLength(5)).validate());
|
||||
assertEquals("One of [alias] or [aliases] is required", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testEmptyIndex() {
|
||||
Exception e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(randomFrom(AliasActions.Type.values())).index(null));
|
||||
assertEquals("[index] can't be empty string", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(randomFrom(AliasActions.Type.values())).index(""));
|
||||
assertEquals("[index] can't be empty string", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices((String[]) null));
|
||||
assertEquals("[indices] can't be empty", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices(new String[0]));
|
||||
assertEquals("[indices] can't be empty", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices("test", null));
|
||||
assertEquals("[indices] can't contain empty string", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices("test", ""));
|
||||
assertEquals("[indices] can't contain empty string", e.getMessage());
|
||||
}
|
||||
|
||||
public void testEmptyAlias() {
|
||||
AliasActions.Type type = randomValueOtherThan(AliasActions.Type.REMOVE_INDEX, () -> randomFrom(AliasActions.Type.values()));
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).alias(null));
|
||||
assertEquals("[alias] can't be empty string", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).alias(""));
|
||||
assertEquals("[alias] can't be empty string", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases((String[]) null));
|
||||
assertEquals("[aliases] can't be empty", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases(new String[0]));
|
||||
assertEquals("[aliases] can't be empty", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases("test", null));
|
||||
assertEquals("[aliases] can't contain empty string", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases("test", ""));
|
||||
assertEquals("[aliases] can't contain empty string", e.getMessage());
|
||||
}
|
||||
|
||||
public void testBadOptionsInNonIndex() {
|
||||
AliasActions action = randomBoolean() ? AliasActions.remove() : AliasActions.removeIndex();
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> action.routing("test"));
|
||||
assertEquals("[routing] is unsupported for [" + action.actionType() + "]", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> action.searchRouting("test"));
|
||||
assertEquals("[search_routing] is unsupported for [" + action.actionType() + "]", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> action.indexRouting("test"));
|
||||
assertEquals("[index_routing] is unsupported for [" + action.actionType() + "]", e.getMessage());
|
||||
e = expectThrows(IllegalArgumentException.class, () -> action.filter("test"));
|
||||
assertEquals("[filter] is unsupported for [" + action.actionType() + "]", e.getMessage());
|
||||
}
|
||||
|
||||
public void testParseAdd() throws IOException {
|
||||
String[] indices = generateRandomStringArray(10, 5, false, false);
|
||||
String[] aliases = generateRandomStringArray(10, 5, false, false);
|
||||
Map<String, Object> filter = randomBoolean() ? randomMap(5) : null;
|
||||
Object searchRouting = randomBoolean() ? randomRouting() : null;
|
||||
Object indexRouting = randomBoolean() ? randomBoolean() ? searchRouting : randomRouting() : null;
|
||||
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
|
||||
b.startObject(); {
|
||||
b.startObject("add"); {
|
||||
if (indices.length > 1 || randomBoolean()) {
|
||||
b.field("indices", indices);
|
||||
} else {
|
||||
b.field("index", indices[0]);
|
||||
}
|
||||
if (aliases.length > 1 || randomBoolean()) {
|
||||
b.field("aliases", aliases);
|
||||
} else {
|
||||
b.field("alias", aliases[0]);
|
||||
}
|
||||
if (filter != null) {
|
||||
b.field("filter", filter);
|
||||
}
|
||||
if (searchRouting != null) {
|
||||
if (searchRouting.equals(indexRouting)) {
|
||||
b.field("routing", searchRouting);
|
||||
} else {
|
||||
b.field("search_routing", searchRouting);
|
||||
}
|
||||
}
|
||||
if (indexRouting != null && false == indexRouting.equals(searchRouting)) {
|
||||
b.field("index_routing", indexRouting);
|
||||
}
|
||||
}
|
||||
b.endObject();
|
||||
}
|
||||
b.endObject();
|
||||
b = shuffleXContent(b, "filter");
|
||||
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
|
||||
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
|
||||
assertEquals(AliasActions.Type.ADD, action.actionType());
|
||||
assertThat(action.indices(), equalTo(indices));
|
||||
assertThat(action.aliases(), equalTo(aliases));
|
||||
if (filter == null || filter.isEmpty()) {
|
||||
assertNull(action.filter());
|
||||
} else {
|
||||
assertEquals(XContentFactory.contentBuilder(XContentType.JSON).map(filter).string(), action.filter());
|
||||
}
|
||||
assertEquals(Objects.toString(searchRouting, null), action.searchRouting());
|
||||
assertEquals(Objects.toString(indexRouting, null), action.indexRouting());
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseAddDefaultRouting() throws IOException {
|
||||
String index = randomAsciiOfLength(5);
|
||||
String alias = randomAsciiOfLength(5);
|
||||
Object searchRouting = randomRouting();
|
||||
Object indexRouting = randomRouting();
|
||||
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
|
||||
b.startObject(); {
|
||||
b.startObject("add"); {
|
||||
b.field("index", index);
|
||||
b.field("alias", alias);
|
||||
if (randomBoolean()) {
|
||||
b.field("routing", searchRouting);
|
||||
b.field("index_routing", indexRouting);
|
||||
} else {
|
||||
b.field("search_routing", searchRouting);
|
||||
b.field("routing", indexRouting);
|
||||
}
|
||||
}
|
||||
b.endObject();
|
||||
}
|
||||
b.endObject();
|
||||
b = shuffleXContent(b);
|
||||
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
|
||||
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
|
||||
assertEquals(AliasActions.Type.ADD, action.actionType());
|
||||
assertThat(action.indices(), arrayContaining(index));
|
||||
assertThat(action.aliases(), arrayContaining(alias));
|
||||
assertEquals(searchRouting.toString(), action.searchRouting());
|
||||
assertEquals(indexRouting.toString(), action.indexRouting());
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseRemove() throws IOException {
|
||||
String[] indices = generateRandomStringArray(10, 5, false, false);
|
||||
String[] aliases = generateRandomStringArray(10, 5, false, false);
|
||||
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
|
||||
b.startObject(); {
|
||||
b.startObject("remove"); {
|
||||
if (indices.length > 1 || randomBoolean()) {
|
||||
b.field("indices", indices);
|
||||
} else {
|
||||
b.field("index", indices[0]);
|
||||
}
|
||||
if (aliases.length > 1 || randomBoolean()) {
|
||||
b.field("aliases", aliases);
|
||||
} else {
|
||||
b.field("alias", aliases[0]);
|
||||
}
|
||||
}
|
||||
b.endObject();
|
||||
}
|
||||
b.endObject();
|
||||
b = shuffleXContent(b);
|
||||
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
|
||||
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
|
||||
assertEquals(AliasActions.Type.REMOVE, action.actionType());
|
||||
assertThat(action.indices(), equalTo(indices));
|
||||
assertThat(action.aliases(), equalTo(aliases));
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseRemoveIndex() throws IOException {
|
||||
String[] indices = randomBoolean() ? new String[] {randomAsciiOfLength(5)} : generateRandomStringArray(10, 5, false, false);
|
||||
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
|
||||
b.startObject(); {
|
||||
b.startObject("remove_index"); {
|
||||
if (indices.length > 1 || randomBoolean()) {
|
||||
b.field("indices", indices);
|
||||
} else {
|
||||
b.field("index", indices[0]);
|
||||
}
|
||||
}
|
||||
b.endObject();
|
||||
}
|
||||
b.endObject();
|
||||
b = shuffleXContent(b);
|
||||
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
|
||||
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
|
||||
assertEquals(AliasActions.Type.REMOVE_INDEX, action.actionType());
|
||||
assertArrayEquals(indices, action.indices());
|
||||
assertThat(action.aliases(), arrayWithSize(0));
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseIndexAndIndicesThrowsError() throws IOException {
|
||||
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
|
||||
b.startObject(); {
|
||||
b.startObject(randomFrom("add", "remove")); {
|
||||
b.field("index", randomAsciiOfLength(5));
|
||||
b.field("indices", generateRandomStringArray(10, 5, false, false));
|
||||
b.field("alias", randomAsciiOfLength(5));
|
||||
}
|
||||
b.endObject();
|
||||
}
|
||||
b.endObject();
|
||||
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
|
||||
Exception e = expectThrows(ParsingException.class, () -> AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT));
|
||||
assertThat(e.getCause().getCause(), instanceOf(IllegalArgumentException.class));
|
||||
assertEquals("Only one of [index] and [indices] is supported", e.getCause().getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseAliasAndAliasesThrowsError() throws IOException {
|
||||
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
|
||||
b.startObject(); {
|
||||
b.startObject(randomFrom("add", "remove")); {
|
||||
b.field("index", randomAsciiOfLength(5));
|
||||
b.field("alias", randomAsciiOfLength(5));
|
||||
b.field("aliases", generateRandomStringArray(10, 5, false, false));
|
||||
}
|
||||
b.endObject();
|
||||
}
|
||||
b.endObject();
|
||||
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
|
||||
Exception e = expectThrows(ParsingException.class, () -> AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT));
|
||||
assertThat(e.getCause().getCause(), instanceOf(IllegalArgumentException.class));
|
||||
assertEquals("Only one of [alias] and [aliases] is supported", e.getCause().getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testRoundTrip() throws IOException {
|
||||
AliasActions action = new AliasActions(randomFrom(AliasActions.Type.values()));
|
||||
if (randomBoolean()) {
|
||||
action.index(randomAsciiOfLength(5));
|
||||
} else {
|
||||
action.indices(generateRandomStringArray(5, 5, false, false));
|
||||
}
|
||||
if (action.actionType() != AliasActions.Type.REMOVE_INDEX) {
|
||||
if (randomBoolean()) {
|
||||
action.alias(randomAsciiOfLength(5));
|
||||
} else {
|
||||
action.aliases(generateRandomStringArray(5, 5, false, false));
|
||||
}
|
||||
}
|
||||
if (action.actionType() == AliasActions.Type.ADD) {
|
||||
if (randomBoolean()) {
|
||||
action.filter(randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
if (randomBoolean()) {
|
||||
action.routing(randomAsciiOfLength(5));
|
||||
} else {
|
||||
action.searchRouting(randomAsciiOfLength(5));
|
||||
action.indexRouting(randomAsciiOfLength(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
try (BytesStreamOutput out = new BytesStreamOutput()) {
|
||||
action.writeTo(out);
|
||||
try (StreamInput in = out.bytes().streamInput()) {
|
||||
AliasActions read = new AliasActions(in);
|
||||
assertEquals(action, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Map<String, Object> randomMap(int maxDepth) {
|
||||
int members = between(0, 5);
|
||||
Map<String, Object> result = new HashMap<>(members);
|
||||
for (int i = 0; i < members; i++) {
|
||||
Object value;
|
||||
switch (between(0, 3)) {
|
||||
case 0:
|
||||
if (maxDepth > 0) {
|
||||
value = randomMap(maxDepth - 1);
|
||||
} else {
|
||||
value = randomAsciiOfLength(5);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
value = randomAsciiOfLength(5);
|
||||
break;
|
||||
case 2:
|
||||
value = randomBoolean();
|
||||
break;
|
||||
case 3:
|
||||
value = randomLong();
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
result.put(randomAsciiOfLength(5), value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object randomRouting() {
|
||||
return randomBoolean() ? randomAsciiOfLength(5) : randomInt();
|
||||
}
|
||||
}
|
|
@ -35,12 +35,13 @@ import org.elasticsearch.index.shard.DocsStats;
|
|||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.action.admin.indices.rollover.TransportRolloverAction.evaluateConditions;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
public class TransportRolloverActionTests extends ESTestCase {
|
||||
|
||||
|
@ -97,19 +98,19 @@ public class TransportRolloverActionTests extends ESTestCase {
|
|||
final IndicesAliasesClusterStateUpdateRequest updateRequest =
|
||||
TransportRolloverAction.prepareRolloverAliasesUpdateRequest(sourceIndex, targetIndex, rolloverRequest);
|
||||
|
||||
final AliasAction[] actions = updateRequest.actions();
|
||||
assertThat(actions.length, equalTo(2));
|
||||
List<AliasAction> actions = updateRequest.actions();
|
||||
assertThat(actions, hasSize(2));
|
||||
boolean foundAdd = false;
|
||||
boolean foundRemove = false;
|
||||
for (AliasAction action : actions) {
|
||||
if (action.actionType() == AliasAction.Type.ADD) {
|
||||
if (action.getIndex().equals(targetIndex)) {
|
||||
assertEquals(sourceAlias, ((AliasAction.Add) action).getAlias());
|
||||
foundAdd = true;
|
||||
assertThat(action.index(), equalTo(targetIndex));
|
||||
assertThat(action.alias(), equalTo(sourceAlias));
|
||||
} else if (action.actionType() == AliasAction.Type.REMOVE) {
|
||||
} else if (action.getIndex().equals(sourceIndex)) {
|
||||
assertEquals(sourceAlias, ((AliasAction.Remove) action).getAlias());
|
||||
foundRemove = true;
|
||||
assertThat(action.index(), equalTo(sourceIndex));
|
||||
assertThat(action.alias(), equalTo(sourceAlias));
|
||||
} else {
|
||||
throw new AssertionError("Unknow index [" + action.getIndex() + "]");
|
||||
}
|
||||
}
|
||||
assertTrue(foundAdd);
|
||||
|
|
|
@ -20,10 +20,8 @@
|
|||
package org.elasticsearch.aliases;
|
||||
|
||||
import org.apache.lucene.search.join.ScoreMode;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistResponse;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
||||
|
@ -31,7 +29,6 @@ import org.elasticsearch.action.index.IndexResponse;
|
|||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.cluster.metadata.AliasOrIndex;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
|
@ -52,14 +49,13 @@ import org.elasticsearch.test.ESIntegTestCase;
|
|||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.client.Requests.createIndexRequest;
|
||||
import static org.elasticsearch.client.Requests.indexRequest;
|
||||
import static org.elasticsearch.cluster.metadata.AliasAction.Type.ADD;
|
||||
import static org.elasticsearch.cluster.metadata.AliasAction.Type.REMOVE;
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_METADATA_BLOCK;
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_READ_ONLY_BLOCK;
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_METADATA;
|
||||
|
@ -114,25 +110,15 @@ public class IndexAliasesIT extends ESIntegTestCase {
|
|||
logger.info("--> creating index [test]");
|
||||
createIndex("test");
|
||||
|
||||
ensureGreen();
|
||||
|
||||
//invalid filter, invalid json
|
||||
IndicesAliasesRequestBuilder indicesAliasesRequestBuilder = admin().indices().prepareAliases().addAlias("test", "alias1", "abcde");
|
||||
try {
|
||||
indicesAliasesRequestBuilder.get();
|
||||
fail("put alias should have been failed due to invalid filter");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
|
||||
}
|
||||
Exception e = expectThrows(IllegalArgumentException.class,
|
||||
() -> admin().indices().prepareAliases().addAlias("test", "alias1", "abcde").get());
|
||||
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
|
||||
|
||||
//valid json , invalid filter
|
||||
indicesAliasesRequestBuilder = admin().indices().prepareAliases().addAlias("test", "alias1", "{ \"test\": {} }");
|
||||
try {
|
||||
indicesAliasesRequestBuilder.get();
|
||||
fail("put alias should have been failed due to invalid filter");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
|
||||
}
|
||||
// valid json , invalid filter
|
||||
e = expectThrows(IllegalArgumentException.class,
|
||||
() -> admin().indices().prepareAliases().addAlias("test", "alias1", "{ \"test\": {} }").get());
|
||||
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
|
||||
}
|
||||
|
||||
public void testFilteringAliases() throws Exception {
|
||||
|
@ -598,7 +584,7 @@ public class IndexAliasesIT extends ESIntegTestCase {
|
|||
.addAlias("foobar", "foo"));
|
||||
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(new AliasAction(ADD, "foobar", "bac").routing("bla")));
|
||||
.addAliasAction(AliasActions.add().index("foobar").alias("bac").routing("bla")));
|
||||
|
||||
logger.info("--> getting bar and baz for index bazbar");
|
||||
getResponse = admin().indices().prepareGetAliases("bar", "bac").addIndices("bazbar").get();
|
||||
|
@ -729,224 +715,6 @@ public class IndexAliasesIT extends ESIntegTestCase {
|
|||
assertThat(existsResponse.exists(), equalTo(false));
|
||||
}
|
||||
|
||||
public void testAddAliasNullWithoutExistingIndices() {
|
||||
try {
|
||||
assertAcked(admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction(null, "alias1")));
|
||||
fail("create alias should have failed due to null index");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat("Exception text does not contain \"Alias action [add]: [index/indices] may not be empty string\"",
|
||||
e.getMessage(), containsString("Alias action [add]: [index/indices] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddAliasNullWithExistingIndices() throws Exception {
|
||||
logger.info("--> creating index [test]");
|
||||
createIndex("test");
|
||||
ensureGreen();
|
||||
|
||||
logger.info("--> aliasing index [null] with [empty-alias]");
|
||||
|
||||
try {
|
||||
assertAcked(admin().indices().prepareAliases().addAlias((String) null, "empty-alias"));
|
||||
fail("create alias should have failed due to null index");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat("Exception text does not contain \"Alias action [add]: [index/indices] may not be empty string\"",
|
||||
e.getMessage(), containsString("Alias action [add]: [index/indices] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddAliasEmptyIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("", "alias1")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "", "alias1")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddAliasNullAlias() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("index1", null)).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "index1", (String)null)).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "index1", (String[])null)).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] is either missing or null"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddAliasEmptyAlias() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("index1", "")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "index1", "")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddAliasNullAliasNullIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction(null, null)).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, null, (String)null)).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddAliasEmptyAliasEmptyIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("", "")).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "", "")).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAliasNullIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction(null, "alias1")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, null, "alias1")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAliasEmptyIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction("", "alias1")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "", "alias1")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAliasNullAlias() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction("index1", null)).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "index1", (String)null)).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "index1", (String[])null)).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] is either missing or null"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAliasEmptyAlias() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction("index1", "")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "index1", "")).get();
|
||||
fail("Expected ActionRequestValidationException");
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAliasNullAliasNullIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction(null, null)).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, null, (String)null)).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, (String[])null, (String[])null)).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRemoveAliasEmptyAliasEmptyIndex() {
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("", "")).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
try {
|
||||
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "", "")).get();
|
||||
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.validationErrors(), notNullValue());
|
||||
assertThat(e.validationErrors().size(), equalTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetAllAliasesWorks() {
|
||||
createIndex("index1");
|
||||
createIndex("index2");
|
||||
|
@ -1113,6 +881,13 @@ public class IndexAliasesIT extends ESIntegTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testRemoveIndexAndReplaceWithAlias() throws InterruptedException, ExecutionException {
|
||||
assertAcked(client().admin().indices().prepareCreate("test"));
|
||||
indexRandom(true, client().prepareIndex("test_2", "test", "test").setSource("test", "test"));
|
||||
assertAcked(client().admin().indices().prepareAliases().addAlias("test_2", "test").removeIndex("test"));
|
||||
assertHitCount(client().prepareSearch("test").get(), 1);
|
||||
}
|
||||
|
||||
private void checkAliases() {
|
||||
GetAliasesResponse getAliasesResponse = admin().indices().prepareGetAliases("alias1").get();
|
||||
assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(1));
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.SnapshotsInProgress;
|
||||
import org.elasticsearch.cluster.block.ClusterBlocks;
|
||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||
import org.elasticsearch.cluster.routing.allocation.AllocationService;
|
||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.repositories.IndexId;
|
||||
import org.elasticsearch.snapshots.Snapshot;
|
||||
import org.elasticsearch.snapshots.SnapshotId;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.VersionUtils;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
public class MetaDataDeleteIndexServiceTests extends ESTestCase {
|
||||
private final AllocationService allocationService = mock(AllocationService.class);
|
||||
private final MetaDataDeleteIndexService service = new MetaDataDeleteIndexService(Settings.EMPTY, null, allocationService);
|
||||
|
||||
public void testDeleteMissing() {
|
||||
Index index = new Index("missing", "doesn't matter");
|
||||
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).build();
|
||||
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> service.deleteIndices(state, singleton(index)));
|
||||
assertEquals(index, e.getIndex());
|
||||
}
|
||||
|
||||
public void testDeleteSnapshotting() {
|
||||
String index = randomAsciiOfLength(5);
|
||||
Snapshot snapshot = new Snapshot("doesn't matter", new SnapshotId("snapshot name", "snapshot uuid"));
|
||||
SnapshotsInProgress snaps = new SnapshotsInProgress(new SnapshotsInProgress.Entry(snapshot, true, false,
|
||||
SnapshotsInProgress.State.INIT, singletonList(new IndexId(index, "doesn't matter")),
|
||||
System.currentTimeMillis(), ImmutableOpenMap.of()));
|
||||
ClusterState state = ClusterState.builder(clusterState(index))
|
||||
.putCustom(SnapshotsInProgress.TYPE, snaps)
|
||||
.build();
|
||||
Exception e = expectThrows(IllegalArgumentException.class,
|
||||
() -> service.deleteIndices(state, singleton(state.metaData().getIndices().get(index).getIndex())));
|
||||
assertEquals("Cannot delete indices that are being snapshotted: [[" + index + "]]. Try again after snapshot finishes "
|
||||
+ "or cancel the currently running snapshot.", e.getMessage());
|
||||
}
|
||||
|
||||
public void testDeleteUnassigned() {
|
||||
// Create an unassigned index
|
||||
String index = randomAsciiOfLength(5);
|
||||
ClusterState before = clusterState(index);
|
||||
|
||||
// Mock the built reroute
|
||||
when(allocationService.reroute(any(ClusterState.class), any(String.class))).then(
|
||||
i -> RoutingAllocation.Result.unchanged((ClusterState) i.getArguments()[0]));
|
||||
|
||||
// Remove it
|
||||
ClusterState after = service.deleteIndices(before, singleton(before.metaData().getIndices().get(index).getIndex()));
|
||||
|
||||
// It is gone
|
||||
assertNull(after.metaData().getIndices().get(index));
|
||||
assertNull(after.routingTable().index(index));
|
||||
assertNull(after.blocks().indices().get(index));
|
||||
|
||||
// Make sure we actually attempted to reroute
|
||||
verify(allocationService).reroute(any(ClusterState.class), any(String.class));
|
||||
}
|
||||
|
||||
private ClusterState clusterState(String index) {
|
||||
IndexMetaData indexMetaData = IndexMetaData.builder(index)
|
||||
.settings(Settings.builder().put("index.version.created", VersionUtils.randomVersion(random())))
|
||||
.numberOfShards(1)
|
||||
.numberOfReplicas(1)
|
||||
.build();
|
||||
return ClusterState.builder(ClusterName.DEFAULT)
|
||||
.metaData(MetaData.builder().put(indexMetaData, false))
|
||||
.routingTable(RoutingTable.builder().addAsNew(indexMetaData).build())
|
||||
.blocks(ClusterBlocks.builder().addBlocks(indexMetaData))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.cluster.metadata;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.VersionUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyCollectionOf;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class MetaDataIndexAliasesServiceTests extends ESTestCase {
|
||||
private final AliasValidator aliasValidator = new AliasValidator(Settings.EMPTY);
|
||||
private final MetaDataDeleteIndexService deleteIndexService = mock(MetaDataDeleteIndexService.class);
|
||||
private final MetaDataIndexAliasesService service = new MetaDataIndexAliasesService(Settings.EMPTY, null, null, aliasValidator,
|
||||
null, deleteIndexService);
|
||||
|
||||
public MetaDataIndexAliasesServiceTests() {
|
||||
// Mock any deletes so we don't need to worry about how MetaDataDeleteIndexService does its job
|
||||
when(deleteIndexService.deleteIndices(any(ClusterState.class), anyCollectionOf(Index.class))).then(i -> {
|
||||
ClusterState state = (ClusterState) i.getArguments()[0];
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Index> indices = (Collection<Index>) i.getArguments()[1];
|
||||
MetaData.Builder meta = MetaData.builder(state.metaData());
|
||||
for (Index index : indices) {
|
||||
assertTrue("index now found", state.metaData().hasConcreteIndex(index.getName()));
|
||||
meta.remove(index.getName()); // We only think about metadata for this test. Not routing or any other fun stuff.
|
||||
}
|
||||
return ClusterState.builder(state).metaData(meta).build();
|
||||
});
|
||||
}
|
||||
|
||||
public void testAddAndRemove() {
|
||||
// Create a state with a single index
|
||||
String index = randomAsciiOfLength(5);
|
||||
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), index);
|
||||
|
||||
// Add an alias to it
|
||||
ClusterState after = service.innerExecute(before, singletonList(new AliasAction.Add(index, "test", null, null, null)));
|
||||
AliasOrIndex alias = after.metaData().getAliasAndIndexLookup().get("test");
|
||||
assertNotNull(alias);
|
||||
assertTrue(alias.isAlias());
|
||||
assertThat(alias.getIndices(), contains(after.metaData().index(index)));
|
||||
|
||||
// Remove the alias from it while adding another one
|
||||
before = after;
|
||||
after = service.innerExecute(before, Arrays.asList(
|
||||
new AliasAction.Remove(index, "test"),
|
||||
new AliasAction.Add(index, "test_2", null, null, null)));
|
||||
assertNull(after.metaData().getAliasAndIndexLookup().get("test"));
|
||||
alias = after.metaData().getAliasAndIndexLookup().get("test_2");
|
||||
assertNotNull(alias);
|
||||
assertTrue(alias.isAlias());
|
||||
assertThat(alias.getIndices(), contains(after.metaData().index(index)));
|
||||
|
||||
// Now just remove on its own
|
||||
before = after;
|
||||
after = service.innerExecute(before, singletonList(new AliasAction.Remove(index, "test_2")));
|
||||
assertNull(after.metaData().getAliasAndIndexLookup().get("test"));
|
||||
assertNull(after.metaData().getAliasAndIndexLookup().get("test_2"));
|
||||
}
|
||||
|
||||
public void testSwapIndexWithAlias() {
|
||||
// Create "test" and "test_2"
|
||||
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), "test");
|
||||
before = createIndex(before, "test_2");
|
||||
|
||||
// Now remove "test" and add an alias to "test" to "test_2" in one go
|
||||
ClusterState after = service.innerExecute(before, Arrays.asList(
|
||||
new AliasAction.Add("test_2", "test", null, null, null),
|
||||
new AliasAction.RemoveIndex("test")));
|
||||
AliasOrIndex alias = after.metaData().getAliasAndIndexLookup().get("test");
|
||||
assertNotNull(alias);
|
||||
assertTrue(alias.isAlias());
|
||||
assertThat(alias.getIndices(), contains(after.metaData().index("test_2")));
|
||||
}
|
||||
|
||||
public void testAddAliasToRemovedIndex() {
|
||||
// Create "test"
|
||||
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), "test");
|
||||
|
||||
// Attempt to add an alias to "test" at the same time as we remove it
|
||||
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> service.innerExecute(before, Arrays.asList(
|
||||
new AliasAction.Add("test", "alias", null, null, null),
|
||||
new AliasAction.RemoveIndex("test"))));
|
||||
assertEquals("test", e.getIndex().getName());
|
||||
}
|
||||
|
||||
public void testRemoveIndexTwice() {
|
||||
// Create "test"
|
||||
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), "test");
|
||||
|
||||
// Try to remove an index twice. This should just remove the index once....
|
||||
ClusterState after = service.innerExecute(before, Arrays.asList(
|
||||
new AliasAction.RemoveIndex("test"),
|
||||
new AliasAction.RemoveIndex("test")));
|
||||
assertNull(after.metaData().getAliasAndIndexLookup().get("test"));
|
||||
}
|
||||
|
||||
private ClusterState createIndex(ClusterState state, String index) {
|
||||
IndexMetaData indexMetaData = IndexMetaData.builder(index)
|
||||
.settings(Settings.builder().put("index.version.created", VersionUtils.randomVersion(random())))
|
||||
.numberOfShards(1)
|
||||
.numberOfReplicas(1)
|
||||
.build();
|
||||
return ClusterState.builder(state)
|
||||
.metaData(MetaData.builder(state.metaData()).put(indexMetaData, false))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ package org.elasticsearch.cluster.shards;
|
|||
|
||||
import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup;
|
||||
import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
|
@ -99,9 +99,9 @@ public class ClusterSearchShardsIT extends ESIntegTestCase {
|
|||
client().admin().indices().prepareCreate("test2").setSettings(Settings.builder()
|
||||
.put("index.number_of_shards", "4").put("index.number_of_replicas", 1)).execute().actionGet();
|
||||
client().admin().indices().prepareAliases()
|
||||
.addAliasAction(AliasAction.newAddAliasAction("test1", "routing_alias").routing("ABC"))
|
||||
.addAliasAction(AliasAction.newAddAliasAction("test2", "routing_alias").routing("EFG"))
|
||||
.execute().actionGet();
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("routing_alias").routing("ABC"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("routing_alias").routing("EFG"))
|
||||
.get();
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
ClusterSearchShardsResponse response = client().admin().cluster().prepareSearchShards("routing_alias").execute().actionGet();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
package org.elasticsearch.common.xcontent.support.filtering;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.common.xcontent.XContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
|
@ -28,7 +29,11 @@ import org.elasticsearch.common.xcontent.XContentType;
|
|||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
@ -86,12 +91,16 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
return XContentBuilder.builder(getXContentType().xContent());
|
||||
}
|
||||
|
||||
private XContentBuilder newXContentBuilder(String filter, boolean inclusive) throws IOException {
|
||||
return XContentBuilder.builder(getXContentType().xContent(), new String[] { filter }, inclusive);
|
||||
private XContentBuilder newXContentBuilderWithIncludes(String filter) throws IOException {
|
||||
return newXContentBuilder(singleton(filter), emptySet());
|
||||
}
|
||||
|
||||
private XContentBuilder newXContentBuilder(String[] filters, boolean inclusive) throws IOException {
|
||||
return XContentBuilder.builder(getXContentType().xContent(), filters, inclusive);
|
||||
private XContentBuilder newXContentBuilderWithExcludes(String filter) throws IOException {
|
||||
return newXContentBuilder(emptySet(), singleton(filter));
|
||||
}
|
||||
|
||||
private XContentBuilder newXContentBuilder(Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return XContentBuilder.builder(getXContentType().xContent(), includes, excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,20 +182,22 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instanciates a new XContentBuilder with the given filters and builds a
|
||||
* sample with it.
|
||||
* @param inclusive
|
||||
* Specifies if filters are inclusive or exclusive
|
||||
*/
|
||||
private XContentBuilder sample(String filter, boolean inclusive) throws IOException {
|
||||
return sample(newXContentBuilder(filter, inclusive));
|
||||
/** Create a new {@link XContentBuilder} and use it to build the sample using the given inclusive filter **/
|
||||
private XContentBuilder sampleWithIncludes(String filter) throws IOException {
|
||||
return sample(newXContentBuilderWithIncludes(filter));
|
||||
}
|
||||
|
||||
private XContentBuilder sample(String[] filters, boolean inclusive) throws IOException {
|
||||
return sample(newXContentBuilder(filters, inclusive));
|
||||
/** Create a new {@link XContentBuilder} and use it to build the sample using the given exclusive filter **/
|
||||
private XContentBuilder sampleWithExcludes(String filter) throws IOException {
|
||||
return sample(newXContentBuilderWithExcludes(filter));
|
||||
}
|
||||
|
||||
/** Create a new {@link XContentBuilder} and use it to build the sample using the given includes and exclusive filters **/
|
||||
private XContentBuilder sampleWithFilters(Set<String> includes, Set<String> excludes) throws IOException {
|
||||
return sample(newXContentBuilder(includes, excludes));
|
||||
}
|
||||
|
||||
/** Create a new {@link XContentBuilder} and use it to build the sample **/
|
||||
private XContentBuilder sample() throws IOException {
|
||||
return sample(newXContentBuilder());
|
||||
}
|
||||
|
@ -195,23 +206,23 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
XContentBuilder expected = sample();
|
||||
|
||||
assertXContentBuilder(expected, sample());
|
||||
assertXContentBuilder(expected, sample("*", true));
|
||||
assertXContentBuilder(expected, sample("**", true));
|
||||
assertXContentBuilder(expected, sample("xyz", false));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("*"));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("**"));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("xyz"));
|
||||
}
|
||||
|
||||
public void testNoMatch() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject().endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("xyz", true));
|
||||
assertXContentBuilder(expected, sample("*", false));
|
||||
assertXContentBuilder(expected, sample("**", false));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("xyz"));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("*"));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("**"));
|
||||
}
|
||||
|
||||
public void testSimpleFieldInclusive() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject().field("title", "My awesome book").endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("title", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("title"));
|
||||
}
|
||||
|
||||
public void testSimpleFieldExclusive() throws Exception {
|
||||
|
@ -286,10 +297,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("title", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("title"));
|
||||
}
|
||||
|
||||
|
||||
public void testSimpleFieldWithWildcardInclusive() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject()
|
||||
.field("price", 27.99)
|
||||
|
@ -343,7 +353,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("pr*", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("pr*"));
|
||||
}
|
||||
|
||||
public void testSimpleFieldWithWildcardExclusive() throws Exception {
|
||||
|
@ -370,7 +380,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endArray()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("pr*", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("pr*"));
|
||||
}
|
||||
|
||||
public void testMultipleFieldsInclusive() throws Exception {
|
||||
|
@ -379,7 +389,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.field("pages", 456)
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample(new String[] { "title", "pages" }, true));
|
||||
assertXContentBuilder(expected, sampleWithFilters(Sets.newHashSet("title", "pages"), emptySet()));
|
||||
}
|
||||
|
||||
public void testMultipleFieldsExclusive() throws Exception {
|
||||
|
@ -453,10 +463,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample(new String[] { "title", "pages" }, false));
|
||||
assertXContentBuilder(expected, sample(newXContentBuilder(emptySet(), Sets.newHashSet("title", "pages"))));
|
||||
}
|
||||
|
||||
|
||||
public void testSimpleArrayInclusive() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject()
|
||||
.startArray("tags")
|
||||
|
@ -465,7 +474,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endArray()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("tags", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("tags"));
|
||||
}
|
||||
|
||||
public void testSimpleArrayExclusive() throws Exception {
|
||||
|
@ -537,10 +546,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("tags", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("tags"));
|
||||
}
|
||||
|
||||
|
||||
public void testSimpleArrayOfObjectsInclusive() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject()
|
||||
.startArray("authors")
|
||||
|
@ -557,9 +565,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endArray()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("authors", true));
|
||||
assertXContentBuilder(expected, sample("authors.*", true));
|
||||
assertXContentBuilder(expected, sample("authors.*name", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("authors"));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("authors.*"));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("authors.*name"));
|
||||
}
|
||||
|
||||
public void testSimpleArrayOfObjectsExclusive() throws Exception {
|
||||
|
@ -623,9 +631,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("authors", false));
|
||||
assertXContentBuilder(expected, sample("authors.*", false));
|
||||
assertXContentBuilder(expected, sample("authors.*name", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("authors"));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("authors.*"));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("authors.*name"));
|
||||
}
|
||||
|
||||
public void testSimpleArrayOfObjectsPropertyInclusive() throws Exception {
|
||||
|
@ -640,8 +648,8 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endArray()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("authors.lastname", true));
|
||||
assertXContentBuilder(expected, sample("authors.l*", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("authors.lastname"));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("authors.l*"));
|
||||
}
|
||||
|
||||
public void testSimpleArrayOfObjectsPropertyExclusive() throws Exception {
|
||||
|
@ -715,8 +723,8 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("authors.lastname", false));
|
||||
assertXContentBuilder(expected, sample("authors.l*", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("authors.lastname"));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("authors.l*"));
|
||||
}
|
||||
|
||||
public void testRecurseField1Inclusive() throws Exception {
|
||||
|
@ -768,7 +776,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("**.name", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("**.name"));
|
||||
}
|
||||
|
||||
public void testRecurseField1Exclusive() throws Exception {
|
||||
|
@ -831,7 +839,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("**.name", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("**.name"));
|
||||
}
|
||||
|
||||
public void testRecurseField2Inclusive() throws Exception {
|
||||
|
@ -875,7 +883,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("properties.**.name", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("properties.**.name"));
|
||||
}
|
||||
|
||||
public void testRecurseField2Exclusive() throws Exception {
|
||||
|
@ -940,10 +948,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("properties.**.name", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("properties.**.name"));
|
||||
}
|
||||
|
||||
|
||||
public void testRecurseField3Inclusive() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject()
|
||||
.startObject("properties")
|
||||
|
@ -970,7 +977,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("properties.*.en.**.name", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("properties.*.en.**.name"));
|
||||
}
|
||||
|
||||
public void testRecurseField3Exclusive() throws Exception {
|
||||
|
@ -1040,10 +1047,9 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("properties.*.en.**.name", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("properties.*.en.**.name"));
|
||||
}
|
||||
|
||||
|
||||
public void testRecurseField4Inclusive() throws Exception {
|
||||
XContentBuilder expected = newXContentBuilder().startObject()
|
||||
.startObject("properties")
|
||||
|
@ -1072,7 +1078,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("properties.**.distributors.name", true));
|
||||
assertXContentBuilder(expected, sampleWithIncludes("properties.**.distributors.name"));
|
||||
}
|
||||
|
||||
public void testRecurseField4Exclusive() throws Exception {
|
||||
|
@ -1140,7 +1146,7 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sample("properties.**.distributors.name", false));
|
||||
assertXContentBuilder(expected, sampleWithExcludes("properties.**.distributors.name"));
|
||||
}
|
||||
|
||||
public void testRawField() throws Exception {
|
||||
|
@ -1155,24 +1161,24 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
// Test method: rawField(String fieldName, BytesReference content)
|
||||
assertXContentBuilder(expectedRawField, newXContentBuilder().startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
assertXContentBuilder(expectedRawFieldFiltered,
|
||||
newXContentBuilder("f*", true).startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
newXContentBuilderWithIncludes("f*").startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
assertXContentBuilder(expectedRawFieldFiltered,
|
||||
newXContentBuilder("r*", false).startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
newXContentBuilderWithExcludes("r*").startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
assertXContentBuilder(expectedRawFieldNotFiltered,
|
||||
newXContentBuilder("r*", true).startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
newXContentBuilderWithIncludes("r*").startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
assertXContentBuilder(expectedRawFieldNotFiltered,
|
||||
newXContentBuilder("f*", false).startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
newXContentBuilderWithExcludes("f*").startObject().field("foo", 0).rawField("raw", raw).endObject());
|
||||
|
||||
// Test method: rawField(String fieldName, InputStream content)
|
||||
assertXContentBuilder(expectedRawField,
|
||||
newXContentBuilder().startObject().field("foo", 0).rawField("raw", raw.streamInput()).endObject());
|
||||
assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilder("f*", true).startObject().field("foo", 0)
|
||||
assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilderWithIncludes("f*").startObject().field("foo", 0)
|
||||
.rawField("raw", raw.streamInput()).endObject());
|
||||
assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilder("r*", false).startObject().field("foo", 0)
|
||||
assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilderWithExcludes("r*").startObject().field("foo", 0)
|
||||
.rawField("raw", raw.streamInput()).endObject());
|
||||
assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilder("r*", true).startObject().field("foo", 0)
|
||||
assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilderWithIncludes("r*").startObject().field("foo", 0)
|
||||
.rawField("raw", raw.streamInput()).endObject());
|
||||
assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilder("f*", false).startObject().field("foo", 0)
|
||||
assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilderWithExcludes("f*").startObject().field("foo", 0)
|
||||
.rawField("raw", raw.streamInput()).endObject());
|
||||
}
|
||||
|
||||
|
@ -1180,48 +1186,209 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase
|
|||
// Test: Array of values (no filtering)
|
||||
XContentBuilder expected = newXContentBuilder().startObject().startArray("tags").value("lorem").value("ipsum").value("dolor")
|
||||
.endArray().endObject();
|
||||
assertXContentBuilder(expected, newXContentBuilder("t*", true).startObject().startArray("tags").value("lorem").value("ipsum")
|
||||
assertXContentBuilder(expected, newXContentBuilderWithIncludes("t*").startObject().startArray("tags").value("lorem").value("ipsum")
|
||||
.value("dolor").endArray().endObject());
|
||||
assertXContentBuilder(expected, newXContentBuilder("tags", true).startObject().startArray("tags").value("lorem").value("ipsum")
|
||||
.value("dolor").endArray().endObject());
|
||||
assertXContentBuilder(expected, newXContentBuilder("a", false).startObject().startArray("tags").value("lorem").value("ipsum")
|
||||
assertXContentBuilder(expected, newXContentBuilderWithIncludes("tags").startObject().startArray("tags").value("lorem")
|
||||
.value("ipsum").value("dolor").endArray().endObject());
|
||||
assertXContentBuilder(expected, newXContentBuilderWithExcludes("a").startObject().startArray("tags").value("lorem").value("ipsum")
|
||||
.value("dolor").endArray().endObject());
|
||||
|
||||
// Test: Array of values (with filtering)
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("foo", true).startObject()
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilderWithIncludes("foo").startObject()
|
||||
.startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject());
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("t*", false).startObject()
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilderWithExcludes("t*").startObject()
|
||||
.startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject());
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("tags", false).startObject()
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilderWithExcludes("tags").startObject()
|
||||
.startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject());
|
||||
|
||||
// Test: Array of objects (no filtering)
|
||||
expected = newXContentBuilder().startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject()
|
||||
.field("firstname", "ipsum").endObject().endArray().endObject();
|
||||
assertXContentBuilder(expected, newXContentBuilder("t*", true).startObject().startArray("tags").startObject()
|
||||
assertXContentBuilder(expected, newXContentBuilderWithIncludes("t*").startObject().startArray("tags").startObject()
|
||||
.field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
assertXContentBuilder(expected, newXContentBuilder("tags", true).startObject().startArray("tags").startObject()
|
||||
assertXContentBuilder(expected, newXContentBuilderWithIncludes("tags").startObject().startArray("tags").startObject()
|
||||
.field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
assertXContentBuilder(expected, newXContentBuilder("a", false).startObject().startArray("tags").startObject()
|
||||
assertXContentBuilder(expected, newXContentBuilderWithExcludes("a").startObject().startArray("tags").startObject()
|
||||
.field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
|
||||
// Test: Array of objects (with filtering)
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(),
|
||||
newXContentBuilder("foo", true).startObject().startArray("tags").startObject().field("lastname", "lorem").endObject()
|
||||
newXContentBuilderWithIncludes("foo").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject()
|
||||
.startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(),
|
||||
newXContentBuilder("t*", false).startObject().startArray("tags").startObject().field("lastname", "lorem").endObject()
|
||||
newXContentBuilderWithExcludes("t*").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject()
|
||||
.startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
assertXContentBuilder(newXContentBuilder().startObject().endObject(),
|
||||
newXContentBuilder("tags", false).startObject().startArray("tags").startObject().field("lastname", "lorem").endObject()
|
||||
newXContentBuilderWithExcludes("tags").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject()
|
||||
.startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
|
||||
// Test: Array of objects (with partial filtering)
|
||||
expected = newXContentBuilder().startObject().startArray("tags").startObject().field("firstname", "ipsum").endObject().endArray()
|
||||
.endObject();
|
||||
assertXContentBuilder(expected, newXContentBuilder("t*.firstname", true).startObject().startArray("tags").startObject()
|
||||
assertXContentBuilder(expected, newXContentBuilderWithIncludes("t*.firstname").startObject().startArray("tags").startObject()
|
||||
.field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
assertXContentBuilder(expected, newXContentBuilder("t*.lastname", false).startObject().startArray("tags").startObject()
|
||||
assertXContentBuilder(expected, newXContentBuilderWithExcludes("t*.lastname").startObject().startArray("tags").startObject()
|
||||
.field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject());
|
||||
}
|
||||
|
||||
public void testEmptyObject() throws IOException {
|
||||
final Function<XContentBuilder, XContentBuilder> build = builder -> {
|
||||
try {
|
||||
return builder.startObject().startObject("foo").endObject().endObject();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
XContentBuilder expected = build.apply(newXContentBuilder());
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithIncludes("foo")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithExcludes("bar")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilder(singleton("f*"), singleton("baz"))));
|
||||
|
||||
expected = newXContentBuilder().startObject().endObject();
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithExcludes("foo")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithIncludes("bar")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilder(singleton("f*"), singleton("foo"))));
|
||||
}
|
||||
|
||||
public void testSingleFieldObject() throws IOException {
|
||||
final Function<XContentBuilder, XContentBuilder> build = builder -> {
|
||||
try {
|
||||
return builder.startObject().startObject("foo").field("bar", "test").endObject().endObject();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
XContentBuilder expected = build.apply(newXContentBuilder());
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithIncludes("foo.bar")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithExcludes("foo.baz")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilder(singleton("foo"), singleton("foo.baz"))));
|
||||
|
||||
expected = newXContentBuilder().startObject().endObject();
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithExcludes("foo.bar")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilder(singleton("foo"), singleton("foo.b*"))));
|
||||
}
|
||||
|
||||
public void testSingleFieldWithBothExcludesIncludes() throws IOException {
|
||||
XContentBuilder expected = newXContentBuilder()
|
||||
.startObject()
|
||||
.field("pages", 456)
|
||||
.field("price", 27.99)
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sampleWithFilters(singleton("p*"), singleton("properties")));
|
||||
}
|
||||
|
||||
public void testObjectsInArrayWithBothExcludesIncludes() throws IOException {
|
||||
Set<String> includes = Sets.newHashSet("tags", "authors");
|
||||
Set<String> excludes = singleton("authors.name");
|
||||
|
||||
XContentBuilder expected = newXContentBuilder()
|
||||
.startObject()
|
||||
.startArray("tags")
|
||||
.value("elasticsearch")
|
||||
.value("java")
|
||||
.endArray()
|
||||
.startArray("authors")
|
||||
.startObject()
|
||||
.field("lastname", "John")
|
||||
.field("firstname", "Doe")
|
||||
.endObject()
|
||||
.startObject()
|
||||
.field("lastname", "William")
|
||||
.field("firstname", "Smith")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sampleWithFilters(includes, excludes));
|
||||
}
|
||||
|
||||
public void testRecursiveObjectsInArrayWithBothExcludesIncludes() throws IOException {
|
||||
Set<String> includes = Sets.newHashSet("**.language", "properties.weight");
|
||||
Set<String> excludes = singleton("**.distributors");
|
||||
|
||||
XContentBuilder expected = newXContentBuilder()
|
||||
.startObject()
|
||||
.startObject("properties")
|
||||
.field("weight", 0.8d)
|
||||
.startObject("language")
|
||||
.startObject("en")
|
||||
.field("lang", "English")
|
||||
.field("available", true)
|
||||
.endObject()
|
||||
.startObject("fr")
|
||||
.field("lang", "French")
|
||||
.field("available", false)
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sampleWithFilters(includes, excludes));
|
||||
}
|
||||
|
||||
public void testRecursiveSameObjectWithBothExcludesIncludes() throws IOException {
|
||||
Set<String> includes = singleton("**.distributors");
|
||||
Set<String> excludes = singleton("**.distributors");
|
||||
|
||||
XContentBuilder expected = newXContentBuilder().startObject().endObject();
|
||||
assertXContentBuilder(expected, sampleWithFilters(includes, excludes));
|
||||
}
|
||||
|
||||
public void testRecursiveObjectsPropertiesWithBothExcludesIncludes() throws IOException {
|
||||
Set<String> includes = singleton("**.en.*");
|
||||
Set<String> excludes = Sets.newHashSet("**.distributors.*.name", "**.street");
|
||||
|
||||
XContentBuilder expected = newXContentBuilder()
|
||||
.startObject()
|
||||
.startObject("properties")
|
||||
.startObject("language")
|
||||
.startObject("en")
|
||||
.field("lang", "English")
|
||||
.field("available", true)
|
||||
.startArray("distributors")
|
||||
.startObject()
|
||||
.field("name", "The Book Shop")
|
||||
.startArray("addresses")
|
||||
.startObject()
|
||||
.field("city", "London")
|
||||
.endObject()
|
||||
.startObject()
|
||||
.field("city", "Stornoway")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.startObject()
|
||||
.field("name", "Sussex Books House")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
|
||||
assertXContentBuilder(expected, sampleWithFilters(includes, excludes));
|
||||
}
|
||||
|
||||
public void testWithLfAtEnd() throws IOException {
|
||||
final Function<XContentBuilder, XContentBuilder> build = builder -> {
|
||||
try {
|
||||
return builder.startObject().startObject("foo").field("bar", "baz").endObject().endObject().prettyPrint().lfAtEnd();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
XContentBuilder expected = build.apply(newXContentBuilder());
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithIncludes("foo")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithExcludes("bar")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilder(singleton("f*"), singleton("baz"))));
|
||||
|
||||
expected = newXContentBuilder().startObject().endObject().prettyPrint().lfAtEnd();
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithExcludes("foo")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilderWithIncludes("bar")));
|
||||
assertXContentBuilder(expected, build.apply(newXContentBuilder(singleton("f*"), singleton("foo"))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import com.fasterxml.jackson.core.filter.FilteringGeneratorDelegate;
|
|||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class FilterPathGeneratorFilteringTests extends ESTestCase {
|
||||
|
@ -135,7 +137,7 @@ public class FilterPathGeneratorFilteringTests extends ESTestCase {
|
|||
private void assertResult(String input, String filter, boolean inclusive, String expected) throws Exception {
|
||||
try (BytesStreamOutput os = new BytesStreamOutput()) {
|
||||
try (FilteringGeneratorDelegate generator = new FilteringGeneratorDelegate(JSON_FACTORY.createGenerator(os),
|
||||
new FilterPathBasedFilter(new String[] { filter }, inclusive), true, true)) {
|
||||
new FilterPathBasedFilter(Collections.singleton(filter), inclusive), true, true)) {
|
||||
try (JsonParser parser = JSON_FACTORY.createParser(replaceQuotes(input))) {
|
||||
while (parser.nextToken() != null) {
|
||||
generator.copyCurrentStructure(parser);
|
||||
|
|
|
@ -19,8 +19,12 @@
|
|||
|
||||
package org.elasticsearch.common.xcontent.support.filtering;
|
||||
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.hamcrest.Matchers.arrayWithSize;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -33,7 +37,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testSimpleFilterPath() {
|
||||
final String input = "test";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -52,7 +56,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testFilterPathWithSubField() {
|
||||
final String input = "foo.bar";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -76,7 +80,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testFilterPathWithSubFields() {
|
||||
final String input = "foo.bar.quz";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -103,13 +107,13 @@ public class FilterPathTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testEmptyFilterPath() {
|
||||
FilterPath[] filterPaths = FilterPath.compile("");
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(""));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(0));
|
||||
}
|
||||
|
||||
public void testNullFilterPath() {
|
||||
FilterPath[] filterPaths = FilterPath.compile((String) null);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(null));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(0));
|
||||
}
|
||||
|
@ -117,7 +121,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testFilterPathWithEscapedDots() {
|
||||
String input = "w.0.0.t";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -149,7 +153,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
|
||||
input = "w\\.0\\.0\\.t";
|
||||
|
||||
filterPaths = FilterPath.compile(input);
|
||||
filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -167,7 +171,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
|
||||
input = "w\\.0.0\\.t";
|
||||
|
||||
filterPaths = FilterPath.compile(input);
|
||||
filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -188,7 +192,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testSimpleWildcardFilterPath() {
|
||||
FilterPath[] filterPaths = FilterPath.compile("*");
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton("*"));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -206,7 +210,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testWildcardInNameFilterPath() {
|
||||
String input = "f*o.bar";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -232,7 +236,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testDoubleWildcardFilterPath() {
|
||||
FilterPath[] filterPaths = FilterPath.compile("**");
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton("**"));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -250,7 +254,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testStartsWithDoubleWildcardFilterPath() {
|
||||
String input = "**.bar";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -274,7 +278,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
public void testContainsDoubleWildcardFilterPath() {
|
||||
String input = "foo.**.bar";
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(input);
|
||||
FilterPath[] filterPaths = FilterPath.compile(singleton(input));
|
||||
assertNotNull(filterPaths);
|
||||
assertThat(filterPaths, arrayWithSize(1));
|
||||
|
||||
|
@ -302,7 +306,7 @@ public class FilterPathTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testMultipleFilterPaths() {
|
||||
String[] inputs = {"foo.**.bar.*", "test.dot\\.ted"};
|
||||
Set<String> inputs = Sets.newHashSet("foo.**.bar.*", "test.dot\\.ted");
|
||||
|
||||
FilterPath[] filterPaths = FilterPath.compile(inputs);
|
||||
assertNotNull(filterPaths);
|
||||
|
|
|
@ -18,12 +18,9 @@
|
|||
*/
|
||||
package org.elasticsearch.indices;
|
||||
|
||||
import org.apache.lucene.store.LockObtainFailedException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||
import org.elasticsearch.cluster.metadata.IndexGraveyard;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
|
@ -250,14 +247,11 @@ public class IndicesServiceTests extends ESSingleNodeTestCase {
|
|||
public void testDanglingIndicesWithAliasConflict() throws Exception {
|
||||
final String indexName = "test-idx1";
|
||||
final String alias = "test-alias";
|
||||
final IndicesService indicesService = getIndicesService();
|
||||
final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
|
||||
final IndexService test = createIndex(indexName);
|
||||
createIndex(indexName);
|
||||
|
||||
// create the alias for the index
|
||||
AliasAction action = new AliasAction(AliasAction.Type.ADD, indexName, alias);
|
||||
IndicesAliasesRequest request = new IndicesAliasesRequest().addAliasAction(action);
|
||||
client().admin().indices().aliases(request).actionGet();
|
||||
client().admin().indices().prepareAliases().addAlias(indexName, alias).get();
|
||||
final ClusterState originalState = clusterService.state();
|
||||
|
||||
// try to import a dangling index with the same name as the alias, it should fail
|
||||
|
@ -276,9 +270,7 @@ public class IndicesServiceTests extends ESSingleNodeTestCase {
|
|||
assertThat(clusterService.state(), equalTo(originalState));
|
||||
|
||||
// remove the alias
|
||||
action = new AliasAction(AliasAction.Type.REMOVE, indexName, alias);
|
||||
request = new IndicesAliasesRequest().addAliasAction(action);
|
||||
client().admin().indices().aliases(request).actionGet();
|
||||
client().admin().indices().prepareAliases().removeAlias(indexName, alias).get();
|
||||
|
||||
// now try importing a dangling index with the same name as the alias, it should succeed.
|
||||
listener = new DanglingListener();
|
||||
|
|
|
@ -239,6 +239,15 @@ public class IngestDocumentTests extends ESTestCase {
|
|||
assertFalse(ingestDocument.hasField("list.10"));
|
||||
}
|
||||
|
||||
public void testListHasFieldIndexOutOfBounds_fail() {
|
||||
assertTrue(ingestDocument.hasField("list.0", true));
|
||||
assertTrue(ingestDocument.hasField("list.1", true));
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> ingestDocument.hasField("list.2", true));
|
||||
assertThat(e.getMessage(), equalTo("[2] is out of bounds for array with length [2] as part of path [list.2]"));
|
||||
e = expectThrows(IllegalArgumentException.class, () -> ingestDocument.hasField("list.10", true));
|
||||
assertThat(e.getMessage(), equalTo("[10] is out of bounds for array with length [2] as part of path [list.10]"));
|
||||
}
|
||||
|
||||
public void testListHasFieldIndexNotNumeric() {
|
||||
assertFalse(ingestDocument.hasField("list.test"));
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.routing;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
|
@ -30,7 +31,6 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.elasticsearch.cluster.metadata.AliasAction.newAddAliasAction;
|
||||
import static org.elasticsearch.common.util.set.Sets.newHashSet;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
|
@ -62,14 +62,15 @@ public class AliasResolveRoutingIT extends ESIntegTestCase {
|
|||
createIndex("test2");
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias10").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias110").searchRouting("1,0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias12").routing("2")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias20").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias21").routing("1")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias0").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias0").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases()
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias"))
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias10").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias110").searchRouting("1,0"))
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias12").routing("2"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("alias20").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("alias21").routing("1"))
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias0").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("alias0").routing("0")).get();
|
||||
|
||||
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "test1"), nullValue());
|
||||
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "alias"), nullValue());
|
||||
|
@ -103,12 +104,13 @@ public class AliasResolveRoutingIT extends ESIntegTestCase {
|
|||
createIndex("test2");
|
||||
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias10").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias20").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias21").routing("1")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias0").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias0").routing("0")).execute().actionGet();
|
||||
client().admin().indices().prepareAliases()
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias"))
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias10").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("alias20").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("alias21").routing("1"))
|
||||
.addAliasAction(AliasActions.add().index("test1").alias("alias0").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test2").alias("alias0").routing("0")).get();
|
||||
|
||||
ClusterState state = clusterService().state();
|
||||
IndexNameExpressionResolver indexNameExpressionResolver = internalCluster().getInstance(IndexNameExpressionResolver.class);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.routing;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
||||
|
@ -26,12 +27,11 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
|
||||
import static org.elasticsearch.cluster.metadata.AliasAction.newAddAliasAction;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
*
|
||||
* Test aliases with routing.
|
||||
*/
|
||||
public class AliasRoutingIT extends ESIntegTestCase {
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
public void testAliasCrudRouting() throws Exception {
|
||||
createIndex("test");
|
||||
ensureGreen();
|
||||
assertAcked(admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test", "alias0").routing("0")));
|
||||
assertAcked(admin().indices().prepareAliases().addAliasAction(AliasActions.add().index("test").alias("alias0").routing("0")));
|
||||
|
||||
logger.info("--> indexing with id [1], and routing [0] using alias");
|
||||
client().prepareIndex("alias0", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
@ -105,10 +105,10 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
createIndex("test");
|
||||
ensureGreen();
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("test", "alias"))
|
||||
.addAliasAction(newAddAliasAction("test", "alias0").routing("0"))
|
||||
.addAliasAction(newAddAliasAction("test", "alias1").routing("1"))
|
||||
.addAliasAction(newAddAliasAction("test", "alias01").searchRouting("0,1")));
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias"))
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias0").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias1").routing("1"))
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias01").searchRouting("0,1")));
|
||||
|
||||
logger.info("--> indexing with id [1], and routing [0] using alias");
|
||||
client().prepareIndex("alias0", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
@ -200,12 +200,12 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
createIndex("test-b");
|
||||
ensureGreen();
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("test-a", "alias-a0").routing("0"))
|
||||
.addAliasAction(newAddAliasAction("test-a", "alias-a1").routing("1"))
|
||||
.addAliasAction(newAddAliasAction("test-b", "alias-b0").routing("0"))
|
||||
.addAliasAction(newAddAliasAction("test-b", "alias-b1").routing("1"))
|
||||
.addAliasAction(newAddAliasAction("test-a", "alias-ab").searchRouting("0"))
|
||||
.addAliasAction(newAddAliasAction("test-b", "alias-ab").searchRouting("1")));
|
||||
.addAliasAction(AliasActions.add().index("test-a").alias("alias-a0").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test-a").alias("alias-a1").routing("1"))
|
||||
.addAliasAction(AliasActions.add().index("test-b").alias("alias-b0").routing("0"))
|
||||
.addAliasAction(AliasActions.add().index("test-b").alias("alias-b1").routing("1"))
|
||||
.addAliasAction(AliasActions.add().index("test-a").alias("alias-ab").searchRouting("0"))
|
||||
.addAliasAction(AliasActions.add().index("test-b").alias("alias-ab").searchRouting("1")));
|
||||
ensureGreen(); // wait for events again to make sure we got the aliases on all nodes
|
||||
logger.info("--> indexing with id [1], and routing [0] using alias to test-a");
|
||||
client().prepareIndex("alias-a0", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
@ -259,7 +259,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
createIndex("index", "index_2");
|
||||
ensureGreen();
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("index", "index_1").routing("1")));
|
||||
.addAliasAction(AliasActions.add().index("index").alias("index_1").routing("1")));
|
||||
|
||||
logger.info("--> indexing on index_1 which is an alias for index with routing [1]");
|
||||
client().prepareIndex("index_1", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
@ -284,7 +284,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
createIndex("index", "index_2");
|
||||
ensureGreen();
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("index", "index_1").routing("1")));
|
||||
.addAliasAction(AliasActions.add().index("index").alias("index_1").routing("1")));
|
||||
|
||||
logger.info("--> indexing on index_1 which is an alias for index with routing [1]");
|
||||
client().prepareIndex("index_1", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
@ -305,7 +305,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
ensureGreen();
|
||||
logger.info("--> creating alias with routing [3]");
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("test", "alias").routing("3")));
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias").routing("3")));
|
||||
|
||||
logger.info("--> indexing with id [0], and routing [3]");
|
||||
client().prepareIndex("alias", "type1", "0").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
@ -320,7 +320,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
|
||||
logger.info("--> creating alias with routing [4]");
|
||||
assertAcked(admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("test", "alias").routing("4")));
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias").routing("4")));
|
||||
|
||||
logger.info("--> verifying search with wrong routing should not find");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
|
@ -330,7 +330,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
|
|||
|
||||
logger.info("--> creating alias with search routing [3,4] and index routing 4");
|
||||
assertAcked(client().admin().indices().prepareAliases()
|
||||
.addAliasAction(newAddAliasAction("test", "alias").searchRouting("3,4").indexRouting("4")));
|
||||
.addAliasAction(AliasActions.add().index("test").alias("alias").searchRouting("3,4").indexRouting("4")));
|
||||
|
||||
logger.info("--> indexing with id [1], and routing [4]");
|
||||
client().prepareIndex("alias", "type1", "1").setSource("field", "value2").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
|
||||
|
|
|
@ -38,7 +38,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.elasticsearch.client.Requests.indexAliasesRequest;
|
||||
import static org.elasticsearch.client.Requests.indexRequest;
|
||||
import static org.elasticsearch.client.Requests.refreshRequest;
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
|
||||
|
@ -108,8 +107,9 @@ public class MoreLikeThisIT extends ESIntegTestCase {
|
|||
.startObject("text").field("type", "text").endObject()
|
||||
.endObject().endObject().endObject()));
|
||||
logger.info("Creating aliases alias release");
|
||||
client().admin().indices().aliases(indexAliasesRequest().addAlias("release", termQuery("text", "release"), "test")).actionGet();
|
||||
client().admin().indices().aliases(indexAliasesRequest().addAlias("beta", termQuery("text", "beta"), "test")).actionGet();
|
||||
client().admin().indices().prepareAliases()
|
||||
.addAlias("test", "release", termQuery("text", "release"))
|
||||
.addAlias("test", "beta", termQuery("text", "beta")).get();
|
||||
|
||||
logger.info("Running Cluster Health");
|
||||
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));
|
||||
|
@ -155,8 +155,8 @@ public class MoreLikeThisIT extends ESIntegTestCase {
|
|||
.startObject("properties")
|
||||
.endObject()
|
||||
.endObject().endObject().string();
|
||||
client().admin().indices().prepareCreate(indexName).addMapping(typeName, mapping).execute().actionGet();
|
||||
client().admin().indices().aliases(indexAliasesRequest().addAlias(aliasName, indexName)).actionGet();
|
||||
client().admin().indices().prepareCreate(indexName).addMapping(typeName, mapping).get();
|
||||
client().admin().indices().prepareAliases().addAlias(indexName, aliasName).get();
|
||||
|
||||
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));
|
||||
|
||||
|
|
|
@ -90,16 +90,67 @@ http://hc.apache.org/httpcomponents-asyncclient-dev/httpasyncclient/apidocs/org/
|
|||
=== Performing requests
|
||||
|
||||
Once the `RestClient` has been created, requests can be sent by calling one of
|
||||
the available `performRequest` method variants. The ones that return the
|
||||
`Response` are executed synchronously, meaning that the client will block and
|
||||
wait for a response to be returned. The `performRequest` variants that return
|
||||
`void` accept a `ResponseListener` as an argument and are executed
|
||||
asynchronously. The provided listener will be notified upon completion or
|
||||
failure. The following are the arguments accepted by the different
|
||||
`performRequest` methods:
|
||||
the available `performRequest` or `performRequestAsync` method variants.
|
||||
The `performRequest` methods are synchronous and they return the `Response`
|
||||
directly, meaning that the client will block and wait for a response to be returned.
|
||||
The `performRequestAsync` variants, which return `void` and accept an extra
|
||||
`ResponseListener` as an argument, are executed asynchronously. The provided
|
||||
listener will be notified upon completion or failure.
|
||||
|
||||
[source,java]
|
||||
--------------------------------------------------
|
||||
// Synchronous variants
|
||||
Response performRequest(String method, String endpoint,
|
||||
Header... headers)
|
||||
throws IOException;
|
||||
|
||||
Response performRequest(String method, String endpoint,
|
||||
Map<String, String> params, Header... headers)
|
||||
throws IOException;
|
||||
|
||||
Response performRequest(String method, String endpoint,
|
||||
Map<String, String> params,
|
||||
HttpEntity entity,
|
||||
Header... headers)
|
||||
throws IOException;
|
||||
|
||||
Response performRequest(String method, String endpoint,
|
||||
Map<String, String> params,
|
||||
HttpEntity entity,
|
||||
HttpAsyncResponseConsumer<HttpResponse> responseConsumer,
|
||||
Header... headers)
|
||||
throws IOException;
|
||||
|
||||
// Asynchronous variants
|
||||
void performRequestAsync(String method, String endpoint,
|
||||
ResponseListener responseListener,
|
||||
Header... headers);
|
||||
|
||||
void performRequestAsync(String method, String endpoint,
|
||||
Map<String, String> params,
|
||||
ResponseListener responseListener,
|
||||
Header... headers);
|
||||
|
||||
void performRequestAsync(String method, String endpoint,
|
||||
Map<String, String> params,
|
||||
HttpEntity entity,
|
||||
ResponseListener responseListener,
|
||||
Header... headers);
|
||||
|
||||
void performRequestAsync(String method, String endpoint,
|
||||
Map<String, String> params,
|
||||
HttpEntity entity,
|
||||
ResponseListener responseListener,
|
||||
HttpAsyncResponseConsumer<HttpResponse> responseConsumer,
|
||||
Header... headers);
|
||||
--------------------------------------------------
|
||||
|
||||
==== Request Arguments
|
||||
|
||||
The following are the arguments accepted by the different methods:
|
||||
|
||||
`method`:: the http method or verb
|
||||
`endpoint`:: the request path, which identifies the Elasticsearch api to
|
||||
`endpoint`:: the request path, which identifies the Elasticsearch API to
|
||||
call (e.g. `/_cluster/health`)
|
||||
`params`:: the optional parameters to be sent as querystring parameters
|
||||
`entity`:: the optional request body enclosed in an
|
||||
|
@ -109,14 +160,14 @@ http://hc.apache.org/httpcomponents-core-ga/httpcore-nio/apidocs/org/apache/http
|
|||
callback. Controls how the response body gets streamed from a non-blocking
|
||||
HTTP connection on the client side. When not provided, the default
|
||||
implementation is used which buffers the whole response body in heap memory
|
||||
`responseListener`:: the listener to be notified upon request success or failure
|
||||
whenever the async `performRequest` method variants are used
|
||||
`responseListener`:: the listener to be notified upon asynchronous
|
||||
request success or failure
|
||||
`headers`:: optional request headers
|
||||
|
||||
=== Reading responses
|
||||
|
||||
The `Response` object, either returned by the sync `performRequest` methods or
|
||||
received as an argument in `ResponseListener#onSucces(Response)`, wraps the
|
||||
The `Response` object, either returned by the synchronous `performRequest` methods or
|
||||
received as an argument in `ResponseListener#onSuccess(Response)`, wraps the
|
||||
response object returned by the http client and exposes the following information:
|
||||
|
||||
`getRequestLine`:: information about the performed request
|
||||
|
@ -129,14 +180,19 @@ https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/Ht
|
|||
object
|
||||
|
||||
When performing a request, an exception is thrown (or received as an argument
|
||||
in `ResponseListener#onSucces(Exception)` in the following scenarios:
|
||||
in `ResponseListener#onFailure(Exception)` in the following scenarios:
|
||||
|
||||
`IOException`:: communication problem (e.g. SocketTimeoutException etc.)
|
||||
`ResponseException`:: a response was returned, but its status code indicated
|
||||
an error (either `4xx` or `5xx`). A `ResponseException` originates from a valid
|
||||
an error (not `2xx`). A `ResponseException` originates from a valid
|
||||
http response, hence it exposes its corresponding `Response` object which gives
|
||||
access to the returned response.
|
||||
|
||||
NOTE: A `ResponseException` is **not** thrown for `HEAD` requests that return
|
||||
a `404` status code because it is an expected `HEAD` response that simply
|
||||
denotes that the resource is not found. All other HTTP methods (e.g., `GET`)
|
||||
throw a `ResponseException` for `404` responses.
|
||||
|
||||
|
||||
=== Example requests
|
||||
|
||||
|
@ -167,6 +223,7 @@ Response indexResponse = restClient.performRequest(
|
|||
Note that the low-level client doesn't expose any helper for json marshalling
|
||||
and un-marshalling. Users are free to use the library that they prefer for that
|
||||
purpose.
|
||||
|
||||
The underlying Apache Async Http Client ships with different
|
||||
https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/HttpEntity.html[`org.apache.http.HttpEntity`]
|
||||
implementations that allow to provide the request body in different formats
|
||||
|
@ -184,7 +241,7 @@ The following is a basic example of how async requests can be sent:
|
|||
int numRequests = 10;
|
||||
final CountDownLatch latch = new CountDownLatch(numRequests);
|
||||
for (int i = 0; i < numRequests; i++) {
|
||||
restClient.performRequest(
|
||||
restClient.performRequestAsync(
|
||||
"PUT",
|
||||
"/twitter/tweet/" + i,
|
||||
Collections.<String, String>emptyMap(),
|
||||
|
|
|
@ -276,6 +276,41 @@ curl 'localhost:9200/_segments?pretty&filter_path=indices.**.version'
|
|||
}
|
||||
--------------------------------------------------
|
||||
|
||||
It is also possible to exclude one or more fields by prefixing the filter with the char `-`:
|
||||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
curl -XGET 'localhost:9200/_count?filter_path=-_shards'
|
||||
{
|
||||
"count" : 1
|
||||
}
|
||||
%
|
||||
--------------------------------------------------
|
||||
|
||||
And for more control, both inclusive and exclusive filters can be combined in the same expression. In
|
||||
this case, the exclusive filters will be applied first and the result will be filtered again using the
|
||||
inclusive filters:
|
||||
|
||||
[source,sh]
|
||||
--------------------------------------------------
|
||||
curl -XGET 'localhost:9200/_cluster/state?filter_path=metadata.indices.*.state,-metadata.indices.logs-*'
|
||||
{
|
||||
"metadata" : {
|
||||
"indices" : {
|
||||
"index-1" : {
|
||||
"state" : "open"
|
||||
},
|
||||
"index-3" : {
|
||||
"state" : "open"
|
||||
},
|
||||
"index-2" : {
|
||||
"state" : "open"
|
||||
}
|
||||
}
|
||||
}
|
||||
}%
|
||||
--------------------------------------------------
|
||||
|
||||
Note that elasticsearch sometimes returns directly the raw value of a field,
|
||||
like the `_source` field. If you want to filter `_source` fields, you should
|
||||
consider combining the already existing `_source` parameter (see
|
||||
|
|
|
@ -48,7 +48,7 @@ will still add it, since its a list):
|
|||
--------------------------------------------------
|
||||
curl -XPOST 'localhost:9200/test/type1/1/_update' -d '{
|
||||
"script" : {
|
||||
"inline": "ctx._source.tags += params.tag",
|
||||
"inline": "ctx._source.tags.add(params.tag)",
|
||||
"lang": "painless",
|
||||
"params" : {
|
||||
"tag" : "blue"
|
||||
|
@ -247,7 +247,7 @@ Timeout waiting for a shard to become available.
|
|||
|
||||
`wait_for_active_shards`::
|
||||
|
||||
The number of shard copies required to be active before proceeding with the update operation.
|
||||
The number of shard copies required to be active before proceeding with the update operation.
|
||||
See <<index-wait-for-active-shards,here>> for details.
|
||||
|
||||
`refresh`::
|
||||
|
|
|
@ -24,7 +24,7 @@ POST /_aliases
|
|||
// CONSOLE
|
||||
// TEST[s/^/PUT test1\nPUT test2\n/]
|
||||
|
||||
An alias can also be removed, for example:
|
||||
And here is removing that same alias:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
|
@ -109,6 +109,25 @@ indices that match this pattern are added/removed.
|
|||
|
||||
It is an error to index to an alias which points to more than one index.
|
||||
|
||||
It is also possible to swap an index with an alias in one operation:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
PUT test <1>
|
||||
PUT test_2 <2>
|
||||
POST /_aliases
|
||||
{
|
||||
"actions" : [
|
||||
{ "add": { "index": "test_2", "alias": "test" } },
|
||||
{ "remove_index": { "index": "test" } } <3>
|
||||
]
|
||||
}
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
<1> An index we've added by mistake
|
||||
<2> The index we should have added
|
||||
<3> `remove_index` is just like <<indices-delete-index>>
|
||||
|
||||
[float]
|
||||
[[filtered]]
|
||||
=== Filtered Aliases
|
||||
|
|
|
@ -57,3 +57,10 @@ that the multi-index matching and expansion is not confused. It was previously
|
|||
possible (but a really bad idea) to create indices starting with a hyphen or
|
||||
plus sign. Any index already existing with these preceding characters will
|
||||
continue to work normally.
|
||||
|
||||
==== Aliases API
|
||||
|
||||
The `/_aliases` API no longer supports `indexRouting` and `index-routing`, only
|
||||
`index_routing`. It also no longer support `searchRouting` and `search-routing`,
|
||||
only `search_routing`. These were removed because they were untested and we
|
||||
prefer there to be only one (obvious) way to do things like this.
|
||||
|
|
|
@ -3,6 +3,4 @@
|
|||
|
||||
deprecated[5.0.0,Percolate and multi percolate APIs are deprecated and have been replaced by the new <<query-dsl-percolate-query,`percolate` query>>]
|
||||
|
||||
added[5.0.0,Percolate query modifications only become visible after a refresh has occurred. Previously, they became visible immediately]
|
||||
|
||||
added[5.0.0,For indices created on or after version 5.0.0-alpha1 the percolator automatically indexes the query terms with the percolator queries. This allows the percolator to percolate documents more quickly. It is advisable to reindex any pre 5.0.0 indices to take advantage of this new optimization]
|
||||
For indices created on or after version 5.0.0-alpha1 the percolator automatically indexes the query terms with the percolator queries. This allows the percolator to percolate documents more quickly. It is advisable to reindex any pre 5.0.0 indices to take advantage of this new optimization.
|
||||
|
|
|
@ -52,20 +52,24 @@ public final class RenameProcessor extends AbstractProcessor {
|
|||
|
||||
@Override
|
||||
public void execute(IngestDocument document) {
|
||||
if (document.hasField(field) == false) {
|
||||
if (document.hasField(field, true) == false) {
|
||||
throw new IllegalArgumentException("field [" + field + "] doesn't exist");
|
||||
}
|
||||
if (document.hasField(targetField)) {
|
||||
// We fail here if the target field point to an array slot that is out of range.
|
||||
// If we didn't do this then we would fail if we set the value in the target_field
|
||||
// and then on failure processors would not see that value we tried to rename as we already
|
||||
// removed it.
|
||||
if (document.hasField(targetField, true)) {
|
||||
throw new IllegalArgumentException("field [" + targetField + "] already exists");
|
||||
}
|
||||
|
||||
Object oldValue = document.getFieldValue(field, Object.class);
|
||||
document.setFieldValue(targetField, oldValue);
|
||||
Object value = document.getFieldValue(field, Object.class);
|
||||
document.removeField(field);
|
||||
try {
|
||||
document.removeField(field);
|
||||
document.setFieldValue(targetField, value);
|
||||
} catch (Exception e) {
|
||||
//remove the new field if the removal of the old one failed
|
||||
document.removeField(targetField);
|
||||
// setting the value back to the original field shouldn't as we just fetched it from that field:
|
||||
document.setFieldValue(field, value);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,4 +171,27 @@ public class RenameProcessorTests extends ESTestCase {
|
|||
assertThat(ingestDocument.getSourceAndMetadata().containsKey("new_field"), equalTo(false));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRenameLeafIntoBranch() throws Exception {
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("foo", "bar");
|
||||
IngestDocument ingestDocument = new IngestDocument(source, Collections.emptyMap());
|
||||
Processor processor1 = new RenameProcessor(randomAsciiOfLength(10), "foo", "foo.bar");
|
||||
processor1.execute(ingestDocument);
|
||||
assertThat(ingestDocument.getFieldValue("foo", Map.class), equalTo(Collections.singletonMap("bar", "bar")));
|
||||
assertThat(ingestDocument.getFieldValue("foo.bar", String.class), equalTo("bar"));
|
||||
|
||||
Processor processor2 = new RenameProcessor(randomAsciiOfLength(10), "foo.bar", "foo.bar.baz");
|
||||
processor2.execute(ingestDocument);
|
||||
assertThat(ingestDocument.getFieldValue("foo", Map.class), equalTo(Collections.singletonMap("bar",
|
||||
Collections.singletonMap("baz", "bar"))));
|
||||
assertThat(ingestDocument.getFieldValue("foo.bar", Map.class), equalTo(Collections.singletonMap("baz", "bar")));
|
||||
assertThat(ingestDocument.getFieldValue("foo.bar.baz", String.class), equalTo("bar"));
|
||||
|
||||
// for fun lets try to restore it (which don't allow today)
|
||||
Processor processor3 = new RenameProcessor(randomAsciiOfLength(10), "foo.bar.baz", "foo");
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> processor3.execute(ingestDocument));
|
||||
assertThat(e.getMessage(), equalTo("field [foo] already exists"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,13 +33,13 @@ compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-tr
|
|||
|
||||
dependencies {
|
||||
// network stack
|
||||
compile "io.netty:netty-buffer:4.1.4.Final"
|
||||
compile "io.netty:netty-codec:4.1.4.Final"
|
||||
compile "io.netty:netty-codec-http:4.1.4.Final"
|
||||
compile "io.netty:netty-common:4.1.4.Final"
|
||||
compile "io.netty:netty-handler:4.1.4.Final"
|
||||
compile "io.netty:netty-resolver:4.1.4.Final"
|
||||
compile "io.netty:netty-transport:4.1.4.Final"
|
||||
compile "io.netty:netty-buffer:4.1.5.Final"
|
||||
compile "io.netty:netty-codec:4.1.5.Final"
|
||||
compile "io.netty:netty-codec-http:4.1.5.Final"
|
||||
compile "io.netty:netty-common:4.1.5.Final"
|
||||
compile "io.netty:netty-handler:4.1.5.Final"
|
||||
compile "io.netty:netty-resolver:4.1.5.Final"
|
||||
compile "io.netty:netty-transport:4.1.5.Final"
|
||||
}
|
||||
|
||||
integTest {
|
||||
|
@ -114,9 +114,9 @@ thirdPartyAudit.excludes = [
|
|||
'com.ning.compress.lzf.LZFEncoder',
|
||||
'com.ning.compress.lzf.util.ChunkDecoderFactory',
|
||||
'com.ning.compress.lzf.util.ChunkEncoderFactory',
|
||||
'javassist/ClassClassPath',
|
||||
'javassist/ClassPath',
|
||||
'javassist/ClassPool',
|
||||
'javassist.ClassClassPath',
|
||||
'javassist.ClassPath',
|
||||
'javassist.ClassPool',
|
||||
'javassist.CtClass',
|
||||
'javassist.CtMethod',
|
||||
'lzma.sdk.lzma.Encoder',
|
||||
|
@ -127,7 +127,9 @@ thirdPartyAudit.excludes = [
|
|||
'net.jpountz.xxhash.XXHashFactory',
|
||||
'org.apache.logging.log4j.LogManager',
|
||||
'org.apache.logging.log4j.Logger',
|
||||
'org.apache.tomcat.Apr',
|
||||
'org.apache.tomcat.jni.CertificateRequestedCallback',
|
||||
'org.apache.tomcat.jni.CertificateRequestedCallback$KeyMaterial',
|
||||
'org.apache.tomcat.jni.CertificateVerifier',
|
||||
'org.apache.tomcat.jni.SessionTicketKey',
|
||||
'org.eclipse.jetty.alpn.ALPN$ClientProvider',
|
||||
|
@ -136,6 +138,8 @@ thirdPartyAudit.excludes = [
|
|||
|
||||
'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator',
|
||||
'io.netty.util.internal.PlatformDependent0',
|
||||
'io.netty.util.internal.PlatformDependent0$2',
|
||||
'io.netty.util.internal.PlatformDependent0$3',
|
||||
'io.netty.util.internal.UnsafeAtomicIntegerFieldUpdater',
|
||||
'io.netty.util.internal.UnsafeAtomicLongFieldUpdater',
|
||||
'io.netty.util.internal.UnsafeAtomicReferenceFieldUpdater',
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
5e199381c808377faeeb80e69f365246004d6f9f
|
|
@ -0,0 +1 @@
|
|||
b5fb6bccda4d63d4a74c9faccdf32f77ab66abc1
|
|
@ -1 +0,0 @@
|
|||
86fc9367492cdca25542fb037467ab853a77ff62
|
|
@ -0,0 +1 @@
|
|||
66bbf9324fa36467d041083f89328e2a24ec4f67
|
|
@ -1 +0,0 @@
|
|||
f6ef27ed46dd806dc27c1f0ed2e6bcfad12d28cc
|
|
@ -0,0 +1 @@
|
|||
087bda1b9ec7e3f75ca721fc87735cbedad2aa1a
|
|
@ -1 +0,0 @@
|
|||
e267ca463a0dc6292cf5e0528c4b59d6d5f76ff5
|
|
@ -0,0 +1 @@
|
|||
607f8433d8782445e72abe34e43a7e57e86a5e6c
|
|
@ -1 +0,0 @@
|
|||
5674b3bc1203d913da71ba98bb7d43832c7a71e7
|
|
@ -0,0 +1 @@
|
|||
6262900ee9487e62560030a136160df953b1cd6b
|
|
@ -1 +0,0 @@
|
|||
19f86688069a018cf5e45f7a9f9a02971b9e31ee
|
|
@ -0,0 +1 @@
|
|||
5f367bedcdc185a727fda3296b9a18014cdc22c4
|
|
@ -1 +0,0 @@
|
|||
6c99d2323d75d74b9571c4ddbdc411d96bf8d780
|
|
@ -0,0 +1 @@
|
|||
37126b370722ff9631ee13c91139aacec0a71d1d
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
grant codeBase "${codebase.netty-transport-4.1.4.Final.jar}" {
|
||||
grant codeBase "${codebase.netty-transport-4.1.5.Final.jar}" {
|
||||
// Netty NioEventLoop wants to change this, because of https://bugs.openjdk.java.net/browse/JDK-6427854
|
||||
// the bug says it only happened rarely, and that its fixed, but apparently it still happens rarely!
|
||||
permission java.util.PropertyPermission "sun.nio.ch.bugLevel", "write";
|
||||
|
|
|
@ -82,3 +82,87 @@
|
|||
|
||||
- match: {test_index.aliases.test_alias1: {'index_routing': 'routing_value', 'search_routing': 'routing_value'}}
|
||||
- match: {test_index.aliases.test_alias2: {'index_routing': 'routing_value', 'search_routing': 'routing_value'}}
|
||||
|
||||
---
|
||||
"Remove alias":
|
||||
- do:
|
||||
indices.create:
|
||||
index: test_index
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias1
|
||||
- is_false: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias2
|
||||
- is_false: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias3
|
||||
- is_false: ''
|
||||
|
||||
- do:
|
||||
indices.update_aliases:
|
||||
body:
|
||||
actions:
|
||||
- add:
|
||||
index: test_index
|
||||
aliases: [test_alias1, test_alias2]
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias1
|
||||
- is_true: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias2
|
||||
- is_true: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias3
|
||||
- is_false: ''
|
||||
|
||||
- do:
|
||||
indices.update_aliases:
|
||||
body:
|
||||
actions:
|
||||
- remove:
|
||||
index: test_index
|
||||
alias: test_alias1
|
||||
- add:
|
||||
index: test_index
|
||||
alias: test_alias3
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias1
|
||||
- is_false: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias2
|
||||
- is_true: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias3
|
||||
- is_true: ''
|
||||
|
||||
- do:
|
||||
indices.update_aliases:
|
||||
body:
|
||||
actions:
|
||||
- remove:
|
||||
index: test_index
|
||||
alias: test_alias2
|
||||
- remove:
|
||||
index: test_index
|
||||
alias: test_alias3
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias1
|
||||
- is_false: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias2
|
||||
- is_false: ''
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_alias3
|
||||
- is_false: ''
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
"Remove and index and replace it with an alias":
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: test
|
||||
- do:
|
||||
indices.create:
|
||||
index: test_2
|
||||
|
||||
- do:
|
||||
indices.update_aliases:
|
||||
body:
|
||||
actions:
|
||||
- add:
|
||||
index: test_2
|
||||
aliases: [test, test_write]
|
||||
- remove_index:
|
||||
index: test
|
||||
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test
|
||||
- is_true: ''
|
||||
|
||||
- do:
|
||||
indices.exists_alias:
|
||||
name: test_write
|
||||
- is_true: ''
|
||||
|
||||
- do:
|
||||
indices.get_mapping:
|
||||
index: test
|
||||
- is_true: test_2 # the name of the index that the alias points to, would be `test` if the index were still there
|
|
@ -152,3 +152,49 @@
|
|||
- is_false: nodes.$master.fs.data.0.path
|
||||
- is_true: nodes.$master.fs.data.0.type
|
||||
- is_true: nodes.$master.fs.data.0.total_in_bytes
|
||||
|
||||
---
|
||||
"Nodes Stats filtered using both includes and excludes filters":
|
||||
- do:
|
||||
cluster.state: {}
|
||||
|
||||
# Get master node id
|
||||
- set: { master_node: master }
|
||||
|
||||
# Nodes Stats with "nodes" field but no JVM stats
|
||||
- do:
|
||||
nodes.stats:
|
||||
filter_path: [ "nodes", "-nodes.*.jvm", "-nodes.*.indices" ]
|
||||
|
||||
- is_false: cluster_name
|
||||
- is_true: nodes
|
||||
- is_true: nodes.$master.name
|
||||
- is_true: nodes.$master.os
|
||||
- is_false: nodes.$master.indices
|
||||
- is_false: nodes.$master.jvm
|
||||
|
||||
# Nodes Stats with "nodes.*.indices" field and sub-fields but no indices segments
|
||||
- do:
|
||||
nodes.stats:
|
||||
filter_path: "nodes.*.indices,-nodes.*.indices.segments"
|
||||
|
||||
- is_false: cluster_name
|
||||
- is_true: nodes
|
||||
- is_false: nodes.$master.name
|
||||
- is_true: nodes.$master.indices
|
||||
- is_true: nodes.$master.indices.docs
|
||||
- is_false: nodes.$master.indices.segments
|
||||
|
||||
# Nodes Stats with "nodes.*.fs.data.t*" fields but no "type" field
|
||||
- do:
|
||||
nodes.stats:
|
||||
filter_path: "nodes.*.fs.data.t*,-**.type"
|
||||
|
||||
- is_false: cluster_name
|
||||
- is_true: nodes
|
||||
- is_false: nodes.$master.name
|
||||
- is_false: nodes.$master.indices
|
||||
- is_false: nodes.$master.jvm
|
||||
- is_true: nodes.$master.fs.data
|
||||
- is_false: nodes.$master.fs.data.0.type
|
||||
- is_true: nodes.$master.fs.data.0.total_in_bytes
|
||||
|
|
|
@ -84,3 +84,64 @@
|
|||
- is_true: hits.hits.1._index
|
||||
- is_false: hits.hits.1._type
|
||||
- is_true: hits.hits.1._id
|
||||
|
||||
---
|
||||
"Search results filtered using both includes and excludes filters":
|
||||
- do:
|
||||
bulk:
|
||||
refresh: true
|
||||
body: |
|
||||
{"index": {"_index": "index-1", "_type": "type-1", "_id": "1"}}
|
||||
{"name": "First document", "properties": {"size": 123, "meta": {"foo": "bar"}}}
|
||||
{"index": {"_index": "index-1", "_type": "type-1", "_id": "2"}}
|
||||
{"name": "Second document", "properties": {"size": 465, "meta": {"foo": "bar", "baz": "qux"}}}
|
||||
|
||||
- do:
|
||||
search:
|
||||
filter_path: [ "-**._source.properties", "**._source" ]
|
||||
body: { query: { match_all: {} } }
|
||||
|
||||
- is_false: took
|
||||
- is_true: hits.hits.0._source
|
||||
- is_true: hits.hits.0._source.name
|
||||
- is_false: hits.hits.0._source.properties
|
||||
- is_true: hits.hits.1._source
|
||||
- is_true: hits.hits.1._source.name
|
||||
- is_false: hits.hits.1._source.properties
|
||||
|
||||
- do:
|
||||
search:
|
||||
filter_path: [ "**.properties" , "-hits.hits._source.properties.meta" ]
|
||||
body: { query: { match_all: {} } }
|
||||
|
||||
- is_false: took
|
||||
- is_true: hits.hits.0._source
|
||||
- is_false: hits.hits.0._source.name
|
||||
- is_true: hits.hits.0._source.properties
|
||||
- is_true: hits.hits.0._source.properties.size
|
||||
- is_false: hits.hits.0._source.properties.meta
|
||||
- is_true: hits.hits.1._source
|
||||
- is_false: hits.hits.1._source.name
|
||||
- is_true: hits.hits.1._source.properties
|
||||
- is_true: hits.hits.1._source.properties.size
|
||||
- is_false: hits.hits.1._source.properties.meta
|
||||
|
||||
- do:
|
||||
search:
|
||||
filter_path: "**._source,-**.meta.foo"
|
||||
body: { query: { match_all: {} } }
|
||||
|
||||
- is_false: took
|
||||
- is_true: hits.hits.0._source
|
||||
- is_true: hits.hits.0._source.name
|
||||
- is_true: hits.hits.0._source.properties
|
||||
- is_true: hits.hits.0._source.properties.size
|
||||
- is_false: hits.hits.0._source.properties.meta.foo
|
||||
|
||||
- do:
|
||||
count:
|
||||
filter_path: "-*"
|
||||
body: { query: { match_all: {} } }
|
||||
|
||||
- is_false: count
|
||||
- is_false: _shards
|
||||
|
|
Loading…
Reference in New Issue