diff --git a/build.gradle b/build.gradle index 03260146e3b84..5ce25e7bbbf42 100644 --- a/build.gradle +++ b/build.gradle @@ -122,6 +122,7 @@ subprojects { "org.elasticsearch.plugin:transport-netty4-client:${version}": ':modules:transport-netty4', "org.elasticsearch.plugin:reindex-client:${version}": ':modules:reindex', "org.elasticsearch.plugin:lang-mustache-client:${version}": ':modules:lang-mustache', + "org.elasticsearch.plugin:parent-join-client:${version}": ':modules:parent-join', "org.elasticsearch.plugin:percolator-client:${version}": ':modules:percolator', ] configurations.all { diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index d47257153cf0c..dd8187bbe40dd 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -676,7 +676,6 @@ - @@ -745,7 +744,6 @@ - @@ -772,7 +770,6 @@ - diff --git a/client/transport/build.gradle b/client/transport/build.gradle index c3dc2d84982b1..ece3a1daf7344 100644 --- a/client/transport/build.gradle +++ b/client/transport/build.gradle @@ -32,6 +32,7 @@ dependencies { compile "org.elasticsearch.plugin:reindex-client:${version}" compile "org.elasticsearch.plugin:lang-mustache-client:${version}" compile "org.elasticsearch.plugin:percolator-client:${version}" + compile "org.elasticsearch.plugin:parent-join-client:${version}" testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testCompile "junit:junit:${versions.junit}" testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}" diff --git a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java index dd23429dbdb72..b6eb84b03b7f2 100644 --- a/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/InnerHitBuilder.java @@ -195,7 +195,8 @@ private InnerHitBuilder(InnerHitBuilder other) { } } - InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType, boolean ignoreUnmapped) { + // NORELEASE Do not use this ctr, it is public for hasChild and hasParent query but this is temporary + public InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType, boolean ignoreUnmapped) { this(other); this.query = query; this.parentChildType = parentChildType; @@ -751,7 +752,8 @@ public static void extractInnerHits(QueryBuilder query, Map fromXContent(QueryParseContext parseC } else if (IGNORE_UNMAPPED_FIELD.match(currentFieldName)) { ignoreUnmapped = parser.booleanValue(); } else if (SCORE_MODE_FIELD.match(currentFieldName)) { - scoreMode = HasChildQueryBuilder.parseScoreMode(parser.text()); + scoreMode = parseScoreMode(parser.text()); } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName)) { queryName = parser.text(); } else { @@ -207,6 +207,30 @@ public static Optional fromXContent(QueryParseContext parseC return Optional.of(queryBuilder); } + public static ScoreMode parseScoreMode(String scoreModeString) { + if ("none".equals(scoreModeString)) { + return ScoreMode.None; + } else if ("min".equals(scoreModeString)) { + return ScoreMode.Min; + } else if ("max".equals(scoreModeString)) { + return ScoreMode.Max; + } else if ("avg".equals(scoreModeString)) { + return ScoreMode.Avg; + } else if ("sum".equals(scoreModeString)) { + return ScoreMode.Total; + } + throw new IllegalArgumentException("No score mode for child query [" + scoreModeString + "] found"); + } + + public static String scoreModeAsString(ScoreMode scoreMode) { + if (scoreMode == ScoreMode.Total) { + // Lucene uses 'total' but 'sum' is more consistent with other elasticsearch APIs + return "sum"; + } else { + return scoreMode.name().toLowerCase(Locale.ROOT); + } + } + @Override public final String getWriteableName() { return NAME; diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java b/core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java index f5e3b429d8c92..f9b711e72f692 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryBuilders.java @@ -471,30 +471,6 @@ public static MoreLikeThisQueryBuilder moreLikeThisQuery(Item[] likeItems) { return moreLikeThisQuery(null, null, likeItems); } - /** - * Constructs a new has_child query, with the child type and the query to run on the child documents. The - * results of this query are the parent docs that those child docs matched. - * - * @param type The child type. - * @param query The query. - * @param scoreMode How the scores from the children hits should be aggregated into the parent hit. - */ - public static HasChildQueryBuilder hasChildQuery(String type, QueryBuilder query, ScoreMode scoreMode) { - return new HasChildQueryBuilder(type, query, scoreMode); - } - - /** - * Constructs a new parent query, with the parent type and the query to run on the parent documents. The - * results of this query are the children docs that those parent docs matched. - * - * @param type The parent type. - * @param query The query. - * @param score Whether the score from the parent hit should propogate to the child hit - */ - public static HasParentQueryBuilder hasParentQuery(String type, QueryBuilder query, boolean score) { - return new HasParentQueryBuilder(type, query, score); - } - /** * Constructs a new parent id query that returns all child documents of the specified type that * point to the specified id. diff --git a/core/src/main/java/org/elasticsearch/search/SearchModule.java b/core/src/main/java/org/elasticsearch/search/SearchModule.java index afdcd0fcd5463..c15a60e586cb7 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchModule.java +++ b/core/src/main/java/org/elasticsearch/search/SearchModule.java @@ -20,8 +20,6 @@ package org.elasticsearch.search; import org.apache.lucene.search.BooleanQuery; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.NamedRegistry; import org.elasticsearch.common.geo.ShapesAvailability; import org.elasticsearch.common.geo.builders.ShapeBuilders; @@ -47,8 +45,6 @@ import org.elasticsearch.index.query.GeoPolygonQueryBuilder; import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.GeohashCellQuery; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.IdsQueryBuilder; import org.elasticsearch.index.query.IndicesQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; @@ -106,8 +102,6 @@ import org.elasticsearch.search.aggregations.PipelineAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.InternalAdjacencyMatrix; -import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.children.InternalChildren; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.InternalFilter; import org.elasticsearch.search.aggregations.bucket.filters.FiltersAggregationBuilder; @@ -260,7 +254,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -414,9 +407,6 @@ private void registerAggregations(List plugins) { GeoCentroidAggregationBuilder::parse).addResultReader(InternalGeoCentroid::new)); registerAggregation(new AggregationSpec(ScriptedMetricAggregationBuilder.NAME, ScriptedMetricAggregationBuilder::new, ScriptedMetricAggregationBuilder::parse).addResultReader(InternalScriptedMetric::new)); - registerAggregation(new AggregationSpec(ChildrenAggregationBuilder.NAME, ChildrenAggregationBuilder::new, - ChildrenAggregationBuilder::parse).addResultReader(InternalChildren::new)); - registerFromPlugin(plugins, SearchPlugin::getAggregations, this::registerAggregation); } @@ -710,8 +700,6 @@ private void registerQueryParsers(List plugins) { MatchPhrasePrefixQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MultiMatchQueryBuilder.NAME, MultiMatchQueryBuilder::new, MultiMatchQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(NestedQueryBuilder.NAME, NestedQueryBuilder::new, NestedQueryBuilder::fromXContent)); - registerQuery(new QuerySpec<>(HasChildQueryBuilder.NAME, HasChildQueryBuilder::new, HasChildQueryBuilder::fromXContent)); - registerQuery(new QuerySpec<>(HasParentQueryBuilder.NAME, HasParentQueryBuilder::new, HasParentQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(DisMaxQueryBuilder.NAME, DisMaxQueryBuilder::new, DisMaxQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(IdsQueryBuilder.NAME, IdsQueryBuilder::new, IdsQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MatchAllQueryBuilder.NAME, MatchAllQueryBuilder::new, MatchAllQueryBuilder::fromXContent)); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java b/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java index f43c2670abdb4..8b704ee8a69a2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilders.java @@ -23,8 +23,6 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrix; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.children.Children; -import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.filters.Filters; @@ -163,20 +161,20 @@ public static FiltersAggregationBuilder filters(String name, KeyedFilter... filt public static FiltersAggregationBuilder filters(String name, QueryBuilder... filters) { return new FiltersAggregationBuilder(name, filters); } - + /** * Create a new {@link AdjacencyMatrix} aggregation with the given name. */ public static AdjacencyMatrixAggregationBuilder adjacencyMatrix(String name, Map filters) { return new AdjacencyMatrixAggregationBuilder(name, filters); - } - + } + /** * Create a new {@link AdjacencyMatrix} aggregation with the given name and separator */ public static AdjacencyMatrixAggregationBuilder adjacencyMatrix(String name, String separator, Map filters) { return new AdjacencyMatrixAggregationBuilder(name, separator, filters); - } + } /** * Create a new {@link Sampler} aggregation with the given name. @@ -220,13 +218,6 @@ public static ReverseNestedAggregationBuilder reverseNested(String name) { return new ReverseNestedAggregationBuilder(name); } - /** - * Create a new {@link Children} aggregation with the given name. - */ - public static ChildrenAggregationBuilder children(String name, String childType) { - return new ChildrenAggregationBuilder(name, childType); - } - /** * Create a new {@link GeoDistance} aggregation with the given name. */ diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java index e25e7b74941d7..2f728e21b6f34 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/CustomQueryScorer.java @@ -26,7 +26,6 @@ import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; -import org.elasticsearch.index.query.HasChildQueryBuilder; import java.io.IOException; import java.util.Map; @@ -83,7 +82,7 @@ protected void extractUnknownQuery(Query query, } protected void extract(Query query, float boost, Map terms) throws IOException { - if (query instanceof HasChildQueryBuilder.LateParsingQuery) { + if (isChildOrParentQuery(query.getClass())) { // skip has_child or has_parent queries, see: https://github.com/elastic/elasticsearch/issues/14999 return; } else if (query instanceof FunctionScoreQuery) { @@ -94,5 +93,13 @@ protected void extract(Query query, float boost, Map t super.extract(query, boost, terms); } } + + /** + * Workaround to detect parent/child query + */ + private static final String PARENT_CHILD_QUERY_NAME = "HasChildQueryBuilder$LateParsingQuery"; + private static boolean isChildOrParentQuery(Class clazz) { + return clazz.getName().endsWith(PARENT_CHILD_QUERY_NAME); + } } } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java b/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java index fba2b8f258c62..83d57a69e4ea2 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java @@ -34,7 +34,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; -import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; @@ -56,7 +55,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -453,155 +451,6 @@ public void testBulkIndexingWhileInitializing() throws Exception { assertHitCount(countResponse, numDocs); } - /* - Test for https://github.com/elastic/elasticsearch/issues/3444 - */ - public void testBulkUpdateDocAsUpsertWithParent() throws Exception { - client().admin().indices().prepareCreate("test") - .addMapping("parent", "{\"parent\":{}}") - .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}") - .execute().actionGet(); - ensureGreen(); - - BulkRequestBuilder builder = client().prepareBulk(); - - // It's important to use JSON parsing here and request objects: issue 3444 is related to incomplete option parsing - byte[] addParent = new BytesArray( - "{" + - " \"index\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"parent\"," + - " \"_id\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"field1\" : \"value1\"" + - "}" + - "\n").array(); - - byte[] addChild = new BytesArray( - "{" + - " \"update\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"child\"," + - " \"_id\" : \"child1\"," + - " \"parent\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"doc\" : {" + - " \"field1\" : \"value1\"" + - " }," + - " \"doc_as_upsert\" : \"true\"" + - "}" + - "\n").array(); - - builder.add(addParent, 0, addParent.length, XContentType.JSON); - builder.add(addChild, 0, addChild.length, XContentType.JSON); - - BulkResponse bulkResponse = builder.get(); - assertThat(bulkResponse.getItems().length, equalTo(2)); - assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); - assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); - - client().admin().indices().prepareRefresh("test").get(); - - //we check that the _parent field was set on the child document by using the has parent query - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) - .get(); - - assertNoFailures(searchResponse); - assertSearchHits(searchResponse, "child1"); - } - - /* - Test for https://github.com/elastic/elasticsearch/issues/3444 - */ - public void testBulkUpdateUpsertWithParent() throws Exception { - assertAcked(prepareCreate("test") - .addMapping("parent", "{\"parent\":{}}") - .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}")); - ensureGreen(); - - BulkRequestBuilder builder = client().prepareBulk(); - - byte[] addParent = new BytesArray( - "{" + - " \"index\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"parent\"," + - " \"_id\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"field1\" : \"value1\"" + - "}" + - "\n").array(); - - byte[] addChild1 = new BytesArray( - "{" + - " \"update\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"child\"," + - " \"_id\" : \"child1\"," + - " \"parent\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"script\" : {" + - " \"inline\" : \"ctx._source.field2 = 'value2'\"" + - " }," + - " \"lang\" : \"" + CustomScriptPlugin.NAME + "\"," + - " \"upsert\" : {" + - " \"field1\" : \"value1'\"" + - " }" + - "}" + - "\n").array(); - - byte[] addChild2 = new BytesArray( - "{" + - " \"update\" : {" + - " \"_index\" : \"test\"," + - " \"_type\" : \"child\"," + - " \"_id\" : \"child1\"," + - " \"parent\" : \"parent1\"" + - " }" + - "}" + - "\n" + - "{" + - " \"script\" : \"ctx._source.field2 = 'value2'\"," + - " \"upsert\" : {" + - " \"field1\" : \"value1'\"" + - " }" + - "}" + - "\n").array(); - - builder.add(addParent, 0, addParent.length, XContentType.JSON); - builder.add(addChild1, 0, addChild1.length, XContentType.JSON); - builder.add(addChild2, 0, addChild2.length, XContentType.JSON); - - BulkResponse bulkResponse = builder.get(); - assertThat(bulkResponse.getItems().length, equalTo(3)); - assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); - assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); - assertThat(bulkResponse.getItems()[2].isFailed(), equalTo(true)); - assertThat(bulkResponse.getItems()[2].getFailure().getCause().getCause().getMessage(), - equalTo("script_lang not supported [painless]")); - - client().admin().indices().prepareRefresh("test").get(); - - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) - .get(); - - assertSearchHits(searchResponse, "child1"); - } - /* * Test for https://github.com/elastic/elasticsearch/issues/8365 */ diff --git a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java index 139297df1f2e0..73e110ba8757e 100644 --- a/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java +++ b/core/src/test/java/org/elasticsearch/aliases/IndexAliasesIT.java @@ -19,7 +19,6 @@ package org.elasticsearch.aliases; -import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistResponse; @@ -63,9 +62,6 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_READ; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.test.hamcrest.CollectionAssertions.hasKey; @@ -822,26 +818,6 @@ public void testAliasFilterWithNowInRangeFilterAndQuery() throws Exception { } } - public void testAliasesFilterWithHasChildQuery() throws Exception { - assertAcked(prepareCreate("my-index") - .addMapping("parent") - .addMapping("child", "_parent", "type=parent") - ); - client().prepareIndex("my-index", "parent", "1").setSource("{}", XContentType.JSON).get(); - client().prepareIndex("my-index", "child", "2").setSource("{}", XContentType.JSON).setParent("1").get(); - refresh(); - - assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter1", hasChildQuery("child", matchAllQuery(), ScoreMode.None))); - assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter2", hasParentQuery("parent", matchAllQuery(), false))); - - SearchResponse response = client().prepareSearch("filter1").get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).id(), equalTo("1")); - response = client().prepareSearch("filter2").get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).id(), equalTo("2")); - } - public void testAliasesWithBlocks() { createIndex("test"); ensureGreen(); diff --git a/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java index cd6622368c931..9e199f71cea6d 100644 --- a/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java @@ -153,24 +153,6 @@ public void testInlineLeafInnerHitsNestedQuery() throws Exception { assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); } - public void testInlineLeafInnerHitsHasChildQuery() throws Exception { - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits, false); - Map innerHitBuilders = new HashMap<>(); - hasChildQueryBuilder.extractInnerHitBuilders(innerHitBuilders); - assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); - } - - public void testInlineLeafInnerHitsHasParentQuery() throws Exception { - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasParentQueryBuilder hasParentQueryBuilder = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) - .innerHit(leafInnerHits, false); - Map innerHitBuilders = new HashMap<>(); - hasParentQueryBuilder.extractInnerHitBuilders(innerHitBuilders); - assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); - } - public void testInlineLeafInnerHitsNestedQueryViaBoolQuery() { InnerHitBuilder leafInnerHits = randomInnerHits(); NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) @@ -181,25 +163,6 @@ public void testInlineLeafInnerHitsNestedQueryViaBoolQuery() { assertThat(innerHitBuilders.get(leafInnerHits.getName()), notNullValue()); } - public void testInlineLeafInnerHitsNestedQueryViaDisMaxQuery() { - InnerHitBuilder leafInnerHits1 = randomInnerHits(); - NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits1, false); - - InnerHitBuilder leafInnerHits2 = randomInnerHits(); - HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits2, false); - - DisMaxQueryBuilder disMaxQueryBuilder = new DisMaxQueryBuilder(); - disMaxQueryBuilder.add(nestedQueryBuilder); - disMaxQueryBuilder.add(hasChildQueryBuilder); - Map innerHitBuilders = new HashMap<>(); - disMaxQueryBuilder.extractInnerHitBuilders(innerHitBuilders); - assertThat(innerHitBuilders.size(), equalTo(2)); - assertThat(innerHitBuilders.get(leafInnerHits1.getName()), notNullValue()); - assertThat(innerHitBuilders.get(leafInnerHits2.getName()), notNullValue()); - } - public void testInlineLeafInnerHitsNestedQueryViaConstantScoreQuery() { InnerHitBuilder leafInnerHits = randomInnerHits(); NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("path", new MatchAllQueryBuilder(), ScoreMode.None) @@ -252,43 +215,6 @@ public void testBuildIgnoreUnmappedNestQuery() throws Exception { assertThat(innerHitsContext.getInnerHits().size(), equalTo(0)); } - public void testBuild_ignoreUnmappedHasChildQuery() throws Exception { - QueryShardContext queryShardContext = mock(QueryShardContext.class); - when(queryShardContext.documentMapper("type")).thenReturn(null); - SearchContext searchContext = mock(SearchContext.class); - when(searchContext.getQueryShardContext()).thenReturn(queryShardContext); - - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasChildQueryBuilder query1 = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits, false); - expectThrows(IllegalStateException.class, () -> query1.innerHit().build(searchContext, new InnerHitsContext())); - - HasChildQueryBuilder query2 = new HasChildQueryBuilder("type", new MatchAllQueryBuilder(), ScoreMode.None) - .innerHit(leafInnerHits, true); - InnerHitsContext innerHitsContext = new InnerHitsContext(); - query2.innerHit().build(searchContext, innerHitsContext); - assertThat(innerHitsContext.getInnerHits().size(), equalTo(0)); - } - - public void testBuild_ingoreUnmappedHasParentQuery() throws Exception { - QueryShardContext queryShardContext = mock(QueryShardContext.class); - when(queryShardContext.documentMapper("type")).thenReturn(null); - SearchContext searchContext = mock(SearchContext.class); - when(searchContext.getQueryShardContext()).thenReturn(queryShardContext); - - InnerHitBuilder leafInnerHits = randomInnerHits(); - HasParentQueryBuilder query1 = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) - .innerHit(leafInnerHits, false); - expectThrows(IllegalStateException.class, () -> query1.innerHit().build(searchContext, new InnerHitsContext())); - - HasParentQueryBuilder query2 = new HasParentQueryBuilder("type", new MatchAllQueryBuilder(), false) - .innerHit(leafInnerHits, true); - InnerHitsContext innerHitsContext = new InnerHitsContext(); - query2.innerHit().build(searchContext, innerHitsContext); - assertThat(innerHitsContext.getInnerHits().size(), equalTo(0)); - } - - public static InnerHitBuilder randomInnerHits() { return randomInnerHits(true, true); } diff --git a/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java index bf7261822030b..192b237dd2cdd 100644 --- a/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java @@ -71,7 +71,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws } /** - * @return a {@link HasChildQueryBuilder} with random values all over the place + * @return a {@link NestedQueryBuilder} with random values all over the place */ @Override protected NestedQueryBuilder doCreateTestQueryBuilder() { @@ -214,4 +214,46 @@ public void testIgnoreUnmappedWithRewrite() throws IOException { assertThat(query, notNullValue()); assertThat(query, instanceOf(MatchNoDocsQuery.class)); } + + public void testMinFromString() { + assertThat("fromString(min) != MIN", ScoreMode.Min, equalTo(NestedQueryBuilder.parseScoreMode("min"))); + assertThat("min", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Min))); + } + + public void testMaxFromString() { + assertThat("fromString(max) != MAX", ScoreMode.Max, equalTo(NestedQueryBuilder.parseScoreMode("max"))); + assertThat("max", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Max))); + } + + public void testAvgFromString() { + assertThat("fromString(avg) != AVG", ScoreMode.Avg, equalTo(NestedQueryBuilder.parseScoreMode("avg"))); + assertThat("avg", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Avg))); + } + + public void testSumFromString() { + assertThat("fromString(total) != SUM", ScoreMode.Total, equalTo(NestedQueryBuilder.parseScoreMode("sum"))); + assertThat("sum", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.Total))); + } + + public void testNoneFromString() { + assertThat("fromString(none) != NONE", ScoreMode.None, equalTo(NestedQueryBuilder.parseScoreMode("none"))); + assertThat("none", equalTo(NestedQueryBuilder.scoreModeAsString(ScoreMode.None))); + } + + /** + * Should throw {@link IllegalArgumentException} instead of NPE. + */ + public void testThatNullFromStringThrowsException() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> NestedQueryBuilder.parseScoreMode(null)); + assertEquals("No score mode for child query [null] found", e.getMessage()); + } + + /** + * Failure should not change (and the value should never match anything...). + */ + public void testThatUnrecognizedFromStringThrowsException() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> NestedQueryBuilder.parseScoreMode("unrecognized value")); + assertEquals("No score mode for child query [unrecognized value] found", e.getMessage()); + } } diff --git a/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java b/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java index 7fa1e9efe3e9d..05126ca3a1b09 100644 --- a/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java +++ b/core/src/test/java/org/elasticsearch/search/SearchModuleTests.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.search; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.inject.ModuleTestCase; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -69,6 +71,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -272,8 +275,6 @@ public List getPipelineAggregations() { "geo_polygon", "geo_shape", "geohash_cell", - "has_child", - "has_parent", "ids", "indices", "match", diff --git a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index e9a78231c1aea..31b180358b6f8 100644 --- a/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/core/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -55,8 +55,6 @@ import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; @@ -246,264 +244,6 @@ public void testRandomNested() throws Exception { } } - public void testSimpleParentChild() throws Exception { - assertAcked(prepareCreate("articles") - .addMapping("article", "title", "type=text") - .addMapping("comment", "_parent", "type=article", "message", "type=text,fielddata=true") - ); - - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); - requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); - requests.add(client().prepareIndex("articles", "comment", "2").setParent("1").setSource("message", "fox ate rabbit x y z")); - requests.add(client().prepareIndex("articles", "comment", "3").setParent("1").setSource("message", "rabbit got away")); - requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); - requests.add(client().prepareIndex("articles", "comment", "4").setParent("2").setSource("message", "elephant captured")); - requests.add(client().prepareIndex("articles", "comment", "5").setParent("2").setSource("message", "mice squashed by elephant x")); - requests.add(client().prepareIndex("articles", "comment", "6").setParent("2").setSource("message", "elephant scared by mice x y")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false)) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("1")); - assertThat(response.getHits().getAt(0).getShard(), notNullValue()); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), equalTo(2L)); - - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).type(), equalTo("comment")); - assertThat(innerHits.getAt(1).getId(), equalTo("2")); - assertThat(innerHits.getAt(1).type(), equalTo("comment")); - - response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false)) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("2")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), equalTo(3L)); - - assertThat(innerHits.getAt(0).getId(), equalTo("4")); - assertThat(innerHits.getAt(0).type(), equalTo("comment")); - assertThat(innerHits.getAt(1).getId(), equalTo("5")); - assertThat(innerHits.getAt(1).type(), equalTo("comment")); - assertThat(innerHits.getAt(2).getId(), equalTo("6")); - assertThat(innerHits.getAt(2).type(), equalTo("comment")); - - response = client().prepareSearch("articles") - .setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder() - .addDocValueField("message") - .setHighlightBuilder(new HighlightBuilder().field("message")) - .setExplain(true).setSize(1) - .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", - Collections.emptyMap())), - false) - ).get(); - assertNoFailures(response); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getHits().length, equalTo(1)); - assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); - assertThat(innerHits.getAt(0).explanation().toString(), containsString("weight(message:fox")); - assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); - assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); - } - - public void testRandomParentChild() throws Exception { - assertAcked(prepareCreate("idx") - .addMapping("parent") - .addMapping("child1", "_parent", "type=parent") - .addMapping("child2", "_parent", "type=parent") - ); - int numDocs = scaledRandomIntBetween(5, 50); - List requestBuilders = new ArrayList<>(); - - int child1 = 0; - int child2 = 0; - int[] child1InnerObjects = new int[numDocs]; - int[] child2InnerObjects = new int[numDocs]; - for (int parent = 0; parent < numDocs; parent++) { - String parentId = String.format(Locale.ENGLISH, "%03d", parent); - requestBuilders.add(client().prepareIndex("idx", "parent", parentId).setSource("{}", XContentType.JSON)); - - int numChildDocs = child1InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); - int limit = child1 + numChildDocs; - for (; child1 < limit; child1++) { - requestBuilders.add(client().prepareIndex("idx", "child1", - String.format(Locale.ENGLISH, "%04d", child1)).setParent(parentId).setSource("{}", XContentType.JSON)); - } - numChildDocs = child2InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); - limit = child2 + numChildDocs; - for (; child2 < limit; child2++) { - requestBuilders.add(client().prepareIndex("idx", "child2", - String.format(Locale.ENGLISH, "%04d", child2)).setParent(parentId).setSource("{}", XContentType.JSON)); - } - } - indexRandom(true, requestBuilders); - - int size = randomIntBetween(0, numDocs); - BoolQueryBuilder boolQuery = new BoolQueryBuilder(); - boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder().setName("a") - .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); - boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder().setName("b") - .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); - SearchResponse searchResponse = client().prepareSearch("idx") - .setSize(numDocs) - .setTypes("parent") - .addSort("_uid", SortOrder.ASC) - .setQuery(boolQuery) - .get(); - - assertNoFailures(searchResponse); - assertHitCount(searchResponse, numDocs); - assertThat(searchResponse.getHits().getHits().length, equalTo(numDocs)); - - int offset1 = 0; - int offset2 = 0; - for (int parent = 0; parent < numDocs; parent++) { - SearchHit searchHit = searchResponse.getHits().getAt(parent); - assertThat(searchHit.getType(), equalTo("parent")); - assertThat(searchHit.getId(), equalTo(String.format(Locale.ENGLISH, "%03d", parent))); - assertThat(searchHit.getShard(), notNullValue()); - - SearchHits inner = searchHit.getInnerHits().get("a"); - assertThat(inner.totalHits(), equalTo((long) child1InnerObjects[parent])); - for (int child = 0; child < child1InnerObjects[parent] && child < size; child++) { - SearchHit innerHit = inner.getAt(child); - assertThat(innerHit.getType(), equalTo("child1")); - String childId = String.format(Locale.ENGLISH, "%04d", offset1 + child); - assertThat(innerHit.getId(), equalTo(childId)); - assertThat(innerHit.getNestedIdentity(), nullValue()); - } - offset1 += child1InnerObjects[parent]; - - inner = searchHit.getInnerHits().get("b"); - assertThat(inner.totalHits(), equalTo((long) child2InnerObjects[parent])); - for (int child = 0; child < child2InnerObjects[parent] && child < size; child++) { - SearchHit innerHit = inner.getAt(child); - assertThat(innerHit.getType(), equalTo("child2")); - String childId = String.format(Locale.ENGLISH, "%04d", offset2 + child); - assertThat(innerHit.getId(), equalTo(childId)); - assertThat(innerHit.getNestedIdentity(), nullValue()); - } - offset2 += child2InnerObjects[parent]; - } - } - - public void testInnerHitsOnHasParent() throws Exception { - assertAcked(prepareCreate("stack") - .addMapping("question", "body", "type=text") - .addMapping("answer", "_parent", "type=question", "body", "type=text") - ); - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("stack", "question", "1").setSource("body", "I'm using HTTPS + Basic authentication " - + "to protect a resource. How can I throttle authentication attempts to protect against brute force attacks?")); - requests.add(client().prepareIndex("stack", "answer", "1").setParent("1").setSource("body", - "install fail2ban and enable rules for apache")); - requests.add(client().prepareIndex("stack", "question", "2").setSource("body", - "I have firewall rules set up and also denyhosts installed.\\ndo I also need to install fail2ban?")); - requests.add(client().prepareIndex("stack", "answer", "2").setParent("2").setSource("body", - "Denyhosts protects only ssh; Fail2Ban protects all daemons.")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("stack") - .setTypes("answer") - .addSort("_uid", SortOrder.ASC) - .setQuery( - boolQuery() - .must(matchQuery("body", "fail2ban")) - .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder(), false)) - ).get(); - assertNoFailures(response); - assertHitCount(response, 2); - - SearchHit searchHit = response.getHits().getAt(0); - assertThat(searchHit.getId(), equalTo("1")); - assertThat(searchHit.getType(), equalTo("answer")); - assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); - assertThat(searchHit.getInnerHits().get("question").getAt(0).id(), equalTo("1")); - - searchHit = response.getHits().getAt(1); - assertThat(searchHit.getId(), equalTo("2")); - assertThat(searchHit.getType(), equalTo("answer")); - assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); - assertThat(searchHit.getInnerHits().get("question").getAt(0).id(), equalTo("2")); - } - - public void testParentChildMultipleLayers() throws Exception { - assertAcked(prepareCreate("articles") - .addMapping("article", "title", "type=text") - .addMapping("comment", "_parent", "type=article", "message", "type=text") - .addMapping("remark", "_parent", "type=comment", "message", "type=text") - ); - - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); - requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); - requests.add(client().prepareIndex("articles", "remark", "1").setParent("1").setRouting("1").setSource("message", "good")); - requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); - requests.add(client().prepareIndex("articles", "comment", "2").setParent("2").setSource("message", "elephant captured")); - requests.add(client().prepareIndex("articles", "remark", "2").setParent("2").setRouting("2").setSource("message", "bad")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", - hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), - ScoreMode.None).innerHit(new InnerHitBuilder(), false)) - .get(); - - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("1")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).type(), equalTo("comment")); - - innerHits = innerHits.getAt(0).getInnerHits().get("remark"); - assertThat(innerHits.totalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("1")); - assertThat(innerHits.getAt(0).type(), equalTo("remark")); - - response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", - hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), - ScoreMode.None).innerHit(new InnerHitBuilder(), false)) - .get(); - - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("2")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.totalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("2")); - assertThat(innerHits.getAt(0).type(), equalTo("comment")); - - innerHits = innerHits.getAt(0).getInnerHits().get("remark"); - assertThat(innerHits.totalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("2")); - assertThat(innerHits.getAt(0).type(), equalTo("remark")); - } - public void testNestedMultipleLayers() throws Exception { assertAcked(prepareCreate("articles").addMapping("article", jsonBuilder().startObject() .startObject("article").startObject("properties") @@ -720,91 +460,6 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { assertThat(messages.getAt(0).getNestedIdentity().getChild(), nullValue()); } - public void testRoyals() throws Exception { - assertAcked( - prepareCreate("royals") - .addMapping("king") - .addMapping("prince", "_parent", "type=king") - .addMapping("duke", "_parent", "type=prince") - .addMapping("earl", "_parent", "type=duke") - .addMapping("baron", "_parent", "type=earl") - ); - - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("royals", "king", "king").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "prince", "prince").setParent("king").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "duke", "duke").setParent("prince").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl1").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl2").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl3").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "earl", "earl4").setParent("duke").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron1").setParent("earl1").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron2").setParent("earl2").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron3").setParent("earl3").setRouting("king") - .setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("royals", "baron", "baron4").setParent("earl4").setRouting("king") - .setSource("{}", XContentType.JSON)); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("royals") - .setTypes("duke") - .setQuery(boolQuery() - .filter(hasParentQuery("prince", - hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings"), false), - false).innerHit(new InnerHitBuilder().setName("princes"), false) - ) - .filter(hasChildQuery("earl", - hasChildQuery("baron", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder().setName("barons"), false), - ScoreMode.None).innerHit(new InnerHitBuilder() - .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC)) - .setName("earls") - .setSize(4), false) - ) - ) - .get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); - - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("earls"); - assertThat(innerHits.getTotalHits(), equalTo(4L)); - assertThat(innerHits.getAt(0).getId(), equalTo("earl1")); - assertThat(innerHits.getAt(1).getId(), equalTo("earl2")); - assertThat(innerHits.getAt(2).getId(), equalTo("earl3")); - assertThat(innerHits.getAt(3).getId(), equalTo("earl4")); - - SearchHits innerInnerHits = innerHits.getAt(0).getInnerHits().get("barons"); - assertThat(innerInnerHits.totalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron1")); - - innerInnerHits = innerHits.getAt(1).getInnerHits().get("barons"); - assertThat(innerInnerHits.totalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron2")); - - innerInnerHits = innerHits.getAt(2).getInnerHits().get("barons"); - assertThat(innerInnerHits.totalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron3")); - - innerInnerHits = innerHits.getAt(3).getInnerHits().get("barons"); - assertThat(innerInnerHits.totalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron4")); - - innerHits = response.getHits().getAt(0).getInnerHits().get("princes"); - assertThat(innerHits.getTotalHits(), equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("prince")); - - innerInnerHits = innerHits.getAt(0).getInnerHits().get("kings"); - assertThat(innerInnerHits.totalHits(), equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); - } - public void testMatchesQueriesNestedInnerHits() throws Exception { XContentBuilder builder = jsonBuilder().startObject() .startObject("type1") @@ -909,45 +564,6 @@ public void testMatchesQueriesNestedInnerHits() throws Exception { } } - public void testMatchesQueriesParentChildInnerHits() throws Exception { - assertAcked(prepareCreate("index").addMapping("child", "_parent", "type=parent")); - List requests = new ArrayList<>(); - requests.add(client().prepareIndex("index", "parent", "1").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("index", "child", "1").setParent("1").setSource("field", "value1")); - requests.add(client().prepareIndex("index", "child", "2").setParent("1").setSource("field", "value2")); - requests.add(client().prepareIndex("index", "parent", "2").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("index", "child", "3").setParent("2").setSource("field", "value1")); - indexRandom(true, requests); - - SearchResponse response = client().prepareSearch("index") - .setQuery(hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false)) - .addSort("_uid", SortOrder.ASC) - .get(); - assertHitCount(response, 2); - assertThat(response.getHits().getAt(0).id(), equalTo("1")); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); - - assertThat(response.getHits().getAt(1).id(), equalTo("2")); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); - - QueryBuilder query = hasChildQuery("child", matchQuery("field", "value2").queryName("_name2"), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false); - response = client().prepareSearch("index") - .setQuery(query) - .addSort("_uid", SortOrder.ASC) - .get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).id(), equalTo("1")); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); - } - public void testDontExplode() throws Exception { assertAcked(prepareCreate("index1").addMapping("child", "_parent", "type=parent")); List requests = new ArrayList<>(); @@ -955,30 +571,11 @@ public void testDontExplode() throws Exception { requests.add(client().prepareIndex("index1", "child", "1").setParent("1").setSource("field", "value1")); indexRandom(true, requests); - QueryBuilder query = hasChildQuery("child", matchQuery("field", "value1"), ScoreMode.None) - .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); - SearchResponse response = client().prepareSearch("index1") - .setQuery(query) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - - assertAcked(prepareCreate("index2").addMapping("type", "nested", "type=nested")); - client().prepareIndex("index2", "type", "1").setSource(jsonBuilder().startObject() - .startArray("nested") - .startObject() - .field("field", "value1") - .endObject() - .endArray() - .endObject()) - .setRefreshPolicy(IMMEDIATE) - .get(); - - query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg) - .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); - response = client().prepareSearch("index2") - .setQuery(query) - .get(); + QueryBuilder query = query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); + SearchResponse response = client().prepareSearch("index2") + .setQuery(query) + .get(); assertNoFailures(response); assertHitCount(response, 1); } @@ -1012,23 +609,6 @@ public void testNestedSourceFiltering() throws Exception { equalTo("fox ate rabbit x y z")); } - public void testNestedInnerHitWrappedInParentChildInnerhit() throws Exception { - assertAcked(prepareCreate("test").addMapping("child_type", "_parent", "type=parent_type", "nested_type", "type=nested")); - client().prepareIndex("test", "parent_type", "1").setSource("key", "value").get(); - client().prepareIndex("test", "child_type", "2").setParent("1").setSource("nested_type", Collections.singletonMap("key", "value")) - .get(); - refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(boolQuery().must(matchQuery("key", "value")) - .should(hasChildQuery("child_type", nestedQuery("nested_type", matchAllQuery(), ScoreMode.None) - .innerHit(new InnerHitBuilder(), false), ScoreMode.None).innerHit(new InnerHitBuilder(), false))) - .get(); - assertHitCount(response, 1); - SearchHit hit = response.getHits().getAt(0); - assertThat(hit.getInnerHits().get("child_type").getAt(0).field("_parent").getValue(), equalTo("1")); - assertThat(hit.getInnerHits().get("child_type").getAt(0).getInnerHits().get("nested_type").getAt(0).field("_parent"), nullValue()); - } - public void testInnerHitsWithIgnoreUnmapped() throws Exception { assertAcked(prepareCreate("index1") .addMapping("parent_type", "nested_type", "type=nested") @@ -1050,17 +630,6 @@ public void testInnerHitsWithIgnoreUnmapped() throws Exception { assertNoFailures(response); assertHitCount(response, 2); assertSearchHits(response, "1", "3"); - - response = client().prepareSearch("index1", "index2") - .setQuery(boolQuery() - .should(hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder(), true)) - .should(termQuery("key", "value")) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 2); - assertSearchHits(response, "1", "3"); } } diff --git a/docs/java-api/aggregations/bucket/children-aggregation.asciidoc b/docs/java-api/aggregations/bucket/children-aggregation.asciidoc index 1bf8a5b26e69e..f6a23fdafe976 100644 --- a/docs/java-api/aggregations/bucket/children-aggregation.asciidoc +++ b/docs/java-api/aggregations/bucket/children-aggregation.asciidoc @@ -24,7 +24,7 @@ Import Aggregation definition classes: [source,java] -------------------------------------------------- -import org.elasticsearch.search.aggregations.bucket.children.Children; +import org.elasticsearch.join.aggregations.Children; -------------------------------------------------- [source,java] diff --git a/modules/parent-join/build.gradle b/modules/parent-join/build.gradle new file mode 100644 index 0000000000000..67bcc9d54e8e7 --- /dev/null +++ b/modules/parent-join/build.gradle @@ -0,0 +1,24 @@ +/* + * 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. + */ + +esplugin { + description 'This module adds the support parent-child queries and aggregations' + classname 'org.elasticsearch.join.ParentJoinPlugin' + hasClientJar = true +} diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java b/modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java new file mode 100644 index 0000000000000..dec3950836aab --- /dev/null +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/ParentJoinPlugin.java @@ -0,0 +1,54 @@ +/* + * 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.join; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.join.aggregations.ChildrenAggregationBuilder; +import org.elasticsearch.join.aggregations.InternalChildren; +import org.elasticsearch.join.query.HasChildQueryBuilder; +import org.elasticsearch.join.query.HasParentQueryBuilder; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.SearchPlugin; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ParentJoinPlugin extends Plugin implements SearchPlugin { + public ParentJoinPlugin(Settings settings) {} + + @Override + public List> getQueries() { + return Arrays.asList( + new QuerySpec<>(HasChildQueryBuilder.NAME, HasChildQueryBuilder::new, HasChildQueryBuilder::fromXContent), + new QuerySpec<>(HasParentQueryBuilder.NAME, HasParentQueryBuilder::new, HasParentQueryBuilder::fromXContent) + ); + } + + @Override + public List getAggregations() { + return Collections.singletonList( + new AggregationSpec(ChildrenAggregationBuilder.NAME, ChildrenAggregationBuilder::new, ChildrenAggregationBuilder::parse) + .addResultReader(InternalChildren::new) + ); + } + + +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/Children.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/Children.java similarity index 94% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/Children.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/Children.java index b1e4b2877a3a0..394c690709d8a 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/Children.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/Children.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.elasticsearch.search.aggregations.bucket.SingleBucketAggregation; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java similarity index 99% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java index 3a0d2fff982d8..d04b1f0a66052 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.search.Query; import org.elasticsearch.common.ParsingException; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregatorFactory.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregatorFactory.java similarity index 98% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregatorFactory.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregatorFactory.java index b0a4c64305aa1..800be74ba6fe6 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ChildrenAggregatorFactory.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregatorFactory.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.search.Query; import org.elasticsearch.search.aggregations.Aggregator; diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildren.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/InternalChildren.java similarity index 97% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildren.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/InternalChildren.java index 05a38c8cd59b5..05cd40e3d3366 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/InternalChildren.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/InternalChildren.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.search.aggregations.InternalAggregations; diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java new file mode 100644 index 0000000000000..73522a68b4595 --- /dev/null +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/JoinAggregationBuilders.java @@ -0,0 +1,29 @@ +/* + * 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.join.aggregations; + +public abstract class JoinAggregationBuilders { + /** + * Create a new {@link Children} aggregation with the given name. + */ + public static ChildrenAggregationBuilder children(String name, String childType) { + return new ChildrenAggregationBuilder(name, childType); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregator.java similarity index 99% rename from core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregator.java index 5aa55c771b27c..a41b87b3854ac 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java similarity index 94% rename from core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java index 0c87f09a622c2..3900fcafc7be9 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; @@ -38,6 +38,14 @@ import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; +import org.elasticsearch.index.query.AbstractQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.NestedQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryRewriteContext; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; import java.io.IOException; import java.util.Locale; @@ -211,7 +219,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep builder.field(QUERY_FIELD.getPreferredName()); query.toXContent(builder, params); builder.field(TYPE_FIELD.getPreferredName(), type); - builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreModeAsString(scoreMode)); + builder.field(SCORE_MODE_FIELD.getPreferredName(), NestedQueryBuilder.scoreModeAsString(scoreMode)); builder.field(MIN_CHILDREN_FIELD.getPreferredName(), minChildren); builder.field(MAX_CHILDREN_FIELD.getPreferredName(), maxChildren); builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped); @@ -252,7 +260,7 @@ public static Optional fromXContent(QueryParseContext pars if (TYPE_FIELD.match(currentFieldName)) { childType = parser.text(); } else if (SCORE_MODE_FIELD.match(currentFieldName)) { - scoreMode = parseScoreMode(parser.text()); + scoreMode = NestedQueryBuilder.parseScoreMode(parser.text()); } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName)) { boost = parser.floatValue(); } else if (MIN_CHILDREN_FIELD.match(currentFieldName)) { @@ -285,30 +293,6 @@ public static Optional fromXContent(QueryParseContext pars return Optional.of(hasChildQueryBuilder); } - public static ScoreMode parseScoreMode(String scoreModeString) { - if ("none".equals(scoreModeString)) { - return ScoreMode.None; - } else if ("min".equals(scoreModeString)) { - return ScoreMode.Min; - } else if ("max".equals(scoreModeString)) { - return ScoreMode.Max; - } else if ("avg".equals(scoreModeString)) { - return ScoreMode.Avg; - } else if ("sum".equals(scoreModeString)) { - return ScoreMode.Total; - } - throw new IllegalArgumentException("No score mode for child query [" + scoreModeString + "] found"); - } - - public static String scoreModeAsString(ScoreMode scoreMode) { - if (scoreMode == ScoreMode.Total) { - // Lucene uses 'total' but 'sum' is more consistent with other elasticsearch APIs - return "sum"; - } else { - return scoreMode.name().toLowerCase(Locale.ROOT); - } - } - @Override public String getWriteableName() { return NAME; diff --git a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java similarity index 96% rename from core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java rename to modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java index 88104007c90ac..0be972a782682 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; @@ -33,6 +33,13 @@ import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; +import org.elasticsearch.index.query.AbstractQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryRewriteContext; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; import java.io.IOException; import java.util.HashSet; diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java new file mode 100644 index 0000000000000..af778f400f7b3 --- /dev/null +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/JoinQueryBuilders.java @@ -0,0 +1,50 @@ +/* + * 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.join.query; + +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.index.query.QueryBuilder; + +public abstract class JoinQueryBuilders { + /** + * Constructs a new has_child query, with the child type and the query to run on the child documents. The + * results of this query are the parent docs that those child docs matched. + * + * @param type The child type. + * @param query The query. + * @param scoreMode How the scores from the children hits should be aggregated into the parent hit. + */ + public static HasChildQueryBuilder hasChildQuery(String type, QueryBuilder query, ScoreMode scoreMode) { + return new HasChildQueryBuilder(type, query, scoreMode); + } + + /** + * Constructs a new parent query, with the parent type and the query to run on the parent documents. The + * results of this query are the children docs that those parent docs matched. + * + * @param type The parent type. + * @param query The query. + * @param score Whether the score from the parent hit should propagate to the child hit + */ + public static HasParentQueryBuilder hasParentQuery(String type, QueryBuilder query, boolean score) { + return new HasParentQueryBuilder(type, query, score); + } + +} diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java new file mode 100644 index 0000000000000..666fa736d45de --- /dev/null +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/ParentChildClientYamlTestSuiteIT.java @@ -0,0 +1,37 @@ +/* + * 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.join; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; +import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; + +public class ParentChildClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { + public ParentChildClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { + super(testCandidate); + } + + @ParametersFactory + public static Iterable parameters() throws Exception { + return createParameters(); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java similarity index 91% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java index 2affcc8da9256..6b2648c58e0b9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.search.aggregations.bucket; +package org.elasticsearch.join.aggregations; import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.index.IndexRequestBuilder; @@ -26,27 +26,33 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.bucket.children.Children; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.metrics.tophits.TopHits; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.test.ESIntegTestCase.Scope; +import org.junit.Before; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; -import static org.elasticsearch.search.aggregations.AggregationBuilders.children; +import static org.elasticsearch.join.aggregations.JoinAggregationBuilders.children; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; @@ -59,15 +65,28 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; -/** - */ -@ESIntegTestCase.SuiteScopeTestCase +@ClusterScope(scope = Scope.SUITE) public class ChildrenIT extends ESIntegTestCase { - private static final Map categoryToControl = new HashMap<>(); @Override - public void setupSuiteScopeCluster() throws Exception { + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Collections.singleton(ParentJoinPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + + @Before + public void setupCluster() throws Exception { + categoryToControl.clear(); assertAcked( prepareCreate("test") .addMapping("article", "category", "type=keyword") @@ -96,7 +115,8 @@ public void setupSuiteScopeCluster() throws Exception { control.articleIds.add(id); } - requests.add(client().prepareIndex("test", "article", id).setCreate(true).setSource("category", categories, "randomized", true)); + requests.add(client() + .prepareIndex("test", "article", id).setCreate(true).setSource("category", categories, "randomized", true)); } String[] commenters = new String[randomIntBetween(5, 50)]; @@ -117,17 +137,24 @@ public void setupSuiteScopeCluster() throws Exception { control.commenterToCommentId.put(commenter, ids = new HashSet<>()); } ids.add(idValue); - requests.add(client().prepareIndex("test", "comment", idValue).setCreate(true).setParent(articleId).setSource("commenter", commenter)); + requests.add(client().prepareIndex("test", "comment", idValue) + .setCreate(true).setParent(articleId).setSource("commenter", commenter)); } } } - requests.add(client().prepareIndex("test", "article", "a").setSource("category", new String[]{"a"}, "randomized", false)); - requests.add(client().prepareIndex("test", "article", "b").setSource("category", new String[]{"a", "b"}, "randomized", false)); - requests.add(client().prepareIndex("test", "article", "c").setSource("category", new String[]{"a", "b", "c"}, "randomized", false)); - requests.add(client().prepareIndex("test", "article", "d").setSource("category", new String[]{"c"}, "randomized", false)); - requests.add(client().prepareIndex("test", "comment", "a").setParent("a").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex("test", "comment", "c").setParent("c").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("test", "article", "a") + .setSource("category", new String[]{"a"}, "randomized", false)); + requests.add(client().prepareIndex("test", "article", "b") + .setSource("category", new String[]{"a", "b"}, "randomized", false)); + requests.add(client().prepareIndex("test", "article", "c") + .setSource("category", new String[]{"a", "b", "c"}, "randomized", false)); + requests.add(client().prepareIndex("test", "article", "d") + .setSource("category", new String[]{"c"}, "randomized", false)); + requests.add(client().prepareIndex("test", "comment", "a") + .setParent("a").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("test", "comment", "c") + .setParent("c").setSource("{}", XContentType.JSON)); indexRandom(true, requests); ensureSearchable("test"); @@ -156,7 +183,8 @@ public void testChildrenAggs() throws Exception { Children childrenBucket = categoryBucket.getAggregations().get("to_comment"); assertThat(childrenBucket.getName(), equalTo("to_comment")); assertThat(childrenBucket.getDocCount(), equalTo((long) entry1.getValue().commentIds.size())); - assertThat((long) ((InternalAggregation)childrenBucket).getProperty("_count"), equalTo((long) entry1.getValue().commentIds.size())); + assertThat((long) ((InternalAggregation)childrenBucket).getProperty("_count"), + equalTo((long) entry1.getValue().commentIds.size())); Terms commentersTerms = childrenBucket.getAggregations().get("commenters"); assertThat((Terms) ((InternalAggregation)childrenBucket).getProperty("commenters"), sameInstance(commentersTerms)); @@ -283,7 +311,7 @@ public void testWithDeletes() throws Exception { public void testNonExistingChildType() throws Exception { SearchResponse searchResponse = client().prepareSearch("test") .addAggregation( -children("non-existing", "xyz") + children("non-existing", "xyz") ).get(); assertSearchResponse(searchResponse); @@ -303,7 +331,8 @@ public void testPostCollection() throws Exception { ); List requests = new ArrayList<>(); - requests.add(client().prepareIndex(indexName, masterType, "1").setSource("brand", "Levis", "name", "Style 501", "material", "Denim")); + requests.add(client().prepareIndex(indexName, masterType, "1") + .setSource("brand", "Levis", "name", "Style 501", "material", "Denim")); requests.add(client().prepareIndex(indexName, childType, "0").setParent("1").setSource("color", "blue", "size", "32")); requests.add(client().prepareIndex(indexName, childType, "1").setParent("1").setSource("color", "blue", "size", "34")); requests.add(client().prepareIndex(indexName, childType, "2").setParent("1").setSource("color", "blue", "size", "36")); @@ -311,7 +340,8 @@ public void testPostCollection() throws Exception { requests.add(client().prepareIndex(indexName, childType, "4").setParent("1").setSource("color", "black", "size", "40")); requests.add(client().prepareIndex(indexName, childType, "5").setParent("1").setSource("color", "gray", "size", "36")); - requests.add(client().prepareIndex(indexName, masterType, "2").setSource("brand", "Wrangler", "name", "Regular Cut", "material", "Leather")); + requests.add(client().prepareIndex(indexName, masterType, "2") + .setSource("brand", "Wrangler", "name", "Regular Cut", "material", "Leather")); requests.add(client().prepareIndex(indexName, childType, "6").setParent("2").setSource("color", "blue", "size", "32")); requests.add(client().prepareIndex(indexName, childType, "7").setParent("2").setSource("color", "blue", "size", "34")); requests.add(client().prepareIndex(indexName, childType, "8").setParent("2").setSource("color", "black", "size", "36")); @@ -323,7 +353,7 @@ public void testPostCollection() throws Exception { SearchResponse response = client().prepareSearch(indexName).setTypes(masterType) .setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None)) -.addAggregation(children("my-refinements", childType) + .addAggregation(children("my-refinements", childType) .subAggregation(terms("my-colors").field("color")) .subAggregation(terms("my-sizes").field("size")) ).get(); @@ -422,7 +452,7 @@ public void testPostCollectAllLeafReaders() throws Exception { .setSize(0) .addAggregation(AggregationBuilders.terms("towns").field("town") .subAggregation(AggregationBuilders.terms("parent_names").field("name") -.subAggregation(AggregationBuilders.children("child_docs", "childType")) + .subAggregation(children("child_docs", "childType")) ) ) .get(); @@ -465,5 +495,4 @@ private Control(String category) { this.category = category; } } - } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenTests.java similarity index 78% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenTests.java index 4098e85c62eda..85a97c4b9b413 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenTests.java @@ -17,13 +17,22 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket; +package org.elasticsearch.join.aggregations; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; -import org.elasticsearch.search.aggregations.bucket.children.ChildrenAggregationBuilder; + +import java.util.Collection; +import java.util.Collections; public class ChildrenTests extends BaseAggregationTestCase { + @Override + protected Collection> getPlugins() { + return Collections.singleton(ParentJoinPlugin.class); + } + @Override protected ChildrenAggregationBuilder createTestAggregatorBuilder() { String name = randomAlphaOfLengthBetween(3, 20); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java similarity index 99% rename from core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java index 17152bc450a22..0a00b2d1c26d8 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregatorTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.search.aggregations.bucket.children; +package org.elasticsearch.join.aggregations; import org.apache.lucene.document.Field; import org.apache.lucene.document.SortedDocValuesField; diff --git a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java similarity index 87% rename from core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java index 16c6c2a0ff024..b28bdd92e9849 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java @@ -16,17 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch.search.child; +package org.elasticsearch.join.query; import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse; +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.explain.ExplainResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.lucene.search.function.CombineFunction; import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery; import org.elasticsearch.common.settings.Settings; @@ -34,8 +37,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.IdsQueryBuilder; import org.elasticsearch.index.query.InnerHitBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; @@ -43,6 +44,8 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AggregationBuilders; @@ -62,6 +65,8 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -72,8 +77,6 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasChildQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.idsQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; @@ -83,6 +86,8 @@ import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.QueryBuilders.termsQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.fieldValueFactorFunction; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.weightFactorFunction; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -100,6 +105,21 @@ @ClusterScope(scope = Scope.SUITE) public class ChildQuerySearchIT extends ESIntegTestCase { + + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Collections.singleton(ParentJoinPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } @Override public Settings indexSettings() { @@ -144,26 +164,30 @@ public void testMultiLevelChild() throws Exception { assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false))).execute() + .setQuery(boolQuery().must(matchAllQuery()) + .filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false))).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false))).execute() + .setQuery(boolQuery().must(matchAllQuery()) + .filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false))).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).id(), equalTo("gc1")); - searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)).execute() + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c1")); - searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)).execute() + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); @@ -206,7 +230,8 @@ public void testSimpleChildQuery() throws Exception { refresh(); // TEST FETCHING _parent from child - SearchResponse searchResponse = client().prepareSearch("test").setQuery(idsQuery("child").addIds("c1")).storedFields("_parent").execute() + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(idsQuery("child").addIds("c1")).storedFields("_parent").execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); @@ -280,7 +305,8 @@ public void testCachingBugWithFqueryFilter() throws Exception { builders.add(client().prepareIndex("test", "child", Integer.toString(i)).setSource("c_field", i).setParent("" + 0)); } for (int i = 0; i < 10; i++) { - builders.add(client().prepareIndex("test", "child", Integer.toString(i + 10)).setSource("c_field", i + 10).setParent(Integer.toString(i))); + builders.add(client().prepareIndex("test", "child", Integer.toString(i + 10)) + .setSource("c_field", i + 10).setParent(Integer.toString(i))); } if (randomBoolean()) { @@ -438,9 +464,11 @@ public void testScopedFacet() throws Exception { SearchResponse searchResponse = client() .prepareSearch("test") - .setQuery(hasChildQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), ScoreMode.None)) + .setQuery(hasChildQuery("child", + boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), ScoreMode.None)) .addAggregation(AggregationBuilders.global("global").subAggregation( - AggregationBuilders.filter("filter", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow"))).subAggregation( + AggregationBuilders.filter("filter", + boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow"))).subAggregation( AggregationBuilders.terms("facet1").field("c_field")))).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(2L)); @@ -514,7 +542,8 @@ public void testDfsSearchType() throws Exception { assertNoFailures(searchResponse); searchResponse = client().prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .setQuery(boolQuery().mustNot(hasParentQuery("parent", boolQuery().should(queryStringQuery("p_field:*")), false))).execute() + .setQuery(boolQuery().mustNot(hasParentQuery("parent", + boolQuery().should(queryStringQuery("p_field:*")), false))).execute() .actionGet(); assertNoFailures(searchResponse); } @@ -559,7 +588,8 @@ public void testCountApiUsage() throws Exception { .get(); assertHitCount(countResponse, 1L); - countResponse = client().prepareSearch("test").setSize(0).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)) + countResponse = client().prepareSearch("test").setSize(0) + .setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)) .get(); assertHitCount(countResponse, 1L); @@ -568,7 +598,8 @@ public void testCountApiUsage() throws Exception { .get(); assertHitCount(countResponse, 1L); - countResponse = client().prepareSearch("test").setSize(0).setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false))) + countResponse = client().prepareSearch("test").setSize(0) + .setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false))) .get(); assertHitCount(countResponse, 1L); } @@ -672,11 +703,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { SearchResponse response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), - fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), ScoreMode.Total)).get(); + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), + fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), ScoreMode.Total)).get(); assertThat(response.getHits().totalHits(), equalTo(3L)); assertThat(response.getHits().hits()[0].id(), equalTo("1")); @@ -689,11 +720,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), - fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), ScoreMode.Max)).get(); + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), + fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), ScoreMode.Max)).get(); assertThat(response.getHits().totalHits(), equalTo(3L)); assertThat(response.getHits().hits()[0].id(), equalTo("3")); @@ -706,11 +737,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), - fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), ScoreMode.Avg)).get(); + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), + fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), ScoreMode.Avg)).get(); assertThat(response.getHits().totalHits(), equalTo(3L)); assertThat(response.getHits().hits()[0].id(), equalTo("3")); @@ -723,11 +754,11 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { response = client() .prepareSearch("test") .setQuery( - QueryBuilders.hasParentQuery( - "parent", - QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), - fieldValueFactorFunction("p_field2")) - .boostMode(CombineFunction.REPLACE), true)) + hasParentQuery( + "parent", + QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), + fieldValueFactorFunction("p_field2")) + .boostMode(CombineFunction.REPLACE), true)) .addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get(); assertThat(response.getHits().totalHits(), equalTo(7L)); @@ -755,7 +786,7 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep ensureGreen(); SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); + .setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); assertNoFailures(response); assertThat(response.getHits().totalHits(), equalTo(0L)); @@ -763,20 +794,21 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep .setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); response = client().prepareSearch("test") - .setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); + .setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); assertNoFailures(response); assertThat(response.getHits().totalHits(), equalTo(0L)); - response = client().prepareSearch("test").setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)) + response = client().prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)) .get(); assertNoFailures(response); assertThat(response.getHits().totalHits(), equalTo(0L)); - response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("parent", matchQuery("text", "value"), false)).get(); + response = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", matchQuery("text", "value"), false)).get(); assertNoFailures(response); assertThat(response.getHits().totalHits(), equalTo(0L)); - response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("parent", matchQuery("text", "value"), true)) + response = client().prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), true)) .get(); assertNoFailures(response); assertThat(response.getHits().totalHits(), equalTo(0L)); @@ -803,7 +835,8 @@ public void testHasChildAndHasParentFilter_withFilter() throws Exception { assertThat(searchResponse.getHits().hits()[0].id(), equalTo("1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", 1), false))).get(); + .setQuery(boolQuery().must(matchAllQuery()) + .filter(hasParentQuery("parent", termQuery("p_field", 1), false))).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); assertThat(searchResponse.getHits().hits()[0].id(), equalTo("2")); @@ -856,12 +889,14 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { assertSearchHit(searchResponse, 1, hasId("2")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)))) + .setQuery(boolQuery().must(matchAllQuery()) + .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)))) .get(); assertSearchHit(searchResponse, 1, hasId("1")); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false)))).get(); + .setQuery(boolQuery().must(matchAllQuery()) + .filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false)))).get(); assertSearchHit(searchResponse, 1, hasId("2")); } @@ -955,7 +990,8 @@ public void testReIndexingParentAndChildDocuments() throws Exception { client().admin().indices().prepareRefresh("test").get(); } - searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)) + searchResponse = client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); @@ -989,7 +1025,8 @@ public void testHasChildQueryWithMinimumScore() throws Exception { client().prepareIndex("test", "child", "c5").setSource("c_field", "x").setParent("p2").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) + SearchResponse searchResponse = client() + .prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) .setMinScore(3) // Score needs to be 3 or above! .get(); assertNoFailures(searchResponse); @@ -1198,7 +1235,8 @@ public void testAddingParentToExistingMapping() throws IOException { createIndex("test"); ensureGreen(); - PutMappingResponse putMappingResponse = client().admin().indices().preparePutMapping("test").setType("child").setSource("number", "type=integer") + PutMappingResponse putMappingResponse = client().admin().indices() + .preparePutMapping("test").setType("child").setSource("number", "type=integer") .get(); assertThat(putMappingResponse.isAcknowledged(), equalTo(true)); @@ -1247,13 +1285,15 @@ public void testHasChildQueryWithNestedInnerObjects() throws Exception { ScoreMode scoreMode = randomFrom(ScoreMode.values()); SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)).filter(boolQuery().mustNot(termQuery("p_field", "3")))) + .setQuery(boolQuery().must(hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3")))) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "red"), scoreMode)).filter(boolQuery().mustNot(termQuery("p_field", "3")))) + .setQuery(boolQuery().must(hasChildQuery("child", termQuery("c_field", "red"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3")))) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(2L)); @@ -1270,25 +1310,29 @@ public void testNamedFilters() throws Exception { client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max).queryName("test")) + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", + termQuery("c_field", "1"), ScoreMode.Max).queryName("test")) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true).queryName("test")) + searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", + termQuery("p_field", "1"), true).queryName("test")) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None).queryName("test"))) + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", + termQuery("c_field", "1"), ScoreMode.None).queryName("test"))) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false).queryName("test"))) + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasParentQuery("parent", + termQuery("p_field", "1"), false).queryName("test"))) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); @@ -1381,7 +1425,7 @@ public void testParentChildCaching() throws Exception { for (int i = 0; i < 2; i++) { SearchResponse searchResponse = client().prepareSearch() .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery() - .must(QueryBuilders.hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) + .must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) .must(matchAllQuery()))) .get(); assertThat(searchResponse.getHits().totalHits(), equalTo(2L)); @@ -1393,7 +1437,7 @@ public void testParentChildCaching() throws Exception { SearchResponse searchResponse = client().prepareSearch() .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery() - .must(QueryBuilders.hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) + .must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)) .must(matchAllQuery()))) .get(); @@ -1457,7 +1501,8 @@ public void testQueryBeforeChildType() throws Exception { SearchResponse resp; resp = client().prepareSearch("test") - .setSource(new SearchSourceBuilder().query(QueryBuilders.hasChildQuery("posts", QueryBuilders.matchQuery("field", "bar"), ScoreMode.None))) + .setSource(new SearchSourceBuilder().query(hasChildQuery("posts", + QueryBuilders.matchQuery("field", "bar"), ScoreMode.None))) .get(); assertHitCount(resp, 1L); } @@ -1557,8 +1602,10 @@ private SearchResponse minMaxQuery(ScoreMode scoreMode, int minChildren, Integer QueryBuilders.functionScoreQuery(constantScoreQuery(QueryBuilders.termQuery("foo", "two")), new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ new FunctionScoreQueryBuilder.FilterFunctionBuilder(weightFactorFunction(1)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "three"), weightFactorFunction(1)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "four"), weightFactorFunction(1)) + new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "three"), + weightFactorFunction(1)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("foo", "four"), + weightFactorFunction(1)) }).boostMode(CombineFunction.REPLACE).scoreMode(FiltersFunctionScoreQuery.ScoreMode.SUM), scoreMode) .minMaxChildren(minChildren, maxChildren != null ? maxChildren : HasChildQueryBuilder.DEFAULT_MAX_CHILDREN); @@ -1890,7 +1937,7 @@ public void testParentFieldToNonExistingType() { try { client().prepareSearch("test") - .setQuery(QueryBuilders.hasChildQuery("child", matchAllQuery(), ScoreMode.None)) + .setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) .get(); fail(); } catch (SearchPhaseExecutionException e) { @@ -1904,7 +1951,7 @@ public void testHasParentInnerQueryType() { refresh(); //make sure that when we explicitly set a type, the inner query is executed in the context of the parent type instead SearchResponse searchResponse = client().prepareSearch("test").setTypes("child-type").setQuery( - QueryBuilders.hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)).get(); + hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)).get(); assertSearchHits(searchResponse, "child-id"); } @@ -1915,7 +1962,7 @@ public void testHasChildInnerQueryType() { refresh(); //make sure that when we explicitly set a type, the inner query is executed in the context of the child type instead SearchResponse searchResponse = client().prepareSearch("test").setTypes("parent-type").setQuery( - QueryBuilders.hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)).get(); + hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)).get(); assertSearchHits(searchResponse, "parent-id"); } @@ -1924,8 +1971,10 @@ public void testHighlightersIgnoreParentChild() { .addMapping("parent-type", "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets") .addMapping("child-type", "_parent", "type=parent-type", "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets")); - client().prepareIndex("test", "parent-type", "parent-id").setSource("searchText", "quick brown fox").get(); - client().prepareIndex("test", "child-type", "child-id").setParent("parent-id").setSource("searchText", "quick brown fox").get(); + client().prepareIndex("test", "parent-type", "parent-id") + .setSource("searchText", "quick brown fox").get(); + client().prepareIndex("test", "child-type", "child-id") + .setParent("parent-id").setSource("searchText", "quick brown fox").get(); refresh(); String[] highlightTypes = new String[] {"plain", "fvh", "postings"}; @@ -1957,4 +2006,177 @@ public void testHighlightersIgnoreParentChild() { } } + public void testAliasesFilterWithHasChildQuery() throws Exception { + assertAcked(prepareCreate("my-index") + .setSettings("index.mapping.single_type", false) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + ); + client().prepareIndex("my-index", "parent", "1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("my-index", "child", "2").setSource("{}", XContentType.JSON).setParent("1").get(); + refresh(); + + assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter1", + hasChildQuery("child", matchAllQuery(), ScoreMode.None))); + assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter2", + hasParentQuery("parent", matchAllQuery(), false))); + + SearchResponse response = client().prepareSearch("filter1").get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + response = client().prepareSearch("filter2").get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("2")); + } + + /* + Test for https://github.com/elastic/elasticsearch/issues/3444 + */ + public void testBulkUpdateDocAsUpsertWithParent() throws Exception { + client().admin().indices().prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) + .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON) + .execute().actionGet(); + ensureGreen(); + + BulkRequestBuilder builder = client().prepareBulk(); + + // It's important to use JSON parsing here and request objects: issue 3444 is related to incomplete option parsing + byte[] addParent = new BytesArray( + "{" + + " \"index\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"parent\"," + + " \"_id\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"field1\" : \"value1\"" + + "}" + + "\n").array(); + + byte[] addChild = new BytesArray( + "{" + + " \"update\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"child\"," + + " \"_id\" : \"child1\"," + + " \"parent\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"doc\" : {" + + " \"field1\" : \"value1\"" + + " }," + + " \"doc_as_upsert\" : \"true\"" + + "}" + + "\n").array(); + + builder.add(addParent, 0, addParent.length, XContentType.JSON); + builder.add(addChild, 0, addChild.length, XContentType.JSON); + + BulkResponse bulkResponse = builder.get(); + assertThat(bulkResponse.getItems().length, equalTo(2)); + assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); + assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); + + client().admin().indices().prepareRefresh("test").get(); + + //we check that the _parent field was set on the child document by using the has parent query + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) + .get(); + + assertNoFailures(searchResponse); + assertSearchHits(searchResponse, "child1"); + } + + /* + Test for https://github.com/elastic/elasticsearch/issues/3444 + */ + public void testBulkUpdateUpsertWithParent() throws Exception { + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) + .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); + ensureGreen(); + + BulkRequestBuilder builder = client().prepareBulk(); + + byte[] addParent = new BytesArray( + "{" + + " \"index\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"parent\"," + + " \"_id\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"field1\" : \"value1\"" + + "}" + + "\n").array(); + + byte[] addChild1 = new BytesArray( + "{" + + " \"update\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"child\"," + + " \"_id\" : \"child1\"," + + " \"parent\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"script\" : {" + + " \"inline\" : \"ctx._source.field2 = 'value2'\"" + + " }," + + " \"lang\" : \"" + InnerHitsIT.CustomScriptPlugin.NAME + "\"," + + " \"upsert\" : {" + + " \"field1\" : \"value1'\"" + + " }" + + "}" + + "\n").array(); + + byte[] addChild2 = new BytesArray( + "{" + + " \"update\" : {" + + " \"_index\" : \"test\"," + + " \"_type\" : \"child\"," + + " \"_id\" : \"child1\"," + + " \"parent\" : \"parent1\"" + + " }" + + "}" + + "\n" + + "{" + + " \"script\" : \"ctx._source.field2 = 'value2'\"," + + " \"upsert\" : {" + + " \"field1\" : \"value1'\"" + + " }" + + "}" + + "\n").array(); + + builder.add(addParent, 0, addParent.length, XContentType.JSON); + builder.add(addChild1, 0, addChild1.length, XContentType.JSON); + builder.add(addChild2, 0, addChild2.length, XContentType.JSON); + + BulkResponse bulkResponse = builder.get(); + assertThat(bulkResponse.getItems().length, equalTo(3)); + assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false)); + assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false)); + assertThat(bulkResponse.getItems()[2].isFailed(), equalTo(true)); + assertThat(bulkResponse.getItems()[2].getFailure().getCause().getCause().getMessage(), + equalTo("script_lang not supported [painless]")); + + client().admin().indices().prepareRefresh("test").get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", QueryBuilders.matchAllQuery(), false)) + .get(); + + assertSearchHits(searchResponse, "child1"); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java similarity index 84% rename from core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java index 017d084306a86..3d33148e0ddfd 100644 --- a/core/src/test/java/org/elasticsearch/index/query/HasChildQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.search.TermInSetQuery; @@ -40,7 +40,17 @@ import org.elasticsearch.index.mapper.TypeFieldMapper; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; +import org.elasticsearch.index.query.IdsQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.index.query.WrapperQueryBuilder; import org.elasticsearch.index.similarity.SimilarityService; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.fetch.subphase.InnerHitsContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; @@ -48,11 +58,13 @@ import org.elasticsearch.test.AbstractQueryTestCase; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -66,6 +78,11 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase> getPlugins() { + return Collections.singletonList(ParentJoinPlugin.class); + } + @Override protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { similarity = randomFrom("classic", "BM25"); @@ -98,7 +115,7 @@ protected HasChildQueryBuilder doCreateTestQueryBuilder() { int min = randomIntBetween(0, Integer.MAX_VALUE / 2); int max = randomIntBetween(min, Integer.MAX_VALUE); - QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random()); + QueryBuilder innerQueryBuilder = new MatchAllQueryBuilder(); if (randomBoolean()) { requiresRewrite = true; innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString()); @@ -145,19 +162,19 @@ protected void doAssertLuceneQuery(HasChildQueryBuilder queryBuilder, Query quer } public void testIllegalValues() { - QueryBuilder query = RandomQueryBuilder.createQuery(random()); + QueryBuilder query = new MatchAllQueryBuilder(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> QueryBuilders.hasChildQuery(null, query, ScoreMode.None)); + () -> hasChildQuery(null, query, ScoreMode.None)); assertEquals("[has_child] requires 'type' field", e.getMessage()); - e = expectThrows(IllegalArgumentException.class, () -> QueryBuilders.hasChildQuery("foo", null, ScoreMode.None)); + e = expectThrows(IllegalArgumentException.class, () -> hasChildQuery("foo", null, ScoreMode.None)); assertEquals("[has_child] requires 'query' field", e.getMessage()); - e = expectThrows(IllegalArgumentException.class, () -> QueryBuilders.hasChildQuery("foo", query, null)); + e = expectThrows(IllegalArgumentException.class, () -> hasChildQuery("foo", query, null)); assertEquals("[has_child] requires 'score_mode' field", e.getMessage()); int positiveValue = randomIntBetween(0, Integer.MAX_VALUE); - HasChildQueryBuilder foo = QueryBuilders.hasChildQuery("foo", query, ScoreMode.None); // all good + HasChildQueryBuilder foo = hasChildQuery("foo", query, ScoreMode.None); // all good e = expectThrows(IllegalArgumentException.class, () -> foo.minMaxChildren(randomIntBetween(Integer.MIN_VALUE, -1), positiveValue)); assertEquals("[has_child] requires non-negative 'min_children' field", e.getMessage()); @@ -243,7 +260,7 @@ public void testToQueryInnerQueryType() throws IOException { String[] searchTypes = new String[]{PARENT_TYPE}; QueryShardContext shardContext = createShardContext(); shardContext.setTypes(searchTypes); - HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(CHILD_TYPE, new IdsQueryBuilder().addIds("id"), ScoreMode.None); + HasChildQueryBuilder hasChildQueryBuilder = hasChildQuery(CHILD_TYPE, new IdsQueryBuilder().addIds("id"), ScoreMode.None); Query query = hasChildQueryBuilder.toQuery(shardContext); //verify that the context types are still the same as the ones we previously set assertThat(shardContext.getTypes(), equalTo(searchTypes)); @@ -270,7 +287,8 @@ static void assertLateParsingQuery(Query query, String type, String id) throws I assertThat(booleanTermsQuery.clauses().get(0).getQuery(), instanceOf(TermQuery.class)); TermQuery termQuery = (TermQuery) booleanTermsQuery.clauses().get(0).getQuery(); assertThat(termQuery.getTerm().field(), equalTo(UidFieldMapper.NAME)); - //we want to make sure that the inner ids query gets executed against the child type rather than the main type we initially set to the context + //we want to make sure that the inner ids query gets executed against the child type rather + // than the main type we initially set to the context BytesRef[] ids = Uid.createUidsForTypesAndIds(Collections.singletonList(type), Collections.singletonList(id)); assertThat(termQuery.getTerm().bytes(), equalTo(ids[0])); //check the type filter @@ -291,54 +309,13 @@ public void testMustRewrite() throws IOException { public void testNonDefaultSimilarity() throws Exception { QueryShardContext shardContext = createShardContext(); - HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(CHILD_TYPE, new TermQueryBuilder("custom_string", "value"), ScoreMode.None); + HasChildQueryBuilder hasChildQueryBuilder = + hasChildQuery(CHILD_TYPE, new TermQueryBuilder("custom_string", "value"), ScoreMode.None); HasChildQueryBuilder.LateParsingQuery query = (HasChildQueryBuilder.LateParsingQuery) hasChildQueryBuilder.toQuery(shardContext); Similarity expected = SimilarityService.BUILT_IN.get(similarity).apply(similarity, Settings.EMPTY).get(); assertThat(((PerFieldSimilarityWrapper) query.getSimilarity()).get("custom_string"), instanceOf(expected.getClass())); } - public void testMinFromString() { - assertThat("fromString(min) != MIN", ScoreMode.Min, equalTo(HasChildQueryBuilder.parseScoreMode("min"))); - assertThat("min", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Min))); - } - - public void testMaxFromString() { - assertThat("fromString(max) != MAX", ScoreMode.Max, equalTo(HasChildQueryBuilder.parseScoreMode("max"))); - assertThat("max", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Max))); - } - - public void testAvgFromString() { - assertThat("fromString(avg) != AVG", ScoreMode.Avg, equalTo(HasChildQueryBuilder.parseScoreMode("avg"))); - assertThat("avg", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Avg))); - } - - public void testSumFromString() { - assertThat("fromString(total) != SUM", ScoreMode.Total, equalTo(HasChildQueryBuilder.parseScoreMode("sum"))); - assertThat("sum", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.Total))); - } - - public void testNoneFromString() { - assertThat("fromString(none) != NONE", ScoreMode.None, equalTo(HasChildQueryBuilder.parseScoreMode("none"))); - assertThat("none", equalTo(HasChildQueryBuilder.scoreModeAsString(ScoreMode.None))); - } - - /** - * Should throw {@link IllegalArgumentException} instead of NPE. - */ - public void testThatNullFromStringThrowsException() { - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> HasChildQueryBuilder.parseScoreMode(null)); - assertEquals("No score mode for child query [null] found", e.getMessage()); - } - - /** - * Failure should not change (and the value should never match anything...). - */ - public void testThatUnrecognizedFromStringThrowsException() { - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> HasChildQueryBuilder.parseScoreMode("unrecognized value")); - assertEquals("No score mode for child query [unrecognized value] found", e.getMessage()); - } - public void testIgnoreUnmapped() throws IOException { final HasChildQueryBuilder queryBuilder = new HasChildQueryBuilder("unmapped", new MatchAllQueryBuilder(), ScoreMode.None); queryBuilder.ignoreUnmapped(true); diff --git a/core/src/test/java/org/elasticsearch/index/query/HasParentQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java similarity index 90% rename from core/src/test/java/org/elasticsearch/index/query/HasParentQueryBuilderTests.java rename to modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java index 8f44b849481c4..c23aa33408ae6 100644 --- a/core/src/test/java/org/elasticsearch/index/query/HasParentQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.index.query; +package org.elasticsearch.join.query; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; @@ -30,6 +30,16 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.query.IdsQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.index.query.WrapperQueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.fetch.subphase.InnerHitsContext; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; @@ -37,10 +47,13 @@ import org.elasticsearch.test.AbstractQueryTestCase; import java.io.IOException; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -52,6 +65,11 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase> getPlugins() { + return Collections.singletonList(ParentJoinPlugin.class); + } + @Override protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE, @@ -82,7 +100,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws */ @Override protected HasParentQueryBuilder doCreateTestQueryBuilder() { - QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random()); + QueryBuilder innerQueryBuilder = new MatchAllQueryBuilder(); if (randomBoolean()) { requiresRewrite = true; innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString()); @@ -127,17 +145,17 @@ protected void doAssertLuceneQuery(HasParentQueryBuilder queryBuilder, Query que } public void testIllegalValues() throws IOException { - QueryBuilder query = RandomQueryBuilder.createQuery(random()); + QueryBuilder query = new MatchAllQueryBuilder(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> QueryBuilders.hasParentQuery(null, query, false)); + () -> hasParentQuery(null, query, false)); assertThat(e.getMessage(), equalTo("[has_parent] requires 'type' field")); e = expectThrows(IllegalArgumentException.class, - () -> QueryBuilders.hasParentQuery("foo", null, false)); + () -> hasParentQuery("foo", null, false)); assertThat(e.getMessage(), equalTo("[has_parent] requires 'query' field")); QueryShardContext context = createShardContext(); - HasParentQueryBuilder qb = QueryBuilders.hasParentQuery("just_a_type", new MatchAllQueryBuilder(), false); + HasParentQueryBuilder qb = hasParentQuery("just_a_type", new MatchAllQueryBuilder(), false); QueryShardException qse = expectThrows(QueryShardException.class, () -> qb.doToQuery(context)); assertThat(qse.getMessage(), equalTo("[has_parent] no child types found for type [just_a_type]")); } diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java new file mode 100644 index 0000000000000..ad8e49e9f5d31 --- /dev/null +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/InnerHitsIT.java @@ -0,0 +1,568 @@ +/* + * 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.join.query; + +import org.apache.lucene.search.join.ScoreMode; +import org.apache.lucene.util.ArrayUtil; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.InnerHitBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.script.MockScriptEngine; +import org.elasticsearch.script.MockScriptPlugin; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.sort.FieldSortBuilder; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.test.ESIntegTestCase.Scope; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; +import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +@ClusterScope(scope = Scope.SUITE) +public class InnerHitsIT extends ESIntegTestCase { + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(ParentJoinPlugin.class, CustomScriptPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + + public static class CustomScriptPlugin extends MockScriptPlugin { + @Override + protected Map, Object>> pluginScripts() { + return Collections.singletonMap("5", script -> "5"); + } + } + + public void testSimpleParentChild() throws Exception { + assertAcked(prepareCreate("articles") + .setSettings("index.mapping.single_type", false) + .addMapping("article", "title", "type=text") + .addMapping("comment", "_parent", "type=article", "message", "type=text,fielddata=true") + ); + + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); + requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); + requests.add(client().prepareIndex("articles", "comment", "2").setParent("1").setSource("message", "fox ate rabbit x y z")); + requests.add(client().prepareIndex("articles", "comment", "3").setParent("1").setSource("message", "rabbit got away")); + requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); + requests.add(client().prepareIndex("articles", "comment", "4").setParent("2").setSource("message", "elephant captured")); + requests.add(client().prepareIndex("articles", "comment", "5").setParent("2").setSource("message", "mice squashed by elephant x")); + requests.add(client().prepareIndex("articles", "comment", "6").setParent("2").setSource("message", "elephant scared by mice x y")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false)) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("1")); + assertThat(response.getHits().getAt(0).getShard(), notNullValue()); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(2L)); + + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + assertThat(innerHits.getAt(1).getId(), equalTo("2")); + assertThat(innerHits.getAt(1).getType(), equalTo("comment")); + + response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false)) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("2")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(3L)); + + assertThat(innerHits.getAt(0).getId(), equalTo("4")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + assertThat(innerHits.getAt(1).getId(), equalTo("5")); + assertThat(innerHits.getAt(1).getType(), equalTo("comment")); + assertThat(innerHits.getAt(2).getId(), equalTo("6")); + assertThat(innerHits.getAt(2).getType(), equalTo("comment")); + + response = client().prepareSearch("articles") + .setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder() + .addDocValueField("message") + .setHighlightBuilder(new HighlightBuilder().field("message")) + .setExplain(true).setSize(1) + .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", + Collections.emptyMap())), + false) + ).get(); + assertNoFailures(response); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getHits().length, equalTo(1)); + assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); + assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(message:fox")); + assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); + assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); + } + + public void testRandomParentChild() throws Exception { + assertAcked(prepareCreate("idx") + .setSettings("index.mapping.single_type", false) + .addMapping("parent") + .addMapping("child1", "_parent", "type=parent") + .addMapping("child2", "_parent", "type=parent") + ); + int numDocs = scaledRandomIntBetween(5, 50); + List requestBuilders = new ArrayList<>(); + + int child1 = 0; + int child2 = 0; + int[] child1InnerObjects = new int[numDocs]; + int[] child2InnerObjects = new int[numDocs]; + for (int parent = 0; parent < numDocs; parent++) { + String parentId = String.format(Locale.ENGLISH, "%03d", parent); + requestBuilders.add(client().prepareIndex("idx", "parent", parentId).setSource("{}", XContentType.JSON)); + + int numChildDocs = child1InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); + int limit = child1 + numChildDocs; + for (; child1 < limit; child1++) { + requestBuilders.add(client().prepareIndex("idx", "child1", + String.format(Locale.ENGLISH, "%04d", child1)).setParent(parentId).setSource("{}", XContentType.JSON)); + } + numChildDocs = child2InnerObjects[parent] = scaledRandomIntBetween(1, numDocs); + limit = child2 + numChildDocs; + for (; child2 < limit; child2++) { + requestBuilders.add(client().prepareIndex("idx", "child2", + String.format(Locale.ENGLISH, "%04d", child2)).setParent(parentId).setSource("{}", XContentType.JSON)); + } + } + indexRandom(true, requestBuilders); + + int size = randomIntBetween(0, numDocs); + BoolQueryBuilder boolQuery = new BoolQueryBuilder(); + boolQuery.should(constantScoreQuery(hasChildQuery("child1", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("a") + .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); + boolQuery.should(constantScoreQuery(hasChildQuery("child2", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("b") + .addSort(new FieldSortBuilder("_uid").order(SortOrder.ASC)).setSize(size), false))); + SearchResponse searchResponse = client().prepareSearch("idx") + .setSize(numDocs) + .setTypes("parent") + .addSort("_uid", SortOrder.ASC) + .setQuery(boolQuery) + .get(); + + assertNoFailures(searchResponse); + assertHitCount(searchResponse, numDocs); + assertThat(searchResponse.getHits().getHits().length, equalTo(numDocs)); + + int offset1 = 0; + int offset2 = 0; + for (int parent = 0; parent < numDocs; parent++) { + SearchHit searchHit = searchResponse.getHits().getAt(parent); + assertThat(searchHit.getType(), equalTo("parent")); + assertThat(searchHit.getId(), equalTo(String.format(Locale.ENGLISH, "%03d", parent))); + assertThat(searchHit.getShard(), notNullValue()); + + SearchHits inner = searchHit.getInnerHits().get("a"); + assertThat(inner.getTotalHits(), equalTo((long) child1InnerObjects[parent])); + for (int child = 0; child < child1InnerObjects[parent] && child < size; child++) { + SearchHit innerHit = inner.getAt(child); + assertThat(innerHit.getType(), equalTo("child1")); + String childId = String.format(Locale.ENGLISH, "%04d", offset1 + child); + assertThat(innerHit.getId(), equalTo(childId)); + assertThat(innerHit.getNestedIdentity(), nullValue()); + } + offset1 += child1InnerObjects[parent]; + + inner = searchHit.getInnerHits().get("b"); + assertThat(inner.getTotalHits(), equalTo((long) child2InnerObjects[parent])); + for (int child = 0; child < child2InnerObjects[parent] && child < size; child++) { + SearchHit innerHit = inner.getAt(child); + assertThat(innerHit.getType(), equalTo("child2")); + String childId = String.format(Locale.ENGLISH, "%04d", offset2 + child); + assertThat(innerHit.getId(), equalTo(childId)); + assertThat(innerHit.getNestedIdentity(), nullValue()); + } + offset2 += child2InnerObjects[parent]; + } + } + + public void testInnerHitsOnHasParent() throws Exception { + assertAcked(prepareCreate("stack") + .setSettings("index.mapping.single_type", false) + .addMapping("question", "body", "type=text") + .addMapping("answer", "_parent", "type=question", "body", "type=text") + ); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("stack", "question", "1").setSource("body", "I'm using HTTPS + Basic authentication " + + "to protect a resource. How can I throttle authentication attempts to protect against brute force attacks?")); + requests.add(client().prepareIndex("stack", "answer", "1").setParent("1").setSource("body", + "install fail2ban and enable rules for apache")); + requests.add(client().prepareIndex("stack", "question", "2").setSource("body", + "I have firewall rules set up and also denyhosts installed.\\ndo I also need to install fail2ban?")); + requests.add(client().prepareIndex("stack", "answer", "2").setParent("2").setSource("body", + "Denyhosts protects only ssh; Fail2Ban protects all daemons.")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("stack") + .setTypes("answer") + .addSort("_uid", SortOrder.ASC) + .setQuery( + boolQuery() + .must(matchQuery("body", "fail2ban")) + .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder(), false)) + ).get(); + assertNoFailures(response); + assertHitCount(response, 2); + + SearchHit searchHit = response.getHits().getAt(0); + assertThat(searchHit.getId(), equalTo("1")); + assertThat(searchHit.getType(), equalTo("answer")); + assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("1")); + + searchHit = response.getHits().getAt(1); + assertThat(searchHit.getId(), equalTo("2")); + assertThat(searchHit.getType(), equalTo("answer")); + assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1L)); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question")); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("2")); + } + + public void testParentChildMultipleLayers() throws Exception { + assertAcked(prepareCreate("articles") + .setSettings("index.mapping.single_type", false) + .addMapping("article", "title", "type=text") + .addMapping("comment", "_parent", "type=article", "message", "type=text") + .addMapping("remark", "_parent", "type=comment", "message", "type=text") + ); + + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("articles", "article", "1").setSource("title", "quick brown fox")); + requests.add(client().prepareIndex("articles", "comment", "1").setParent("1").setSource("message", "fox eat quick")); + requests.add(client().prepareIndex("articles", "remark", "1").setParent("1").setRouting("1").setSource("message", "good")); + requests.add(client().prepareIndex("articles", "article", "2").setSource("title", "big gray elephant")); + requests.add(client().prepareIndex("articles", "comment", "2").setParent("2").setSource("message", "elephant captured")); + requests.add(client().prepareIndex("articles", "remark", "2").setParent("2").setRouting("2").setSource("message", "bad")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", + hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), + ScoreMode.None).innerHit(new InnerHitBuilder(), false)) + .get(); + + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("1")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + + innerHits = innerHits.getAt(0).getInnerHits().get("remark"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("1")); + assertThat(innerHits.getAt(0).getType(), equalTo("remark")); + + response = client().prepareSearch("articles") + .setQuery(hasChildQuery("comment", + hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder(), false), + ScoreMode.None).innerHit(new InnerHitBuilder(), false)) + .get(); + + assertNoFailures(response); + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("2")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("2")); + assertThat(innerHits.getAt(0).getType(), equalTo("comment")); + + innerHits = innerHits.getAt(0).getInnerHits().get("remark"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("2")); + assertThat(innerHits.getAt(0).getType(), equalTo("remark")); + } + + public void testRoyals() throws Exception { + assertAcked( + prepareCreate("royals") + .setSettings("index.mapping.single_type", false) + .addMapping("king") + .addMapping("prince", "_parent", "type=king") + .addMapping("duke", "_parent", "type=prince") + .addMapping("earl", "_parent", "type=duke") + .addMapping("baron", "_parent", "type=earl") + ); + + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("royals", "king", "king").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "prince", "prince").setParent("king").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "duke", "duke").setParent("prince").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl1").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl2").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl3").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "earl", "earl4").setParent("duke").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron1").setParent("earl1").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron2").setParent("earl2").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron3").setParent("earl3").setRouting("king") + .setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("royals", "baron", "baron4").setParent("earl4").setRouting("king") + .setSource("{}", XContentType.JSON)); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("royals") + .setTypes("duke") + .setQuery(boolQuery() + .filter(hasParentQuery("prince", + hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings"), false), + false).innerHit(new InnerHitBuilder().setName("princes"), false) + ) + .filter(hasChildQuery("earl", + hasChildQuery("baron", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder().setName("barons"), false), + ScoreMode.None).innerHit(new InnerHitBuilder() + .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC)) + .setName("earls") + .setSize(4), false) + ) + ) + .get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); + + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("earls"); + assertThat(innerHits.getTotalHits(), equalTo(4L)); + assertThat(innerHits.getAt(0).getId(), equalTo("earl1")); + assertThat(innerHits.getAt(1).getId(), equalTo("earl2")); + assertThat(innerHits.getAt(2).getId(), equalTo("earl3")); + assertThat(innerHits.getAt(3).getId(), equalTo("earl4")); + + SearchHits innerInnerHits = innerHits.getAt(0).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron1")); + + innerInnerHits = innerHits.getAt(1).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron2")); + + innerInnerHits = innerHits.getAt(2).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron3")); + + innerInnerHits = innerHits.getAt(3).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron4")); + + innerHits = response.getHits().getAt(0).getInnerHits().get("princes"); + assertThat(innerHits.getTotalHits(), equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("prince")); + + innerInnerHits = innerHits.getAt(0).getInnerHits().get("kings"); + assertThat(innerInnerHits.getTotalHits(), equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); + } + + public void testMatchesQueriesParentChildInnerHits() throws Exception { + assertAcked(prepareCreate("index") + .setSettings("index.mapping.single_type", false) + .addMapping("child", "_parent", "type=parent")); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("index", "parent", "1").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("index", "child", "1").setParent("1").setSource("field", "value1")); + requests.add(client().prepareIndex("index", "child", "2").setParent("1").setSource("field", "value2")); + requests.add(client().prepareIndex("index", "parent", "2").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("index", "child", "3").setParent("2").setSource("field", "value1")); + indexRandom(true, requests); + + SearchResponse response = client().prepareSearch("index") + .setQuery(hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false)) + .addSort("_uid", SortOrder.ASC) + .get(); + assertHitCount(response, 2); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + assertThat(response.getHits().getAt(1).getId(), equalTo("2")); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + QueryBuilder query = hasChildQuery("child", matchQuery("field", "value2").queryName("_name2"), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false); + response = client().prepareSearch("index") + .setQuery(query) + .addSort("_uid", SortOrder.ASC) + .get(); + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); + } + + public void testDontExplode() throws Exception { + assertAcked(prepareCreate("index1") + .setSettings("index.mapping.single_type", false) + .addMapping("child", "_parent", "type=parent")); + List requests = new ArrayList<>(); + requests.add(client().prepareIndex("index1", "parent", "1").setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex("index1", "child", "1").setParent("1").setSource("field", "value1")); + indexRandom(true, requests); + + QueryBuilder query = hasChildQuery("child", matchQuery("field", "value1"), ScoreMode.None) + .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); + SearchResponse response = client().prepareSearch("index1") + .setQuery(query) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + + assertAcked(prepareCreate("index2").addMapping("type", "nested", "type=nested")); + client().prepareIndex("index2", "type", "1").setSource(jsonBuilder().startObject() + .startArray("nested") + .startObject() + .field("field", "value1") + .endObject() + .endArray() + .endObject()) + .setRefreshPolicy(IMMEDIATE) + .get(); + + query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg) + .innerHit(new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1), false); + response = client().prepareSearch("index2") + .setQuery(query) + .get(); + assertNoFailures(response); + assertHitCount(response, 1); + } + + public void testNestedInnerHitWrappedInParentChildInnerhit() throws Exception { + assertAcked(prepareCreate("test") + .setSettings("index.mapping.single_type", false) + .addMapping("child_type", "_parent", "type=parent_type", "nested_type", "type=nested")); + client().prepareIndex("test", "parent_type", "1").setSource("key", "value").get(); + client().prepareIndex("test", "child_type", "2").setParent("1").setSource("nested_type", Collections.singletonMap("key", "value")) + .get(); + refresh(); + SearchResponse response = client().prepareSearch("test") + .setQuery(boolQuery().must(matchQuery("key", "value")) + .should(hasChildQuery("child_type", nestedQuery("nested_type", matchAllQuery(), ScoreMode.None) + .innerHit(new InnerHitBuilder(), false), ScoreMode.None).innerHit(new InnerHitBuilder(), false))) + .get(); + assertHitCount(response, 1); + SearchHit hit = response.getHits().getAt(0); + assertThat(hit.getInnerHits().get("child_type").getAt(0).field("_parent").getValue(), equalTo("1")); + assertThat(hit.getInnerHits().get("child_type").getAt(0).getInnerHits().get("nested_type").getAt(0).field("_parent"), nullValue()); + } + + public void testInnerHitsWithIgnoreUnmapped() throws Exception { + assertAcked(prepareCreate("index1") + .setSettings("index.mapping.single_type", false) + .addMapping("parent_type", "nested_type", "type=nested") + .addMapping("child_type", "_parent", "type=parent_type") + ); + createIndex("index2"); + client().prepareIndex("index1", "parent_type", "1").setSource("nested_type", Collections.singletonMap("key", "value")).get(); + client().prepareIndex("index1", "child_type", "2").setParent("1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("index2", "type", "3").setSource("key", "value").get(); + refresh(); + + SearchResponse response = client().prepareSearch("index1", "index2") + .setQuery(boolQuery() + .should(hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder(), true)) + .should(termQuery("key", "value")) + ) + .get(); + assertNoFailures(response); + assertHitCount(response, 2); + assertSearchHits(response, "1", "3"); + } +} diff --git a/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml b/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml new file mode 100644 index 0000000000000..f5a5808012914 --- /dev/null +++ b/modules/parent-join/src/test/resources/rest-api-spec/test/10_basic.yaml @@ -0,0 +1,48 @@ +setup: + - do: + indices.create: + index: test + body: + settings: + mapping.single_type: false + mappings: + type_2: {} + type_3: + _parent: + type: type_2 + +--- +"Parent/child inner hits": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + + - do: + index: + index: test + type: type_2 + id: 1 + body: {"foo": "bar"} + + - do: + index: + index: test + type: type_3 + id: 1 + parent: 1 + body: {"bar": "baz"} + + - do: + indices.refresh: {} + + - do: + search: + body: { "query" : { "has_child" : { "type" : "type_3", "query" : { "match_all" : {} }, "inner_hits" : {} } } } + - match: { hits.total: 1 } + - match: { hits.hits.0._index: "test" } + - match: { hits.hits.0._type: "type_2" } + - match: { hits.hits.0._id: "1" } + - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._index + - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._type: "type_3" } + - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._id: "1" } + - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._nested diff --git a/modules/percolator/build.gradle b/modules/percolator/build.gradle index 60fb82bdf4e32..cf55368861aef 100644 --- a/modules/percolator/build.gradle +++ b/modules/percolator/build.gradle @@ -23,5 +23,9 @@ esplugin { hasClientJar = true } +dependencies { + // for testing hasChild and hasParent rejections + testCompile project(path: ':modules:parent-join', configuration: 'runtime') +} compileJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes" compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes" diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java index 07677c25e114e..caffe81cbd9bd 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java @@ -57,8 +57,6 @@ import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; import org.elasticsearch.index.query.DisMaxQueryBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; @@ -373,15 +371,16 @@ protected String contentType() { return CONTENT_TYPE; } + /** * Fails if a percolator contains an unsupported query. The following queries are not supported: * 1) a has_child query * 2) a has_parent query */ static void verifyQuery(QueryBuilder queryBuilder) { - if (queryBuilder instanceof HasChildQueryBuilder) { + if (queryBuilder.getName().equals("has_child")) { throw new IllegalArgumentException("the [has_child] query is unsupported inside a percolator query"); - } else if (queryBuilder instanceof HasParentQueryBuilder) { + } else if (queryBuilder.getName().equals("has_parent")) { throw new IllegalArgumentException("the [has_parent] query is unsupported inside a percolator query"); } else if (queryBuilder instanceof BoolQueryBuilder) { BoolQueryBuilder boolQueryBuilder = (BoolQueryBuilder) queryBuilder; diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java index 5836ed857fdce..b5fb1cf560b26 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java @@ -56,8 +56,6 @@ import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; import org.elasticsearch.index.query.DisMaxQueryBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.HasParentQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; @@ -66,6 +64,9 @@ import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder; import org.elasticsearch.indices.TermsLookup; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.join.query.HasChildQueryBuilder; +import org.elasticsearch.join.query.HasParentQueryBuilder; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; @@ -110,7 +111,7 @@ public class PercolatorFieldMapperTests extends ESSingleNodeTestCase { @Override protected Collection> getPlugins() { - return pluginList(InternalSettingsPlugin.class, PercolatorPlugin.class, FoolMeScriptPlugin.class); + return pluginList(InternalSettingsPlugin.class, PercolatorPlugin.class, FoolMeScriptPlugin.class, ParentJoinPlugin.class); } @Before diff --git a/modules/reindex/build.gradle b/modules/reindex/build.gradle index 20a8d6f0c5a3b..2e71fad28e111 100644 --- a/modules/reindex/build.gradle +++ b/modules/reindex/build.gradle @@ -38,6 +38,8 @@ dependencies { // for http - testing reindex from remote testCompile project(path: ':modules:transport-netty3', configuration: 'runtime') testCompile project(path: ':modules:transport-netty4', configuration: 'runtime') + // for parent/child testing + testCompile project(path: ':modules:parent-join', configuration: 'runtime') } dependencyLicenses { diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java index 7bcddee581b0e..fc3e02ef91e01 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexParentChildTests.java @@ -21,9 +21,16 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.join.ParentJoinPlugin; +import org.elasticsearch.plugins.Plugin; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; import static org.elasticsearch.index.query.QueryBuilders.idsQuery; +import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.hamcrest.Matchers.containsString; @@ -39,6 +46,23 @@ public class ReindexParentChildTests extends ReindexTestCase { QueryBuilder findsCity; QueryBuilder findsNeighborhood; + @Override + protected boolean ignoreExternalCluster() { + return true; + } + + @Override + protected Collection> nodePlugins() { + final List> plugins = new ArrayList<>(super.nodePlugins()); + plugins.add(ParentJoinPlugin.class); + return Collections.unmodifiableList(plugins); + } + + @Override + protected Collection> transportClientPlugins() { + return nodePlugins(); + } + public void testParentChild() throws Exception { createParentChildIndex("source"); createParentChildIndex("dest"); diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml index 8690d329d3877..88a5327ebf142 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/90_remote.yaml @@ -158,82 +158,6 @@ metric: search - match: {indices.source.total.search.open_contexts: 0} ---- -"Reindex from remote with parent/child": - - do: - indices.create: - index: source - body: - mappings: - foo: {} - bar: - _parent: - type: foo - - do: - indices.create: - index: dest - body: - mappings: - foo: {} - bar: - _parent: - type: foo - - do: - index: - index: source - type: foo - id: 1 - body: { "text": "test" } - - do: - index: - index: source - type: bar - id: 1 - parent: 1 - body: { "text": "test2" } - - do: - indices.refresh: {} - - # Fetch the http host. We use the host of the master because we know there will always be a master. - - do: - cluster.state: {} - - set: { master_node: master } - - do: - nodes.info: - metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} - - do: - reindex: - refresh: true - body: - source: - remote: - host: http://${host} - index: source - dest: - index: dest - - match: {created: 2} - - - do: - search: - index: dest - body: - query: - has_parent: - parent_type: foo - query: - match: - text: test - - match: {hits.total: 1} - - # Make sure reindex closed all the scroll contexts - - do: - indices.stats: - index: source - metric: search - - match: {indices.source.total.search.open_contexts: 0} - --- "Reindex from remote with timeouts": # Validates that you can configure the socket_timeout and connect_timeout, diff --git a/qa/smoke-test-reindex-with-painless/build.gradle b/qa/smoke-test-reindex-with-all-modules/build.gradle similarity index 89% rename from qa/smoke-test-reindex-with-painless/build.gradle rename to qa/smoke-test-reindex-with-all-modules/build.gradle index b32f4ee80bef9..cab01cb9412ec 100644 --- a/qa/smoke-test-reindex-with-painless/build.gradle +++ b/qa/smoke-test-reindex-with-all-modules/build.gradle @@ -22,4 +22,6 @@ apply plugin: 'elasticsearch.rest-test' integTestCluster { setting 'script.max_compilations_per_minute', '1000' -} + // Whitelist reindexing from the local node so we can test it. + setting 'reindex.remote.whitelist', '127.0.0.1:*' +} \ No newline at end of file diff --git a/qa/smoke-test-reindex-with-painless/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java b/qa/smoke-test-reindex-with-all-modules/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java rename to qa/smoke-test-reindex-with-all-modules/src/test/java/org/elasticsearch/smoketest/SmokeTestReindexWithPainlessClientYamlTestSuiteIT.java diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/10_script.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/10_script.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/20_broken.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/30_timeout.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/40_search_failures.yaml diff --git a/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml new file mode 100644 index 0000000000000..81e142c9195c1 --- /dev/null +++ b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/reindex/50_reindex_with_parentchild.yaml @@ -0,0 +1,79 @@ +--- +"Reindex from remote with parent/child": + - do: + indices.create: + index: source + body: + settings: + mapping.single_type: false + mappings: + foo: {} + bar: + _parent: + type: foo + - do: + indices.create: + index: dest + body: + settings: + mapping.single_type: false + mappings: + foo: {} + bar: + _parent: + type: foo + - do: + index: + index: source + type: foo + id: 1 + body: { "text": "test" } + - do: + index: + index: source + type: bar + id: 1 + parent: 1 + body: { "text": "test2" } + - do: + indices.refresh: {} + + # Fetch the http host. We use the host of the master because we know there will always be a master. + - do: + cluster.state: {} + - set: { master_node: master } + - do: + nodes.info: + metric: [ http ] + - is_true: nodes.$master.http.publish_address + - set: {nodes.$master.http.publish_address: host} + - do: + reindex: + refresh: true + body: + source: + remote: + host: http://${host} + index: source + dest: + index: dest + - match: {created: 2} + + - do: + search: + index: dest + body: + query: + has_parent: + parent_type: foo + query: + match: + text: test + - match: {hits.total: 1} + + # Make sure reindex closed all the scroll contexts + - do: + indices.stats: + index: source + metric: search + - match: {indices.source.total.search.open_contexts: 0} diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/10_script.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/20_broken.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/30_timeout.yaml diff --git a/qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml b/qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml similarity index 100% rename from qa/smoke-test-reindex-with-painless/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml rename to qa/smoke-test-reindex-with-all-modules/src/test/resources/rest-api-spec/test/update_by_query/40_search_failure.yaml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml index 100b44dcb0479..034a13ae72255 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yaml @@ -9,10 +9,6 @@ setup: properties: nested_field: type: nested - type_2: {} - type_3: - _parent: - type: type_2 --- "Nested inner hits": @@ -41,34 +37,3 @@ setup: - match: { hits.hits.0.inner_hits.nested_field.hits.hits.0._nested.offset: 0 } - is_false: hits.hits.0.inner_hits.nested_field.hits.hits.0._nested.child ---- -"Parent/child inner hits": - - do: - index: - index: test - type: type_2 - id: 1 - body: {"foo": "bar"} - - - do: - index: - index: test - type: type_3 - id: 1 - parent: 1 - body: {"bar": "baz"} - - - do: - indices.refresh: {} - - - do: - search: - body: { "query" : { "has_child" : { "type" : "type_3", "query" : { "match_all" : {} }, "inner_hits" : {} } } } - - match: { hits.total: 1 } - - match: { hits.hits.0._index: "test" } - - match: { hits.hits.0._type: "type_2" } - - match: { hits.hits.0._id: "1" } - - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._index - - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._type: "type_3" } - - match: { hits.hits.0.inner_hits.type_3.hits.hits.0._id: "1" } - - is_false: hits.hits.0.inner_hits.type_3.hits.hits.0._nested diff --git a/settings.gradle b/settings.gradle index d697cd5e0011f..afdfb26760aa2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -36,6 +36,7 @@ List projects = [ 'modules:transport-netty4', 'modules:reindex', 'modules:percolator', + 'modules:parent-join', 'plugins:analysis-icu', 'plugins:analysis-kuromoji', 'plugins:analysis-phonetic', @@ -73,7 +74,7 @@ List projects = [ 'qa:smoke-test-ingest-disabled', 'qa:smoke-test-multinode', 'qa:smoke-test-plugins', - 'qa:smoke-test-reindex-with-painless', + 'qa:smoke-test-reindex-with-all-modules', 'qa:smoke-test-tribe-node', 'qa:vagrant', 'qa:wildfly' diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java similarity index 100% rename from core/src/test/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java rename to test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java similarity index 95% rename from core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java rename to test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java index c76d1a5f0ddc5..e27199918f412 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java @@ -33,6 +33,9 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.indices.IndicesModule; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.PluginsService; +import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.elasticsearch.test.AbstractQueryTestCase; @@ -40,6 +43,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -66,6 +70,10 @@ protected String[] getCurrentTypes() { private NamedXContentRegistry xContentRegistry; protected abstract AB createTestAggregatorBuilder(); + protected Collection> getPlugins() { + return Collections.emptyList(); + } + /** * Setup for the whole base test class. */ @@ -77,7 +85,8 @@ public void setUp() throws Exception { .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .build(); IndicesModule indicesModule = new IndicesModule(Collections.emptyList()); - SearchModule searchModule = new SearchModule(settings, false, emptyList()); + PluginsService pluginsService = new PluginsService(settings, null, null, getPlugins()); + SearchModule searchModule = new SearchModule(settings, false, pluginsService.filterPlugins(SearchPlugin.class)); List entries = new ArrayList<>(); entries.addAll(indicesModule.getNamedWriteables()); entries.addAll(searchModule.getNamedWriteables()); @@ -145,7 +154,6 @@ public void testSerialization() throws IOException { } } - public void testEqualsAndHashcode() throws IOException { // TODO we only change name and boost, we should extend by any sub-test supplying a "mutate" method that randomly changes one // aspect of the object under test