Support wildcard and +/- notation for multi index APIs, closes #2074.

This commit is contained in:
Shay Banon 2012-07-01 18:16:04 +02:00
parent 22bdd161ad
commit e5c89def42
4 changed files with 242 additions and 88 deletions

View File

@ -84,6 +84,10 @@ public class AliasMetaData {
return indexRouting;
}
public static Builder builder(String alias) {
return new Builder(alias);
}
public static Builder newAliasMetaDataBuilder(String alias) {
return new Builder(alias);
}

View File

@ -368,6 +368,10 @@ public class IndexMetaData {
return result;
}
public static Builder builder(String index) {
return new Builder(index);
}
public static Builder newIndexMetaDataBuilder(String index) {
return new Builder(index);
}

View File

@ -19,7 +19,10 @@
package org.elasticsearch.cluster.metadata;
import com.google.common.collect.*;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.UnmodifiableIterator;
import gnu.trove.set.hash.THashSet;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.cluster.block.ClusterBlock;
@ -303,20 +306,6 @@ public class MetaData implements Iterable<IndexMetaData> {
return allOpenIndices;
}
/**
* Translates the provided indices (possibly aliased) into actual indices.
*/
public String[] concreteIndices(String[] indices) throws IndexMissingException {
return concreteIndices(indices, false, false);
}
/**
* Translates the provided indices (possibly aliased) into actual indices.
*/
public String[] concreteIndicesIgnoreMissing(String[] indices) {
return concreteIndices(indices, true, false);
}
/**
* Returns indexing routing for the given index.
*/
@ -346,83 +335,29 @@ public class MetaData implements Iterable<IndexMetaData> {
return routing;
}
/**
* Sets the same routing for all indices
*/
private Map<String, Set<String>> resolveSearchRoutingAllIndices(String routing) {
if (routing != null) {
Set<String> r = Strings.splitStringByCommaToSet(routing);
Map<String, Set<String>> routings = newHashMap();
String[] concreteIndices = concreteAllIndices();
for (String index : concreteIndices) {
routings.put(index, r);
}
return routings;
}
return null;
}
public Map<String, Set<String>> resolveSearchRouting(@Nullable String routing, String aliasOrIndex) {
Map<String, Set<String>> routings = null;
Set<String> paramRouting = null;
if (routing != null) {
paramRouting = Strings.splitStringByCommaToSet(routing);
}
ImmutableMap<String, ImmutableSet<String>> indexToRoutingMap = aliasToIndexToSearchRoutingMap.get(aliasOrIndex);
if (indexToRoutingMap != null && !indexToRoutingMap.isEmpty()) {
// It's an alias
for (Map.Entry<String, ImmutableSet<String>> indexRouting : indexToRoutingMap.entrySet()) {
if (!indexRouting.getValue().isEmpty()) {
// Routing alias
Set<String> r = new THashSet<String>(indexRouting.getValue());
if (paramRouting != null) {
r.retainAll(paramRouting);
}
if (!r.isEmpty()) {
if (routings == null) {
routings = newHashMap();
}
routings.put(indexRouting.getKey(), r);
}
} else {
// Non-routing alias
if (paramRouting != null) {
Set<String> r = new THashSet<String>(paramRouting);
if (routings == null) {
routings = newHashMap();
}
routings.put(indexRouting.getKey(), r);
}
}
}
} else {
// It's an index
if (paramRouting != null) {
routings = ImmutableMap.of(aliasOrIndex, paramRouting);
}
}
return routings;
return resolveSearchRouting(routing, convertFromWildcards(new String[]{aliasOrIndex}, true, true));
}
public Map<String, Set<String>> resolveSearchRouting(@Nullable String routing, String[] aliasesOrIndices) {
if (aliasesOrIndices == null || aliasesOrIndices.length == 0) {
return resolveSearchRoutingAllIndices(routing);
}
aliasesOrIndices = convertFromWildcards(aliasesOrIndices, true, true);
if (aliasesOrIndices.length == 1) {
if (aliasesOrIndices[0].equals("_all")) {
return resolveSearchRoutingAllIndices(routing);
} else {
return resolveSearchRouting(routing, aliasesOrIndices[0]);
return resolveSearchRoutingSingleValue(routing, aliasesOrIndices[0]);
}
}
Map<String, Set<String>> routings = null;
Set<String> paramRouting = null;
// List of indices that don't require any routing
Set<String> norouting = newHashSet();
Set<String> norouting = new THashSet<String>();
if (routing != null) {
paramRouting = Strings.splitStringByCommaToSet(routing);
}
@ -493,30 +428,104 @@ public class MetaData implements Iterable<IndexMetaData> {
return routings;
}
private Map<String, Set<String>> resolveSearchRoutingSingleValue(@Nullable String routing, String aliasOrIndex) {
Map<String, Set<String>> routings = null;
Set<String> paramRouting = null;
if (routing != null) {
paramRouting = Strings.splitStringByCommaToSet(routing);
}
ImmutableMap<String, ImmutableSet<String>> indexToRoutingMap = aliasToIndexToSearchRoutingMap.get(aliasOrIndex);
if (indexToRoutingMap != null && !indexToRoutingMap.isEmpty()) {
// It's an alias
for (Map.Entry<String, ImmutableSet<String>> indexRouting : indexToRoutingMap.entrySet()) {
if (!indexRouting.getValue().isEmpty()) {
// Routing alias
Set<String> r = new THashSet<String>(indexRouting.getValue());
if (paramRouting != null) {
r.retainAll(paramRouting);
}
if (!r.isEmpty()) {
if (routings == null) {
routings = newHashMap();
}
routings.put(indexRouting.getKey(), r);
}
} else {
// Non-routing alias
if (paramRouting != null) {
Set<String> r = new THashSet<String>(paramRouting);
if (routings == null) {
routings = newHashMap();
}
routings.put(indexRouting.getKey(), r);
}
}
}
} else {
// It's an index
if (paramRouting != null) {
routings = ImmutableMap.of(aliasOrIndex, paramRouting);
}
}
return routings;
}
/**
* Sets the same routing for all indices
*/
private Map<String, Set<String>> resolveSearchRoutingAllIndices(String routing) {
if (routing != null) {
Set<String> r = Strings.splitStringByCommaToSet(routing);
Map<String, Set<String>> routings = newHashMap();
String[] concreteIndices = concreteAllIndices();
for (String index : concreteIndices) {
routings.put(index, r);
}
return routings;
}
return null;
}
/**
* Translates the provided indices (possibly aliased) into actual indices.
*/
public String[] concreteIndices(String[] indices, boolean ignoreMissing, boolean allOnlyOpen) throws IndexMissingException {
if (indices == null || indices.length == 0) {
public String[] concreteIndices(String[] indices) throws IndexMissingException {
return concreteIndices(indices, false, false);
}
/**
* Translates the provided indices (possibly aliased) into actual indices.
*/
public String[] concreteIndicesIgnoreMissing(String[] indices) {
return concreteIndices(indices, true, false);
}
/**
* Translates the provided indices (possibly aliased) into actual indices.
*/
public String[] concreteIndices(String[] aliasesOrIndices, boolean ignoreMissing, boolean allOnlyOpen) throws IndexMissingException {
if (aliasesOrIndices == null || aliasesOrIndices.length == 0) {
return allOnlyOpen ? concreteAllOpenIndices() : concreteAllIndices();
}
aliasesOrIndices = convertFromWildcards(aliasesOrIndices, allOnlyOpen, false);
// optimize for single element index (common case)
if (indices.length == 1) {
String index = indices[0];
if (index.length() == 0) {
if (aliasesOrIndices.length == 1) {
String aliasOrIndex = aliasesOrIndices[0];
if (aliasOrIndex.length() == 0) {
return allOnlyOpen ? concreteAllOpenIndices() : concreteAllIndices();
}
if (index.equals("_all")) {
if (aliasOrIndex.equals("_all")) {
return allOnlyOpen ? concreteAllOpenIndices() : concreteAllIndices();
}
// if a direct index name, just return the array provided
if (this.indices.containsKey(index)) {
return indices;
if (this.indices.containsKey(aliasOrIndex)) {
return aliasesOrIndices;
}
String[] actualLst = aliasAndIndexToIndexMap.get(index);
String[] actualLst = aliasAndIndexToIndexMap.get(aliasOrIndex);
if (actualLst == null) {
if (!ignoreMissing) {
throw new IndexMissingException(new Index(index));
throw new IndexMissingException(new Index(aliasOrIndex));
} else {
return Strings.EMPTY_ARRAY;
}
@ -528,18 +537,18 @@ public class MetaData implements Iterable<IndexMetaData> {
// check if its a possible aliased index, if not, just return the
// passed array
boolean possiblyAliased = false;
for (String index : indices) {
for (String index : aliasesOrIndices) {
if (!this.indices.containsKey(index)) {
possiblyAliased = true;
break;
}
}
if (!possiblyAliased) {
return indices;
return aliasesOrIndices;
}
Set<String> actualIndices = Sets.newHashSetWithExpectedSize(indices.length);
for (String index : indices) {
Set<String> actualIndices = new THashSet<String>();
for (String index : aliasesOrIndices) {
String[] actualLst = aliasAndIndexToIndexMap.get(index);
if (actualLst == null) {
if (!ignoreMissing) {
@ -570,6 +579,75 @@ public class MetaData implements Iterable<IndexMetaData> {
return lst[0];
}
public String[] convertFromWildcards(String[] aliasesOrIndices, boolean wildcardOnlyOpen, boolean ignoreMissing) {
Set<String> result = null;
for (int i = 0; i < aliasesOrIndices.length; i++) {
String aliasOrIndex = aliasesOrIndices[i];
if (aliasAndIndexToIndexMap.containsKey(aliasOrIndex)) {
if (result != null) {
result.add(aliasOrIndex);
}
continue;
}
boolean add = true;
if (aliasOrIndex.charAt(0) == '+') {
add = true;
aliasOrIndex = aliasOrIndex.substring(1);
} else if (aliasOrIndex.charAt(0) == '-') {
// if its the first, fill it with all the indices...
if (i == 0) {
result = new THashSet<String>(Arrays.asList(wildcardOnlyOpen ? concreteAllOpenIndices() : concreteAllIndices()));
}
add = false;
aliasOrIndex = aliasOrIndex.substring(1);
}
if (!Regex.isSimpleMatchPattern(aliasOrIndex)) {
if (result != null) {
if (add) {
result.add(aliasOrIndex);
} else {
result.remove(aliasOrIndex);
}
}
continue;
}
if (result == null) {
// add all the previous ones...
result = new THashSet<String>();
result.addAll(Arrays.asList(aliasesOrIndices).subList(0, i));
}
String[] indices = wildcardOnlyOpen ? concreteAllOpenIndices() : concreteAllIndices();
boolean found = false;
for (String index : indices) {
if (Regex.simpleMatch(aliasOrIndex, index)) {
found = true;
if (add) {
result.add(index);
} else {
result.remove(index);
}
}
}
for (String alias : aliases.keySet()) {
if (Regex.simpleMatch(aliasOrIndex, alias)) {
found = true;
if (add) {
result.add(alias);
} else {
result.remove(alias);
}
}
}
if (!found && !ignoreMissing) {
throw new IndexMissingException(new Index(aliasOrIndex));
}
}
if (result == null) {
return aliasesOrIndices;
}
return result.toArray(new String[result.size()]);
}
public boolean hasIndex(String index) {
return indices.containsKey(index);
}

View File

@ -0,0 +1,68 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.test.unit.cluster.metadata;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.testng.annotations.Test;
import static com.google.common.collect.Sets.newHashSet;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
/**
*/
public class MetaDataTests {
@Test
public void convertWildcardsJustIndicesTests() {
MetaData.Builder mdBuilder = MetaData.builder()
.put(indexBuilder("testXXX"))
.put(indexBuilder("testXYY"))
.put(indexBuilder("testYYY"))
.put(indexBuilder("kuku"));
MetaData md = mdBuilder.build();
assertThat(newHashSet(md.convertFromWildcards(new String[]{"testXXX"}, true, true)), equalTo(newHashSet("testXXX")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"testXXX", "testYYY"}, true, true)), equalTo(newHashSet("testXXX", "testYYY")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"testXXX", "ku*"}, true, true)), equalTo(newHashSet("testXXX", "kuku")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"test*"}, true, true)), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"testX*"}, true, true)), equalTo(newHashSet("testXXX", "testXYY")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"testX*", "kuku"}, true, true)), equalTo(newHashSet("testXXX", "testXYY", "kuku")));
}
@Test
public void convertWildcardsTests() {
MetaData.Builder mdBuilder = MetaData.builder()
.put(indexBuilder("testXXX").putAlias(AliasMetaData.builder("alias1")).putAlias(AliasMetaData.builder("alias2")))
.put(indexBuilder("testXYY").putAlias(AliasMetaData.builder("alias2")))
.put(indexBuilder("testYYY").putAlias(AliasMetaData.builder("alias3")))
.put(indexBuilder("kuku"));
MetaData md = mdBuilder.build();
assertThat(newHashSet(md.convertFromWildcards(new String[]{"testYY*", "alias*"}, true, true)), equalTo(newHashSet("alias1", "alias2", "alias3", "testYYY")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"-kuku"}, true, true)), equalTo(newHashSet("testXXX", "testXYY", "testYYY")));
assertThat(newHashSet(md.convertFromWildcards(new String[]{"+test*", "-testYYY"}, true, true)), equalTo(newHashSet("testXXX", "testXYY")));
}
private IndexMetaData.Builder indexBuilder(String index) {
return IndexMetaData.builder(index).settings(ImmutableSettings.settingsBuilder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0));
}
}