/* * 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.search.internal; import org.elasticsearch.Version; import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.RandomQueryBuilder; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.InvalidAliasNameException; import org.elasticsearch.search.AbstractSearchTestCase; import java.io.IOException; import java.util.Base64; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; public class ShardSearchTransportRequestTests extends AbstractSearchTestCase { private IndexMetaData baseMetaData = IndexMetaData.builder("test").settings(Settings.builder() .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build()) .numberOfShards(1).numberOfReplicas(1).build(); public void testSerialization() throws Exception { ShardSearchTransportRequest shardSearchTransportRequest = createShardSearchTransportRequest(); try (BytesStreamOutput output = new BytesStreamOutput()) { shardSearchTransportRequest.writeTo(output); try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) { ShardSearchTransportRequest deserializedRequest = new ShardSearchTransportRequest(); deserializedRequest.readFrom(in); assertEquals(deserializedRequest.scroll(), shardSearchTransportRequest.scroll()); assertEquals(deserializedRequest.filteringAliases(), shardSearchTransportRequest.filteringAliases()); assertArrayEquals(deserializedRequest.indices(), shardSearchTransportRequest.indices()); assertArrayEquals(deserializedRequest.types(), shardSearchTransportRequest.types()); assertEquals(deserializedRequest.indicesOptions(), shardSearchTransportRequest.indicesOptions()); assertEquals(deserializedRequest.isProfile(), shardSearchTransportRequest.isProfile()); assertEquals(deserializedRequest.nowInMillis(), shardSearchTransportRequest.nowInMillis()); assertEquals(deserializedRequest.source(), shardSearchTransportRequest.source()); assertEquals(deserializedRequest.searchType(), shardSearchTransportRequest.searchType()); assertEquals(deserializedRequest.shardId(), shardSearchTransportRequest.shardId()); assertEquals(deserializedRequest.numberOfShards(), shardSearchTransportRequest.numberOfShards()); assertEquals(deserializedRequest.cacheKey(), shardSearchTransportRequest.cacheKey()); assertNotSame(deserializedRequest, shardSearchTransportRequest); assertEquals(deserializedRequest.filteringAliases(), shardSearchTransportRequest.filteringAliases()); assertEquals(deserializedRequest.indexBoost(), shardSearchTransportRequest.indexBoost(), 0.0f); } } } private ShardSearchTransportRequest createShardSearchTransportRequest() throws IOException { SearchRequest searchRequest = createSearchRequest(); ShardId shardId = new ShardId(randomAlphaOfLengthBetween(2, 10), randomAlphaOfLengthBetween(2, 10), randomInt()); final AliasFilter filteringAliases; if (randomBoolean()) { String[] strings = generateRandomStringArray(10, 10, false, false); filteringAliases = new AliasFilter(RandomQueryBuilder.createQuery(random()), strings); } else { filteringAliases = new AliasFilter(null, Strings.EMPTY_ARRAY); } return new ShardSearchTransportRequest(new OriginalIndices(searchRequest), searchRequest, shardId, randomIntBetween(1, 100), filteringAliases, randomBoolean() ? 1.0f : randomFloat(), Math.abs(randomLong())); } public void testFilteringAliases() throws Exception { IndexMetaData indexMetaData = baseMetaData; indexMetaData = add(indexMetaData, "cats", filter(termQuery("animal", "cat"))); indexMetaData = add(indexMetaData, "dogs", filter(termQuery("animal", "dog"))); indexMetaData = add(indexMetaData, "all", null); assertThat(indexMetaData.getAliases().containsKey("cats"), equalTo(true)); assertThat(indexMetaData.getAliases().containsKey("dogs"), equalTo(true)); assertThat(indexMetaData.getAliases().containsKey("turtles"), equalTo(false)); assertEquals(aliasFilter(indexMetaData, "cats"), QueryBuilders.termQuery("animal", "cat")); assertEquals(aliasFilter(indexMetaData, "cats", "dogs"), QueryBuilders.boolQuery().should(QueryBuilders.termQuery("animal", "cat")) .should(QueryBuilders.termQuery("animal", "dog"))); // Non-filtering alias should turn off all filters because filters are ORed assertThat(aliasFilter(indexMetaData,"all"), nullValue()); assertThat(aliasFilter(indexMetaData, "cats", "all"), nullValue()); assertThat(aliasFilter(indexMetaData, "all", "cats"), nullValue()); indexMetaData = add(indexMetaData, "cats", filter(termQuery("animal", "feline"))); indexMetaData = add(indexMetaData, "dogs", filter(termQuery("animal", "canine"))); assertEquals(aliasFilter(indexMetaData, "dogs", "cats"),QueryBuilders.boolQuery() .should(QueryBuilders.termQuery("animal", "canine")) .should(QueryBuilders.termQuery("animal", "feline"))); } public void testRemovedAliasFilter() throws Exception { IndexMetaData indexMetaData = baseMetaData; indexMetaData = add(indexMetaData, "cats", filter(termQuery("animal", "cat"))); indexMetaData = remove(indexMetaData, "cats"); try { aliasFilter(indexMetaData, "cats"); fail("Expected InvalidAliasNameException"); } catch (InvalidAliasNameException e) { assertThat(e.getMessage(), containsString("Invalid alias name [cats]")); } } public void testUnknownAliasFilter() throws Exception { IndexMetaData indexMetaData = baseMetaData; indexMetaData = add(indexMetaData, "cats", filter(termQuery("animal", "cat"))); indexMetaData = add(indexMetaData, "dogs", filter(termQuery("animal", "dog"))); IndexMetaData finalIndexMetadata = indexMetaData; expectThrows(InvalidAliasNameException.class, () -> aliasFilter(finalIndexMetadata, "unknown")); } public static CompressedXContent filter(QueryBuilder filterBuilder) throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); filterBuilder.toXContent(builder, ToXContent.EMPTY_PARAMS); builder.close(); return new CompressedXContent(builder.string()); } private IndexMetaData remove(IndexMetaData indexMetaData, String alias) { IndexMetaData build = IndexMetaData.builder(indexMetaData).removeAlias(alias).build(); return build; } private IndexMetaData add(IndexMetaData indexMetaData, String alias, @Nullable CompressedXContent filter) { return IndexMetaData.builder(indexMetaData).putAlias(AliasMetaData.builder(alias).filter(filter).build()).build(); } public QueryBuilder aliasFilter(IndexMetaData indexMetaData, String... aliasNames) { CheckedFunction<byte[], QueryBuilder, IOException> filterParser = bytes -> { try (XContentParser parser = XContentFactory.xContent(bytes).createParser(xContentRegistry(), bytes)) { return new QueryParseContext(parser).parseInnerQueryBuilder(); } }; return ShardSearchRequest.parseAliasFilter(filterParser, indexMetaData, aliasNames); } // BWC test for changes from #20916 public void testSerialize50Request() throws IOException { BytesArray requestBytes = new BytesArray(Base64.getDecoder() // this is a base64 encoded request generated with the same input .decode("AAh4cXptdEhJcgdnT0d1ZldWyfL/sgQBJAHkDAMBAAIBAQ4TWlljWlZ5TkVmRU5xQnFQVHBjVBRZbUpod2pRV2dDSXVxRXpRaEdGVBRFZWFJY0plT2hn" + "UEpISFhmSXR6Qw5XZ1hQcmFidWhWalFSQghuUWNwZ2JjQxBtZldRREJPaGF3UnlQSE56EVhQSUtRa25Iekh3bU5kbGVECWlFT2NIeEh3RgZIYXpMTWgUeGJq" + "VU9Tdkdua3RORU5QZkNrb1EOalRyWGh5WXhvZ3plV2UUcWlXZFl2eUFUSXdPVGdMUUtYTHAJU3RKR3JxQkVJEkdEQ01xUHpnWWNaT3N3U3prSRIUeURlVFpM" + "Q1lBZERZcWpDb3NOVWIST1NyQlZtdUNrd0F1UXRvdVRjEGp6RlVMd1dqc3VtUVNaTk0JT3N2cnpLQ3ZLBmRpS1J6cgdYbmVhZnBxBUlTUU9pEEJMcm1ERXVs" + "eXhESlBoVkgTaWdUUmtVZGh4d0FFc2ZKRm9ZahNrb01XTnFFd2NWSVVDU3pWS2xBC3JVTWV3V2tUUWJUE3VGQU1Hd21CYUFMTmNQZkxobXUIZ3dxWHBxWXcF" + "bmNDZUEOTFBSTEpYZVF6Z3d2eE0PV1BucUFacll6WWRxa1hCDGxkbXNMaVRzcUZXbAtSY0NsY3FNdlJQcv8BAP////8PAQAAARQAAQp5THlIcHdQeGtMAAAB" + "AQAAAAEDbkVLAQMBCgACAAADAQABAAAAAQhIc25wRGxQbwEBQgABAAACAQMAAAEIAAAJMF9OSG9kSmh2HwABAwljRW5MVWxFbVQFemlxWG8KcXZQTkRUUGJk" + "bgECCkpMbXVMT1dtVnkISEdUUHhsd0cBAAEJAAABA2lkcz+rKsUAAAAAAAAAAAECAQYAAgwxX0ZlRWxSQkhzQ07/////DwABAAEDCnRyYXFHR1hjVHkKTERY" + "aE1HRWVySghuSWtzbEtXUwABCgEHSlRwQnhwdwAAAQECAgAAAAAAAQcyX3FlYmNDGQEEBklxZU9iUQdTc01Gek5YCWlMd2xuamNRQwNiVncAAUHt61kAAQR0" + "ZXJtP4AAAAANbUtDSnpHU3lidm5KUBUMaVpqeG9vcm5QSFlvAAEBLGdtcWxuRWpWTXdvTlhMSHh0RWlFdHBnbEF1cUNmVmhoUVlwRFZxVllnWWV1A2ZvbwEA" + "AQhwYWlubGVzc/8AALk4AAAAAAABAAAAAAAAAwpKU09PU0ZmWnhFClVqTGxMa2p3V2gKdUJwZ3R3dXFER5Hg97uT7MOmPgEADw")); try (StreamInput in = new NamedWriteableAwareStreamInput(requestBytes.streamInput(), namedWriteableRegistry)) { in.setVersion(Version.V_5_0_0); ShardSearchTransportRequest readRequest = new ShardSearchTransportRequest(); readRequest.readFrom(in); assertEquals(0, in.available()); IllegalStateException illegalStateException = expectThrows(IllegalStateException.class, () -> readRequest.filteringAliases()); assertEquals("alias filter for aliases: [JSOOSFfZxE, UjLlLkjwWh, uBpgtwuqDG] must be rewritten first", illegalStateException.getMessage()); IndexMetaData.Builder indexMetadata = new IndexMetaData.Builder(baseMetaData) .putAlias(AliasMetaData.newAliasMetaDataBuilder("JSOOSFfZxE").filter("{\"term\" : {\"foo\" : \"bar\"}}")) .putAlias(AliasMetaData.newAliasMetaDataBuilder("UjLlLkjwWh").filter("{\"term\" : {\"foo\" : \"bar1\"}}")) .putAlias(AliasMetaData.newAliasMetaDataBuilder("uBpgtwuqDG").filter("{\"term\" : {\"foo\" : \"bar2\"}}")); IndexSettings indexSettings = new IndexSettings(indexMetadata.build(), Settings.EMPTY); final long nowInMillis = randomNonNegativeLong(); QueryShardContext context = new QueryShardContext( 0, indexSettings, null, null, null, null, null, xContentRegistry(), null, null, () -> nowInMillis); readRequest.rewrite(context); QueryBuilder queryBuilder = readRequest.filteringAliases(); assertEquals(queryBuilder, QueryBuilders.boolQuery() .should(QueryBuilders.termQuery("foo", "bar")) .should(QueryBuilders.termQuery("foo", "bar1")) .should(QueryBuilders.termQuery("foo", "bar2")) ); BytesStreamOutput output = new BytesStreamOutput(); output.setVersion(Version.V_5_0_0); readRequest.writeTo(output); assertEquals(output.bytes().toBytesRef(), requestBytes.toBytesRef()); } } // BWC test for changes from #21393 public void testSerialize50RequestForIndexBoost() throws IOException { BytesArray requestBytes = new BytesArray(Base64.getDecoder() // this is a base64 encoded request generated with the same input .decode("AAZpbmRleDEWTjEyM2trbHFUT21XZDY1Z2VDYlo5ZwABBAABAAIA/wD/////DwABBmluZGV4MUAAAAAAAAAAAP////8PAAAAAAAAAgAAAA" + "AAAPa/q8mOKwIAJg==")); try (StreamInput in = new NamedWriteableAwareStreamInput(requestBytes.streamInput(), namedWriteableRegistry)) { in.setVersion(Version.V_5_0_0); ShardSearchTransportRequest readRequest = new ShardSearchTransportRequest(); readRequest.readFrom(in); assertEquals(0, in.available()); assertEquals(2.0f, readRequest.indexBoost(), 0); BytesStreamOutput output = new BytesStreamOutput(); output.setVersion(Version.V_5_0_0); readRequest.writeTo(output); assertEquals(output.bytes().toBytesRef(), requestBytes.toBytesRef()); } } }