Add support for filtering aliases to Search

This commit is contained in:
Igor Motov 2011-05-23 14:20:12 -04:00 committed by kimchy
parent becf4baaa2
commit e3bffba327
11 changed files with 395 additions and 65 deletions

View File

@ -84,8 +84,8 @@ public class TransportSearchAction extends BaseAction<SearchRequest, SearchRespo
if (optimizeSingleShard && searchRequest.searchType() != SCAN && searchRequest.searchType() != COUNT) { if (optimizeSingleShard && searchRequest.searchType() != SCAN && searchRequest.searchType() != COUNT) {
try { try {
ClusterState clusterState = clusterService.state(); ClusterState clusterState = clusterService.state();
searchRequest.indices(clusterState.metaData().concreteIndices(searchRequest.indices())); String[] concreteIndices = clusterState.metaData().concreteIndices(searchRequest.indices());
GroupShardsIterator groupIt = clusterService.operationRouting().searchShards(clusterState, searchRequest.indices(), searchRequest.queryHint(), searchRequest.routing(), searchRequest.preference()); GroupShardsIterator groupIt = clusterService.operationRouting().searchShards(clusterState, concreteIndices, searchRequest.queryHint(), searchRequest.routing(), searchRequest.preference());
if (groupIt.size() == 1) { if (groupIt.size() == 1) {
// if we only have one group, then we always want Q_A_F, no need for DFS, and no need to do THEN since we hit one shard // if we only have one group, then we always want Q_A_F, no need for DFS, and no need to do THEN since we hit one shard
searchRequest.searchType(QUERY_AND_FETCH); searchRequest.searchType(QUERY_AND_FETCH);

View File

@ -67,12 +67,13 @@ public abstract class TransportSearchHelper {
return ret; return ret;
} }
public static InternalSearchRequest internalSearchRequest(ShardRouting shardRouting, int numberOfShards, SearchRequest request) { public static InternalSearchRequest internalSearchRequest(ShardRouting shardRouting, int numberOfShards, SearchRequest request, String[] filteringAliases) {
InternalSearchRequest internalRequest = new InternalSearchRequest(shardRouting, numberOfShards, request.searchType()); InternalSearchRequest internalRequest = new InternalSearchRequest(shardRouting, numberOfShards, request.searchType());
internalRequest.source(request.source(), request.sourceOffset(), request.sourceLength()); internalRequest.source(request.source(), request.sourceOffset(), request.sourceLength());
internalRequest.extraSource(request.extraSource(), request.extraSourceOffset(), request.extraSourceLength()); internalRequest.extraSource(request.extraSource(), request.extraSourceOffset(), request.extraSourceLength());
internalRequest.scroll(request.scroll()); internalRequest.scroll(request.scroll());
internalRequest.timeout(request.timeout()); internalRequest.timeout(request.timeout());
internalRequest.filteringAliases(filteringAliases);
internalRequest.types(request.types()); internalRequest.types(request.types());
return internalRequest; return internalRequest;
} }

View File

@ -104,13 +104,13 @@ public abstract class TransportSearchTypeAction extends BaseAction<SearchRequest
nodes = clusterState.nodes(); nodes = clusterState.nodes();
request.indices(clusterState.metaData().concreteIndices(request.indices())); String[] concreteIndices = clusterState.metaData().concreteIndices(request.indices());
for (String index : request.indices()) { for (String index : concreteIndices) {
clusterState.blocks().indexBlockedRaiseException(ClusterBlockLevel.READ, index); clusterState.blocks().indexBlockedRaiseException(ClusterBlockLevel.READ, index);
} }
shardsIts = clusterService.operationRouting().searchShards(clusterState, request.indices(), request.queryHint(), request.routing(), request.preference()); shardsIts = clusterService.operationRouting().searchShards(clusterState, concreteIndices, request.queryHint(), request.routing(), request.preference());
expectedSuccessfulOps = shardsIts.size(); expectedSuccessfulOps = shardsIts.size();
// we need to add 1 for non active partition, since we count it in the total! // we need to add 1 for non active partition, since we count it in the total!
expectedTotalOps = shardsIts.totalSizeActiveWith1ForEmpty(); expectedTotalOps = shardsIts.totalSizeActiveWith1ForEmpty();
@ -189,7 +189,8 @@ public abstract class TransportSearchTypeAction extends BaseAction<SearchRequest
if (node == null) { if (node == null) {
onFirstPhaseResult(shard, shardIt, null); onFirstPhaseResult(shard, shardIt, null);
} else { } else {
sendExecuteFirstPhase(node, internalSearchRequest(shard, shardsIts.size(), request), new SearchServiceListener<FirstResult>() { String[] filteringAliases = clusterService.state().metaData().filteringAliases(shard.index(), request.indices());
sendExecuteFirstPhase(node, internalSearchRequest(shard, shardsIts.size(), request, filteringAliases), new SearchServiceListener<FirstResult>() {
@Override public void onResult(FirstResult result) { @Override public void onResult(FirstResult result) {
onFirstPhaseResult(shard, result, shardIt); onFirstPhaseResult(shard, result, shardIt);
} }

View File

@ -33,6 +33,7 @@ import org.elasticsearch.indices.IndexMissingException;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import static org.elasticsearch.common.collect.Lists.newArrayList;
import static org.elasticsearch.common.collect.MapBuilder.*; import static org.elasticsearch.common.collect.MapBuilder.*;
import static org.elasticsearch.common.collect.Sets.*; import static org.elasticsearch.common.collect.Sets.*;
import static org.elasticsearch.common.settings.ImmutableSettings.*; import static org.elasticsearch.common.settings.ImmutableSettings.*;
@ -54,6 +55,9 @@ public class MetaData implements Iterable<IndexMetaData> {
private final ImmutableSet<String> aliases; private final ImmutableSet<String> aliases;
// This map indicates if an alias associated with an index is filtering alias
private final ImmutableMap<String, ImmutableMap<String, Boolean>> indexToAliasFilteringRequiredMap;
private final ImmutableMap<String, String[]> aliasAndIndexToIndexMap; private final ImmutableMap<String, String[]> aliasAndIndexToIndexMap;
private MetaData(ImmutableMap<String, IndexMetaData> indices, ImmutableMap<String, IndexTemplateMetaData> templates) { private MetaData(ImmutableMap<String, IndexMetaData> indices, ImmutableMap<String, IndexTemplateMetaData> templates) {
@ -79,6 +83,23 @@ public class MetaData implements Iterable<IndexMetaData> {
} }
this.aliases = ImmutableSet.copyOf(aliases); this.aliases = ImmutableSet.copyOf(aliases);
// build filtering required map
MapBuilder<String, ImmutableMap<String, Boolean>> filteringRequiredMap = newMapBuilder();
for (IndexMetaData indexMetaData : indices.values()) {
MapBuilder<String, Boolean> indexFilteringRequiredMap = newMapBuilder();
// Filtering is not required for the index itself
indexFilteringRequiredMap.put(indexMetaData.index(), false);
for (AliasMetaData aliasMetaData : indexMetaData.aliases().values()) {
if (aliasMetaData.filter() != null) {
indexFilteringRequiredMap.put(aliasMetaData.alias(), true);
} else {
indexFilteringRequiredMap.put(aliasMetaData.alias(), false);
}
}
filteringRequiredMap.put(indexMetaData.index(), indexFilteringRequiredMap.immutableMap());
}
indexToAliasFilteringRequiredMap = filteringRequiredMap.immutableMap();
// build aliasAndIndex to Index map // build aliasAndIndex to Index map
MapBuilder<String, Set<String>> tmpAliasAndIndexToIndexBuilder = newMapBuilder(); MapBuilder<String, Set<String>> tmpAliasAndIndexToIndexBuilder = newMapBuilder();
for (IndexMetaData indexMetaData : indices.values()) { for (IndexMetaData indexMetaData : indices.values()) {
@ -252,6 +273,66 @@ public class MetaData implements Iterable<IndexMetaData> {
return totalNumberOfShards(); return totalNumberOfShards();
} }
/**
* Iterates through the list of indices and selects the effective list of filtering aliases for the
* given index.
*
* <p>Only aliases with filters are returned. If the indices list contains a non-filtering reference to
* the index itself - null is returned. Returns <tt>null</tt> if no filtering is required.</p>
*/
public String[] filteringAliases(String index, String... indices) {
if (indices == null || indices.length == 0) {
return null;
}
// optimize for the most common single index/alias scenario
if (indices.length == 1) {
String alias = indices[0];
// This list contains "_all" - no filtering needed
if (alias.equals("_all")) {
return null;
}
ImmutableMap<String, Boolean> aliasToFilteringRequiredMap = indexToAliasFilteringRequiredMap.get(index);
if (aliasToFilteringRequiredMap == null) {
// Shouldn't happen
throw new IndexMissingException(new Index(index));
}
Boolean filteringRequired = aliasToFilteringRequiredMap.get(alias);
if (filteringRequired == null || !filteringRequired) {
return null;
}
return new String[]{alias};
}
List<String> filteringAliases = null;
for (String alias : indices) {
ImmutableMap<String, Boolean> aliasToFilteringRequiredMap = indexToAliasFilteringRequiredMap.get(index);
if (aliasToFilteringRequiredMap == null) {
// Shouldn't happen
throw new IndexMissingException(new Index(index));
}
Boolean filteringRequired = aliasToFilteringRequiredMap.get(alias);
// Check that this is an alias for the current index
// Otherwise - skip it
if (filteringRequired != null) {
if (filteringRequired) {
// If filtering required - add it to the list of filters
if (filteringAliases == null) {
filteringAliases = newArrayList();
}
filteringAliases.add(alias);
} else {
// If not, we have a non filtering alias for this index - no filtering needed
return null;
}
}
}
if (filteringAliases == null) {
return null;
}
return filteringAliases.toArray(new String[filteringAliases.size()]);
}
@Override public UnmodifiableIterator<IndexMetaData> iterator() { @Override public UnmodifiableIterator<IndexMetaData> iterator() {
return indices.values().iterator(); return indices.values().iterator();
} }

View File

@ -37,11 +37,10 @@ import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser; import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.indices.AliasFilterParsingException; import org.elasticsearch.indices.AliasFilterParsingException;
import org.elasticsearch.indices.InvalidAliasNameException;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import static org.elasticsearch.common.collect.Lists.*;
import static org.elasticsearch.common.collect.MapBuilder.*; import static org.elasticsearch.common.collect.MapBuilder.*;
/** /**
@ -73,56 +72,42 @@ public class IndexAliasesService extends AbstractIndexComponent implements Itera
} }
/** /**
* Returns the filter associated with a possibly aliased indices. * Returns the filter associated with listed filtering aliases.
* *
* <p>Returns <tt>null</tt> if no filtering is required. Note, if no alias if found for the provided value * <p>The list of filtering aliases should be obtained by calling MetaData.filteringAliases.
* then no filtering is done.</p> * Returns <tt>null</tt> if no filtering is required.</p>
*/ */
public Filter aliasFilter(String... indices) { public Filter aliasFilter(String... aliases) {
if (indices == null) { if (aliases == null || aliases.length == 0) {
return null; return null;
} }
// optimize for the most common single index/alias scenario if (aliases.length == 1) {
if (indices.length == 1) { IndexAlias indexAlias = alias(aliases[0]);
String alias = indices[0];
// The list contains the index itself - no filtering needed
if (alias.equals(index.name())) {
return null;
}
IndexAlias indexAlias = aliases.get(alias);
if (indexAlias == null) { if (indexAlias == null) {
return null; // This shouldn't happen unless alias disappeared after filteringAliases was called.
throw new InvalidAliasNameException(index, aliases[0], "Unknown alias name was passed to alias Filter");
} }
return indexAlias.parsedFilter(); return indexAlias.parsedFilter();
}
List<Filter> filters = null;
for (String alias : indices) {
// The list contains the index itself - no filtering needed
if (alias.equals(index.name())) {
return null;
}
IndexAlias indexAlias = aliases.get(alias);
if (indexAlias != null) {
// The list contains a non-filtering alias - no filtering needed
if (indexAlias.parsedFilter() == null) {
return null;
} else {
if (filters == null) {
filters = newArrayList();
}
filters.add(indexAlias.parsedFilter());
}
}
}
if (filters == null) {
return null;
}
if (filters.size() == 1) {
return filters.get(0);
} else { } else {
XBooleanFilter combined = new XBooleanFilter(); XBooleanFilter combined = new XBooleanFilter();
for (Filter filter : filters) { for (String alias : aliases) {
combined.add(new FilterClause(filter, BooleanClause.Occur.SHOULD)); IndexAlias indexAlias = alias(alias);
if (indexAlias == null) {
// This shouldn't happen unless alias disappeared after filteringAliases was called.
throw new InvalidAliasNameException(index, aliases[0], "Unknown alias name was passed to alias Filter");
}
if (indexAlias.parsedFilter() != null) {
combined.add(new FilterClause(indexAlias.parsedFilter(), BooleanClause.Occur.SHOULD));
} else {
// The filter might be null only if filter was removed after filteringAliases was called
return null;
}
}
if (combined.getShouldFilters().size() == 0) {
return null;
}
if (combined.getShouldFilters().size() == 1) {
return combined.getShouldFilters().get(0);
} }
return combined; return combined;
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.search; package org.elasticsearch.search;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopDocs;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.SearchType;
@ -396,6 +397,9 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
context.size(10); context.size(10);
} }
Filter aliasFilter = indexService.aliasesService().aliasFilter(request.filteringAliases());
context.aliasFilter(aliasFilter);
// pre process // pre process
dfsPhase.preProcess(context); dfsPhase.preProcess(context);
queryPhase.preProcess(context); queryPhase.preProcess(context);

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.collect.Maps;
import org.elasticsearch.common.lucene.MinimumScoreCollector; import org.elasticsearch.common.lucene.MinimumScoreCollector;
import org.elasticsearch.common.lucene.MultiCollector; import org.elasticsearch.common.lucene.MultiCollector;
import org.elasticsearch.common.lucene.search.FilteredCollector; import org.elasticsearch.common.lucene.search.FilteredCollector;
import org.elasticsearch.common.lucene.search.XBooleanFilter;
import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.search.dfs.CachedDfSource; import org.elasticsearch.search.dfs.CachedDfSource;
@ -165,16 +166,30 @@ public class ContextIndexSearcher extends ExtendedIndexSearcher {
collector = new MinimumScoreCollector(collector, searchContext.minimumScore()); collector = new MinimumScoreCollector(collector, searchContext.minimumScore());
} }
Filter combinedFilter;
if (filter == null) {
combinedFilter = searchContext.aliasFilter();
} else {
if (searchContext.aliasFilter() != null) {
XBooleanFilter booleanFilter = new XBooleanFilter();
booleanFilter.add(new FilterClause(filter, BooleanClause.Occur.MUST));
booleanFilter.add(new FilterClause(searchContext.aliasFilter(), BooleanClause.Occur.MUST));
combinedFilter = booleanFilter;
} else {
combinedFilter = filter;
}
}
// we only compute the doc id set once since within a context, we execute the same query always... // we only compute the doc id set once since within a context, we execute the same query always...
if (searchContext.timeout() != null) { if (searchContext.timeout() != null) {
searchContext.queryResult().searchTimedOut(false); searchContext.queryResult().searchTimedOut(false);
try { try {
super.search(weight, filter, collector); super.search(weight, combinedFilter, collector);
} catch (TimeLimitingCollector.TimeExceededException e) { } catch (TimeLimitingCollector.TimeExceededException e) {
searchContext.queryResult().searchTimedOut(true); searchContext.queryResult().searchTimedOut(true);
} }
} else { } else {
super.search(weight, filter, collector); super.search(weight, combinedFilter, collector);
} }
} }
} }

View File

@ -70,6 +70,8 @@ public class InternalSearchRequest implements Streamable {
private String[] types = Strings.EMPTY_ARRAY; private String[] types = Strings.EMPTY_ARRAY;
private String[] filteringAliases;
private byte[] source; private byte[] source;
private int sourceOffset; private int sourceOffset;
private int sourceLength; private int sourceLength;
@ -168,6 +170,14 @@ public class InternalSearchRequest implements Streamable {
return this; return this;
} }
public String[] filteringAliases() {
return filteringAliases;
}
public void filteringAliases(String[] filteringAliases) {
this.filteringAliases = filteringAliases;
}
public String[] types() { public String[] types() {
return types; return types;
} }
@ -210,6 +220,15 @@ public class InternalSearchRequest implements Streamable {
types[i] = in.readUTF(); types[i] = in.readUTF();
} }
} }
int indicesSize = in.readVInt();
if (indicesSize > 0) {
filteringAliases = new String[indicesSize];
for (int i = 0; i < indicesSize; i++) {
filteringAliases[i] = in.readUTF();
}
} else {
filteringAliases = null;
}
} }
@Override public void writeTo(StreamOutput out) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException {
@ -245,5 +264,13 @@ public class InternalSearchRequest implements Streamable {
for (String type : types) { for (String type : types) {
out.writeUTF(type); out.writeUTF(type);
} }
if (filteringAliases != null) {
out.writeVInt(filteringAliases.length);
for (String index : filteringAliases) {
out.writeUTF(index);
}
} else {
out.writeVInt(0);
}
} }
} }

View File

@ -128,6 +128,8 @@ public class SearchContext implements Releasable {
private Filter filter; private Filter filter;
private Filter aliasFilter;
private int[] docIdsToLoad; private int[] docIdsToLoad;
private int docsIdsToLoadFrom; private int docsIdsToLoadFrom;
@ -343,6 +345,15 @@ public class SearchContext implements Releasable {
return this.filter; return this.filter;
} }
public SearchContext aliasFilter(Filter aliasFilter) {
this.aliasFilter = aliasFilter;
return this;
}
public Filter aliasFilter() {
return aliasFilter;
}
public String queryParserName() { public String queryParserName() {
return queryParserName; return queryParserName;
} }

View File

@ -37,12 +37,13 @@ import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; import org.elasticsearch.index.query.xcontent.XContentFilterBuilder;
import org.elasticsearch.index.settings.IndexSettingsModule; import org.elasticsearch.index.settings.IndexSettingsModule;
import org.elasticsearch.index.similarity.SimilarityModule; import org.elasticsearch.index.similarity.SimilarityModule;
import org.elasticsearch.indices.InvalidAliasNameException;
import org.elasticsearch.script.ScriptModule; import org.elasticsearch.script.ScriptModule;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.io.IOException; import java.io.IOException;
import static org.elasticsearch.index.query.xcontent.FilterBuilders.termFilter; import static org.elasticsearch.index.query.xcontent.FilterBuilders.*;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
@ -76,7 +77,7 @@ public class IndexAliasesServiceTests {
return new CompressedString(builder.string()); return new CompressedString(builder.string());
} }
@Test public void testAliasFilter() throws Exception { @Test public void testFilteringAliases() throws Exception {
IndexAliasesService indexAliasesService = newIndexAliasesService(); IndexAliasesService indexAliasesService = newIndexAliasesService();
indexAliasesService.add("cats", filter(termFilter("animal", "cat"))); indexAliasesService.add("cats", filter(termFilter("animal", "cat")));
indexAliasesService.add("dogs", filter(termFilter("animal", "dog"))); indexAliasesService.add("dogs", filter(termFilter("animal", "dog")));
@ -94,19 +95,41 @@ public class IndexAliasesServiceTests {
assertThat(indexAliasesService.aliasFilter("cats", "all"), nullValue()); assertThat(indexAliasesService.aliasFilter("cats", "all"), nullValue());
assertThat(indexAliasesService.aliasFilter("all", "cats"), nullValue()); assertThat(indexAliasesService.aliasFilter("all", "cats"), nullValue());
// Index itself should turn off al filters as well
assertThat(indexAliasesService.aliasFilter("test", "cats"), nullValue());
// Unknown aliases should be ignored
assertThat(indexAliasesService.aliasFilter("unknown", "cats").toString(), equalTo("FilterCacheFilterWrapper(animal:cat)"));
assertThat(indexAliasesService.aliasFilter("unknown", "all"), nullValue());
indexAliasesService.remove("cats");
assertThat(indexAliasesService.aliasFilter("cats"), nullValue());
indexAliasesService.add("cats", filter(termFilter("animal", "feline"))); indexAliasesService.add("cats", filter(termFilter("animal", "feline")));
indexAliasesService.add("dogs", filter(termFilter("animal", "canine"))); indexAliasesService.add("dogs", filter(termFilter("animal", "canine")));
assertThat(indexAliasesService.aliasFilter("dogs", "cats").toString(), equalTo("BooleanFilter( FilterCacheFilterWrapper(animal:canine) FilterCacheFilterWrapper(animal:feline))")); assertThat(indexAliasesService.aliasFilter("dogs", "cats").toString(), equalTo("BooleanFilter( FilterCacheFilterWrapper(animal:canine) FilterCacheFilterWrapper(animal:feline))"));
} }
@Test public void testAliasFilters() throws Exception {
IndexAliasesService indexAliasesService = newIndexAliasesService();
indexAliasesService.add("cats", filter(termFilter("animal", "cat")));
indexAliasesService.add("dogs", filter(termFilter("animal", "dog")));
assertThat(indexAliasesService.aliasFilter(), nullValue());
assertThat(indexAliasesService.aliasFilter("dogs").toString(), equalTo("FilterCacheFilterWrapper(animal:dog)"));
assertThat(indexAliasesService.aliasFilter("dogs", "cats").toString(), equalTo("BooleanFilter( FilterCacheFilterWrapper(animal:dog) FilterCacheFilterWrapper(animal:cat))"));
indexAliasesService.add("cats", filter(termFilter("animal", "feline")));
indexAliasesService.add("dogs", filter(termFilter("animal", "canine")));
assertThat(indexAliasesService.aliasFilter("dogs", "cats").toString(), equalTo("BooleanFilter( FilterCacheFilterWrapper(animal:canine) FilterCacheFilterWrapper(animal:feline))"));
}
@Test(expectedExceptions = InvalidAliasNameException.class) public void testRemovedAliasFilter() throws Exception {
IndexAliasesService indexAliasesService = newIndexAliasesService();
indexAliasesService.add("cats", filter(termFilter("animal", "cat")));
indexAliasesService.remove("cats");
indexAliasesService.aliasFilter("cats");
}
@Test(expectedExceptions = InvalidAliasNameException.class) public void testUnknownAliasFilter() throws Exception {
IndexAliasesService indexAliasesService = newIndexAliasesService();
indexAliasesService.add("cats", filter(termFilter("animal", "cat")));
indexAliasesService.add("dogs", filter(termFilter("animal", "dog")));
indexAliasesService.aliasFilter("unknown");
}
} }

View File

@ -22,18 +22,25 @@ package org.elasticsearch.test.integration.aliases;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.index.query.xcontent.QueryBuilders;
import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; import org.elasticsearch.index.query.xcontent.XContentFilterBuilder;
import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.indices.IndexMissingException;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.test.integration.AbstractNodesTests; import org.elasticsearch.test.integration.AbstractNodesTests;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.util.Set;
import static org.elasticsearch.client.Requests.*; import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.query.xcontent.FilterBuilders.termFilter; import static org.elasticsearch.common.collect.Sets.newHashSet;
import static org.elasticsearch.index.query.xcontent.FilterBuilders.*;
import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
@ -135,6 +142,181 @@ public class IndexAliasesTests extends AbstractNodesTests {
} }
@Test public void testSearchingFilteringAliasesSingleIndex() throws Exception {
logger.info("--> creating index [test]");
client1.admin().indices().create(createIndexRequest("test")).actionGet();
logger.info("--> running cluster_health");
ClusterHealthResponse clusterHealth = client1.admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet();
logger.info("--> done cluster_health, status " + clusterHealth.status());
assertThat(clusterHealth.timedOut(), equalTo(false));
assertThat(clusterHealth.status(), equalTo(ClusterHealthStatus.GREEN));
logger.info("--> adding filtering aliases to index [test]");
client1.admin().indices().prepareAliases().addAlias("test", "alias1").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test", "alias2").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test", "foos", termFilter("name", "foo")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test", "bars", termFilter("name", "bar")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test", "tests", termFilter("name", "test")).execute().actionGet();
Thread.sleep(300);
logger.info("--> indexing against [test]");
client1.index(indexRequest("test").type("type1").id("1").source(source("1", "foo test")).refresh(true)).actionGet();
client1.index(indexRequest("test").type("type1").id("2").source(source("2", "bar test")).refresh(true)).actionGet();
client1.index(indexRequest("test").type("type1").id("3").source(source("3", "baz test")).refresh(true)).actionGet();
client1.index(indexRequest("test").type("type1").id("4").source(source("4", "something else")).refresh(true)).actionGet();
logger.info("--> checking single filtering alias search");
SearchResponse searchResponse = client1.prepareSearch("foos").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1");
searchResponse = client1.prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2", "3");
searchResponse = client1.prepareSearch("foos", "bars").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2");
logger.info("--> checking single non-filtering alias search");
searchResponse = client1.prepareSearch("alias1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2", "3", "4");
logger.info("--> checking non-filtering alias and filtering alias search");
searchResponse = client1.prepareSearch("alias1", "foos").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2", "3", "4");
logger.info("--> checking index and filtering alias search");
searchResponse = client1.prepareSearch("test", "foos").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2", "3", "4");
}
@Test public void testSearchingFilteringAliasesTwoIndices() throws Exception {
logger.info("--> creating index [test1]");
client1.admin().indices().create(createIndexRequest("test1")).actionGet();
logger.info("--> creating index [test2]");
client1.admin().indices().create(createIndexRequest("test2")).actionGet();
logger.info("--> running cluster_health");
ClusterHealthResponse clusterHealth = client1.admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet();
logger.info("--> done cluster_health, status " + clusterHealth.status());
assertThat(clusterHealth.timedOut(), equalTo(false));
assertThat(clusterHealth.status(), equalTo(ClusterHealthStatus.GREEN));
logger.info("--> adding filtering aliases to index [test1]");
client1.admin().indices().prepareAliases().addAlias("test1", "aliasToTest1").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test1", "aliasToTests").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test1", "foos", termFilter("name", "foo")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test1", "bars", termFilter("name", "bar")).execute().actionGet();
logger.info("--> adding filtering aliases to index [test2]");
client1.admin().indices().prepareAliases().addAlias("test2", "aliasToTest2").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test2", "aliasToTests").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test2", "foos", termFilter("name", "foo")).execute().actionGet();
Thread.sleep(300);
logger.info("--> indexing against [test1]");
client1.index(indexRequest("test1").type("type1").id("1").source(source("1", "foo test")).refresh(true)).actionGet();
client1.index(indexRequest("test1").type("type1").id("2").source(source("2", "bar test")).refresh(true)).actionGet();
client1.index(indexRequest("test1").type("type1").id("3").source(source("3", "baz test")).refresh(true)).actionGet();
client1.index(indexRequest("test1").type("type1").id("4").source(source("4", "something else")).refresh(true)).actionGet();
logger.info("--> indexing against [test2]");
client1.index(indexRequest("test2").type("type1").id("5").source(source("5", "foo test")).refresh(true)).actionGet();
client1.index(indexRequest("test2").type("type1").id("6").source(source("6", "bar test")).refresh(true)).actionGet();
client1.index(indexRequest("test2").type("type1").id("7").source(source("7", "baz test")).refresh(true)).actionGet();
client1.index(indexRequest("test2").type("type1").id("8").source(source("8", "something else")).refresh(true)).actionGet();
logger.info("--> checking filtering alias for two indices");
SearchResponse searchResponse = client1.prepareSearch("foos").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "5");
logger.info("--> checking filtering alias for one index");
searchResponse = client1.prepareSearch("bars").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "2");
logger.info("--> checking filtering alias for two indices and one complete index");
searchResponse = client1.prepareSearch("foos", "test1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2", "3", "4", "5");
logger.info("--> checking filtering alias for two indices and non-filtering alias for one index");
searchResponse = client1.prepareSearch("foos", "aliasToTest1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "1", "2", "3", "4", "5");
logger.info("--> checking filtering alias for two indices and non-filtering alias for both indices");
searchResponse = client1.prepareSearch("foos", "aliasToTests").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertThat(searchResponse.hits().totalHits(), equalTo(8L));
}
@Test public void testSearchingFilteringAliasesMultipleIndices() throws Exception {
logger.info("--> creating indices");
client1.admin().indices().create(createIndexRequest("test1")).actionGet();
client1.admin().indices().create(createIndexRequest("test2")).actionGet();
client1.admin().indices().create(createIndexRequest("test3")).actionGet();
logger.info("--> running cluster_health");
ClusterHealthResponse clusterHealth = client1.admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet();
logger.info("--> done cluster_health, status " + clusterHealth.status());
assertThat(clusterHealth.timedOut(), equalTo(false));
assertThat(clusterHealth.status(), equalTo(ClusterHealthStatus.GREEN));
logger.info("--> adding aliases to indices");
client1.admin().indices().prepareAliases().addAlias("test1", "alias12").execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test2", "alias12").execute().actionGet();
logger.info("--> adding filtering aliases to indices");
client1.admin().indices().prepareAliases().addAlias("test1", "filter1", termFilter("name", "test1")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test2", "filter23", termFilter("name", "foo")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test3", "filter23", termFilter("name", "foo")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test1", "filter13", termFilter("name", "baz")).execute().actionGet();
client1.admin().indices().prepareAliases().addAlias("test3", "filter13", termFilter("name", "baz")).execute().actionGet();
Thread.sleep(300);
logger.info("--> indexing against [test1]");
client1.index(indexRequest("test1").type("type1").id("11").source(source("11", "foo test1")).refresh(true)).actionGet();
client1.index(indexRequest("test1").type("type1").id("12").source(source("12", "bar test1")).refresh(true)).actionGet();
client1.index(indexRequest("test1").type("type1").id("13").source(source("13", "baz test1")).refresh(true)).actionGet();
client1.index(indexRequest("test2").type("type1").id("21").source(source("21", "foo test2")).refresh(true)).actionGet();
client1.index(indexRequest("test2").type("type1").id("22").source(source("22", "bar test2")).refresh(true)).actionGet();
client1.index(indexRequest("test2").type("type1").id("23").source(source("23", "baz test2")).refresh(true)).actionGet();
client1.index(indexRequest("test3").type("type1").id("31").source(source("31", "foo test3")).refresh(true)).actionGet();
client1.index(indexRequest("test3").type("type1").id("32").source(source("32", "bar test3")).refresh(true)).actionGet();
client1.index(indexRequest("test3").type("type1").id("33").source(source("33", "baz test3")).refresh(true)).actionGet();
logger.info("--> checking filtering alias for multiple indices");
SearchResponse searchResponse = client1.prepareSearch("filter23", "filter13").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "21", "31", "13", "33");
searchResponse = client1.prepareSearch("filter23", "filter1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "21", "31", "11", "12", "13");
searchResponse = client1.prepareSearch("filter13", "filter1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "11", "12", "13", "33");
searchResponse = client1.prepareSearch("filter13", "filter1", "filter23").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "11", "12", "13", "21", "31", "33");
searchResponse = client1.prepareSearch("filter23", "filter13", "test2").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "21", "22", "23", "31", "13", "33");
searchResponse = client1.prepareSearch("filter23", "filter13", "test1", "test2").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet();
assertHits(searchResponse.hits(), "11", "12", "13", "21", "22", "23", "31", "33");
}
private void assertHits(SearchHits hits, String... ids) {
assertThat(hits.totalHits(), equalTo((long) ids.length));
Set<String> hitIds = newHashSet();
for (SearchHit hit : hits.getHits()) {
hitIds.add(hit.id());
}
assertThat(hitIds, containsInAnyOrder(ids));
}
private String source(String id, String nameValue) { private String source(String id, String nameValue) {
return "{ type1 : { \"id\" : \"" + id + "\", \"name\" : \"" + nameValue + "\" } }"; return "{ type1 : { \"id\" : \"" + id + "\", \"name\" : \"" + nameValue + "\" } }";
} }