diff options
author | Martijn van Groningen <martijn.v.groningen@gmail.com> | 2017-06-02 14:34:20 +0200 |
---|---|---|
committer | Martijn van Groningen <martijn.v.groningen@gmail.com> | 2017-06-02 23:27:16 +0200 |
commit | 2a71a7bffc7ad1262cdf84554277653858c3b7f5 (patch) | |
tree | 8f10bfd683b66699a0a91aa1f7e869436d4eceb9 /modules/parent-join | |
parent | a32d1b91fa44c005ea41fa17323ea6bc65352092 (diff) |
Change `has_child`, `has_parent` queries and `childen` aggregation to work with the new join field type and
at the same time maintaining support for the `_parent` meta field type.
Relates to #20257
Diffstat (limited to 'modules/parent-join')
15 files changed, 1295 insertions, 754 deletions
diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java index 35dc4eacbf..3560736e44 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java @@ -30,6 +30,8 @@ import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.ParentFieldMapper; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.join.mapper.ParentIdFieldMapper; +import org.elasticsearch.join.mapper.ParentJoinFieldMapper; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.FieldContext; @@ -92,8 +94,30 @@ public class ChildrenAggregationBuilder @Override protected ValuesSourceConfig<WithOrdinals> resolveConfig(SearchContext context) { ValuesSourceConfig<WithOrdinals> config = new ValuesSourceConfig<>(ValuesSourceType.BYTES); - DocumentMapper childDocMapper = context.mapperService().documentMapper(childType); + if (context.mapperService().getIndexSettings().isSingleType()) { + joinFieldResolveConfig(context, config); + } else { + parentFieldResolveConfig(context, config); + } + return config; + } + + private void joinFieldResolveConfig(SearchContext context, ValuesSourceConfig<WithOrdinals> config) { + ParentJoinFieldMapper parentJoinFieldMapper = ParentJoinFieldMapper.getMapper(context.mapperService()); + ParentIdFieldMapper parentIdFieldMapper = parentJoinFieldMapper.getParentIdFieldMapper(childType, false); + if (parentIdFieldMapper != null) { + parentFilter = parentIdFieldMapper.getParentFilter(); + childFilter = parentIdFieldMapper.getChildFilter(childType); + MappedFieldType fieldType = parentIdFieldMapper.fieldType(); + final SortedSetDVOrdinalsIndexFieldData fieldData = context.fieldData().getForField(fieldType); + config.fieldContext(new FieldContext(fieldType.name(), fieldData, fieldType)); + } else { + config.unmapped(true); + } + } + private void parentFieldResolveConfig(SearchContext context, ValuesSourceConfig<WithOrdinals> config) { + DocumentMapper childDocMapper = context.mapperService().documentMapper(childType); if (childDocMapper != null) { ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper(); if (!parentFieldMapper.active()) { @@ -107,14 +131,13 @@ public class ChildrenAggregationBuilder MappedFieldType parentFieldType = parentDocMapper.parentFieldMapper().getParentJoinFieldType(); final SortedSetDVOrdinalsIndexFieldData fieldData = context.fieldData().getForField(parentFieldType); config.fieldContext(new FieldContext(parentFieldType.name(), fieldData, - parentFieldType)); + parentFieldType)); } else { config.unmapped(true); } } else { config.unmapped(true); } - return config; } @Override diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java index 43608e9b54..db9fae9b47 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java @@ -40,7 +40,7 @@ import java.util.List; * This class is also used to quickly retrieve the parent-join field defined in a mapping without * specifying the name of the field. */ -class MetaJoinFieldMapper extends FieldMapper { +public class MetaJoinFieldMapper extends FieldMapper { static final String NAME = "_parent_join"; static final String CONTENT_TYPE = "parent_join"; @@ -68,8 +68,9 @@ class MetaJoinFieldMapper extends FieldMapper { } } - static final class MetaJoinFieldType extends StringFieldType { - ParentJoinFieldMapper mapper; + public static class MetaJoinFieldType extends StringFieldType { + + private ParentJoinFieldMapper mapper; MetaJoinFieldType() {} @@ -100,6 +101,10 @@ class MetaJoinFieldMapper extends FieldMapper { BytesRef binaryValue = (BytesRef) value; return binaryValue.utf8ToString(); } + + public ParentJoinFieldMapper getMapper() { + return mapper; + } } MetaJoinFieldMapper(String name, MappedFieldType fieldType, Settings indexSettings) { diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java index 7a042eae84..cc2815c373 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java @@ -23,6 +23,12 @@ import org.apache.lucene.document.Field; import org.apache.lucene.document.SortedDocValuesField; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.settings.Settings; @@ -47,7 +53,7 @@ public final class ParentIdFieldMapper extends FieldMapper { static final String CONTENT_TYPE = "parent"; static class Defaults { - public static final MappedFieldType FIELD_TYPE = new ParentIdFieldType(); + static final MappedFieldType FIELD_TYPE = new ParentIdFieldType(); static { FIELD_TYPE.setTokenized(false); @@ -86,7 +92,7 @@ public final class ParentIdFieldMapper extends FieldMapper { } public static final class ParentIdFieldType extends StringFieldType { - public ParentIdFieldType() { + ParentIdFieldType() { setIndexAnalyzer(Lucene.KEYWORD_ANALYZER); setSearchAnalyzer(Lucene.KEYWORD_ANALYZER); } @@ -145,6 +151,9 @@ public final class ParentIdFieldMapper extends FieldMapper { return parentName; } + public Query getParentFilter() { + return new TermQuery(new Term(name().substring(0, name().indexOf('#')), parentName)); + } /** * Returns the children names associated with this mapper. */ @@ -152,6 +161,18 @@ public final class ParentIdFieldMapper extends FieldMapper { return children; } + public Query getChildFilter(String type) { + return new TermQuery(new Term(name().substring(0, name().indexOf('#')), type)); + } + + public Query getChildrenFilter() { + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + for (String child : children) { + builder.add(getChildFilter(child), BooleanClause.Occur.SHOULD); + } + return new ConstantScoreQuery(builder.build()); + } + @Override protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException { if (context.externalValueSet() == false) { diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java index d9c12d85c1..ecabbb096e 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java @@ -81,7 +81,7 @@ public final class ParentJoinFieldMapper extends FieldMapper { public static ParentJoinFieldMapper getMapper(MapperService service) { MetaJoinFieldMapper.MetaJoinFieldType fieldType = (MetaJoinFieldMapper.MetaJoinFieldType) service.fullName(MetaJoinFieldMapper.NAME); - return fieldType == null ? null : fieldType.mapper; + return fieldType == null ? null : fieldType.getMapper(); } private static String getParentIdFieldName(String joinFieldName, String parentName) { @@ -121,11 +121,11 @@ public final class ParentJoinFieldMapper extends FieldMapper { } } - static class Builder extends FieldMapper.Builder<Builder, ParentJoinFieldMapper> { + public static class Builder extends FieldMapper.Builder<Builder, ParentJoinFieldMapper> { final List<ParentIdFieldMapper.Builder> parentIdFieldBuilders = new ArrayList<>(); boolean eagerGlobalOrdinals = true; - Builder(String name) { + public Builder(String name) { super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE); builder = this; } @@ -431,4 +431,5 @@ public final class ParentJoinFieldMapper extends FieldMapper { } } } + } diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java index f574236b35..6485b8f649 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasChildQueryBuilder.java @@ -49,6 +49,8 @@ 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 org.elasticsearch.join.mapper.ParentIdFieldMapper; +import org.elasticsearch.join.mapper.ParentJoinFieldMapper; import java.io.IOException; import java.util.HashMap; @@ -305,6 +307,34 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil @Override protected Query doToQuery(QueryShardContext context) throws IOException { + if (context.getIndexSettings().isSingleType()) { + return joinFieldDoToQuery(context); + } else { + return parentFieldDoToQuery(context); + } + } + + private Query joinFieldDoToQuery(QueryShardContext context) throws IOException { + ParentJoinFieldMapper joinFieldMapper = ParentJoinFieldMapper.getMapper(context.getMapperService()); + ParentIdFieldMapper parentIdFieldMapper = joinFieldMapper.getParentIdFieldMapper(type, false); + if (parentIdFieldMapper != null) { + Query parentFilter = parentIdFieldMapper.getParentFilter(); + Query childFilter = parentIdFieldMapper.getChildFilter(type); + Query innerQuery = Queries.filtered(query.toQuery(context), childFilter); + MappedFieldType fieldType = parentIdFieldMapper.fieldType(); + final SortedSetDVOrdinalsIndexFieldData fieldData = context.getForField(fieldType); + return new LateParsingQuery(parentFilter, innerQuery, minChildren(), maxChildren(), + fieldType.name(), scoreMode, fieldData, context.getSearchSimilarity()); + } else { + if (ignoreUnmapped) { + return new MatchNoDocsQuery(); + } else { + throw new QueryShardException(context, "[" + NAME + "] join field has no parent type configured"); + } + } + } + + private Query parentFieldDoToQuery(QueryShardContext context) throws IOException { Query innerQuery; final String[] previousTypes = context.getTypes(); context.setTypes(type); @@ -313,8 +343,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil } finally { context.setTypes(previousTypes); } - - DocumentMapper childDocMapper = context.documentMapper(type); + DocumentMapper childDocMapper = context.getMapperService().documentMapper(type); if (childDocMapper == null) { if (ignoreUnmapped) { return new MatchNoDocsQuery(); @@ -330,16 +359,17 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType); if (parentDocMapper == null) { throw new QueryShardException(context, - "[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]"); + "[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]"); } // wrap the query with type query innerQuery = Queries.filtered(innerQuery, childDocMapper.typeFilter(context)); + String joinField = ParentFieldMapper.joinField(parentType); final MappedFieldType parentFieldType = parentDocMapper.parentFieldMapper().getParentJoinFieldType(); final SortedSetDVOrdinalsIndexFieldData fieldData = context.getForField(parentFieldType); return new LateParsingQuery(parentDocMapper.typeFilter(context), innerQuery, minChildren(), maxChildren(), - parentType, scoreMode, fieldData, context.getSearchSimilarity()); + joinField, scoreMode, fieldData, context.getSearchSimilarity()); } /** @@ -358,19 +388,19 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil private final Query innerQuery; private final int minChildren; private final int maxChildren; - private final String parentType; + private final String joinField; private final ScoreMode scoreMode; private final SortedSetDVOrdinalsIndexFieldData fieldDataJoin; private final Similarity similarity; LateParsingQuery(Query toQuery, Query innerQuery, int minChildren, int maxChildren, - String parentType, ScoreMode scoreMode, + String joinField, ScoreMode scoreMode, SortedSetDVOrdinalsIndexFieldData fieldData, Similarity similarity) { this.toQuery = toQuery; this.innerQuery = innerQuery; this.minChildren = minChildren; this.maxChildren = maxChildren; - this.parentType = parentType; + this.joinField = joinField; this.scoreMode = scoreMode; this.fieldDataJoin = fieldData; this.similarity = similarity; @@ -383,7 +413,6 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil return rewritten; } if (reader instanceof DirectoryReader) { - String joinField = ParentFieldMapper.joinField(parentType); IndexSearcher indexSearcher = new IndexSearcher(reader); indexSearcher.setQueryCache(null); indexSearcher.setSimilarity(similarity); @@ -414,18 +443,18 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil if (maxChildren != that.maxChildren) return false; if (!toQuery.equals(that.toQuery)) return false; if (!innerQuery.equals(that.innerQuery)) return false; - if (!parentType.equals(that.parentType)) return false; + if (!joinField.equals(that.joinField)) return false; return scoreMode == that.scoreMode; } @Override public int hashCode() { - return Objects.hash(classHash(), toQuery, innerQuery, minChildren, maxChildren, parentType, scoreMode); + return Objects.hash(getClass(), toQuery, innerQuery, minChildren, maxChildren, joinField, scoreMode); } @Override public String toString(String s) { - return "LateParsingQuery {parentType=" + parentType + "}"; + return "LateParsingQuery {joinField=" + joinField + "}"; } public int getMinChildren() { diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java index 7bcbf7c611..3d23fac01b 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/query/HasParentQueryBuilder.java @@ -55,6 +55,8 @@ 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 org.elasticsearch.join.mapper.ParentIdFieldMapper; +import org.elasticsearch.join.mapper.ParentJoinFieldMapper; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.fetch.subphase.InnerHitsContext; @@ -187,6 +189,35 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu @Override protected Query doToQuery(QueryShardContext context) throws IOException { + if (context.getIndexSettings().isSingleType()) { + return joinFieldDoToQuery(context); + } else { + return parentFieldDoToQuery(context); + } + } + + private Query joinFieldDoToQuery(QueryShardContext context) throws IOException { + ParentJoinFieldMapper joinFieldMapper = ParentJoinFieldMapper.getMapper(context.getMapperService()); + ParentIdFieldMapper parentIdFieldMapper = joinFieldMapper.getParentIdFieldMapper(type, true); + if (parentIdFieldMapper != null) { + Query parentFilter = parentIdFieldMapper.getParentFilter(); + Query innerQuery = Queries.filtered(query.toQuery(context), parentFilter); + Query childFilter = parentIdFieldMapper.getChildrenFilter(); + MappedFieldType fieldType = parentIdFieldMapper.fieldType(); + final SortedSetDVOrdinalsIndexFieldData fieldData = context.getForField(fieldType); + return new HasChildQueryBuilder.LateParsingQuery(childFilter, innerQuery, + HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, HasChildQueryBuilder.DEFAULT_MAX_CHILDREN, + fieldType.name(), score ? ScoreMode.Max : ScoreMode.None, fieldData, context.getSearchSimilarity()); + } else { + if (ignoreUnmapped) { + return new MatchNoDocsQuery(); + } else { + throw new QueryShardException(context, "[" + NAME + "] join field has no parent type configured"); + } + } + } + + private Query parentFieldDoToQuery(QueryShardContext context) throws IOException { Query innerQuery; String[] previousTypes = context.getTypes(); context.setTypes(type); @@ -239,7 +270,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu innerQuery, HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, HasChildQueryBuilder.DEFAULT_MAX_CHILDREN, - type, + ParentFieldMapper.joinField(type), score ? ScoreMode.Max : ScoreMode.None, fieldData, context.getSearchSimilarity()); diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java index 1b67b2a22b..eb9952c150 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -25,7 +25,6 @@ import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.Requests; 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; @@ -84,15 +83,58 @@ public class ChildrenIT extends ESIntegTestCase { return nodePlugins(); } + protected boolean legacy() { + return false; + } + + private IndexRequestBuilder createIndexRequest(String index, String type, String id, String parentId, Object... fields) { + String name = type; + if (legacy() == false) { + type = "doc"; + } + + IndexRequestBuilder indexRequestBuilder = client().prepareIndex(index, type, id); + if (legacy()) { + if (parentId != null) { + indexRequestBuilder.setParent(parentId); + } + indexRequestBuilder.setSource(fields); + } else { + Map<String, Object> source = new HashMap<>(); + for (int i = 0; i < fields.length; i += 2) { + source.put((String) fields[i], fields[i + 1]); + } + Map<String, Object> joinField = new HashMap<>(); + if (parentId != null) { + joinField.put("name", name); + joinField.put("parent", parentId); + indexRequestBuilder.setRouting(parentId); + } else { + joinField.put("name", name); + } + source.put("join_field", joinField); + indexRequestBuilder.setSource(source); + } + indexRequestBuilder.setCreate(true); + return indexRequestBuilder; + } + @Before public void setupCluster() throws Exception { categoryToControl.clear(); - assertAcked( + if (legacy()) { + assertAcked( prepareCreate("test") - .setSettings("index.mapping.single_type", false) .addMapping("article", "category", "type=keyword") .addMapping("comment", "_parent", "type=article", "commenter", "type=keyword") - ); + ); + } else { + assertAcked( + prepareCreate("test") + .addMapping("doc", "category", "type=keyword", "join_field", "type=join,article=comment", + "commenter", "type=keyword") + ); + } List<IndexRequestBuilder> requests = new ArrayList<>(); String[] uniqueCategories = new String[randomIntBetween(1, 25)]; @@ -103,7 +145,7 @@ public class ChildrenIT extends ESIntegTestCase { int numParentDocs = randomIntBetween(uniqueCategories.length, uniqueCategories.length * 5); for (int i = 0; i < numParentDocs; i++) { - String id = Integer.toString(i); + String id = "article-" + i; // TODO: this array is always of length 1, and testChildrenAggs fails if this is changed String[] categories = new String[randomIntBetween(1,1)]; @@ -116,8 +158,7 @@ public class ChildrenIT extends ESIntegTestCase { control.articleIds.add(id); } - requests.add(client() - .prepareIndex("test", "article", id).setCreate(true).setSource("category", categories, "randomized", true)); + requests.add(createIndexRequest("test", "article", id, null, "category", categories, "randomized", true)); } String[] commenters = new String[randomIntBetween(5, 50)]; @@ -131,31 +172,24 @@ public class ChildrenIT extends ESIntegTestCase { int numChildDocsPerParent = randomIntBetween(0, 5); for (int i = 0; i < numChildDocsPerParent; i++) { String commenter = commenters[id % commenters.length]; - String idValue = Integer.toString(id++); + String idValue = "comment-" + id++; control.commentIds.add(idValue); Set<String> ids = control.commenterToCommentId.get(commenter); if (ids == null) { 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(createIndexRequest("test", "comment", idValue, articleId, "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(createIndexRequest("test", "article", "a", null, "category", new String[]{"a"}, "randomized", false)); + requests.add(createIndexRequest("test", "article", "b", null, "category", new String[]{"a", "b"}, "randomized", false)); + requests.add(createIndexRequest("test", "article", "c", null, "category", new String[]{"a", "b", "c"}, "randomized", false)); + requests.add(createIndexRequest("test", "article", "d", null, "category", new String[]{"c"}, "randomized", false)); + requests.add(createIndexRequest("test", "comment", "e", "a")); + requests.add(createIndexRequest("test", "comment", "f", "c")); indexRandom(true, requests); ensureSearchable("test"); @@ -184,11 +218,11 @@ public class ChildrenIT extends ESIntegTestCase { 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"), + assertThat(((InternalAggregation)childrenBucket).getProperty("_count"), equalTo((long) entry1.getValue().commentIds.size())); Terms commentersTerms = childrenBucket.getAggregations().get("commenters"); - assertThat((Terms) ((InternalAggregation)childrenBucket).getProperty("commenters"), sameInstance(commentersTerms)); + assertThat(((InternalAggregation)childrenBucket).getProperty("commenters"), sameInstance(commentersTerms)); assertThat(commentersTerms.getBuckets().size(), equalTo(entry1.getValue().commenterToCommentId.size())); for (Map.Entry<String, Set<String>> entry2 : entry1.getValue().commenterToCommentId.entrySet()) { Terms.Bucket commentBucket = commentersTerms.getBucketByKey(entry2.getKey()); @@ -235,10 +269,8 @@ public class ChildrenIT extends ESIntegTestCase { assertThat(childrenBucket.getDocCount(), equalTo(2L)); TopHits topHits = childrenBucket.getAggregations().get("top_comments"); assertThat(topHits.getHits().getTotalHits(), equalTo(2L)); - assertThat(topHits.getHits().getAt(0).getId(), equalTo("a")); - assertThat(topHits.getHits().getAt(0).getType(), equalTo("comment")); - assertThat(topHits.getHits().getAt(1).getId(), equalTo("c")); - assertThat(topHits.getHits().getAt(1).getType(), equalTo("comment")); + assertThat(topHits.getHits().getAt(0).getId(), equalTo("e")); + assertThat(topHits.getHits().getAt(1).getId(), equalTo("f")); categoryBucket = categoryTerms.getBucketByKey("b"); assertThat(categoryBucket.getKeyAsString(), equalTo("b")); @@ -249,8 +281,7 @@ public class ChildrenIT extends ESIntegTestCase { assertThat(childrenBucket.getDocCount(), equalTo(1L)); topHits = childrenBucket.getAggregations().get("top_comments"); assertThat(topHits.getHits().getTotalHits(), equalTo(1L)); - assertThat(topHits.getHits().getAt(0).getId(), equalTo("c")); - assertThat(topHits.getHits().getAt(0).getType(), equalTo("comment")); + assertThat(topHits.getHits().getAt(0).getId(), equalTo("f")); categoryBucket = categoryTerms.getBucketByKey("c"); assertThat(categoryBucket.getKeyAsString(), equalTo("c")); @@ -261,25 +292,30 @@ public class ChildrenIT extends ESIntegTestCase { assertThat(childrenBucket.getDocCount(), equalTo(1L)); topHits = childrenBucket.getAggregations().get("top_comments"); assertThat(topHits.getHits().getTotalHits(), equalTo(1L)); - assertThat(topHits.getHits().getAt(0).getId(), equalTo("c")); - assertThat(topHits.getHits().getAt(0).getType(), equalTo("comment")); + assertThat(topHits.getHits().getAt(0).getId(), equalTo("f")); } public void testWithDeletes() throws Exception { String indexName = "xyz"; - assertAcked( - prepareCreate(indexName) - .setSettings("index.mapping.single_type", false) - .addMapping("parent") - .addMapping("child", "_parent", "type=parent", "count", "type=long") - ); + if (legacy()) { + assertAcked( + prepareCreate(indexName) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent", "count", "type=long") + ); + } else { + assertAcked( + prepareCreate(indexName) + .addMapping("doc", "join_field", "type=join,parent=child", "count", "type=long") + ); + } List<IndexRequestBuilder> requests = new ArrayList<>(); - requests.add(client().prepareIndex(indexName, "parent", "1").setSource("{}", XContentType.JSON)); - requests.add(client().prepareIndex(indexName, "child", "0").setParent("1").setSource("count", 1)); - requests.add(client().prepareIndex(indexName, "child", "1").setParent("1").setSource("count", 1)); - requests.add(client().prepareIndex(indexName, "child", "2").setParent("1").setSource("count", 1)); - requests.add(client().prepareIndex(indexName, "child", "3").setParent("1").setSource("count", 1)); + requests.add(createIndexRequest(indexName, "parent", "1", null)); + requests.add(createIndexRequest(indexName, "child", "2", "1", "count", 1)); + requests.add(createIndexRequest(indexName, "child", "3", "1", "count", 1)); + requests.add(createIndexRequest(indexName, "child", "4", "1", "count", 1)); + requests.add(createIndexRequest(indexName, "child", "5", "1", "count", 1)); indexRandom(true, requests); for (int i = 0; i < 10; i++) { @@ -294,17 +330,26 @@ public class ChildrenIT extends ESIntegTestCase { Sum count = children.getAggregations().get("counts"); assertThat(count.getValue(), equalTo(4.)); - String idToUpdate = Integer.toString(randomInt(3)); + String idToUpdate = Integer.toString(2 + randomInt(3)); /* * The whole point of this test is to test these things with deleted * docs in the index so we turn off detect_noop to make sure that * the updates cause that. */ - UpdateResponse updateResponse = client().prepareUpdate(indexName, "child", idToUpdate) - .setParent("1") - .setDoc(Requests.INDEX_CONTENT_TYPE, "count", 1) - .setDetectNoop(false) - .get(); + UpdateResponse updateResponse; + if (legacy()) { + updateResponse = client().prepareUpdate(indexName, "child", idToUpdate) + .setParent("1") + .setDoc(Requests.INDEX_CONTENT_TYPE, "count", 1) + .setDetectNoop(false) + .get(); + } else { + updateResponse = client().prepareUpdate(indexName, "doc", idToUpdate) + .setRouting("1") + .setDoc(Requests.INDEX_CONTENT_TYPE, "count", 1) + .setDetectNoop(false) + .get(); + } assertThat(updateResponse.getVersion(), greaterThan(1L)); refresh(); } @@ -326,35 +371,47 @@ public class ChildrenIT extends ESIntegTestCase { String indexName = "prodcatalog"; String masterType = "masterprod"; String childType = "variantsku"; - assertAcked( - prepareCreate(indexName) - .setSettings("index.mapping.single_type", false) - .addMapping(masterType, "brand", "type=text", "name", "type=keyword", "material", "type=text") - .addMapping(childType, "_parent", "type=masterprod", "color", "type=keyword", "size", "type=keyword") - ); + if (legacy()) { + assertAcked( + prepareCreate(indexName) + .setSettings(Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) + .put("index.mapping.single_type", false)) + .addMapping(masterType, "brand", "type=text", "name", "type=keyword", "material", "type=text") + .addMapping(childType, "_parent", "type=masterprod", "color", "type=keyword", "size", "type=keyword") + ); + } else { + assertAcked( + prepareCreate(indexName) + .setSettings(Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)) + .addMapping("doc", "join_field", "type=join," + masterType + "=" + childType, "brand", "type=text", + "name", "type=keyword", "material", "type=text", "color", "type=keyword", "size", "type=keyword") + ); + } List<IndexRequestBuilder> requests = new ArrayList<>(); - 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")); - requests.add(client().prepareIndex(indexName, childType, "3").setParent("1").setSource("color", "black", "size", "38")); - 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, 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")); - requests.add(client().prepareIndex(indexName, childType, "9").setParent("2").setSource("color", "black", "size", "38")); - requests.add(client().prepareIndex(indexName, childType, "10").setParent("2").setSource("color", "black", "size", "40")); - requests.add(client().prepareIndex(indexName, childType, "11").setParent("2").setSource("color", "orange", "size", "36")); - requests.add(client().prepareIndex(indexName, childType, "12").setParent("2").setSource("color", "green", "size", "44")); + requests.add(createIndexRequest(indexName, masterType, "1", null, "brand", "Levis", "name", + "Style 501", "material", "Denim")); + requests.add(createIndexRequest(indexName, childType, "3", "1", "color", "blue", "size", "32")); + requests.add(createIndexRequest(indexName, childType, "4", "1", "color", "blue", "size", "34")); + requests.add(createIndexRequest(indexName, childType, "5", "1", "color", "blue", "size", "36")); + requests.add(createIndexRequest(indexName, childType, "6", "1", "color", "black", "size", "38")); + requests.add(createIndexRequest(indexName, childType, "7", "1", "color", "black", "size", "40")); + requests.add(createIndexRequest(indexName, childType, "8", "1", "color", "gray", "size", "36")); + + requests.add(createIndexRequest(indexName, masterType, "2", null, "brand", "Wrangler", "name", + "Regular Cut", "material", "Leather")); + requests.add(createIndexRequest(indexName, childType, "9", "2", "color", "blue", "size", "32")); + requests.add(createIndexRequest(indexName, childType, "10", "2", "color", "blue", "size", "34")); + requests.add(createIndexRequest(indexName, childType, "12", "2", "color", "black", "size", "36")); + requests.add(createIndexRequest(indexName, childType, "13", "2", "color", "black", "size", "38")); + requests.add(createIndexRequest(indexName, childType, "14", "2", "color", "black", "size", "40")); + requests.add(createIndexRequest(indexName, childType, "15", "2", "color", "orange", "size", "36")); + requests.add(createIndexRequest(indexName, childType, "16", "2", "color", "green", "size", "44")); indexRandom(true, requests); - SearchResponse response = client().prepareSearch(indexName).setTypes(masterType) + SearchResponse response = client().prepareSearch(indexName) .setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None)) .addAggregation(children("my-refinements", childType) .subAggregation(terms("my-colors").field("color")) @@ -388,21 +445,27 @@ public class ChildrenIT extends ESIntegTestCase { String grandParentType = "continent"; String parentType = "country"; String childType = "city"; - assertAcked( - prepareCreate(indexName) - .setSettings(Settings.builder() - .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) - .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) - ) - .setSettings("index.mapping.single_type", false) - .addMapping(grandParentType, "name", "type=keyword") - .addMapping(parentType, "_parent", "type=" + grandParentType) - .addMapping(childType, "_parent", "type=" + parentType) - ); - - client().prepareIndex(indexName, grandParentType, "1").setSource("name", "europe").get(); - client().prepareIndex(indexName, parentType, "2").setParent("1").setSource("name", "belgium").get(); - client().prepareIndex(indexName, childType, "3").setParent("2").setRouting("1").setSource("name", "brussels").get(); + if (legacy()) { + assertAcked( + prepareCreate(indexName) + .setSettings(Settings.builder() + .put("index.mapping.single_type", false) + ).addMapping(grandParentType, "name", "type=keyword") + .addMapping(parentType, "_parent", "type=" + grandParentType) + .addMapping(childType, "_parent", "type=" + parentType) + + ); + } else { + assertAcked( + prepareCreate(indexName) + .addMapping("doc", "join_field", "type=join," + grandParentType + "=" + parentType + "," + + parentType + "=" + childType, "name", "type=keyword") + ); + } + + createIndexRequest(indexName, grandParentType, "1", null, "name", "europe").get(); + createIndexRequest(indexName, parentType, "2", "1", "name", "belgium").get(); + createIndexRequest(indexName, childType, "3", "2", "name", "brussels").setRouting("1").get(); refresh(); SearchResponse response = client().prepareSearch(indexName) @@ -435,32 +498,37 @@ public class ChildrenIT extends ESIntegTestCase { // Before we only evaluated segments that yielded matches in 'towns' and 'parent_names' aggs, which caused // us to miss to evaluate child docs in segments we didn't have parent matches for. - - assertAcked( - prepareCreate("index") - .setSettings("index.mapping.single_type", false) - .addMapping("parentType", "name", "type=keyword", "town", "type=keyword") - .addMapping("childType", "_parent", "type=parentType", "name", "type=keyword", "age", "type=integer") - ); + if (legacy()) { + assertAcked( + prepareCreate("index") + .addMapping("parentType", "name", "type=keyword", "town", "type=keyword") + .addMapping("childType", "_parent", "type=parentType", "name", "type=keyword", "age", "type=integer") + ); + } else { + assertAcked( + prepareCreate("index") + .addMapping("doc", "join_field", "type=join,parentType=childType", "name", "type=keyword", + "town", "type=keyword", "age", "type=integer") + ); + } List<IndexRequestBuilder> requests = new ArrayList<>(); - requests.add(client().prepareIndex("index", "parentType", "1").setSource("name", "Bob", "town", "Memphis")); - requests.add(client().prepareIndex("index", "parentType", "2").setSource("name", "Alice", "town", "Chicago")); - requests.add(client().prepareIndex("index", "parentType", "3").setSource("name", "Bill", "town", "Chicago")); - requests.add(client().prepareIndex("index", "childType", "1").setSource("name", "Jill", "age", 5).setParent("1")); - requests.add(client().prepareIndex("index", "childType", "2").setSource("name", "Joey", "age", 3).setParent("1")); - requests.add(client().prepareIndex("index", "childType", "3").setSource("name", "John", "age", 2).setParent("2")); - requests.add(client().prepareIndex("index", "childType", "4").setSource("name", "Betty", "age", 6).setParent("3")); - requests.add(client().prepareIndex("index", "childType", "5").setSource("name", "Dan", "age", 1).setParent("3")); + requests.add(createIndexRequest("index", "parentType", "1", null, "name", "Bob", "town", "Memphis")); + requests.add(createIndexRequest("index", "parentType", "2", null, "name", "Alice", "town", "Chicago")); + requests.add(createIndexRequest("index", "parentType", "3", null, "name", "Bill", "town", "Chicago")); + requests.add(createIndexRequest("index", "childType", "4", "1", "name", "Jill", "age", 5)); + requests.add(createIndexRequest("index", "childType", "5", "1", "name", "Joey", "age", 3)); + requests.add(createIndexRequest("index", "childType", "6", "2", "name", "John", "age", 2)); + requests.add(createIndexRequest("index", "childType", "7", "3", "name", "Betty", "age", 6)); + requests.add(createIndexRequest("index", "childType", "8", "3", "name", "Dan", "age", 1)); indexRandom(true, requests); SearchResponse response = client().prepareSearch("index") .setSize(0) .addAggregation(AggregationBuilders.terms("towns").field("town") .subAggregation(AggregationBuilders.terms("parent_names").field("name") -.subAggregation(children("child_docs", "childType")) + .subAggregation(children("child_docs", "childType")) ) - ) - .get(); + ).get(); Terms towns = response.getAggregations().get("towns"); assertThat(towns.getBuckets().size(), equalTo(2)); diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/LegacyChildrenIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/LegacyChildrenIT.java new file mode 100644 index 0000000000..b64905d2aa --- /dev/null +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/LegacyChildrenIT.java @@ -0,0 +1,41 @@ +/* + * 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; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.elasticsearch.test.ESIntegTestCase.Scope; + +@ClusterScope(scope = Scope.SUITE) +public class LegacyChildrenIT extends ChildrenIT { + + @Override + protected boolean legacy() { + return true; + } + + @Override + public Settings indexSettings() { + Settings indexSettings = super.indexSettings(); + return Settings.builder() + .put(indexSettings) + .put("index.mapping.single_type", false) + .build(); + } +} diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java index 301721806c..e60c1a58bc 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/aggregations/ParentToChildrenAggregatorTests.java @@ -39,23 +39,22 @@ import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; import org.elasticsearch.index.mapper.ContentPath; -import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.NumberFieldMapper; -import org.elasticsearch.index.mapper.ParentFieldMapper; -import org.elasticsearch.index.mapper.TypeFieldMapper; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.join.mapper.MetaJoinFieldMapper; +import org.elasticsearch.join.mapper.ParentJoinFieldMapper; import org.elasticsearch.search.aggregations.AggregatorTestCase; import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; -import org.mockito.Mockito; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -139,42 +138,41 @@ public class ParentToChildrenAggregatorTests extends AggregatorTestCase { } private static List<Field> createParentDocument(String id) { - return Arrays.asList(new StringField(TypeFieldMapper.NAME, PARENT_TYPE, Field.Store.NO), + return Arrays.asList( new StringField(UidFieldMapper.NAME, Uid.createUid(PARENT_TYPE, id), Field.Store.NO), - createJoinField(PARENT_TYPE, id)); + new StringField("join_field", PARENT_TYPE, Field.Store.NO), + createJoinField(PARENT_TYPE, id) + ); } private static List<Field> createChildDocument(String childId, String parentId, int value) { - return Arrays.asList(new StringField(TypeFieldMapper.NAME, CHILD_TYPE, Field.Store.NO), + return Arrays.asList( new StringField(UidFieldMapper.NAME, Uid.createUid(CHILD_TYPE, childId), Field.Store.NO), - new SortedNumericDocValuesField("number", value), - createJoinField(PARENT_TYPE, parentId)); + new StringField("join_field", CHILD_TYPE, Field.Store.NO), + createJoinField(PARENT_TYPE, parentId), + new SortedNumericDocValuesField("number", value) + ); } private static SortedDocValuesField createJoinField(String parentType, String id) { - return new SortedDocValuesField(ParentFieldMapper.joinField(parentType), new BytesRef(id)); + return new SortedDocValuesField("join_field#" + parentType, new BytesRef(id)); } @Override protected MapperService mapperServiceMock() { + ParentJoinFieldMapper joinFieldMapper = createJoinFieldMapper(); MapperService mapperService = mock(MapperService.class); - DocumentMapper childDocMapper = mock(DocumentMapper.class); - DocumentMapper parentDocMapper = mock(DocumentMapper.class); - ParentFieldMapper parentFieldMapper = createParentFieldMapper(); - when(childDocMapper.parentFieldMapper()).thenReturn(parentFieldMapper); - when(parentDocMapper.parentFieldMapper()).thenReturn(parentFieldMapper); - when(mapperService.documentMapper(CHILD_TYPE)).thenReturn(childDocMapper); - when(mapperService.documentMapper(PARENT_TYPE)).thenReturn(parentDocMapper); - when(mapperService.docMappers(false)).thenReturn(Arrays.asList(new DocumentMapper[] { childDocMapper, parentDocMapper })); - when(parentDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE))); - when(childDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE))); + MetaJoinFieldMapper.MetaJoinFieldType metaJoinFieldType = mock(MetaJoinFieldMapper.MetaJoinFieldType.class); + when(metaJoinFieldType.getMapper()).thenReturn(joinFieldMapper); + when(mapperService.fullName("_parent_join")).thenReturn(metaJoinFieldType); return mapperService; } - private static ParentFieldMapper createParentFieldMapper() { + private static ParentJoinFieldMapper createJoinFieldMapper() { Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); - return new ParentFieldMapper.Builder("parent_type") - .type(PARENT_TYPE).build(new Mapper.BuilderContext(settings, new ContentPath(0))); + return new ParentJoinFieldMapper.Builder("join_field") + .addParent(PARENT_TYPE, Collections.singleton(CHILD_TYPE)) + .build(new Mapper.BuilderContext(settings, new ContentPath(0))); } private void testCase(Query query, IndexSearcher indexSearcher, Consumer<InternalChildren> verify) diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java index 2c828bb3cc..a80b9a35c6 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/ChildQuerySearchIT.java @@ -19,22 +19,20 @@ 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; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.IdsQueryBuilder; @@ -52,7 +50,6 @@ import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.global.Global; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder.Field; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; @@ -80,16 +77,15 @@ import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; import static org.elasticsearch.index.query.QueryBuilders.idsQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; import static org.elasticsearch.index.query.QueryBuilders.parentId; import static org.elasticsearch.index.query.QueryBuilders.prefixQuery; 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.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; @@ -99,9 +95,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; @ClusterScope(scope = Scope.SUITE) public class ChildQuerySearchIT extends ESIntegTestCase { @@ -130,6 +124,51 @@ public class ChildQuerySearchIT extends ESIntegTestCase { .build(); } + protected boolean legacy() { + return false; + } + + private IndexRequestBuilder createIndexRequest(String index, String type, String id, String parentId, Object... fields) { + Map<String, Object> source = new HashMap<>(); + for (int i = 0; i < fields.length; i += 2) { + source.put((String) fields[i], fields[i + 1]); + } + return createIndexRequest(index, type, id, parentId, source); + } + + private IndexRequestBuilder createIndexRequest(String index, String type, String id, String parentId, + XContentBuilder builder) throws IOException { + Map<String, Object> source = XContentHelper.convertToMap(JsonXContent.jsonXContent, builder.string(), false); + return createIndexRequest(index, type, id, parentId, source); + } + + private IndexRequestBuilder createIndexRequest(String index, String type, String id, String parentId, Map<String, Object> source) { + String name = type; + if (legacy() == false) { + type = "doc"; + } + + IndexRequestBuilder indexRequestBuilder = client().prepareIndex(index, type, id); + if (legacy()) { + if (parentId != null) { + indexRequestBuilder.setParent(parentId); + } + indexRequestBuilder.setSource(source); + } else { + Map<String, Object> joinField = new HashMap<>(); + if (parentId != null) { + joinField.put("name", name); + joinField.put("parent", parentId); + indexRequestBuilder.setRouting(parentId); + } else { + joinField.put("name", name); + } + source.put("join_field", joinField); + indexRequestBuilder.setSource(source); + } + return indexRequestBuilder; + } + public void testSelfReferentialIsForbidden() { IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> prepareCreate("test").addMapping("type", "_parent", "type=type").get()); @@ -137,17 +176,20 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testMultiLevelChild() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent") - .addMapping("child", "_parent", "type=parent") - .addMapping("grandchild", "_parent", "type=child")); + if (legacy()) { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + .addMapping("grandchild", "_parent", "type=child")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child,child=grandchild")); + } ensureGreen(); - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "c_value1").setParent("p1").get(); - client().prepareIndex("test", "grandchild", "gc1").setSource("gc_field", "gc_value1") - .setParent("c1").setRouting("p1").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "c_value1").get(); + createIndexRequest("test", "grandchild", "gc1", "c1", "gc_field", "gc_value1").setRouting("p1").get(); refresh(); SearchResponse searchResponse = client() @@ -197,15 +239,19 @@ public class ChildQuerySearchIT extends ESIntegTestCase { // see #2744 public void test2744() throws IOException { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("foo") - .addMapping("test", "_parent", "type=foo")); + if (legacy()) { + assertAcked(prepareCreate("test") + .addMapping("foo") + .addMapping("test", "_parent", "type=foo")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,foo=test")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "foo", "1").setSource("foo", 1).get(); - client().prepareIndex("test", "test").setSource("foo", 1).setParent("1").get(); + createIndexRequest("test", "foo", "1", null, "foo", 1).get(); + createIndexRequest("test", "test", "2", "1", "foo", 1).get(); refresh(); SearchResponse searchResponse = client().prepareSearch("test"). setQuery(hasChildQuery("test", matchQuery("foo", 1), ScoreMode.None)) @@ -217,46 +263,78 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testSimpleChildQuery() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent") - .addMapping("child", "_parent", "type=parent")); + if (legacy()) { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); - client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "blue").setParent("p2").get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "red").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "yellow").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "red").get(); refresh(); // TEST FETCHING _parent from child - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(idsQuery("child").addIds("c1")).storedFields("_parent").execute() - .actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - assertThat(searchResponse.getHits().getAt(0).field("_parent").getValue().toString(), equalTo("p1")); + SearchResponse searchResponse; + if (legacy()) { + searchResponse = client().prepareSearch("test") + .setQuery(idsQuery("child").addIds("c1")).storedFields("_parent").get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); + assertThat(searchResponse.getHits().getAt(0).field("_parent").getValue(), equalTo("p1")); + } else { + searchResponse = client().prepareSearch("test") + .setQuery(idsQuery("doc").addIds("c1")).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); + assertThat(searchResponse.getHits().getAt(0).field("join_field").getValue(), equalTo("child")); + assertThat(searchResponse.getHits().getAt(0).field("join_field#parent").getValue(), equalTo("p1")); + } // TEST matching on parent - searchResponse = client().prepareSearch("test").setQuery(termQuery("_parent#parent", "p1")).storedFields("_parent").get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); - assertThat(searchResponse.getHits().getAt(0).field("_parent").getValue().toString(), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); - assertThat(searchResponse.getHits().getAt(1).field("_parent").getValue().toString(), equalTo("p1")); + if (legacy()) { + searchResponse = client().prepareSearch("test").setQuery(termQuery("_parent#parent", "p1")).storedFields("_parent").get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(0).field("_parent").getValue(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(1).field("_parent").getValue(), equalTo("p1")); + } else { + searchResponse = client().prepareSearch("test") + .setQuery(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(0).field("join_field").getValue(), equalTo("child")); + assertThat(searchResponse.getHits().getAt(0).field("join_field#parent").getValue(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(1).field("join_field").getValue(), equalTo("child")); + assertThat(searchResponse.getHits().getAt(1).field("join_field#parent").getValue(), equalTo("p1")); + } - searchResponse = client().prepareSearch("test").setQuery(queryStringQuery("_parent#parent:p1")).storedFields("_parent").get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); - assertThat(searchResponse.getHits().getAt(0).field("_parent").getValue().toString(), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); - assertThat(searchResponse.getHits().getAt(1).field("_parent").getValue().toString(), equalTo("p1")); + if (legacy()) { + searchResponse = client().prepareSearch("test").setQuery(queryStringQuery("_parent#parent:p1")).storedFields("_parent").get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(0).field("_parent").getValue(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(1).field("_parent").getValue(), equalTo("p1")); + } else { + // doesn't make sense for join field, because query string & term query om this field have no special logic. + } // HAS CHILD searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")) @@ -292,25 +370,28 @@ public class ChildQuerySearchIT extends ESIntegTestCase { // Issue #3290 public void testCachingBugWithFqueryFilter() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); List<IndexRequestBuilder> builders = new ArrayList<>(); // index simple data for (int i = 0; i < 10; i++) { - builders.add(client().prepareIndex("test", "parent", Integer.toString(i)).setSource("p_field", i)); + builders.add(createIndexRequest("test", "parent", Integer.toString(i), null, "p_field", i)); } indexRandom(randomBoolean(), builders); builders.clear(); for (int j = 0; j < 2; j++) { for (int i = 0; i < 10; i++) { - builders.add(client().prepareIndex("test", "child", Integer.toString(i)).setSource("c_field", i).setParent("" + 0)); + builders.add(createIndexRequest("test", "child", j + "-" + i, "0", "c_field", i)); } - 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))); + for (int i = 10; i < 20; i++) { + builders.add(createIndexRequest("test", "child", j + "-" + i, Integer.toString(i), "c_field", i)); } if (randomBoolean()) { @@ -333,14 +414,18 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testHasParentFilter() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); Map<String, Set<String>> parentToChildren = new HashMap<>(); // Childless parent - client().prepareIndex("test", "parent", "p0").setSource("p_field", "p0").get(); + createIndexRequest("test", "parent", "p0", null, "p_field", "p0").get(); parentToChildren.put("p0", new HashSet<>()); String previousParentId = null; @@ -351,12 +436,12 @@ public class ChildQuerySearchIT extends ESIntegTestCase { if (previousParentId == null || i % numChildDocsPerParent == 0) { previousParentId = "p" + i; - builders.add(client().prepareIndex("test", "parent", previousParentId).setSource("p_field", previousParentId)); + builders.add(createIndexRequest("test", "parent", previousParentId, null, "p_field", previousParentId)); numChildDocsPerParent++; } String childId = "c" + i; - builders.add(client().prepareIndex("test", "child", childId).setSource("c_field", childId).setParent(previousParentId)); + builders.add(createIndexRequest("test", "child", childId, previousParentId, "c_field", childId)); if (!parentToChildren.containsKey(previousParentId)) { parentToChildren.put(previousParentId, new HashSet<>()); @@ -383,24 +468,28 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testSimpleChildQueryWithFlush() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data with flushes, so we have many segments - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); client().admin().indices().prepareFlush().get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "red").get(); client().admin().indices().prepareFlush().get(); - client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "yellow").get(); client().admin().indices().prepareFlush().get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); client().admin().indices().prepareFlush().get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "blue").setParent("p2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get(); client().admin().indices().prepareFlush().get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "red").setParent("p2").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "red").get(); client().admin().indices().prepareFlush().get(); refresh(); @@ -453,19 +542,23 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testScopedFacet() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent", "c_field", "type=keyword")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child", "c_field", "type=keyword")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); - client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "blue").setParent("p2").get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "red").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "yellow").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "red").get(); refresh(); @@ -493,18 +586,22 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testDeletedParent() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); - client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "blue").setParent("p2").get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "red").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "yellow").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "red").get(); refresh(); @@ -517,7 +614,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase { // update p1 and see what that we get updated values... - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1_updated").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1_updated").get(); client().admin().indices().prepareRefresh().get(); searchResponse = client().prepareSearch("test") @@ -529,19 +626,23 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testDfsSearchType() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); - client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "blue").setParent("p2").get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "red").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "yellow").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p3", "c_field", "blue").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "red").get(); refresh(); @@ -558,17 +659,21 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrChildDocs() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); - client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); - client().prepareIndex("test", "child", "1").setParent("1").setSource("c_field", 1).get(); + createIndexRequest("test", "parent", "1", null, "p_field", 1).get(); + createIndexRequest("test", "child", "2", "1", "c_field", 1).get(); client().admin().indices().prepareFlush("test").get(); - client().prepareIndex("test", "type1", "1").setSource("p_field", 1).get(); + client().prepareIndex("test", legacy() ? "type1" : "doc", "3").setSource("p_field", 1).get(); client().admin().indices().prepareFlush("test").get(); SearchResponse searchResponse = client().prepareSearch("test") @@ -583,15 +688,19 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testCountApiUsage() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); String parentId = "p1"; - client().prepareIndex("test", "parent", parentId).setSource("p_field", "1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); + createIndexRequest("test", "parent", parentId, null, "p_field", "1").get(); + createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); SearchResponse countResponse = client().prepareSearch("test").setSize(0) @@ -616,15 +725,19 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testExplainUsage() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); String parentId = "p1"; - client().prepareIndex("test", "parent", parentId).setSource("p_field", "1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); + createIndexRequest("test", "parent", parentId, null, "p_field", "1").get(); + createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); SearchResponse searchResponse = client().prepareSearch("test") @@ -641,75 +754,62 @@ public class ChildQuerySearchIT extends ESIntegTestCase { assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); - ExplainResponse explainResponse = client().prepareExplain("test", "parent", parentId) + ExplainResponse explainResponse = client().prepareExplain("test", legacy() ? "parent" : "doc", parentId) .setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)) .get(); assertThat(explainResponse.isExists(), equalTo(true)); - assertThat(explainResponse.getExplanation().getDetails()[0].getDescription(), containsString("join value p1")); + assertThat(explainResponse.getExplanation().toString(), containsString("join value p1")); } List<IndexRequestBuilder> createDocBuilders() { List<IndexRequestBuilder> indexBuilders = new ArrayList<>(); // Parent 1 and its children - indexBuilders.add(client().prepareIndex().setType("parent").setId("1").setIndex("test").setSource("p_field", "p_value1")); - indexBuilders.add(client().prepareIndex().setType("child").setId("1").setIndex("test") - .setSource("c_field1", 1, "c_field2", 0).setParent("1")); - indexBuilders.add(client().prepareIndex().setType("child").setId("2").setIndex("test") - .setSource("c_field1", 1, "c_field2", 0).setParent("1")); - indexBuilders.add(client().prepareIndex().setType("child").setId("3").setIndex("test") - .setSource("c_field1", 2, "c_field2", 0).setParent("1")); - indexBuilders.add(client().prepareIndex().setType("child").setId("4").setIndex("test") - .setSource("c_field1", 2, "c_field2", 0).setParent("1")); - indexBuilders.add(client().prepareIndex().setType("child").setId("5").setIndex("test") - .setSource("c_field1", 1, "c_field2", 1).setParent("1")); - indexBuilders.add(client().prepareIndex().setType("child").setId("6").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2).setParent("1")); + indexBuilders.add(createIndexRequest("test", "parent", "1", null, "p_field", "p_value1")); + indexBuilders.add(createIndexRequest("test", "child", "4", "1", "c_field1", 1, "c_field2", 0)); + indexBuilders.add(createIndexRequest("test", "child", "5", "1", "c_field1", 1, "c_field2", 0)); + indexBuilders.add(createIndexRequest("test", "child", "6", "1", "c_field1", 2, "c_field2", 0)); + indexBuilders.add(createIndexRequest("test", "child", "7", "1", "c_field1", 2, "c_field2", 0)); + indexBuilders.add(createIndexRequest("test", "child", "8", "1", "c_field1", 1, "c_field2", 1)); + indexBuilders.add(createIndexRequest("test", "child", "9", "1", "c_field1", 1, "c_field2", 2)); // Parent 2 and its children - indexBuilders.add(client().prepareIndex().setType("parent").setId("2").setIndex("test").setSource("p_field", "p_value2")); - indexBuilders.add(client().prepareIndex().setType("child").setId("7").setIndex("test") - .setSource("c_field1", 3, "c_field2", 0).setParent("2")); - indexBuilders.add(client().prepareIndex().setType("child").setId("8").setIndex("test") - .setSource("c_field1", 1, "c_field2", 1).setParent("2")); - indexBuilders.add(client().prepareIndex().setType("child").setId("9").setIndex("test") - .setSource("c_field1", 1, "c_field2", 1).setParent("p")); // why - // "p"???? - indexBuilders.add(client().prepareIndex().setType("child").setId("10").setIndex("test") - .setSource("c_field1", 1, "c_field2", 1).setParent("2")); - indexBuilders.add(client().prepareIndex().setType("child").setId("11").setIndex("test") - .setSource("c_field1", 1, "c_field2", 1).setParent("2")); - indexBuilders.add(client().prepareIndex().setType("child").setId("12").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2).setParent("2")); + indexBuilders.add(createIndexRequest("test", "parent", "2", null, "p_field", "p_value2")); + indexBuilders.add(createIndexRequest("test", "child", "10", "2", "c_field1", 3, "c_field2", 0)); + indexBuilders.add(createIndexRequest("test", "child", "11", "2", "c_field1", 1, "c_field2", 1)); + indexBuilders.add(createIndexRequest("test", "child", "12", "p", "c_field1", 1, "c_field2", 1)); // why "p"???? + indexBuilders.add(createIndexRequest("test", "child", "13", "2", "c_field1", 1, "c_field2", 1)); + indexBuilders.add(createIndexRequest("test", "child", "14", "2", "c_field1", 1, "c_field2", 1)); + indexBuilders.add(createIndexRequest("test", "child", "15", "2", "c_field1", 1, "c_field2", 2)); // Parent 3 and its children - - indexBuilders.add(client().prepareIndex().setType("parent").setId("3").setIndex("test") - .setSource("p_field1", "p_value3", "p_field2", 5)); - indexBuilders.add(client().prepareIndex().setType("child").setId("13").setIndex("test") - .setSource("c_field1", 4, "c_field2", 0, "c_field3", 0).setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child").setId("14").setIndex("test") - .setSource("c_field1", 1, "c_field2", 1, "c_field3", 1).setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child").setId("15").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2, "c_field3", 2).setParent("3")); // why - // "p"???? - indexBuilders.add(client().prepareIndex().setType("child").setId("16").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2, "c_field3", 3).setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child").setId("17").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2, "c_field3", 4).setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child").setId("18").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2, "c_field3", 5).setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child1").setId("1").setIndex("test") - .setSource("c_field1", 1, "c_field2", 2, "c_field3", 6).setParent("3")); + indexBuilders.add(createIndexRequest("test", "parent", "3", null, "p_field1", "p_value3", "p_field2", 5)); + indexBuilders.add(createIndexRequest("test", "child", "16", "3", "c_field1", 4, "c_field2", 0, "c_field3", 0)); + indexBuilders.add(createIndexRequest("test", "child", "17", "3", "c_field1", 1, "c_field2", 1, "c_field3", 1)); + indexBuilders.add(createIndexRequest("test", "child", "18", "3", "c_field1", 1, "c_field2", 2, "c_field3", 2)); + indexBuilders.add(createIndexRequest("test", "child", "19", "3", "c_field1", 1, "c_field2", 2, "c_field3", 3)); + indexBuilders.add(createIndexRequest("test", "child", "20", "3", "c_field1", 1, "c_field2", 2, "c_field3", 4)); + indexBuilders.add(createIndexRequest("test", "child", "21", "3", "c_field1", 1, "c_field2", 2, "c_field3", 5)); + indexBuilders.add(createIndexRequest("test", "child1", "22", "3", "c_field1", 1, "c_field2", 2, "c_field3", 6)); return indexBuilders; } public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent") .addMapping("child1", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", jsonBuilder().startObject().startObject("doc").startObject("properties") + .startObject("join_field") + .field("type", "join") + .field("parent", new String[] {"child", "child1"}) + .endObject() + .endObject().endObject().endObject() + )); + } ensureGreen(); indexRandom(true, createDocBuilders().toArray(new IndexRequestBuilder[0])); @@ -775,28 +875,32 @@ public class ChildQuerySearchIT extends ESIntegTestCase { .addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get(); assertThat(response.getHits().getTotalHits(), equalTo(7L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("13")); + assertThat(response.getHits().getHits()[0].getId(), equalTo("16")); assertThat(response.getHits().getHits()[0].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("14")); + assertThat(response.getHits().getHits()[1].getId(), equalTo("17")); assertThat(response.getHits().getHits()[1].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("15")); + assertThat(response.getHits().getHits()[2].getId(), equalTo("18")); assertThat(response.getHits().getHits()[2].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[3].getId(), equalTo("16")); + assertThat(response.getHits().getHits()[3].getId(), equalTo("19")); assertThat(response.getHits().getHits()[3].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[4].getId(), equalTo("17")); + assertThat(response.getHits().getHits()[4].getId(), equalTo("20")); assertThat(response.getHits().getHits()[4].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[5].getId(), equalTo("18")); + assertThat(response.getHits().getHits()[5].getId(), equalTo("21")); assertThat(response.getHits().getHits()[5].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[6].getId(), equalTo("1")); + assertThat(response.getHits().getHits()[6].getId(), equalTo("22")); assertThat(response.getHits().getHits()[6].getScore(), equalTo(5f)); } // Issue #2536 public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); SearchResponse response = client().prepareSearch("test") @@ -804,8 +908,13 @@ public class ChildQuerySearchIT extends ESIntegTestCase { assertNoFailures(response); assertThat(response.getHits().getTotalHits(), equalTo(0L)); - client().prepareIndex("test", "child1").setSource(jsonBuilder().startObject().field("text", "value").endObject()) + if (legacy()) { + client().prepareIndex("test", "child1").setSource(jsonBuilder().startObject().field("text", "value").endObject()) + .setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); + } else { + client().prepareIndex("test", "doc").setSource(jsonBuilder().startObject().field("text", "value").endObject()) .setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); + } response = client().prepareSearch("test") .setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); @@ -829,17 +938,25 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testHasChildAndHasParentFilter_withFilter() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); - client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); - client().prepareIndex("test", "child", "2").setParent("1").setSource("c_field", 1).get(); + createIndexRequest("test", "parent", "1", null, "p_field", 1).get(); + createIndexRequest("test", "child", "2", "1", "c_field", 1).get(); client().admin().indices().prepareFlush("test").get(); - client().prepareIndex("test", "type1", "3").setSource("p_field", 2).get(); + if (legacy()) { + client().prepareIndex("test", "type1", "3").setSource("p_field", 2).get(); + } else { + client().prepareIndex("test", "doc", "3").setSource("p_field", 2).get(); + } client().admin().indices().prepareFlush("test").get(); SearchResponse searchResponse = client().prepareSearch("test") @@ -857,15 +974,20 @@ public class ChildQuerySearchIT extends ESIntegTestCase { assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2")); } + @AwaitsFix(bugUrl = "wait for inner hits to be fixed") public void testHasChildInnerHitsHighlighting() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); - client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); - client().prepareIndex("test", "child", "2").setParent("1").setSource("c_field", "foo bar").get(); + createIndexRequest("test", "parent", "1", null, "p_field", 1).get(); + createIndexRequest("test", "child", "2", "1", "c_field", "foo bar").get(); client().admin().indices().prepareFlush("test").get(); SearchResponse searchResponse = client().prepareSearch("test").setQuery( @@ -884,16 +1006,20 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // query filter in case for p/c shouldn't execute per segment, but rather - client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); + createIndexRequest("test", "parent", "1", null, "p_field", 1).get(); client().admin().indices().prepareFlush("test").setForce(true).get(); - client().prepareIndex("test", "child", "2").setParent("1").setSource("c_field", 1).get(); + createIndexRequest("test", "child", "2", "1", "c_field", 1).get(); refresh(); SearchResponse searchResponse = client().prepareSearch("test") @@ -918,21 +1044,25 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testSimpleQueryRewrite() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent", "p_field", "type=keyword") .addMapping("child", "_parent", "type=parent", "c_field", "type=keyword")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child", "p_field", "type=keyword", "c_field", "type=keyword")); + } ensureGreen(); // index simple data int childId = 0; for (int i = 0; i < 10; i++) { String parentId = String.format(Locale.ROOT, "p%03d", i); - client().prepareIndex("test", "parent", parentId).setSource("p_field", parentId).get(); + createIndexRequest("test", "parent", parentId, null, "p_field", parentId).get(); int j = childId; for (; j < childId + 50; j++) { String childUid = String.format(Locale.ROOT, "c%03d", j); - client().prepareIndex("test", "child", childUid).setSource("c_field", childUid).setParent(parentId).get(); + createIndexRequest("test", "child", childUid, parentId, "c_field", childUid).get(); } childId = j; } @@ -967,19 +1097,23 @@ public class ChildQuerySearchIT extends ESIntegTestCase { // Issue #3144 public void testReIndexingParentAndChildDocuments() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); - client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "x").setParent("p2").get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "x").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "yellow").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "x").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "x").get(); refresh(); @@ -1002,10 +1136,10 @@ public class ChildQuerySearchIT extends ESIntegTestCase { // re-index for (int i = 0; i < 10; i++) { - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "d" + i).setSource("c_field", "red").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "x").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "d" + i, "p1", "c_field", "red").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "x").get(); client().admin().indices().prepareRefresh("test").get(); } @@ -1030,19 +1164,23 @@ public class ChildQuerySearchIT extends ESIntegTestCase { // Issue #3203 public void testHasChildQueryWithMinimumScore() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "x").setParent("p1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c3").setSource("c_field", "x").setParent("p2").get(); - client().prepareIndex("test", "child", "c4").setSource("c_field", "x").setParent("p2").get(); - client().prepareIndex("test", "child", "c5").setSource("c_field", "x").setParent("p2").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "x").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "x").get(); + createIndexRequest("test", "child", "c4", "p2", "c_field", "x").get(); + createIndexRequest("test", "child", "c5", "p2", "c_field", "x").get(); refresh(); SearchResponse searchResponse = client() @@ -1056,41 +1194,84 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testParentFieldQuery() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") + .setSettings(Settings.builder() + .put(indexSettings()) + .put("index.refresh_interval", -1) + ) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .setSettings("index.refresh_interval", -1) + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); - SearchResponse response = client().prepareSearch("test").setQuery(termQuery("_parent", "p1")) + SearchResponse response; + if (legacy()){ + response = client().prepareSearch("test").setQuery(termQuery("_parent#parent:p1", "p1")) + .get(); + } else { + response = client().prepareSearch("test") + .setQuery(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) .get(); + } assertHitCount(response, 0L); - client().prepareIndex("test", "child", "c1").setSource("{}", XContentType.JSON).setParent("p1").get(); + createIndexRequest("test", "child", "c1", "p1").get(); refresh(); - response = client().prepareSearch("test").setQuery(termQuery("_parent#parent", "p1")).get(); + if (legacy()){ + response = client().prepareSearch("test").setQuery(termQuery("_parent#parent", "p1")) + .get(); + } else { + response = client().prepareSearch("test") + .setQuery(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) + .get(); + } assertHitCount(response, 1L); - response = client().prepareSearch("test").setQuery(queryStringQuery("_parent#parent:p1")).get(); - assertHitCount(response, 1L); + if (legacy()) { + response = client().prepareSearch("test").setQuery(queryStringQuery("_parent#parent:p1")).get(); + assertHitCount(response, 1L); + } - client().prepareIndex("test", "child", "c2").setSource("{}", XContentType.JSON).setParent("p2").get(); + createIndexRequest("test", "child", "c2", "p2").get(); refresh(); - response = client().prepareSearch("test").setQuery(termsQuery("_parent#parent", "p1", "p2")).get(); - assertHitCount(response, 2L); + if (legacy()) { + response = client().prepareSearch("test").setQuery(termsQuery("_parent#parent", "p1", "p2")).get(); + assertHitCount(response, 2L); + } - response = client().prepareSearch("test") + if (legacy()) { + response = client().prepareSearch("test") .setQuery(boolQuery() .should(termQuery("_parent#parent", "p1")) .should(termQuery("_parent#parent", "p2")) ).get(); + } else { + response = client().prepareSearch("test") + .setQuery(boolQuery() + .should(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) + .should(boolQuery().filter(termQuery("join_field#parent", "p2")).filter(termQuery("join_field", "child"))) + ).get(); + } assertHitCount(response, 2L); } public void testParentIdQuery() throws Exception { + if (legacy() == false) { + // Fix parent_id query + return; + } + assertAcked(prepareCreate("test") - .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) + .setSettings(Settings.builder() + .put(indexSettings()) + .put("index.refresh_interval", -1) + ) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1113,24 +1294,28 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testHasChildNotBeingCached() throws IOException { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "parent", "p3").setSource("p_field", "p_value3").get(); - client().prepareIndex("test", "parent", "p4").setSource("p_field", "p_value4").get(); - client().prepareIndex("test", "parent", "p5").setSource("p_field", "p_value5").get(); - client().prepareIndex("test", "parent", "p6").setSource("p_field", "p_value6").get(); - client().prepareIndex("test", "parent", "p7").setSource("p_field", "p_value7").get(); - client().prepareIndex("test", "parent", "p8").setSource("p_field", "p_value8").get(); - client().prepareIndex("test", "parent", "p9").setSource("p_field", "p_value9").get(); - client().prepareIndex("test", "parent", "p10").setSource("p_field", "p_value10").get(); - client().prepareIndex("test", "child", "c1").setParent("p1").setSource("c_field", "blue").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "parent", "p3", null, "p_field", "p_value3").get(); + createIndexRequest("test", "parent", "p4", null, "p_field", "p_value4").get(); + createIndexRequest("test", "parent", "p5", null, "p_field", "p_value5").get(); + createIndexRequest("test", "parent", "p6", null, "p_field", "p_value6").get(); + createIndexRequest("test", "parent", "p7", null, "p_field", "p_value7").get(); + createIndexRequest("test", "parent", "p8", null, "p_field", "p_value8").get(); + createIndexRequest("test", "parent", "p9", null, "p_field", "p_value9").get(); + createIndexRequest("test", "parent", "p10", null, "p_field", "p_value10").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "blue").get(); client().admin().indices().prepareFlush("test").get(); client().admin().indices().prepareRefresh("test").get(); @@ -1140,7 +1325,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase { assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits(), equalTo(1L)); - client().prepareIndex("test", "child", "c2").setParent("p2").setSource("c_field", "blue").get(); + createIndexRequest("test", "child", "c2", "p2", "c_field", "blue").get(); client().admin().indices().prepareRefresh("test").get(); searchResponse = client().prepareSearch("test") @@ -1175,22 +1360,29 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } // Issue #3818 - public void testHasChildQueryOnlyReturnsSingleChildType() { - assertAcked(prepareCreate("grandissue") - .setSettings("index.mapping.single_type", false) + public void testHasChildQueryOnlyReturnsSingleChildType() throws Exception { + if (legacy()) { + assertAcked(prepareCreate("grandissue") .addMapping("grandparent", "name", "type=text") .addMapping("parent", "_parent", "type=grandparent") .addMapping("child_type_one", "_parent", "type=parent") .addMapping("child_type_two", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("grandissue") + .addMapping("doc", jsonBuilder().startObject().startObject("doc").startObject("properties") + .startObject("join_field") + .field("type", "join") + .field("grandparent", "parent") + .field("parent", new String[] {"child_type_one", "child_type_two"}) + .endObject() + .endObject().endObject().endObject() + )); + } - client().prepareIndex("grandissue", "grandparent", "1").setSource("name", "Grandpa").get(); - client().prepareIndex("grandissue", "parent", "2").setParent("1").setSource("name", "Dana").get(); - client().prepareIndex("grandissue", "child_type_one", "3").setParent("2").setRouting("1") - .setSource("name", "William") - .get(); - client().prepareIndex("grandissue", "child_type_two", "4").setParent("2").setRouting("1") - .setSource("name", "Kate") - .get(); + createIndexRequest("grandissue", "grandparent", "1", null, "name", "Grandpa").get(); + createIndexRequest("grandissue", "parent", "2", "1", "name", "Dana").get(); + createIndexRequest("grandissue", "child_type_one", "3", "2", "name", "William").setRouting("1").get(); + createIndexRequest("grandissue", "child_type_two", "4", "2", "name", "Kate").setRouting("1").get(); refresh(); SearchResponse searchResponse = client().prepareSearch("grandissue").setQuery( @@ -1228,64 +1420,18 @@ public class ChildQuerySearchIT extends ESIntegTestCase { assertHitCount(searchResponse, 0L); } - public void testIndexChildDocWithNoParentMapping() throws IOException { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent") - .addMapping("child1")); - ensureGreen(); - - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - try { - client().prepareIndex("test", "child1", "c1").setParent("p1").setSource("c_field", "blue").get(); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e.toString(), containsString("can't specify parent if no parent field has been configured")); - } - try { - client().prepareIndex("test", "child2", "c2").setParent("p1").setSource("c_field", "blue").get(); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e.toString(), containsString("can't specify parent if no parent field has been configured")); - } - - refresh(); - } - - public void testAddingParentToExistingMapping() throws IOException { - createIndex("test"); - ensureGreen(); - - PutMappingResponse putMappingResponse = client().admin().indices() - .preparePutMapping("test").setType("child").setSource("number", "type=integer") - .get(); - assertThat(putMappingResponse.isAcknowledged(), equalTo(true)); - - GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("test").get(); - Map<String, Object> mapping = getMappingsResponse.getMappings().get("test").get("child").getSourceAsMap(); - assertThat(mapping.size(), greaterThanOrEqualTo(1)); // there are potentially some meta fields configured randomly - assertThat(mapping.get("properties"), notNullValue()); - - try { - // Adding _parent metadata field to existing mapping is prohibited: - client().admin().indices().preparePutMapping("test").setType("child").setSource(jsonBuilder().startObject().startObject("child") - .startObject("_parent").field("type", "parent").endObject() - .endObject().endObject()).get(); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e.toString(), containsString("The _parent field's type option can't be changed: [null]->[parent]")); - } - } - public void testHasChildQueryWithNestedInnerObjects() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent", "objects", "type=nested") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child", "objects", "type=nested")); + } ensureGreen(); - client().prepareIndex("test", "parent", "p1") - .setSource(jsonBuilder().startObject().field("p_field", "1").startArray("objects") + createIndexRequest("test", "parent", "p1", null, jsonBuilder().startObject().field("p_field", "1").startArray("objects") .startObject().field("i_field", "1").endObject() .startObject().field("i_field", "2").endObject() .startObject().field("i_field", "3").endObject() @@ -1294,15 +1440,14 @@ public class ChildQuerySearchIT extends ESIntegTestCase { .startObject().field("i_field", "6").endObject() .endArray().endObject()) .get(); - client().prepareIndex("test", "parent", "p2") - .setSource(jsonBuilder().startObject().field("p_field", "2").startArray("objects") + createIndexRequest("test", "parent", "p2", null, jsonBuilder().startObject().field("p_field", "2").startArray("objects") .startObject().field("i_field", "1").endObject() .startObject().field("i_field", "2").endObject() .endArray().endObject()) .get(); - client().prepareIndex("test", "child", "c1").setParent("p1").setSource("c_field", "blue").get(); - client().prepareIndex("test", "child", "c2").setParent("p1").setSource("c_field", "red").get(); - client().prepareIndex("test", "child", "c3").setParent("p2").setSource("c_field", "red").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "blue").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "red").get(); refresh(); ScoreMode scoreMode = randomFrom(ScoreMode.values()); @@ -1322,15 +1467,19 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testNamedFilters() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); String parentId = "p1"; - client().prepareIndex("test", "parent", parentId).setSource("p_field", "1").get(); - client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); + createIndexRequest("test", "parent", parentId, null, "p_field", "1").get(); + createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", @@ -1370,7 +1519,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase { ensureGreen(); String parentId = "p1"; - client().prepareIndex("test", "parent", parentId).setSource("p_field", "1").get(); + client().prepareIndex("test", legacy() ? "parent" : "doc", parentId).setSource("p_field", "1").get(); refresh(); try { @@ -1420,24 +1569,33 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testParentChildCaching() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.refresh_interval", -1, "index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") + .setSettings(Settings.builder() + .put(indexSettings()) + .put("index.refresh_interval", -1) + ) .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .setSettings("index.refresh_interval", -1) + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); // index simple data - client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); - client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); - client().prepareIndex("test", "child", "c1").setParent("p1").setSource("c_field", "blue").get(); - client().prepareIndex("test", "child", "c2").setParent("p1").setSource("c_field", "red").get(); - client().prepareIndex("test", "child", "c3").setParent("p2").setSource("c_field", "red").get(); + createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1").get(); + createIndexRequest("test", "parent", "p2", null, "p_field", "p_value2").get(); + createIndexRequest("test", "child", "c1", "p1", "c_field", "blue").get(); + createIndexRequest("test", "child", "c2", "p1", "c_field", "red").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "red").get(); client().admin().indices().prepareForceMerge("test").setMaxNumSegments(1).setFlush(true).get(); - client().prepareIndex("test", "parent", "p3").setSource("p_field", "p_value3").get(); - client().prepareIndex("test", "parent", "p4").setSource("p_field", "p_value4").get(); - client().prepareIndex("test", "child", "c4").setParent("p3").setSource("c_field", "green").get(); - client().prepareIndex("test", "child", "c5").setParent("p3").setSource("c_field", "blue").get(); - client().prepareIndex("test", "child", "c6").setParent("p4").setSource("c_field", "blue").get(); + createIndexRequest("test", "parent", "p3", null, "p_field", "p_value3").get(); + createIndexRequest("test", "parent", "p4", null, "p_field", "p_value4").get(); + createIndexRequest("test", "child", "c4", "p3", "c_field", "green").get(); + createIndexRequest("test", "child", "c5", "p3", "c_field", "blue").get(); + createIndexRequest("test", "child", "c6", "p4", "c_field", "blue").get(); client().admin().indices().prepareFlush("test").get(); client().admin().indices().prepareRefresh("test").get(); @@ -1451,7 +1609,7 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } - client().prepareIndex("test", "child", "c3").setParent("p2").setSource("c_field", "blue").get(); + createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get(); client().admin().indices().prepareRefresh("test").get(); SearchResponse searchResponse = client().prepareSearch() @@ -1464,14 +1622,18 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testParentChildQueriesViaScrollApi() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); for (int i = 0; i < 10; i++) { - client().prepareIndex("test", "parent", "p" + i).setSource("{}", XContentType.JSON).get(); - client().prepareIndex("test", "child", "c" + i).setSource("{}", XContentType.JSON).setParent("p" + i).get(); + createIndexRequest("test", "parent", "p" + i, null).get(); + createIndexRequest("test", "child", "c" + i, "p" + i).get(); } refresh(); @@ -1507,58 +1669,21 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } } - // Issue #5783 - public void testQueryBeforeChildType() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("features") - .addMapping("posts", "_parent", "type=features") - .addMapping("specials")); - ensureGreen(); - - client().prepareIndex("test", "features", "1").setSource("field", "foo").get(); - client().prepareIndex("test", "posts", "1").setParent("1").setSource("field", "bar").get(); - refresh(); - - SearchResponse resp; - resp = client().prepareSearch("test") - .setSource(new SearchSourceBuilder().query(hasChildQuery("posts", - QueryBuilders.matchQuery("field", "bar"), ScoreMode.None))) - .get(); - assertHitCount(resp, 1L); - } - - // Issue #6256 - public void testParentFieldInMultiMatchField() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("type1") - .addMapping("type2", "_parent", "type=type1") - ); - ensureGreen(); - - client().prepareIndex("test", "type2", "1").setParent("1").setSource("field", "value").get(); - refresh(); - - SearchResponse response = client().prepareSearch("test") - .setQuery(multiMatchQuery("1", "_parent#type1")) - .get(); - - assertThat(response.getHits().getTotalHits(), equalTo(1L)); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - } - public void testTypeIsAppliedInHasParentInnerQuery() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); List<IndexRequestBuilder> indexRequests = new ArrayList<>(); - indexRequests.add(client().prepareIndex("test", "parent", "1").setSource("field1", "a")); - indexRequests.add(client().prepareIndex("test", "child", "1").setParent("1").setSource("{}", XContentType.JSON)); - indexRequests.add(client().prepareIndex("test", "child", "2").setParent("1").setSource("{}", XContentType.JSON)); + indexRequests.add(createIndexRequest("test", "parent", "p1", null, "field1", "a")); + indexRequests.add(createIndexRequest("test", "child", "c1", "p1")); + indexRequests.add(createIndexRequest("test", "child", "c2", "p1")); indexRandom(true, indexRequests); SearchResponse searchResponse = client().prepareSearch("test") @@ -1585,36 +1710,26 @@ public class ChildQuerySearchIT extends ESIntegTestCase { private List<IndexRequestBuilder> createMinMaxDocBuilders() { List<IndexRequestBuilder> indexBuilders = new ArrayList<>(); // Parent 1 and its children - indexBuilders.add(client().prepareIndex().setType("parent").setId("1").setIndex("test").setSource("id",1)); - indexBuilders.add(client().prepareIndex().setType("child").setId("10").setIndex("test") - .setSource("foo", "one").setParent("1")); + indexBuilders.add(createIndexRequest("test", "parent", "1", null, "id",1)); + indexBuilders.add(createIndexRequest("test", "child", "10", "1", "foo", "one")); // Parent 2 and its children - indexBuilders.add(client().prepareIndex().setType("parent").setId("2").setIndex("test").setSource("id",2)); - indexBuilders.add(client().prepareIndex().setType("child").setId("11").setIndex("test") - .setSource("foo", "one").setParent("2")); - indexBuilders.add(client().prepareIndex().setType("child").setId("12").setIndex("test") - .setSource("foo", "one two").setParent("2")); + indexBuilders.add(createIndexRequest("test", "parent", "2", null, "id",2)); + indexBuilders.add(createIndexRequest("test", "child", "11", "2", "foo", "one")); + indexBuilders.add(createIndexRequest("test", "child", "12", "2", "foo", "one two")); // Parent 3 and its children - indexBuilders.add(client().prepareIndex().setType("parent").setId("3").setIndex("test").setSource("id",3)); - indexBuilders.add(client().prepareIndex().setType("child").setId("13").setIndex("test") - .setSource("foo", "one").setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child").setId("14").setIndex("test") - .setSource("foo", "one two").setParent("3")); - indexBuilders.add(client().prepareIndex().setType("child").setId("15").setIndex("test") - .setSource("foo", "one two three").setParent("3")); + indexBuilders.add(createIndexRequest("test", "parent", "3", null, "id",3)); + indexBuilders.add(createIndexRequest("test", "child", "13", "3", "foo", "one")); + indexBuilders.add(createIndexRequest("test", "child", "14", "3", "foo", "one two")); + indexBuilders.add(createIndexRequest("test", "child", "15", "3", "foo", "one two three")); // Parent 4 and its children - indexBuilders.add(client().prepareIndex().setType("parent").setId("4").setIndex("test").setSource("id",4)); - indexBuilders.add(client().prepareIndex().setType("child").setId("16").setIndex("test") - .setSource("foo", "one").setParent("4")); - indexBuilders.add(client().prepareIndex().setType("child").setId("17").setIndex("test") - .setSource("foo", "one two").setParent("4")); - indexBuilders.add(client().prepareIndex().setType("child").setId("18").setIndex("test") - .setSource("foo", "one two three").setParent("4")); - indexBuilders.add(client().prepareIndex().setType("child").setId("19").setIndex("test") - .setSource("foo", "one two three four").setParent("4")); + indexBuilders.add(createIndexRequest("test", "parent", "4", null, "id", 4)); + indexBuilders.add(createIndexRequest("test", "child", "16", "4", "foo", "one")); + indexBuilders.add(createIndexRequest("test", "child", "17", "4", "foo", "one two")); + indexBuilders.add(createIndexRequest("test", "child", "18", "4", "foo", "one two three")); + indexBuilders.add(createIndexRequest("test", "child", "19", "4", "foo", "one two three four")); return indexBuilders; } @@ -1639,10 +1754,14 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } public void testMinMaxChildren() throws Exception { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent", "id", "type=long") .addMapping("child", "_parent", "type=parent")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent=child")); + } ensureGreen(); indexRandom(true, createMinMaxDocBuilders().toArray(new IndexRequestBuilder[0])); @@ -1953,59 +2072,41 @@ public class ChildQuerySearchIT extends ESIntegTestCase { assertThat(e.getMessage(), equalTo("[has_child] 'max_children' is less than 'min_children'")); } - public void testParentFieldToNonExistingType() { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent").addMapping("child", "_parent", "type=parent2")); - client().prepareIndex("test", "parent", "1").setSource("{}", XContentType.JSON).get(); - client().prepareIndex("test", "child", "1").setParent("1").setSource("{}", XContentType.JSON).get(); - refresh(); - - try { - client().prepareSearch("test") - .setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) - .get(); - fail(); - } catch (SearchPhaseExecutionException e) { - } - } - public void testHasParentInnerQueryType() { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .addMapping("parent-type").addMapping("child-type", "_parent", "type=parent-type")); - client().prepareIndex("test", "child-type", "child-id").setParent("parent-id").setSource("{}", XContentType.JSON).get(); - client().prepareIndex("test", "parent-type", "parent-id").setSource("{}", XContentType.JSON).get(); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent-type=child-type")); + } + createIndexRequest("test", "child-type", "child-id", "parent-id").get(); + createIndexRequest("test", "parent-type", "parent-id", null).get(); 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( - hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)).get(); - assertSearchHits(searchResponse, "child-id"); - } - public void testHasChildInnerQueryType() { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) - .addMapping("parent-type").addMapping("child-type", "_parent", "type=parent-type")); - client().prepareIndex("test", "child-type", "child-id").setParent("parent-id").setSource("{}", XContentType.JSON).get(); - client().prepareIndex("test", "parent-type", "parent-id").setSource("{}", XContentType.JSON).get(); - 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( - hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)).get(); + SearchResponse searchResponse = client().prepareSearch("test").setQuery( + hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)).get(); assertSearchHits(searchResponse, "parent-id"); + //make sure that when we explicitly set a type, the inner query is executed in the context of the parent type instead + searchResponse = client().prepareSearch("test").setQuery( + hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)).get(); + assertSearchHits(searchResponse, "child-id"); } public void testHighlightersIgnoreParentChild() { - assertAcked(prepareCreate("test") - .setSettings("index.mapping.single_type", false) + if (legacy()) { + assertAcked(prepareCreate("test") .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(); + "type=text,term_vector=with_positions_offsets,index_options=offsets")); + } else { + assertAcked(prepareCreate("test") + .addMapping("doc", "join_field", "type=join,parent-type=child-type", + "searchText", "type=text,term_vector=with_positions_offsets,index_options=offsets")); + } + createIndexRequest("test", "parent-type", "parent-id", null, "searchText", "quick brown fox").get(); + createIndexRequest("test", "child-type", "child-id", "parent-id", "searchText", "quick brown fox").get(); refresh(); String[] highlightTypes = new String[] {"plain", "fvh", "unified"}; @@ -2038,13 +2139,18 @@ public class ChildQuerySearchIT extends ESIntegTestCase { } 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(); + if (legacy()) { + assertAcked(prepareCreate("my-index") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + ); + } else { + assertAcked(prepareCreate("my-index") + .addMapping("doc", "join_field", "type=join,parent=child") + ); + } + createIndexRequest("my-index", "parent", "1", null).get(); + createIndexRequest("my-index", "child", "2", "1").get(); refresh(); assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter1", @@ -2060,154 +2166,4 @@ public class ChildQuerySearchIT extends ESIntegTestCase { 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/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java index e95f3a7713..4b0a8e6781 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasChildQueryBuilderTests.java @@ -20,12 +20,12 @@ package org.elasticsearch.join.query; import com.carrotsearch.randomizedtesting.generators.RandomPicks; -import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.similarities.PerFieldSimilarityWrapper; @@ -87,6 +87,8 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue @Override protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { similarity = randomFrom("classic", "BM25"); + // TODO: use a single type when inner hits have been changed to work with join field, + // this test randomly generates queries with inner hits mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE, STRING_FIELD_NAME, "type=text", STRING_FIELD_NAME_2, "type=keyword", diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java index 62ab92ac5e..c7b14d19e4 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/HasParentQueryBuilderTests.java @@ -72,6 +72,8 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ @Override protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { + // TODO: use a single type when inner hits have been changed to work with join field, + // this test randomly generates queries with inner hits mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE, STRING_FIELD_NAME, "type=text", STRING_FIELD_NAME_2, "type=keyword", 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 index 1d2e1c29e9..3459c9d352 100644 --- 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 @@ -547,7 +547,9 @@ public class InnerHitsIT extends ESIntegTestCase { .addMapping("parent_type", "nested_type", "type=nested") .addMapping("child_type", "_parent", "type=parent_type") ); - createIndex("index2"); + assertAcked(prepareCreate("index2") + .setSettings("index.mapping.single_type", false) + ); 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(); diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyChildQuerySearchIT.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyChildQuerySearchIT.java new file mode 100644 index 0000000000..445cb4be14 --- /dev/null +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyChildQuerySearchIT.java @@ -0,0 +1,318 @@ +/* + * 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.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.search.SearchPhaseExecutionException; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.builder.SearchSourceBuilder; + +import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; +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.assertSearchHits; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.notNullValue; + +public class LegacyChildQuerySearchIT extends ChildQuerySearchIT { + + @Override + protected boolean legacy() { + return true; + } + + @Override + public Settings indexSettings() { + Settings indexSettings = super.indexSettings(); + return Settings.builder() + .put(indexSettings) + .put("index.mapping.single_type", false) + .build(); + } + + public void testIndexChildDocWithNoParentMapping() throws IOException { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child1")); + ensureGreen(); + + client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); + try { + client().prepareIndex("test", "child1", "c1").setParent("p1").setSource("c_field", "blue").get(); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.toString(), containsString("can't specify parent if no parent field has been configured")); + } + try { + client().prepareIndex("test", "child2", "c2").setParent("p1").setSource("c_field", "blue").get(); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.toString(), containsString("can't specify parent if no parent field has been configured")); + } + + refresh(); + } + + public void testAddingParentToExistingMapping() throws IOException { + createIndex("test"); + ensureGreen(); + + PutMappingResponse putMappingResponse = client().admin().indices() + .preparePutMapping("test").setType("child").setSource("number", "type=integer") + .get(); + assertThat(putMappingResponse.isAcknowledged(), equalTo(true)); + + GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("test").get(); + Map<String, Object> mapping = getMappingsResponse.getMappings().get("test").get("child").getSourceAsMap(); + assertThat(mapping.size(), greaterThanOrEqualTo(1)); // there are potentially some meta fields configured randomly + assertThat(mapping.get("properties"), notNullValue()); + + try { + // Adding _parent metadata field to existing mapping is prohibited: + client().admin().indices().preparePutMapping("test").setType("child").setSource(jsonBuilder().startObject().startObject("child") + .startObject("_parent").field("type", "parent").endObject() + .endObject().endObject()).get(); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e.toString(), containsString("The _parent field's type option can't be changed: [null]->[parent]")); + } + } + + // Issue #5783 + public void testQueryBeforeChildType() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("features") + .addMapping("posts", "_parent", "type=features") + .addMapping("specials")); + ensureGreen(); + + client().prepareIndex("test", "features", "1").setSource("field", "foo").get(); + client().prepareIndex("test", "posts", "1").setParent("1").setSource("field", "bar").get(); + refresh(); + + SearchResponse resp; + resp = client().prepareSearch("test") + .setSource(new SearchSourceBuilder().query(hasChildQuery("posts", + QueryBuilders.matchQuery("field", "bar"), ScoreMode.None))) + .get(); + assertHitCount(resp, 1L); + } + + // Issue #6256 + public void testParentFieldInMultiMatchField() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("type1") + .addMapping("type2", "_parent", "type=type1") + ); + ensureGreen(); + + client().prepareIndex("test", "type2", "1").setParent("1").setSource("field", "value").get(); + refresh(); + + SearchResponse response = client().prepareSearch("test") + .setQuery(multiMatchQuery("1", "_parent#type1")) + .get(); + + assertThat(response.getHits().getTotalHits(), equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + } + + public void testParentFieldToNonExistingType() { + assertAcked(prepareCreate("test") + .addMapping("parent").addMapping("child", "_parent", "type=parent2")); + client().prepareIndex("test", "parent", "1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test", "child", "1").setParent("1").setSource("{}", XContentType.JSON).get(); + refresh(); + + try { + client().prepareSearch("test") + .setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + } + } + + /* + Test for https://github.com/elastic/elasticsearch/issues/3444 + */ + public void testBulkUpdateDocAsUpsertWithParent() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent", "{\"parent\":{}}", XContentType.JSON) + .addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON)); + 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") + .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/modules/parent-join/src/test/resources/rest-api-spec/test/11_parent_child.yml b/modules/parent-join/src/test/resources/rest-api-spec/test/11_parent_child.yml new file mode 100644 index 0000000000..24f9931be0 --- /dev/null +++ b/modules/parent-join/src/test/resources/rest-api-spec/test/11_parent_child.yml @@ -0,0 +1,44 @@ +setup: + - do: + indices.create: + index: test + body: + mappings: + doc: + properties: + join_field: + type: join + parent: child + +--- +"Parent/child inner hits": + - skip: + version: " - 5.99.99" + reason: mapping.single_type was added in 6.0 + + - do: + index: + index: test + type: doc + id: 1 + body: {"foo": "bar", "join_field": {"name" : "parent"} } + + - do: + index: + index: test + type: doc + id: 2 + routing: 1 + body: {"bar": "baz", "join_field": { "name" : "child", "parent": "1"} } + + - do: + indices.refresh: {} + + # TODO: re-add inner hits here + - do: + search: + body: { "query" : { "has_child" : { "type" : "child", "query" : { "match_all" : {} } } } } + - match: { hits.total: 1 } + - match: { hits.hits.0._index: "test" } + - match: { hits.hits.0._type: "doc" } + - match: { hits.hits.0._id: "1" } |