From e3bffba3273711c17f3a8a8ba4b97b56ee8decb9 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Mon, 23 May 2011 14:20:12 -0400 Subject: [PATCH] Add support for filtering aliases to Search --- .../action/search/TransportSearchAction.java | 4 +- .../search/type/TransportSearchHelper.java | 3 +- .../type/TransportSearchTypeAction.java | 9 +- .../cluster/metadata/MetaData.java | 81 ++++++++ .../index/aliases/IndexAliasesService.java | 71 +++---- .../elasticsearch/search/SearchService.java | 4 + .../search/internal/ContextIndexSearcher.java | 19 +- .../internal/InternalSearchRequest.java | 27 +++ .../search/internal/SearchContext.java | 11 ++ .../aliases/IndexAliasesServiceTests.java | 47 +++-- .../aliases/IndexAliasesTests.java | 184 +++++++++++++++++- 11 files changed, 395 insertions(+), 65 deletions(-) diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/modules/elasticsearch/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index bdfc84486bd..46041bbe60e 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -84,8 +84,8 @@ public class TransportSearchAction extends BaseAction() { + String[] filteringAliases = clusterService.state().metaData().filteringAliases(shard.index(), request.indices()); + sendExecuteFirstPhase(node, internalSearchRequest(shard, shardsIts.size(), request, filteringAliases), new SearchServiceListener() { @Override public void onResult(FirstResult result) { onFirstPhaseResult(shard, result, shardIt); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java b/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java index 1b9a367d5bd..3c8731a93a9 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java @@ -33,6 +33,7 @@ import org.elasticsearch.indices.IndexMissingException; import java.io.IOException; import java.util.*; +import static org.elasticsearch.common.collect.Lists.newArrayList; import static org.elasticsearch.common.collect.MapBuilder.*; import static org.elasticsearch.common.collect.Sets.*; import static org.elasticsearch.common.settings.ImmutableSettings.*; @@ -54,6 +55,9 @@ public class MetaData implements Iterable { private final ImmutableSet aliases; + // This map indicates if an alias associated with an index is filtering alias + private final ImmutableMap> indexToAliasFilteringRequiredMap; + private final ImmutableMap aliasAndIndexToIndexMap; private MetaData(ImmutableMap indices, ImmutableMap templates) { @@ -79,6 +83,23 @@ public class MetaData implements Iterable { } this.aliases = ImmutableSet.copyOf(aliases); + // build filtering required map + MapBuilder> filteringRequiredMap = newMapBuilder(); + for (IndexMetaData indexMetaData : indices.values()) { + MapBuilder 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 MapBuilder> tmpAliasAndIndexToIndexBuilder = newMapBuilder(); for (IndexMetaData indexMetaData : indices.values()) { @@ -252,6 +273,66 @@ public class MetaData implements Iterable { return totalNumberOfShards(); } + + /** + * Iterates through the list of indices and selects the effective list of filtering aliases for the + * given index. + * + *

Only aliases with filters are returned. If the indices list contains a non-filtering reference to + * the index itself - null is returned. Returns null if no filtering is required.

+ */ + 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 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 filteringAliases = null; + for (String alias : indices) { + ImmutableMap 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 iterator() { return indices.values().iterator(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/aliases/IndexAliasesService.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/aliases/IndexAliasesService.java index 37f426caa28..772b525259a 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/aliases/IndexAliasesService.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/aliases/IndexAliasesService.java @@ -37,11 +37,10 @@ import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser; import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.indices.AliasFilterParsingException; +import org.elasticsearch.indices.InvalidAliasNameException; import java.io.IOException; -import java.util.List; -import static org.elasticsearch.common.collect.Lists.*; 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. * - *

Returns null if no filtering is required. Note, if no alias if found for the provided value - * then no filtering is done.

+ *

The list of filtering aliases should be obtained by calling MetaData.filteringAliases. + * Returns null if no filtering is required.

*/ - public Filter aliasFilter(String... indices) { - if (indices == null) { + public Filter aliasFilter(String... aliases) { + if (aliases == null || aliases.length == 0) { return null; } - // optimize for the most common single index/alias scenario - if (indices.length == 1) { - 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 (aliases.length == 1) { + IndexAlias indexAlias = alias(aliases[0]); 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(); - } - List 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 { XBooleanFilter combined = new XBooleanFilter(); - for (Filter filter : filters) { - combined.add(new FilterClause(filter, BooleanClause.Occur.SHOULD)); + for (String alias : aliases) { + 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; } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/SearchService.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/SearchService.java index 52b8bb3793d..cc636de5610 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/SearchService.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/SearchService.java @@ -19,6 +19,7 @@ package org.elasticsearch.search; +import org.apache.lucene.search.Filter; import org.apache.lucene.search.TopDocs; import org.elasticsearch.ElasticSearchException; import org.elasticsearch.action.search.SearchType; @@ -396,6 +397,9 @@ public class SearchService extends AbstractLifecycleComponent { context.size(10); } + Filter aliasFilter = indexService.aliasesService().aliasFilter(request.filteringAliases()); + context.aliasFilter(aliasFilter); + // pre process dfsPhase.preProcess(context); queryPhase.preProcess(context); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java index d846de8219d..fc21bb15093 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.collect.Maps; import org.elasticsearch.common.lucene.MinimumScoreCollector; import org.elasticsearch.common.lucene.MultiCollector; import org.elasticsearch.common.lucene.search.FilteredCollector; +import org.elasticsearch.common.lucene.search.XBooleanFilter; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.search.dfs.CachedDfSource; @@ -165,16 +166,30 @@ public class ContextIndexSearcher extends ExtendedIndexSearcher { 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... if (searchContext.timeout() != null) { searchContext.queryResult().searchTimedOut(false); try { - super.search(weight, filter, collector); + super.search(weight, combinedFilter, collector); } catch (TimeLimitingCollector.TimeExceededException e) { searchContext.queryResult().searchTimedOut(true); } } else { - super.search(weight, filter, collector); + super.search(weight, combinedFilter, collector); } } } \ No newline at end of file diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/InternalSearchRequest.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/InternalSearchRequest.java index 933627c763b..b694dcc2375 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/InternalSearchRequest.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/InternalSearchRequest.java @@ -70,6 +70,8 @@ public class InternalSearchRequest implements Streamable { private String[] types = Strings.EMPTY_ARRAY; + private String[] filteringAliases; + private byte[] source; private int sourceOffset; private int sourceLength; @@ -168,6 +170,14 @@ public class InternalSearchRequest implements Streamable { return this; } + public String[] filteringAliases() { + return filteringAliases; + } + + public void filteringAliases(String[] filteringAliases) { + this.filteringAliases = filteringAliases; + } + public String[] types() { return types; } @@ -210,6 +220,15 @@ public class InternalSearchRequest implements Streamable { 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 { @@ -245,5 +264,13 @@ public class InternalSearchRequest implements Streamable { for (String type : types) { out.writeUTF(type); } + if (filteringAliases != null) { + out.writeVInt(filteringAliases.length); + for (String index : filteringAliases) { + out.writeUTF(index); + } + } else { + out.writeVInt(0); + } } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/SearchContext.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/SearchContext.java index 8baf674b22d..7cffce2856f 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/SearchContext.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/SearchContext.java @@ -128,6 +128,8 @@ public class SearchContext implements Releasable { private Filter filter; + private Filter aliasFilter; + private int[] docIdsToLoad; private int docsIdsToLoadFrom; @@ -343,6 +345,15 @@ public class SearchContext implements Releasable { return this.filter; } + public SearchContext aliasFilter(Filter aliasFilter) { + this.aliasFilter = aliasFilter; + return this; + } + + public Filter aliasFilter() { + return aliasFilter; + } + public String queryParserName() { return queryParserName; } diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/aliases/IndexAliasesServiceTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/aliases/IndexAliasesServiceTests.java index b17a0854528..aebab56f4aa 100644 --- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/aliases/IndexAliasesServiceTests.java +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/aliases/IndexAliasesServiceTests.java @@ -37,12 +37,13 @@ import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; import org.elasticsearch.index.settings.IndexSettingsModule; import org.elasticsearch.index.similarity.SimilarityModule; +import org.elasticsearch.indices.InvalidAliasNameException; import org.elasticsearch.script.ScriptModule; import org.testng.annotations.Test; 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.Matchers.*; @@ -76,7 +77,7 @@ public class IndexAliasesServiceTests { return new CompressedString(builder.string()); } - @Test public void testAliasFilter() throws Exception { + @Test public void testFilteringAliases() throws Exception { IndexAliasesService indexAliasesService = newIndexAliasesService(); indexAliasesService.add("cats", filter(termFilter("animal", "cat"))); indexAliasesService.add("dogs", filter(termFilter("animal", "dog"))); @@ -94,19 +95,41 @@ public class IndexAliasesServiceTests { assertThat(indexAliasesService.aliasFilter("cats", "all"), 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("dogs", filter(termFilter("animal", "canine"))); 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"); + } + + } diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/aliases/IndexAliasesTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/aliases/IndexAliasesTests.java index 9a04160a1c7..4c22fec4f88 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/aliases/IndexAliasesTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/aliases/IndexAliasesTests.java @@ -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.ClusterHealthStatus; import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.index.query.xcontent.QueryBuilders; import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; import org.elasticsearch.indices.IndexMissingException; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; import org.elasticsearch.test.integration.AbstractNodesTests; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.util.Set; + 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.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 hitIds = newHashSet(); + for (SearchHit hit : hits.getHits()) { + hitIds.add(hit.id()); + } + assertThat(hitIds, containsInAnyOrder(ids)); + } + private String source(String id, String nameValue) { return "{ type1 : { \"id\" : \"" + id + "\", \"name\" : \"" + nameValue + "\" } }"; }