diff --git a/src/main/java/org/elasticsearch/index/AbstractIndexComponent.java b/src/main/java/org/elasticsearch/index/AbstractIndexComponent.java index 6b2cd15a18d2b..c6f1fdf4d27c6 100644 --- a/src/main/java/org/elasticsearch/index/AbstractIndexComponent.java +++ b/src/main/java/org/elasticsearch/index/AbstractIndexComponent.java @@ -52,6 +52,10 @@ public Index index() { return this.index; } + public Settings indexSettings() { + return indexSettings; + } + public String nodeName() { return indexSettings.get("name", ""); } diff --git a/src/main/java/org/elasticsearch/index/fielddata/plain/ParentChildIndexFieldData.java b/src/main/java/org/elasticsearch/index/fielddata/plain/ParentChildIndexFieldData.java index 98cc061f05aa8..40687d00bb27b 100644 --- a/src/main/java/org/elasticsearch/index/fielddata/plain/ParentChildIndexFieldData.java +++ b/src/main/java/org/elasticsearch/index/fielddata/plain/ParentChildIndexFieldData.java @@ -21,16 +21,11 @@ import com.carrotsearch.hppc.ObjectObjectOpenHashMap; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.LeafReader; -import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.*; import org.apache.lucene.index.MultiDocValues.OrdinalMap; -import org.apache.lucene.index.PostingsEnum; -import org.apache.lucene.index.SortedDocValues; -import org.apache.lucene.index.Terms; -import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.BytesRef; @@ -40,6 +35,7 @@ import org.apache.lucene.util.packed.PackedLongValues; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchIllegalStateException; +import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.collect.ImmutableOpenMap; @@ -115,16 +111,54 @@ public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, Mu } @Override - public ParentChildAtomicFieldData loadDirect(LeafReaderContext context) throws Exception { + public AbstractAtomicParentChildFieldData loadDirect(LeafReaderContext context) throws Exception { LeafReader reader = context.reader(); final float acceptableTransientOverheadRatio = fieldDataType.getSettings().getAsFloat( "acceptable_transient_overhead_ratio", OrdinalsBuilder.DEFAULT_ACCEPTABLE_OVERHEAD_RATIO ); + final NavigableSet parentTypes; synchronized (lock) { parentTypes = ImmutableSortedSet.copyOf(BytesRef.getUTF8SortedAsUnicodeComparator(), this.parentTypes); } + if (Version.indexCreated(indexSettings).onOrAfter(Version.V_2_0_0)) { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + for (BytesRef parentType : parentTypes) { + SortedDocValues docValues = DocValues.getSorted(reader, ParentFieldMapper.joinField(parentType.utf8ToString())); + builder.put(parentType.utf8ToString(), docValues); + } + return new AbstractAtomicParentChildFieldData() { + + private final ImmutableMap typeToJoinField = builder.build(); + + @Override + public Set types() { + return typeToJoinField.keySet(); + } + + @Override + public SortedDocValues getOrdinalsValues(String type) { + return typeToJoinField.get(type); + } + + @Override + public long ramBytesUsed() { + // unknown + return 0; + } + + @Override + public Collection getChildResources() { + return Collections.emptyList(); + } + + @Override + public void close() throws ElasticsearchException { + } + }; + } + boolean success = false; ParentChildAtomicFieldData data = null; ParentChildFilteredTermsEnum termsEnum = new ParentChildFilteredTermsEnum( @@ -330,12 +364,14 @@ public IndexParentChildFieldData localGlobalDirect(IndexReader indexReader) thro long ramBytesUsed = 0; final Map perType = new HashMap<>(); + final Map ordinalMapPerType = new HashMap<>(); for (String type : parentTypes) { final AtomicParentChildFieldData[] fieldData = new AtomicParentChildFieldData[indexReader.leaves().size()]; for (LeafReaderContext context : indexReader.leaves()) { fieldData[context.ord] = load(context); } final OrdinalMap ordMap = buildOrdinalMap(fieldData, type); + ordinalMapPerType.put(type, ordMap); ramBytesUsed += ordMap.ramBytesUsed(); perType.put(type, new OrdinalMapAndAtomicFieldData(ordMap, fieldData)); } @@ -353,7 +389,7 @@ public IndexParentChildFieldData localGlobalDirect(IndexReader indexReader) thro ); } - return new GlobalFieldData(indexReader, fielddata, ramBytesUsed); + return new GlobalFieldData(indexReader, fielddata, ramBytesUsed, ordinalMapPerType); } private static class GlobalAtomicFieldData extends AbstractAtomicParentChildFieldData { @@ -437,16 +473,18 @@ public void close() throws ElasticsearchException { } - private class GlobalFieldData implements IndexParentChildFieldData, Accountable { + public class GlobalFieldData implements IndexParentChildFieldData, Accountable { private final AtomicParentChildFieldData[] fielddata; private final IndexReader reader; private final long ramBytesUsed; + private final Map ordinalMapPerType; - GlobalFieldData(IndexReader reader, AtomicParentChildFieldData[] fielddata, long ramBytesUsed) { + GlobalFieldData(IndexReader reader, AtomicParentChildFieldData[] fielddata, long ramBytesUsed, Map ordinalMapPerType) { this.reader = reader; this.ramBytesUsed = ramBytesUsed; this.fielddata = fielddata; + this.ordinalMapPerType = ordinalMapPerType; } @Override @@ -513,6 +551,11 @@ public IndexParentChildFieldData localGlobalDirect(IndexReader indexReader) thro return loadGlobal(indexReader); } + // TODO: Need to find a better way to expose the OrdinalMap... + public OrdinalMap getOrdinalMap(String type) { + return ordinalMapPerType.get(type); + } + } } diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java index 3b733fe3ae6e9..6a532d008e87a 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/ParentFieldMapper.java @@ -19,9 +19,9 @@ package org.elasticsearch.index.mapper.internal; import com.google.common.base.Objects; - import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; +import org.apache.lucene.document.SortedDocValuesField; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.Term; import org.apache.lucene.queries.TermFilter; @@ -39,16 +39,9 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.loader.SettingsLoader; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.fielddata.FieldDataType; -import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.InternalMapper; -import org.elasticsearch.index.mapper.Mapper; -import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.MergeContext; -import org.elasticsearch.index.mapper.MergeMappingException; -import org.elasticsearch.index.mapper.ParseContext; -import org.elasticsearch.index.mapper.RootMapper; -import org.elasticsearch.index.mapper.Uid; +import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; import org.elasticsearch.index.query.QueryParseContext; @@ -91,6 +84,7 @@ public static class Builder extends Mapper.Builder { protected String indexName; + private boolean parent; private String type; protected Settings fieldDataSettings; @@ -100,6 +94,11 @@ public Builder() { builder = this; } + public Builder parent(boolean parent) { + this.parent = parent; + return this; + } + public Builder type(String type) { this.type = type; return builder; @@ -112,10 +111,10 @@ public Builder fieldDataSettings(Settings settings) { @Override public ParentFieldMapper build(BuilderContext context) { - if (type == null) { + if (!parent && type == null) { throw new MapperParsingException("Parent mapping must contain the parent type"); } - return new ParentFieldMapper(name, indexName, type, fieldDataSettings, context.indexSettings()); + return new ParentFieldMapper(name, indexName, type, fieldDataSettings, context.indexSettings(), parent, context.indexCreatedVersion().onOrAfter(Version.V_2_0_0)); } } @@ -130,6 +129,9 @@ public Mapper.Builder parse(String name, Map node, ParserContext if (fieldName.equals("type")) { builder.type(fieldNode.toString()); iterator.remove(); + } else if (fieldName.equals("parent")) { + builder.parent(XContentMapValues.nodeBooleanValue(fieldNode)); + iterator.remove(); } else if (fieldName.equals("postings_format") && parserContext.indexVersionCreated().before(Version.V_2_0_0)) { // ignore before 2.0, reject on and after 2.0 iterator.remove(); @@ -147,18 +149,22 @@ public Mapper.Builder parse(String name, Map node, ParserContext } } + private final boolean parent; private final String type; private final BytesRef typeAsBytes; + private final boolean docValuesJoin; - protected ParentFieldMapper(String name, String indexName, String type, @Nullable Settings fieldDataSettings, Settings indexSettings) { + protected ParentFieldMapper(String name, String indexName, String type, @Nullable Settings fieldDataSettings, Settings indexSettings, boolean parent, boolean docValuesJoin) { super(new Names(name, indexName, indexName, name), Defaults.BOOST, new FieldType(Defaults.FIELD_TYPE), false, Lucene.KEYWORD_ANALYZER, Lucene.KEYWORD_ANALYZER, null, null, fieldDataSettings, indexSettings); this.type = type; + this.parent = parent; + this.docValuesJoin = docValuesJoin; this.typeAsBytes = type == null ? null : new BytesRef(type); } public ParentFieldMapper(Settings indexSettings) { - this(Defaults.NAME, Defaults.NAME, null, null, indexSettings); + this(Defaults.NAME, Defaults.NAME, null, null, indexSettings, false, Version.indexCreated(indexSettings).onOrAfter(Version.V_2_0_0)); this.fieldDataType = new FieldDataType("_parent", settingsBuilder().put(Loading.KEY, Loading.LAZY_VALUE)); } @@ -192,6 +198,11 @@ public boolean includeInObject() { @Override protected void parseCreateField(ParseContext context, List fields) throws IOException { + if (parent) { + assert docValuesJoin; + fields.add(createJoinField(context.type(), context.id())); + } + if (!active()) { return; } @@ -201,6 +212,9 @@ protected void parseCreateField(ParseContext context, List fields) throws String parentId = context.parser().text(); context.sourceToParse().parent(parentId); fields.add(new Field(names.indexName(), Uid.createUid(context.stringBuilder(), type, parentId), fieldType)); + if (docValuesJoin) { + fields.add(createJoinField(type, parentId)); + } } else { // otherwise, we are running it post processing of the xcontent String parsedParentId = context.doc().get(Defaults.NAME); @@ -212,6 +226,9 @@ protected void parseCreateField(ParseContext context, List fields) throws } // we did not add it in the parsing phase, add it now fields.add(new Field(names.indexName(), Uid.createUid(context.stringBuilder(), type, parentId), fieldType)); + if (docValuesJoin) { + fields.add(createJoinField(type, parentId)); + } } else if (parentId != null && !parsedParentId.equals(Uid.createUid(context.stringBuilder(), type, parentId))) { throw new MapperParsingException("Parent id mismatch, document value is [" + Uid.createUid(parsedParentId).id() + "], while external value is [" + parentId + "]"); } @@ -220,6 +237,15 @@ protected void parseCreateField(ParseContext context, List fields) throws // we have parent mapping, yet no value was set, ignore it... } + private SortedDocValuesField createJoinField(String parentType, String id) { + String joinField = joinField(parentType); + return new SortedDocValuesField(joinField, new BytesRef(id)); + } + + public static String joinField(String parentType) { + return ParentFieldMapper.NAME + "#" + parentType; + } + @Override public Uid value(Object value) { if (value == null) { @@ -346,13 +372,18 @@ protected String contentType() { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - if (!active()) { + if (!parent && !active()) { return builder; } boolean includeDefaults = params.paramAsBoolean("include_defaults", false); builder.startObject(CONTENT_TYPE); - builder.field("type", type); + if (type != null) { + builder.field("type", type); + } + if (parent) { + builder.field("parent", parent); + } if (customFieldDataSettings != null) { builder.field("fielddata", (Map) customFieldDataSettings.getAsMap()); } else if (includeDefaults) { diff --git a/src/main/java/org/elasticsearch/index/query/HasChildFilterParser.java b/src/main/java/org/elasticsearch/index/query/HasChildFilterParser.java index 0f83e3408bb4c..205eb78630dd4 100644 --- a/src/main/java/org/elasticsearch/index/query/HasChildFilterParser.java +++ b/src/main/java/org/elasticsearch/index/query/HasChildFilterParser.java @@ -22,6 +22,7 @@ import org.apache.lucene.search.FilteredQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.BitDocIdSetFilter; +import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; @@ -41,6 +42,7 @@ import java.io.IOException; +import static org.elasticsearch.index.query.HasChildQueryParser.joinUtilHelper; import static org.elasticsearch.index.query.QueryParserUtils.ensureNotDeleteByQuery; /** @@ -172,12 +174,16 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar Filter parentFilter = parseContext.cacheFilter(parentDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy()); ParentChildIndexFieldData parentChildIndexFieldData = parseContext.getForField(parentFieldMapper); - Query childrenQuery; - if (minChildren > 1 || maxChildren > 0) { - childrenQuery = new ChildrenQuery(parentChildIndexFieldData, parentType, childType, parentFilter,query,ScoreType.NONE,minChildren, maxChildren, shortCircuitParentDocSet, nonNestedDocsFilter); + final Query childrenQuery; + if (parseContext.indexVersionCreated().onOrAfter(Version.V_2_0_0)) { + childrenQuery = joinUtilHelper(parentType, parentChildIndexFieldData, parentFilter, ScoreType.NONE, query); } else { - childrenQuery = new ChildrenConstantScoreQuery(parentChildIndexFieldData, query, parentType, childType, parentFilter, - shortCircuitParentDocSet, nonNestedDocsFilter); + if (minChildren > 1 || maxChildren > 0) { + childrenQuery = new ChildrenQuery(parentChildIndexFieldData, parentType, childType, parentFilter,query,ScoreType.NONE,minChildren, maxChildren, shortCircuitParentDocSet, nonNestedDocsFilter); + } else { + childrenQuery = new ChildrenConstantScoreQuery(parentChildIndexFieldData, query, parentType, childType, parentFilter, + shortCircuitParentDocSet, nonNestedDocsFilter); + } } if (filterName != null) { parseContext.addNamedFilter(filterName, new CustomQueryWrappingFilter(childrenQuery)); diff --git a/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java b/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java index c7e8f2567d41e..70d12b3ca9cc3 100644 --- a/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java @@ -19,14 +19,18 @@ package org.elasticsearch.index.query; -import org.apache.lucene.search.Filter; -import org.apache.lucene.search.FilteredQuery; -import org.apache.lucene.search.Query; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.MultiDocValues; +import org.apache.lucene.search.*; import org.apache.lucene.search.join.BitDocIdSetFilter; +import org.apache.lucene.search.join.JoinUtil; +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.fielddata.IndexParentChildFieldData; import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.internal.ParentFieldMapper; @@ -38,6 +42,7 @@ import org.elasticsearch.index.search.child.ScoreType; import org.elasticsearch.index.search.nested.NonNestedDocsFilter; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; +import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SubSearchContext; import java.io.IOException; @@ -171,15 +176,19 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars // wrap the query with type query innerQuery = new FilteredQuery(innerQuery, parseContext.cacheFilter(childDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy())); - Query query; - Filter parentFilter = parseContext.cacheFilter(parentDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy()); - ParentChildIndexFieldData parentChildIndexFieldData = parseContext.getForField(parentFieldMapper); - if (minChildren > 1 || maxChildren > 0 || scoreType != ScoreType.NONE) { - query = new ChildrenQuery(parentChildIndexFieldData, parentType, childType, parentFilter, innerQuery, scoreType, minChildren, - maxChildren, shortCircuitParentDocSet, nonNestedDocsFilter); + final Query query; + final ParentChildIndexFieldData parentChildIndexFieldData = parseContext.getForField(parentFieldMapper); + final Filter parentFilter = parseContext.cacheFilter(parentDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy()); + if (parseContext.indexVersionCreated().onOrAfter(Version.V_2_0_0)) { + query = joinUtilHelper(parentType, parentChildIndexFieldData, parentFilter, scoreType, innerQuery); } else { - query = new ChildrenConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentType, childType, parentFilter, - shortCircuitParentDocSet, nonNestedDocsFilter); + if (minChildren > 1 || maxChildren > 0 || scoreType != ScoreType.NONE) { + query = new ChildrenQuery(parentChildIndexFieldData, parentType, childType, parentFilter, innerQuery, scoreType, minChildren, + maxChildren, shortCircuitParentDocSet, nonNestedDocsFilter); + } else { + query = new ChildrenConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentType, childType, parentFilter, + shortCircuitParentDocSet, nonNestedDocsFilter); + } } if (queryName != null) { parseContext.addNamedFilter(queryName, new CustomQueryWrappingFilter(query)); @@ -187,4 +196,43 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars query.setBoost(boost); return query; } + + public static Query joinUtilHelper(String parentType, ParentChildIndexFieldData parentChildIndexFieldData, Filter toFilter, ScoreType scoreType, Query innerQuery) throws IOException { + String joinField = ParentFieldMapper.joinField(parentType); + BooleanQuery toQuery = new BooleanQuery(); + toQuery.add(toFilter, BooleanClause.Occur.FILTER); + SearchContext searchContext = SearchContext.current(); + ScoreMode scoreMode; + // TODO: grrr... we should move over to org.apache.lucene.join.ScoreMode, but we can do that when all our ScoreTypes are in the join module + switch (scoreType) { + case NONE: + scoreMode = ScoreMode.None; + break; + case MIN: + throw new UnsupportedOperationException("score type min not supported"); + case MAX: + scoreMode = ScoreMode.Max; + break; + case SUM: + scoreMode = ScoreMode.Total; + break; + case AVG: + scoreMode = ScoreMode.Avg; + break; + default: + throw new UnsupportedOperationException("score type [" + scoreType + "] not supported"); + } + IndexReader indexReader = searchContext.searcher().getIndexReader(); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + IndexParentChildFieldData indexParentChildFieldData = parentChildIndexFieldData.loadGlobal(indexReader); + MultiDocValues.OrdinalMap ordinalMap; + if (indexParentChildFieldData instanceof ParentChildIndexFieldData.GlobalFieldData) { + // TODO: find a nicer way? + ordinalMap = ((ParentChildIndexFieldData.GlobalFieldData) indexParentChildFieldData).getOrdinalMap(parentType); + } else { + // one segment, local ordinals are global + ordinalMap = null; + } + return JoinUtil.createJoinQuery(joinField, innerQuery, toQuery, indexSearcher, scoreMode, ordinalMap); + } } diff --git a/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java b/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java index a44d5a699177f..b6a679ef8f7fa 100644 --- a/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java @@ -22,6 +22,7 @@ import org.apache.lucene.search.Filter; import org.apache.lucene.search.FilteredQuery; import org.apache.lucene.search.Query; +import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; @@ -36,6 +37,7 @@ import org.elasticsearch.index.search.child.CustomQueryWrappingFilter; import org.elasticsearch.index.search.child.ParentConstantScoreQuery; import org.elasticsearch.index.search.child.ParentQuery; +import org.elasticsearch.index.search.child.ScoreType; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.internal.SubSearchContext; @@ -43,6 +45,7 @@ import java.util.HashSet; import java.util.Set; +import static org.elasticsearch.index.query.HasChildQueryParser.joinUtilHelper; import static org.elasticsearch.index.query.QueryParserUtils.ensureNotDeleteByQuery; public class HasParentQueryParser implements QueryParser { @@ -144,7 +147,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars return query; } - static Query createParentQuery(Query innerQuery, String parentType, boolean score, QueryParseContext parseContext, Tuple innerHits) { + static Query createParentQuery(Query innerQuery, String parentType, boolean score, QueryParseContext parseContext, Tuple innerHits) throws IOException { DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType); if (parentDocMapper == null) { throw new QueryParsingException(parseContext.index(), "[has_parent] query configured 'parent_type' [" + parentType + "] is not a valid type"); @@ -198,10 +201,15 @@ static Query createParentQuery(Query innerQuery, String parentType, boolean scor // wrap the query with type query innerQuery = new FilteredQuery(innerQuery, parseContext.cacheFilter(parentDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy())); Filter childrenFilter = parseContext.cacheFilter(new NotFilter(parentFilter), null, parseContext.autoFilterCachePolicy()); - if (score) { - return new ParentQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter); + if (parseContext.indexVersionCreated().onOrAfter(Version.V_2_0_0)) { + ScoreType scoreMode = score ? ScoreType.MAX : ScoreType.NONE; + return joinUtilHelper(parentType, parentChildIndexFieldData, childrenFilter, scoreMode, innerQuery); } else { - return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter); + if (score) { + return new ParentQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter); + } else { + return new ParentConstantScoreQuery(parentChildIndexFieldData, innerQuery, parentDocMapper.type(), childrenFilter); + } } } diff --git a/src/main/java/org/elasticsearch/index/query/QueryParseContext.java b/src/main/java/org/elasticsearch/index/query/QueryParseContext.java index fe6292735ca70..b2dce20ff94ee 100644 --- a/src/main/java/org/elasticsearch/index/query/QueryParseContext.java +++ b/src/main/java/org/elasticsearch/index/query/QueryParseContext.java @@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; - import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.queryparser.classic.MapperQueryParser; import org.apache.lucene.queryparser.classic.QueryParserSettings; @@ -48,12 +47,7 @@ import org.elasticsearch.index.analysis.AnalysisService; import org.elasticsearch.index.cache.query.parser.QueryParserCache; import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.FieldMappers; -import org.elasticsearch.index.mapper.Mapper; -import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.mapper.MapperBuilders; -import org.elasticsearch.index.mapper.ContentPath; +import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.StringFieldMapper; import org.elasticsearch.index.query.support.NestedScope; import org.elasticsearch.index.search.child.CustomQueryWrappingFilter; @@ -93,6 +87,8 @@ public static void removeTypes() { private final Index index; + private final Version indexVersionCreated; + private boolean propagateNoCache = false; private boolean requireCustomQueryWrappingFilter = false; @@ -121,6 +117,7 @@ public QueryParseContext(Index index, IndexQueryParserService indexQueryParser) public QueryParseContext(Index index, IndexQueryParserService indexQueryParser, boolean disableFilterCaching) { this.index = index; + this.indexVersionCreated = Version.indexCreated(indexQueryParser.indexSettings()); this.indexQueryParser = indexQueryParser; this.propagateNoCache = disableFilterCaching; this.disableFilterCaching = disableFilterCaching; @@ -481,4 +478,8 @@ public boolean requireCustomQueryWrappingFilter() { public NestedScope nestedScope() { return nestedScope; } + + public Version indexVersionCreated() { + return indexVersionCreated; + } } diff --git a/src/main/java/org/elasticsearch/index/query/TopChildrenQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/TopChildrenQueryBuilder.java index a8174230db932..011f681722769 100644 --- a/src/main/java/org/elasticsearch/index/query/TopChildrenQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/TopChildrenQueryBuilder.java @@ -25,6 +25,7 @@ /** * */ +@Deprecated public class TopChildrenQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder { private final QueryBuilder queryBuilder; diff --git a/src/main/java/org/elasticsearch/index/query/TopChildrenQueryParser.java b/src/main/java/org/elasticsearch/index/query/TopChildrenQueryParser.java index a0f5834b44eba..92ed6d1937e6f 100644 --- a/src/main/java/org/elasticsearch/index/query/TopChildrenQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/TopChildrenQueryParser.java @@ -21,6 +21,7 @@ import org.apache.lucene.search.FilteredQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.BitDocIdSetFilter; +import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.xcontent.XContentParser; @@ -40,6 +41,7 @@ /** * */ +@Deprecated public class TopChildrenQueryParser implements QueryParser { public static final String NAME = "top_children"; @@ -58,6 +60,11 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars ensureNotDeleteByQuery(NAME, parseContext); XContentParser parser = parseContext.parser(); + // TODO: maybe remove entirely? + if (parseContext.indexVersionCreated().onOrAfter(Version.V_2_0_0)) { + throw new UnsupportedOperationException("the [top_children] query has been dropped for indices created on or after 2.0"); + } + boolean queryFound = false; float boost = 1.0f; String childType = null; diff --git a/src/main/java/org/elasticsearch/index/search/child/TopChildrenQuery.java b/src/main/java/org/elasticsearch/index/search/child/TopChildrenQuery.java index 7ca3674576155..cfc1cd8719398 100644 --- a/src/main/java/org/elasticsearch/index/search/child/TopChildrenQuery.java +++ b/src/main/java/org/elasticsearch/index/search/child/TopChildrenQuery.java @@ -54,6 +54,7 @@ * This query is most of the times faster than the {@link ChildrenQuery}. Usually enough parent documents can be returned * in the first child document query round. */ +@Deprecated public class TopChildrenQuery extends Query { private static final ParentDocComparator PARENT_DOC_COMP = new ParentDocComparator(); diff --git a/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchBwcTests.java b/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchBwcTests.java new file mode 100644 index 0000000000000..63302018dfd04 --- /dev/null +++ b/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchBwcTests.java @@ -0,0 +1,2720 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.search.child; + +import org.apache.lucene.util.LuceneTestCase; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ElasticsearchIllegalArgumentException; +import org.elasticsearch.Version; +import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheResponse; +import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; +import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse; +import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; +import org.elasticsearch.action.count.CountResponse; +import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse; +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.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.lucene.search.function.CombineFunction; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.index.cache.filter.AutoFilterCachingPolicy; +import org.elasticsearch.index.cache.filter.FilterCacheModule; +import org.elasticsearch.index.cache.filter.weighted.WeightedFilterCache; +import org.elasticsearch.index.fielddata.FieldDataType; +import org.elasticsearch.index.mapper.FieldMapper.Loading; +import org.elasticsearch.index.mapper.MergeMappingException; +import org.elasticsearch.index.query.FilterBuilder; +import org.elasticsearch.index.query.FilterBuilders; +import org.elasticsearch.index.query.HasChildFilterBuilder; +import org.elasticsearch.index.query.HasChildQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.search.child.ScoreType; +import org.elasticsearch.rest.RestStatus; +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.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; +import org.elasticsearch.test.ElasticsearchIntegrationTest.Scope; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import static com.google.common.collect.Maps.newHashMap; +import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; +import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.common.settings.ImmutableSettings.builder; +import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.query.FilterBuilders.boolFilter; +import static org.elasticsearch.index.query.FilterBuilders.hasParentFilter; +import static org.elasticsearch.index.query.FilterBuilders.matchAllFilter; +import static org.elasticsearch.index.query.FilterBuilders.notFilter; +import static org.elasticsearch.index.query.FilterBuilders.queryFilter; +import static org.elasticsearch.index.query.FilterBuilders.termFilter; +import static org.elasticsearch.index.query.FilterBuilders.termsFilter; +import static org.elasticsearch.index.query.QueryBuilders.boolQuery; +import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; +import static org.elasticsearch.index.query.QueryBuilders.filteredQuery; +import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; +import static org.elasticsearch.index.query.QueryBuilders.idsQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.index.query.QueryBuilders.matchQuery; +import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; +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.topChildrenQuery; +import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.factorFunction; +import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; + +/** + * + */ +@ClusterScope(scope = Scope.SUITE) +public class SimpleChildQuerySearchBwcTests extends ElasticsearchIntegrationTest { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return ImmutableSettings.settingsBuilder().put(super.nodeSettings(nodeOrdinal)) + // aggressive filter caching so that we can assert on the filter cache size + .put(FilterCacheModule.FilterCacheSettings.FILTER_CACHE_TYPE, WeightedFilterCache.class) + .put(AutoFilterCachingPolicy.AGGRESSIVE_CACHING_SETTINGS) + .build(); + } + + @Override + public Settings indexSettings() { + return ImmutableSettings.builder() + .put(super.indexSettings()).put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_1_6_0).build(); + } + + @Test + public void multiLevelChild() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + .addMapping("grandchild", "_parent", "type=child")); + 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(); + refresh(); + + SearchResponse searchResponse = client() + .prepareSearch("test") + .setQuery( + filteredQuery( + matchAllQuery(), + hasChildFilter( + "child", + filteredQuery(termQuery("c_field", "c_value1"), + hasChildFilter("grandchild", termQuery("gc_field", "gc_value1")))))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), hasParentFilter("parent", termFilter("p_field", "p_value1")))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), hasParentFilter("child", termFilter("c_field", "c_value1")))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("gc1")); + + searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c1")); + + searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("gc1")); + } + + @Test + // see #6722 + public void test6722() throws ElasticsearchException, IOException { + assertAcked(prepareCreate("test") + .addMapping("foo") + .addMapping("test", "_parent", "type=foo")); + ensureGreen(); + + // index simple data + client().prepareIndex("test", "foo", "1").setSource("foo", 1).get(); + client().prepareIndex("test", "test", "2").setSource("foo", 1).setParent("1").get(); + refresh(); + String query = copyToStringFromClasspath("/org/elasticsearch/search/child/bool-query-with-empty-clauses.json"); + SearchResponse searchResponse = client().prepareSearch("test").setSource(query).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); + } + + @Test + // see #2744 + public void test2744() throws ElasticsearchException, IOException { + assertAcked(prepareCreate("test") + .addMapping("foo") + .addMapping("test", "_parent", "type=foo")); + ensureGreen(); + + // index simple data + client().prepareIndex("test", "foo", "1").setSource("foo", 1).get(); + client().prepareIndex("test", "test").setSource("foo", 1).setParent("1").get(); + refresh(); + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("test", matchQuery("foo", 1))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("1")); + + } + + @Test + public void simpleChildQuery() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + refresh(); + + // TEST FETCHING _parent from child + SearchResponse searchResponse = client().prepareSearch("test").setQuery(idsQuery("child").ids("c1")).addFields("_parent").execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c1")); + assertThat(searchResponse.getHits().getAt(0).field("_parent").value().toString(), equalTo("p1")); + + // TEST matching on parent + searchResponse = client().prepareSearch("test").setQuery(termQuery("_parent", "p1")).addFields("_parent").get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(0).field("_parent").value().toString(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(1).field("_parent").value().toString(), equalTo("p1")); + + searchResponse = client().prepareSearch("test").setQuery(queryStringQuery("_parent:p1")).addFields("_parent").get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(0).field("_parent").value().toString(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(searchResponse.getHits().getAt(1).field("_parent").value().toString(), equalTo("p1")); + + // TOP CHILDREN QUERY + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))).execute() + .actionGet(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "blue"))) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); + + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "red"))).execute() + .actionGet(); + assertHitCount(searchResponse, 2l); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); + + // HAS CHILD + searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + + searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "blue")).execute() + .actionGet(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); + + searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "red")).get(); + assertHitCount(searchResponse, 2l); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); + + // HAS PARENT + searchResponse = client().prepareSearch("test") + .setQuery(randomHasParent("parent", "p_field", "p_value2")).get(); + assertNoFailures(searchResponse); + assertHitCount(searchResponse, 2l); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c3")); + assertThat(searchResponse.getHits().getAt(1).id(), equalTo("c4")); + + searchResponse = client().prepareSearch("test") + .setQuery(randomHasParent("parent", "p_field", "p_value1")).get(); + assertHitCount(searchResponse, 2l); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c1")); + assertThat(searchResponse.getHits().getAt(1).id(), equalTo("c2")); + } + + @Test + @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch/issues/9270") + public void testClearIdCacheBug() throws Exception { + // enforce lazy loading to make sure that p/c stats are not counted as part of field data + assertAcked(prepareCreate("test") + .setSettings(ImmutableSettings.builder().put(indexSettings()) + .put("index.refresh_interval", -1)) // Disable automatic refresh, so that the _parent doesn't get warmed + .addMapping("parent", XContentFactory.jsonBuilder().startObject().startObject("parent") + .startObject("properties") + .startObject("p_field") + .field("type", "string") + .startObject("fielddata") + .field(FieldDataType.FORMAT_KEY, Loading.LAZY) + .endObject() + .endObject() + .endObject().endObject().endObject())); + + ensureGreen(); + + client().prepareIndex("test", "parent", "p0").setSource("p_field", "p_value0").get(); + client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); + + refresh(); + // No _parent field yet, there shouldn't be anything in the parent id cache + IndicesStatsResponse indicesStatsResponse = client().admin().indices() + .prepareStats("test").setIdCache(true).get(); + assertThat(indicesStatsResponse.getTotal().getIdCache().getMemorySizeInBytes(), equalTo(0l)); + + // Now add mapping + children + client().admin().indices().preparePutMapping("test").setType("child") + .setSource(XContentFactory.jsonBuilder().startObject().startObject("child") + .startObject("_parent") + .field("type", "parent") + .endObject() + .startObject("properties") + .startObject("c_field") + .field("type", "string") + .startObject("fielddata") + .field(FieldDataType.FORMAT_KEY, Loading.LAZY) + .endObject() + .endObject() + .endObject().endObject().endObject()) + .get(); + + // index simple data + 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(); + + refresh(); + + indicesStatsResponse = client().admin().indices() + .prepareStats("test").setFieldData(true).get(); + // automatic warm-up has populated the cache since it found a parent field mapper + assertThat(indicesStatsResponse.getTotal().getIdCache().getMemorySizeInBytes(), greaterThan(0l)); + // Even though p/c is field data based the stats stay zero, because _parent field data field is kept + // track of under id cache stats memory wise for bwc + assertThat(indicesStatsResponse.getTotal().getFieldData().getMemorySizeInBytes(), equalTo(0l)); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "blue")))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + + indicesStatsResponse = client().admin().indices() + .prepareStats("test").setFieldData(true).get(); + assertThat(indicesStatsResponse.getTotal().getIdCache().getMemorySizeInBytes(), greaterThan(0l)); + assertThat(indicesStatsResponse.getTotal().getFieldData().getMemorySizeInBytes(), equalTo(0l)); + + ClearIndicesCacheResponse clearCacheResponse = client().admin().indices().prepareClearCache("test").setIdCache(true).get(); + assertNoFailures(clearCacheResponse); + assertAllSuccessful(clearCacheResponse); + indicesStatsResponse = client().admin().indices() + .prepareStats("test").setFieldData(true).get(); + assertThat(indicesStatsResponse.getTotal().getIdCache().getMemorySizeInBytes(), equalTo(0l)); + assertThat(indicesStatsResponse.getTotal().getFieldData().getMemorySizeInBytes(), equalTo(0l)); + } + + @Test + // See: https://github.com/elasticsearch/elasticsearch/issues/3290 + public void testCachingBug_withFqueryFilter() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + List 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)); + } + 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)); + } + 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))); + } + + if (randomBoolean()) { + break; // randomly break out and dont' have deletes / updates + } + } + indexRandom(true, builders); + + for (int i = 1; i <= 10; i++) { + logger.info("Round {}", i); + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(queryFilter(topChildrenQuery("child", matchAllQuery())).cache(true))).execute() + .actionGet(); + assertNoFailures(searchResponse); + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(queryFilter(hasChildQuery("child", matchAllQuery()).scoreType("max")).cache(true))) + .get(); + assertNoFailures(searchResponse); + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(queryFilter(hasParentQuery("parent", matchAllQuery()).scoreType("score")).cache(true))) + .get(); + assertNoFailures(searchResponse); + } + } + + @Test + public void testHasParentFilter() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + Map> parentToChildren = newHashMap(); + // Childless parent + client().prepareIndex("test", "parent", "p0").setSource("p_field", "p0").get(); + parentToChildren.put("p0", new HashSet()); + + String previousParentId = null; + int numChildDocs = 32; + int numChildDocsPerParent = 0; + List builders = new ArrayList<>(); + for (int i = 1; i <= numChildDocs; i++) { + + if (previousParentId == null || i % numChildDocsPerParent == 0) { + previousParentId = "p" + i; + builders.add(client().prepareIndex("test", "parent", previousParentId).setSource("p_field", previousParentId)); + numChildDocsPerParent++; + } + + String childId = "c" + i; + builders.add(client().prepareIndex("test", "child", childId).setSource("c_field", childId).setParent(previousParentId)); + + if (!parentToChildren.containsKey(previousParentId)) { + parentToChildren.put(previousParentId, new HashSet()); + } + assertThat(parentToChildren.get(previousParentId).add(childId), is(true)); + } + indexRandom(true, builders.toArray(new IndexRequestBuilder[builders.size()])); + + assertThat(parentToChildren.isEmpty(), equalTo(false)); + for (Map.Entry> parentToChildrenEntry : parentToChildren.entrySet()) { + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasParentFilter("parent", termQuery("p_field", parentToChildrenEntry.getKey())))) + .setSize(numChildDocsPerParent).get(); + + assertNoFailures(searchResponse); + Set childIds = parentToChildrenEntry.getValue(); + assertThat(searchResponse.getHits().totalHits(), equalTo((long) childIds.size())); + for (int i = 0; i < searchResponse.getHits().totalHits(); i++) { + assertThat(childIds.remove(searchResponse.getHits().getAt(i).id()), is(true)); + assertThat(searchResponse.getHits().getAt(i).score(), is(1.0f)); + } + assertThat(childIds.size(), is(0)); + } + } + + @Test + public void simpleChildQueryWithFlush() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + // index simple data with flushes, so we have many segments + client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test", "child", "c1").setSource("c_field", "red").setParent("p1").get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test", "child", "c2").setSource("c_field", "yellow").setParent("p1").get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test", "parent", "p2").setSource("p_field", "p_value2").get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test", "child", "c3").setSource("c_field", "blue").setParent("p2").get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test", "child", "c4").setSource("c_field", "red").setParent("p2").get(); + client().admin().indices().prepareFlush().get(); + refresh(); + + // TOP CHILDREN QUERY + + SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))) + .get(); + assertNoFailures(searchResponse); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "blue"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); + + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "red"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); + + // HAS CHILD QUERY + + searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + + searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "blue"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); + + searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "red"))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); + + // HAS CHILD FILTER + + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "yellow")))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "blue")))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); + + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "red")))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); + } + + @Test + public void testScopedFacet() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + + refresh(); + + SearchResponse searchResponse = client() + .prepareSearch("test") + .setQuery(topChildrenQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")))) + .addAggregation(AggregationBuilders.global("global").subAggregation( + AggregationBuilders.filter("filter").filter(boolFilter().should(termFilter("c_field", "red")).should(termFilter("c_field", "yellow"))).subAggregation( + AggregationBuilders.terms("facet1").field("c_field")))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); + + Global global = searchResponse.getAggregations().get("global"); + Filter filter = global.getAggregations().get("filter"); + Terms termsFacet = filter.getAggregations().get("facet1"); + assertThat(termsFacet.getBuckets().size(), equalTo(2)); + assertThat(termsFacet.getBuckets().get(0).getKeyAsString(), equalTo("red")); + assertThat(termsFacet.getBuckets().get(0).getDocCount(), equalTo(2L)); + assertThat(termsFacet.getBuckets().get(1).getKeyAsString(), equalTo("yellow")); + assertThat(termsFacet.getBuckets().get(1).getDocCount(), equalTo(1L)); + } + + @Test + public void testDeletedParent() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + + refresh(); + + // TOP CHILDREN QUERY + + SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(0).sourceAsString(), containsString("\"p_value1\"")); + + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "yellow")))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(0).sourceAsString(), containsString("\"p_value1\"")); + + // update p1 and see what that we get updated values... + + client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1_updated").get(); + client().admin().indices().prepareRefresh().get(); + + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(0).sourceAsString(), containsString("\"p_value1_updated\"")); + + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "yellow")))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(0).sourceAsString(), containsString("\"p_value1_updated\"")); + } + + @Test + public void testDfsSearchType() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + .setQuery(boolQuery().mustNot(hasChildQuery("child", boolQuery().should(queryStringQuery("c_field:*"))))).get(); + assertNoFailures(searchResponse); + + searchResponse = client().prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + .setQuery(boolQuery().mustNot(hasParentQuery("parent", boolQuery().should(queryStringQuery("p_field:*"))))).execute() + .actionGet(); + assertNoFailures(searchResponse); + + searchResponse = client().prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + .setQuery(boolQuery().mustNot(topChildrenQuery("child", boolQuery().should(queryStringQuery("c_field:*"))))).execute() + .actionGet(); + assertNoFailures(searchResponse); + } + + @Test + public void testFixAOBEIfTopChildrenIsWrappedInMusNotClause() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) + .setQuery(boolQuery().mustNot(topChildrenQuery("child", boolQuery().should(queryStringQuery("c_field:*"))))).execute() + .actionGet(); + assertNoFailures(searchResponse); + } + + @Test + public void testTopChildrenReSearchBug() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + int numberOfParents = 4; + int numberOfChildrenPerParent = 123; + for (int i = 1; i <= numberOfParents; i++) { + String parentId = String.format(Locale.ROOT, "p%d", i); + client().prepareIndex("test", "parent", parentId).setSource("p_field", String.format(Locale.ROOT, "p_value%d", i)).execute() + .actionGet(); + for (int j = 1; j <= numberOfChildrenPerParent; j++) { + client().prepareIndex("test", "child", String.format(Locale.ROOT, "%s_c%d", parentId, j)) + .setSource("c_field1", parentId, "c_field2", i % 2 == 0 ? "even" : "not_even").setParent(parentId).execute() + .actionGet(); + } + } + + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field1", "p3"))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p3")); + + searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field2", "even"))).execute() + .actionGet(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p4"))); + assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p4"))); + } + + @Test + public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrChildDocs() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); + client().prepareIndex("test", "child", "1").setParent("1").setSource("c_field", 1).get(); + client().admin().indices().prepareFlush("test").get(); + + client().prepareIndex("test", "type1", "1").setSource("p_field", 1).get(); + client().admin().indices().prepareFlush("test").get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), hasChildFilter("child", matchAllQuery()))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), hasParentFilter("parent", matchAllQuery()))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + } + + @Test + public void testCountApiUsage() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + refresh(); + + CountResponse countResponse = client().prepareCount("test").setQuery(topChildrenQuery("child", termQuery("c_field", "1"))) + .get(); + assertHitCount(countResponse, 1l); + + countResponse = client().prepareCount("test").setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) + .get(); + assertHitCount(countResponse, 1l); + + countResponse = client().prepareCount("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score")) + .get(); + assertHitCount(countResponse, 1l); + + countResponse = client().prepareCount("test").setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "1")))) + .get(); + assertHitCount(countResponse, 1l); + + countResponse = client().prepareCount("test").setQuery(constantScoreQuery(hasParentFilter("parent", termQuery("p_field", "1")))) + .get(); + assertHitCount(countResponse, 1l); + } + + @Test + public void testExplainUsage() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setExplain(true) + .setQuery(topChildrenQuery("child", termQuery("c_field", "1"))) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet...")); + + searchResponse = client().prepareSearch("test") + .setExplain(true) + .setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet...")); + + searchResponse = client().prepareSearch("test") + .setExplain(true) + .setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score")) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet...")); + + ExplainResponse explainResponse = client().prepareExplain("test", "parent", parentId) + .setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) + .get(); + assertThat(explainResponse.isExists(), equalTo(true)); + assertThat(explainResponse.getExplanation().getDescription(), equalTo("not implemented yet...")); + } + + List createDocBuilders() { + List 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")); + + // 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")); + + // 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")); + + return indexBuilders; + } + + @Test + public void testScoreForParentChildQueries_withFunctionScore() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + .addMapping("child1", "_parent", "type=parent")); + ensureGreen(); + + indexRandom(true, createDocBuilders().toArray(new IndexRequestBuilder[0])); + SearchResponse response = client() + .prepareSearch("test") + .setQuery( + QueryBuilders.hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), scriptFunction("doc['c_field1'].value")) + .boostMode(CombineFunction.REPLACE.getName())).scoreType("sum")).get(); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("1")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(4f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(3f)); + + response = client() + .prepareSearch("test") + .setQuery( + QueryBuilders.hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), scriptFunction("doc['c_field1'].value")) + .boostMode(CombineFunction.REPLACE.getName())).scoreType("max")).get(); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(4f)); + assertThat(response.getHits().hits()[1].id(), equalTo("2")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + assertThat(response.getHits().hits()[2].id(), equalTo("1")); + assertThat(response.getHits().hits()[2].score(), equalTo(2f)); + + response = client() + .prepareSearch("test") + .setQuery( + QueryBuilders.hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), scriptFunction("doc['c_field1'].value")) + .boostMode(CombineFunction.REPLACE.getName())).scoreType("avg")).get(); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(4f)); + assertThat(response.getHits().hits()[1].id(), equalTo("2")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + assertThat(response.getHits().hits()[2].id(), equalTo("1")); + assertThat(response.getHits().hits()[2].score(), equalTo(1.5f)); + + response = client() + .prepareSearch("test") + .setQuery( + QueryBuilders.hasParentQuery( + "parent", + QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), scriptFunction("doc['p_field2'].value")) + .boostMode(CombineFunction.REPLACE.getName())).scoreType("score")) + .addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get(); + + assertThat(response.getHits().totalHits(), equalTo(7l)); + assertThat(response.getHits().hits()[0].id(), equalTo("13")); + assertThat(response.getHits().hits()[0].score(), equalTo(5f)); + assertThat(response.getHits().hits()[1].id(), equalTo("14")); + assertThat(response.getHits().hits()[1].score(), equalTo(5f)); + assertThat(response.getHits().hits()[2].id(), equalTo("15")); + assertThat(response.getHits().hits()[2].score(), equalTo(5f)); + assertThat(response.getHits().hits()[3].id(), equalTo("16")); + assertThat(response.getHits().hits()[3].score(), equalTo(5f)); + assertThat(response.getHits().hits()[4].id(), equalTo("17")); + assertThat(response.getHits().hits()[4].score(), equalTo(5f)); + assertThat(response.getHits().hits()[5].id(), equalTo("18")); + assertThat(response.getHits().hits()[5].score(), equalTo(5f)); + assertThat(response.getHits().hits()[6].id(), equalTo("1")); + assertThat(response.getHits().hits()[6].score(), equalTo(5f)); + } + + @Test + // https://github.com/elasticsearch/elasticsearch/issues/2536 + public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + SearchResponse response = client().prepareSearch("test") + .setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"))).get(); + assertNoFailures(response); + assertThat(response.getHits().totalHits(), equalTo(0l)); + + client().prepareIndex("test", "child1").setSource(jsonBuilder().startObject().field("text", "value").endObject()).setRefresh(true) + .get(); + + response = client().prepareSearch("test").setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value"))).get(); + assertNoFailures(response); + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = client().prepareSearch("test").setQuery(QueryBuilders.hasChildQuery("child", matchQuery("text", "value")).scoreType("max")) + .get(); + assertNoFailures(response); + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("child", matchQuery("text", "value"))).get(); + assertNoFailures(response); + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = client().prepareSearch("test").setQuery(QueryBuilders.hasParentQuery("child", matchQuery("text", "value")).scoreType("score")) + .get(); + assertNoFailures(response); + assertThat(response.getHits().totalHits(), equalTo(0l)); + } + + @Test + public void testHasChildAndHasParentFilter_withFilter() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); + client().prepareIndex("test", "child", "2").setParent("1").setSource("c_field", 1).get(); + client().admin().indices().prepareFlush("test").get(); + + client().prepareIndex("test", "type1", "3").setSource("p_field", "p_value1").get(); + client().admin().indices().prepareFlush("test").get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), hasChildFilter("child", termFilter("c_field", 1)))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().hits()[0].id(), equalTo("1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), hasParentFilter("parent", termFilter("p_field", 1)))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().hits()[0].id(), equalTo("2")); + } + + @Test + public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + client().admin().indices().prepareFlush("test").setForce(true).get(); + client().prepareIndex("test", "child", "2").setParent("1").setSource("c_field", 1).get(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), queryFilter(hasChildQuery("child", matchQuery("c_field", 1))))).get(); + assertSearchHit(searchResponse, 1, hasId("1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), queryFilter(topChildrenQuery("child", matchQuery("c_field", 1))))).get(); + assertSearchHit(searchResponse, 1, hasId("1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), queryFilter(hasParentQuery("parent", matchQuery("p_field", 1))))).get(); + assertSearchHit(searchResponse, 1, hasId("2")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), queryFilter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1)))))).get(); + assertSearchHit(searchResponse, 1, hasId("1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), queryFilter(boolQuery().must(topChildrenQuery("child", matchQuery("c_field", 1)))))).get(); + assertSearchHit(searchResponse, 1, hasId("1")); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), queryFilter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1)))))).get(); + assertSearchHit(searchResponse, 1, hasId("2")); + } + + @Test + public void testHasChildAndHasParentWrappedInAQueryFilterShouldNeverGetCached() throws Exception { + assertAcked(prepareCreate("test") + .setSettings(ImmutableSettings.builder().put("index.cache.filter.type", "weighted")) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + client().prepareIndex("test", "parent", "1").setSource("p_field", 1).get(); + client().prepareIndex("test", "child", "2").setParent("1").setSource("c_field", 1).get(); + refresh(); + + for (int i = 0; i < 10; i++) { + SearchResponse searchResponse = client().prepareSearch("test") + .setExplain(true) + .setQuery(constantScoreQuery(boolFilter() + .must(queryFilter(hasChildQuery("child", matchQuery("c_field", 1)))) + .cache(true) + )).get(); + assertSearchHit(searchResponse, 1, hasId("1")); + // Can't start with ConstantScore(cache(BooleanFilter( + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), startsWith("ConstantScore(BooleanFilter(")); + + searchResponse = client().prepareSearch("test") + .setExplain(true) + .setQuery(constantScoreQuery(boolFilter() + .must(queryFilter(boolQuery().must(matchAllQuery()).must(hasChildQuery("child", matchQuery("c_field", 1))))) + .cache(true) + )).get(); + assertSearchHit(searchResponse, 1, hasId("1")); + // Can't start with ConstantScore(cache(BooleanFilter( + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), startsWith("ConstantScore(BooleanFilter(")); + } + } + + @Test + public void testSimpleQueryRewrite() throws Exception { + assertAcked(prepareCreate("test") + //top_children query needs at least 2 shards for the totalHits to be accurate + .setSettings(settingsBuilder() + .put(indexSettings()) + .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, between(2, DEFAULT_MAX_NUM_SHARDS))) + .addMapping("parent", "p_field", "type=string") + .addMapping("child", "_parent", "type=parent", "c_field", "type=string")); + 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(); + 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(); + } + childId = j; + } + refresh(); + + SearchType[] searchTypes = new SearchType[]{SearchType.QUERY_THEN_FETCH, SearchType.DFS_QUERY_THEN_FETCH}; + for (SearchType searchType : searchTypes) { + SearchResponse searchResponse = client().prepareSearch("test").setSearchType(searchType) + .setQuery(hasChildQuery("child", prefixQuery("c_field", "c")).scoreType("max")).addSort("p_field", SortOrder.ASC) + .setSize(5).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(10L)); + assertThat(searchResponse.getHits().hits()[0].id(), equalTo("p000")); + assertThat(searchResponse.getHits().hits()[1].id(), equalTo("p001")); + assertThat(searchResponse.getHits().hits()[2].id(), equalTo("p002")); + assertThat(searchResponse.getHits().hits()[3].id(), equalTo("p003")); + assertThat(searchResponse.getHits().hits()[4].id(), equalTo("p004")); + + searchResponse = client().prepareSearch("test").setSearchType(searchType) + .setQuery(hasParentQuery("parent", prefixQuery("p_field", "p")).scoreType("score")).addSort("c_field", SortOrder.ASC) + .setSize(5).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(500L)); + assertThat(searchResponse.getHits().hits()[0].id(), equalTo("c000")); + assertThat(searchResponse.getHits().hits()[1].id(), equalTo("c001")); + assertThat(searchResponse.getHits().hits()[2].id(), equalTo("c002")); + assertThat(searchResponse.getHits().hits()[3].id(), equalTo("c003")); + assertThat(searchResponse.getHits().hits()[4].id(), equalTo("c004")); + + searchResponse = client().prepareSearch("test").setSearchType(searchType) + .setQuery(topChildrenQuery("child", prefixQuery("c_field", "c")).factor(10)).addSort("p_field", SortOrder.ASC).setSize(5) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(10L)); + assertThat(searchResponse.getHits().hits()[0].id(), equalTo("p000")); + assertThat(searchResponse.getHits().hits()[1].id(), equalTo("p001")); + assertThat(searchResponse.getHits().hits()[2].id(), equalTo("p002")); + assertThat(searchResponse.getHits().hits()[3].id(), equalTo("p003")); + assertThat(searchResponse.getHits().hits()[4].id(), equalTo("p004")); + } + } + + @Test + // See also issue: + // https://github.com/elasticsearch/elasticsearch/issues/3144 + public void testReIndexingParentAndChildDocuments() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "yellow")).scoreType("sum")).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(0).sourceAsString(), containsString("\"p_value1\"")); + + searchResponse = client() + .prepareSearch("test") + .setQuery( + boolQuery().must(matchQuery("c_field", "x")).must( + hasParentQuery("parent", termQuery("p_field", "p_value2")).scoreType("score"))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("c3")); + assertThat(searchResponse.getHits().getAt(1).id(), equalTo("c4")); + + // 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(); + client().admin().indices().prepareRefresh("test").get(); + } + + searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow")).scoreType("sum")) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); + assertThat(searchResponse.getHits().getAt(0).sourceAsString(), containsString("\"p_value1\"")); + + searchResponse = client() + .prepareSearch("test") + .setQuery( + boolQuery().must(matchQuery("c_field", "x")).must( + hasParentQuery("parent", termQuery("p_field", "p_value2")).scoreType("score"))).get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + assertThat(searchResponse.getHits().getAt(0).id(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); + assertThat(searchResponse.getHits().getAt(1).id(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); + } + + @Test + // See also issue: + // https://github.com/elasticsearch/elasticsearch/issues/3203 + public void testHasChildQueryWithMinimumScore() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery()).scoreType("sum")) + .setMinScore(3) // Score needs to be 3 or above! + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); + assertThat(searchResponse.getHits().getAt(0).score(), equalTo(3.0f)); + } + + @Test + public void testParentFieldFilter() throws Exception { + assertAcked(prepareCreate("test") + .setSettings(settingsBuilder().put(indexSettings()) + .put("index.refresh_interval", -1)) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent") + .addMapping("child2", "_parent", "type=parent")); + ensureGreen(); + + // test term filter + SearchResponse response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1"))) + .get(); + assertHitCount(response, 0l); + + client().prepareIndex("test", "some_type", "1").setSource("field", "value").get(); + client().prepareIndex("test", "parent", "p1").setSource("p_field", "value").get(); + client().prepareIndex("test", "child", "c1").setSource("c_field", "value").setParent("p1").get(); + + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1"))).execute() + .actionGet(); + assertHitCount(response, 0l); + refresh(); + + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1"))).execute() + .actionGet(); + assertHitCount(response, 1l); + + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "parent#p1"))).execute() + .actionGet(); + assertHitCount(response, 1l); + + client().prepareIndex("test", "parent2", "p1").setSource("p_field", "value").setRefresh(true).get(); + + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1"))).execute() + .actionGet(); + assertHitCount(response, 1l); + + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "parent#p1"))).execute() + .actionGet(); + assertHitCount(response, 1l); + + // test terms filter + client().prepareIndex("test", "child2", "c1").setSource("c_field", "value").setParent("p1").get(); + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termsFilter("_parent", "p1"))).execute() + .actionGet(); + assertHitCount(response, 1l); + + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termsFilter("_parent", "parent#p1"))).execute() + .actionGet(); + assertHitCount(response, 1l); + + refresh(); + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termsFilter("_parent", "p1"))).execute() + .actionGet(); + assertHitCount(response, 2l); + + refresh(); + response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termsFilter("_parent", "p1", "p1"))).execute() + .actionGet(); + assertHitCount(response, 2l); + + response = client().prepareSearch("test") + .setQuery(filteredQuery(matchAllQuery(), termsFilter("_parent", "parent#p1", "parent2#p1"))).get(); + assertHitCount(response, 2l); + } + + @Test + public void testHasChildNotBeingCached() throws ElasticsearchException, IOException { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + client().admin().indices().prepareFlush("test").get(); + client().admin().indices().prepareRefresh("test").get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "blue")).cache(true))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + + client().prepareIndex("test", "child", "c2").setParent("p2").setSource("c_field", "blue").get(); + client().admin().indices().prepareRefresh("test").get(); + + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "blue")).cache(true))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + } + + @Test + public void testDeleteByQuery_has_child() throws Exception { + assertAcked(prepareCreate("test") + .setSettings( + settingsBuilder().put(indexSettings()) + .put("index.refresh_interval", "-1") + ) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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().admin().indices().prepareFlush("test").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(); + client().prepareIndex("test", "parent", "p3").setSource("p_field", "p_value3").get(); + client().admin().indices().prepareFlush("test").get(); + client().prepareIndex("test", "child", "c5").setSource("c_field", "blue").setParent("p3").get(); + client().prepareIndex("test", "child", "c6").setSource("c_field", "red").setParent("p3").get(); + client().admin().indices().prepareRefresh().get(); + // p4 will not be found via search api, but will be deleted via delete_by_query api! + client().prepareIndex("test", "parent", "p4").setSource("p_field", "p_value4").get(); + client().prepareIndex("test", "child", "c7").setSource("c_field", "blue").setParent("p4").get(); + client().prepareIndex("test", "child", "c8").setSource("c_field", "red").setParent("p4").get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(randomHasChild("child", "c_field", "blue")) + .get(); + assertHitCount(searchResponse, 2l); + + // Delete by query doesn't support p/c queries. If the delete by query has a different execution mode + // that doesn't rely on IW#deleteByQuery() then this test can be changed. + DeleteByQueryResponse deleteByQueryResponse = client().prepareDeleteByQuery("test").setQuery(randomHasChild("child", "c_field", "blue")).get(); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getSuccessful(), equalTo(0)); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getFailures().length, equalTo(getNumShards("test").numPrimaries)); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getFailures()[0].reason(), containsString("[has_child] query and filter unsupported in delete_by_query api")); + client().admin().indices().prepareRefresh("test").get(); + + searchResponse = client().prepareSearch("test") + .setQuery(randomHasChild("child", "c_field", "blue")) + .get(); + assertHitCount(searchResponse, 3l); + } + + @Test + public void testDeleteByQuery_has_child_SingleRefresh() throws Exception { + assertAcked(prepareCreate("test") + .setSettings( + settingsBuilder() + .put(indexSettings()) + .put("index.refresh_interval", "-1") + ) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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().admin().indices().prepareFlush().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(); + client().prepareIndex("test", "parent", "p3").setSource("p_field", "p_value3").get(); + client().prepareIndex("test", "child", "c5").setSource("c_field", "blue").setParent("p3").get(); + client().prepareIndex("test", "child", "c6").setSource("c_field", "red").setParent("p3").get(); + client().prepareIndex("test", "parent", "p4").setSource("p_field", "p_value4").get(); + client().prepareIndex("test", "child", "c7").setSource("c_field", "blue").setParent("p4").get(); + client().prepareIndex("test", "child", "c8").setSource("c_field", "red").setParent("p4").get(); + client().admin().indices().prepareRefresh().get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(randomHasChild("child", "c_field", "blue")) + .get(); + assertHitCount(searchResponse, 3l); + + DeleteByQueryResponse deleteByQueryResponse = client().prepareDeleteByQuery("test").setQuery(randomHasChild("child", "c_field", "blue")).get(); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getSuccessful(), equalTo(0)); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getFailures().length, equalTo(getNumShards("test").numPrimaries)); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getFailures()[0].reason(), containsString("[has_child] query and filter unsupported in delete_by_query api")); + client().admin().indices().prepareRefresh("test").get(); + + searchResponse = client().prepareSearch("test") + .setQuery(randomHasChild("child", "c_field", "blue")) + .get(); + assertHitCount(searchResponse, 3l); + } + + private QueryBuilder randomHasChild(String type, String field, String value) { + if (randomBoolean()) { + if (randomBoolean()) { + return constantScoreQuery(hasChildFilter(type, termQuery(field, value))); + } else { + return filteredQuery(matchAllQuery(), hasChildFilter(type, termQuery(field, value))); + } + } else { + return hasChildQuery(type, termQuery(field, value)); + } + } + + @Test + public void testDeleteByQuery_has_parent() throws Exception { + assertAcked(prepareCreate("test") + .setSettings( + settingsBuilder() + .put(indexSettings()) + .put("index.refresh_interval", "-1") + ) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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().admin().indices().prepareFlush("test").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(); + client().admin().indices().prepareRefresh().get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(randomHasParent("parent", "p_field", "p_value2")) + .get(); + assertHitCount(searchResponse, 2l); + + DeleteByQueryResponse deleteByQueryResponse = client().prepareDeleteByQuery("test") + .setQuery(randomHasParent("parent", "p_field", "p_value2")) + .get(); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getSuccessful(), equalTo(0)); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getFailures().length, equalTo(getNumShards("test").numPrimaries)); + assertThat(deleteByQueryResponse.getIndex("test").getShardInfo().getFailures()[0].reason(), containsString("[has_parent] query and filter unsupported in delete_by_query api")); + client().admin().indices().prepareRefresh("test").get(); + client().admin().indices().prepareRefresh("test").get(); + client().admin().indices().prepareRefresh("test").get(); + + searchResponse = client().prepareSearch("test") + .setQuery(randomHasParent("parent", "p_field", "p_value2")) + .get(); + assertHitCount(searchResponse, 2l); + } + + private QueryBuilder randomHasParent(String type, String field, String value) { + if (randomBoolean()) { + if (randomBoolean()) { + return constantScoreQuery(hasParentFilter(type, termQuery(field, value))); + } else { + return filteredQuery(matchAllQuery(), hasParentFilter(type, termQuery(field, value))); + } + } else { + return hasParentQuery(type, termQuery(field, value)); + } + } + + @Test + // Relates to bug: https://github.com/elasticsearch/elasticsearch/issues/3818 + public void testHasChildQueryOnlyReturnsSingleChildType() { + assertAcked(prepareCreate("grandissue") + .addMapping("grandparent", "name", "type=string") + .addMapping("parent", "_parent", "type=grandparent") + .addMapping("child_type_one", "_parent", "type=parent") + .addMapping("child_type_two", "_parent", "type=parent")); + + 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(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("grandissue").setQuery( + boolQuery().must( + hasChildQuery( + "parent", + boolQuery().must( + hasChildQuery( + "child_type_one", + boolQuery().must( + queryStringQuery("name:William*").analyzeWildcard(true) + ) + ) + ) + ) + ) + ).get(); + assertHitCount(searchResponse, 1l); + + searchResponse = client().prepareSearch("grandissue").setQuery( + boolQuery().must( + hasChildQuery( + "parent", + boolQuery().must( + hasChildQuery( + "child_type_two", + boolQuery().must( + queryStringQuery("name:William*").analyzeWildcard(true) + ) + ) + ) + ) + ) + ).get(); + assertHitCount(searchResponse, 0l); + } + + @Test + public void indexChildDocWithNoParentMapping() throws ElasticsearchException, IOException { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child1")); + ensureGreen(); + + client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1", "_parent", "bla").get(); + try { + client().prepareIndex("test", "child1", "c1").setParent("p1").setSource("c_field", "blue").get(); + fail(); + } catch (ElasticsearchIllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("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 (ElasticsearchIllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("Can't specify parent if no parent field has been configured")); + } + + refresh(); + } + + @Test + public void testAddingParentToExistingMapping() throws ElasticsearchException, 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 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 (MergeMappingException e) { + assertThat(e.getMessage(), equalTo("Merge failed with failures {[The _parent field's type option can't be changed]}")); + } + } + + @Test + // The SimpleIdReaderTypeCache#docById method used lget, which can't be used if a map is shared. + public void testTopChildrenBug_concurrencyIssue() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + client().admin().indices().prepareRefresh("test").get(); + + int numThreads = 10; + final CountDownLatch latch = new CountDownLatch(numThreads); + final AtomicReference holder = new AtomicReference<>(); + Runnable r = new Runnable() { + @Override + public void run() { + try { + for (int i = 0; i < 100; i++) { + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(topChildrenQuery("child", termQuery("c_field", "blue"))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + + searchResponse = client().prepareSearch("test") + .setQuery(topChildrenQuery("child", termQuery("c_field", "red"))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + } + } catch (AssertionError error) { + holder.set(error); + } finally { + latch.countDown(); + } + } + }; + + for (int i = 0; i < 10; i++) { + new Thread(r).start(); + } + latch.await(); + if (holder.get() != null) { + throw holder.get(); + } + } + + @Test + public void testHasChildQueryWithNestedInnerObjects() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent", "objects", "type=nested") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + client().prepareIndex("test", "parent", "p1") + .setSource(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() + .startObject().field("i_field", "4").endObject() + .startObject().field("i_field", "5").endObject() + .startObject().field("i_field", "6").endObject() + .endArray().endObject()) + .get(); + client().prepareIndex("test", "parent", "p2") + .setSource(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(); + refresh(); + + String scoreMode = ScoreType.values()[getRandom().nextInt(ScoreType.values().length)].name().toLowerCase(Locale.ROOT); + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(QueryBuilders.hasChildQuery("child", termQuery("c_field", "blue")).scoreType(scoreMode), notFilter(termFilter("p_field", "3")))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + + searchResponse = client().prepareSearch("test") + .setQuery(filteredQuery(QueryBuilders.hasChildQuery("child", termQuery("c_field", "red")).scoreType(scoreMode), notFilter(termFilter("p_field", "3")))) + .get(); + assertNoFailures(searchResponse); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + } + + @Test + public void testNamedFilters() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "1")).queryName("test")) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + + searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max").queryName("test")) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + + searchResponse = client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score").queryName("test")) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field", "1")).filterName("test"))) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + + searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(hasParentFilter("parent", termQuery("p_field", "1")).filterName("test"))) + .get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + } + + @Test + public void testParentChildQueriesNoParentType() throws Exception { + assertAcked(prepareCreate("test") + .setSettings(settingsBuilder() + .put(indexSettings()) + .put("index.refresh_interval", -1))); + ensureGreen(); + + String parentId = "p1"; + client().prepareIndex("test", "parent", parentId).setSource("p_field", "1").get(); + refresh(); + + try { + client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "1"))) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + + try { + client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + + try { + client().prepareSearch("test") + .setPostFilter(hasChildFilter("child", termQuery("c_field", "1"))) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + + try { + client().prepareSearch("test") + .setQuery(topChildrenQuery("child", termQuery("c_field", "1")).score("max")) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + + try { + client().prepareSearch("test") + .setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score")) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + + try { + client().prepareSearch("test") + .setPostFilter(hasParentFilter("parent", termQuery("p_field", "1"))) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + } + + @Test + public void testAdd_ParentFieldAfterIndexingParentDocButBeforeIndexingChildDoc() throws Exception { + assertAcked(prepareCreate("test") + .setSettings(settingsBuilder() + .put(indexSettings()) + .put("index.refresh_interval", -1))); + ensureGreen(); + + String parentId = "p1"; + client().prepareIndex("test", "parent", parentId).setSource("p_field", "1").get(); + refresh(); + assertAcked(client().admin() + .indices() + .preparePutMapping("test") + .setType("child") + .setSource("_parent", "type=parent")); + client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); + client().admin().indices().prepareRefresh().get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "1"))) + .get(); + assertHitCount(searchResponse, 1l); + assertSearchHits(searchResponse, parentId); + + searchResponse = client().prepareSearch("test") + .setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) + .get(); + assertHitCount(searchResponse, 1l); + assertSearchHits(searchResponse, parentId); + + + searchResponse = client().prepareSearch("test") + .setPostFilter(hasChildFilter("child", termQuery("c_field", "1"))) + .get(); + assertHitCount(searchResponse, 1l); + assertSearchHits(searchResponse, parentId); + + searchResponse = client().prepareSearch("test") + .setQuery(topChildrenQuery("child", termQuery("c_field", "1")).score("max")) + .get(); + assertHitCount(searchResponse, 1l); + assertSearchHits(searchResponse, parentId); + + searchResponse = client().prepareSearch("test") + .setPostFilter(hasParentFilter("parent", termQuery("p_field", "1"))) + .get(); + assertHitCount(searchResponse, 1l); + assertSearchHits(searchResponse, "c1"); + + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score")) + .get(); + assertHitCount(searchResponse, 1l); + assertSearchHits(searchResponse, "c1"); + } + + @Test + public void testParentChildCaching() throws Exception { + assertAcked(prepareCreate("test") + .setSettings( + settingsBuilder() + .put(indexSettings()) + .put("index.refresh_interval", -1) + ) + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + 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(); + client().admin().indices().prepareOptimize("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(); + client().admin().indices().prepareFlush("test").get(); + client().admin().indices().prepareRefresh("test").get(); + + for (int i = 0; i < 2; i++) { + SearchResponse searchResponse = client().prepareSearch() + .setQuery(filteredQuery(matchAllQuery(), boolFilter() + .must(FilterBuilders.hasChildFilter("child", matchQuery("c_field", "red"))) + .must(matchAllFilter()) + .cache(true))) + .get(); + assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); + } + + + client().prepareIndex("test", "child", "c3").setParent("p2").setSource("c_field", "blue").get(); + client().admin().indices().prepareRefresh("test").get(); + + SearchResponse searchResponse = client().prepareSearch() + .setQuery(filteredQuery(matchAllQuery(), boolFilter() + .must(FilterBuilders.hasChildFilter("child", matchQuery("c_field", "red"))) + .must(matchAllFilter()) + .cache(true))) + .get(); + + assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); + } + + @Test + public void testParentChildQueriesViaScrollApi() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + for (int i = 0; i < 10; i++) { + client().prepareIndex("test", "parent", "p" + i).setSource("{}").get(); + client().prepareIndex("test", "child", "c" + i).setSource("{}").setParent("p" + i).get(); + } + + refresh(); + + QueryBuilder[] queries = new QueryBuilder[]{ + hasChildQuery("child", matchAllQuery()), + filteredQuery(matchAllQuery(), hasChildFilter("child", matchAllQuery())), + hasParentQuery("parent", matchAllQuery()), + filteredQuery(matchAllQuery(), hasParentFilter("parent", matchAllQuery())), + topChildrenQuery("child", matchAllQuery()).factor(10) + }; + + for (QueryBuilder query : queries) { + SearchResponse scrollResponse = client().prepareSearch("test") + .setScroll(TimeValue.timeValueSeconds(30)) + .setSize(1) + .addField("_id") + .setQuery(query) + .setSearchType("scan") + .execute() + .actionGet(); + + assertNoFailures(scrollResponse); + assertThat(scrollResponse.getHits().totalHits(), equalTo(10l)); + int scannedDocs = 0; + do { + scrollResponse = client() + .prepareSearchScroll(scrollResponse.getScrollId()) + .setScroll(TimeValue.timeValueSeconds(30)).get(); + assertThat(scrollResponse.getHits().totalHits(), equalTo(10l)); + scannedDocs += scrollResponse.getHits().getHits().length; + } while (scrollResponse.getHits().getHits().length > 0); + assertThat(scannedDocs, equalTo(10)); + } + } + + @Test + public void testValidateThatHasChildAndHasParentFilterAreNeverCached() throws Exception { + assertAcked(prepareCreate("test") + .setSettings(builder().put(indexSettings()) + //we need 0 replicas here to make sure we always hit the very same shards + .put(SETTING_NUMBER_OF_REPLICAS, 0)) + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + client().prepareIndex("test", "parent", "1").setSource("field", "value") + .get(); + client().prepareIndex("test", "child", "1").setParent("1").setSource("field", "value") + .setRefresh(true) + .get(); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(hasChildQuery("child", matchAllQuery())) + .get(); + assertHitCount(searchResponse, 1l); + + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", matchAllQuery())) + .get(); + assertHitCount(searchResponse, 1l); + + // Internally the has_child and has_parent use filter for the type field, which end up in the filter cache, + // so by first checking how much they take by executing has_child and has_parent *query* we can set a base line + // for the filter cache size in this test. + IndicesStatsResponse statsResponse = client().admin().indices().prepareStats("test").clear().setFilterCache(true).get(); + long initialCacheSize = statsResponse.getIndex("test").getTotal().getFilterCache().getMemorySizeInBytes(); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.hasChildFilter("child", matchAllQuery()).cache(true))) + .get(); + assertHitCount(searchResponse, 1l); + + statsResponse = client().admin().indices().prepareStats("test").clear().setFilterCache(true).get(); + assertThat(statsResponse.getIndex("test").getTotal().getFilterCache().getMemorySizeInBytes(), equalTo(initialCacheSize)); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.hasParentFilter("parent", matchAllQuery()).cache(true))) + .get(); + assertHitCount(searchResponse, 1l); + + // filter cache should not contain any thing, b/c has_child and has_parent can't be cached. + statsResponse = client().admin().indices().prepareStats("test").clear().setFilterCache(true).get(); + assertThat(statsResponse.getIndex("test").getTotal().getFilterCache().getMemorySizeInBytes(), equalTo(initialCacheSize)); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.filteredQuery( + matchAllQuery(), + FilterBuilders.boolFilter().cache(true) + .must(FilterBuilders.matchAllFilter()) + .must(FilterBuilders.hasChildFilter("child", matchAllQuery()).cache(true)) + )) + .get(); + assertHitCount(searchResponse, 1l); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.filteredQuery( + matchAllQuery(), + FilterBuilders.boolFilter().cache(true) + .must(FilterBuilders.matchAllFilter()) + .must(FilterBuilders.hasParentFilter("parent", matchAllQuery()).cache(true)) + )) + .get(); + assertHitCount(searchResponse, 1l); + + // filter cache should not contain any thing, b/c has_child and has_parent can't be cached. + statsResponse = client().admin().indices().prepareStats("test").clear().setFilterCache(true).get(); + assertThat(statsResponse.getIndex("test").getTotal().getFilterCache().getMemorySizeInBytes(), equalTo(initialCacheSize)); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.filteredQuery( + matchAllQuery(), + FilterBuilders.boolFilter().cache(true) + .must(FilterBuilders.termFilter("field", "value").cache(true)) + .must(FilterBuilders.hasChildFilter("child", matchAllQuery()).cache(true)) + )) + .get(); + assertHitCount(searchResponse, 1l); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.filteredQuery( + matchAllQuery(), + FilterBuilders.boolFilter().cache(true) + .must(FilterBuilders.termFilter("field", "value").cache(true)) + .must(FilterBuilders.hasParentFilter("parent", matchAllQuery()).cache(true)) + )) + .get(); + assertHitCount(searchResponse, 1l); + + // filter cache should not contain any thing, b/c has_child and has_parent can't be cached. + statsResponse = client().admin().indices().prepareStats("test").clear().setFilterCache(true).get(); + assertThat(statsResponse.getIndex("test").getTotal().getFilterCache().getMemorySizeInBytes(), greaterThan(initialCacheSize)); + } + + // https://github.com/elasticsearch/elasticsearch/issues/5783 + @Test + 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("{\"query\": {\"has_child\": {\"type\": \"posts\", \"query\": {\"match\": {\"field\": \"bar\"}}}}}").get(); + assertHitCount(resp, 1L); + + // Now reverse the order for the type after the query + resp = client().prepareSearch("test") + .setSource("{\"query\": {\"has_child\": {\"query\": {\"match\": {\"field\": \"bar\"}}, \"type\": \"posts\"}}}").get(); + assertHitCount(resp, 1L); + + } + + @Test + // https://github.com/elasticsearch/elasticsearch/issues/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")) + .get(); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().getAt(0).id(), equalTo("1")); + } + + @Test + public void testTypeIsAppliedInHasParentInnerQuery() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + List indexRequests = new ArrayList<>(); + indexRequests.add(client().prepareIndex("test", "parent", "1").setSource("field1", "a")); + indexRequests.add(client().prepareIndex("test", "child", "1").setParent("1").setSource("{}")); + indexRequests.add(client().prepareIndex("test", "child", "2").setParent("1").setSource("{}")); + indexRandom(true, indexRequests); + + SearchResponse searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasParentFilter("parent", notFilter(termFilter("field1", "a"))))) + .get(); + assertHitCount(searchResponse, 0l); + + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", constantScoreQuery(notFilter(termFilter("field1", "a"))))) + .get(); + assertHitCount(searchResponse, 0l); + + searchResponse = client().prepareSearch("test") + .setQuery(constantScoreQuery(hasParentFilter("parent", termFilter("field1", "a")))) + .get(); + assertHitCount(searchResponse, 2l); + + searchResponse = client().prepareSearch("test") + .setQuery(hasParentQuery("parent", constantScoreQuery(termFilter("field1", "a")))) + .get(); + assertHitCount(searchResponse, 2l); + } + + private List createMinMaxDocBuilders() { + List 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")); + + // 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")); + + // 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")); + + // 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")); + + return indexBuilders; + } + + private SearchResponse minMaxQuery(String scoreType, int minChildren, int maxChildren, int cutoff) throws SearchPhaseExecutionException { + return client() + .prepareSearch("test") + .setQuery( + QueryBuilders + .hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(constantScoreQuery(FilterBuilders.termFilter("foo", "two"))).boostMode("replace").scoreMode("sum") + .add(FilterBuilders.matchAllFilter(), factorFunction(1)) + .add(FilterBuilders.termFilter("foo", "three"), factorFunction(1)) + .add(FilterBuilders.termFilter("foo", "four"), factorFunction(1))).scoreType(scoreType) + .minChildren(minChildren).maxChildren(maxChildren).setShortCircuitCutoff(cutoff)) + .addSort("_score", SortOrder.DESC).addSort("id", SortOrder.ASC).get(); + } + + private SearchResponse minMaxFilter(int minChildren, int maxChildren, int cutoff) throws SearchPhaseExecutionException { + return client() + .prepareSearch("test") + .setQuery( + QueryBuilders.constantScoreQuery(FilterBuilders.hasChildFilter("child", termFilter("foo", "two")) + .minChildren(minChildren).maxChildren(maxChildren).setShortCircuitCutoff(cutoff))) + .addSort("id", SortOrder.ASC).setTrackScores(true).get(); + } + + @Test + public void testMinMaxChildren() throws Exception { + assertAcked(prepareCreate("test") + .addMapping("parent", "id", "type=long") + .addMapping("child", "_parent", "type=parent")); + ensureGreen(); + + indexRandom(true, createMinMaxDocBuilders().toArray(new IndexRequestBuilder[0])); + SearchResponse response; + int cutoff = getRandom().nextInt(4); + + // Score mode = NONE + response = minMaxQuery("none", 0, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("none", 1, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("none", 2, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("4")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxQuery("none", 3, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + + response = minMaxQuery("none", 4, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = minMaxQuery("none", 0, 4, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("none", 0, 3, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("none", 0, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxQuery("none", 2, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + + try { + response = minMaxQuery("none", 3, 2, cutoff); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.getMessage(), containsString("[has_child] 'max_children' is less than 'min_children'")); + } + + // Score mode = SUM + response = minMaxQuery("sum", 0, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("sum", 1, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("sum", 2, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + + response = minMaxQuery("sum", 3, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + + response = minMaxQuery("sum", 4, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = minMaxQuery("sum", 0, 4, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("sum", 0, 3, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(6f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(3f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("sum", 0, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + assertThat(response.getHits().hits()[1].id(), equalTo("2")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxQuery("sum", 2, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + + try { + response = minMaxQuery("sum", 3, 2, cutoff); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.getMessage(), containsString("[has_child] 'max_children' is less than 'min_children'")); + } + + // Score mode = MAX + response = minMaxQuery("max", 0, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(2f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("max", 1, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(2f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("max", 2, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(2f)); + + response = minMaxQuery("max", 3, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + + response = minMaxQuery("max", 4, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = minMaxQuery("max", 0, 4, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(2f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("max", 0, 3, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(3f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(2f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("max", 0, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + assertThat(response.getHits().hits()[1].id(), equalTo("2")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxQuery("max", 2, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + + try { + response = minMaxQuery("max", 3, 2, cutoff); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.getMessage(), containsString("[has_child] 'max_children' is less than 'min_children'")); + } + + // Score mode = AVG + response = minMaxQuery("avg", 0, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1.5f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("avg", 1, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1.5f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("avg", 2, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1.5f)); + + response = minMaxQuery("avg", 3, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + + response = minMaxQuery("avg", 4, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = minMaxQuery("avg", 0, 4, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1.5f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("avg", 0, 3, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(2f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1.5f)); + assertThat(response.getHits().hits()[2].id(), equalTo("2")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxQuery("avg", 0, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(1.5f)); + assertThat(response.getHits().hits()[1].id(), equalTo("2")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxQuery("avg", 2, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(1.5f)); + + try { + response = minMaxQuery("avg", 3, 2, cutoff); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.getMessage(), containsString("[has_child] 'max_children' is less than 'min_children'")); + } + + // HasChildFilter + response = minMaxFilter(0, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxFilter(1, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxFilter(2, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("4")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxFilter(3, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("4")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + + response = minMaxFilter(4, 0, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(0l)); + + response = minMaxFilter(0, 4, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxFilter(0, 3, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(3l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + assertThat(response.getHits().hits()[2].id(), equalTo("4")); + assertThat(response.getHits().hits()[2].score(), equalTo(1f)); + + response = minMaxFilter(0, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(2l)); + assertThat(response.getHits().hits()[0].id(), equalTo("2")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + assertThat(response.getHits().hits()[1].id(), equalTo("3")); + assertThat(response.getHits().hits()[1].score(), equalTo(1f)); + + response = minMaxFilter(2, 2, cutoff); + + assertThat(response.getHits().totalHits(), equalTo(1l)); + assertThat(response.getHits().hits()[0].id(), equalTo("3")); + assertThat(response.getHits().hits()[0].score(), equalTo(1f)); + + try { + response = minMaxFilter(3, 2, cutoff); + fail(); + } catch (SearchPhaseExecutionException e) { + assertThat(e.getMessage(), containsString("[has_child] 'max_children' is less than 'min_children'")); + } + + } + + @Test + @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch/issues/9461") + public void testParentFieldToNonExistingType() { + assertAcked(prepareCreate("test").addMapping("parent").addMapping("child", "_parent", "type=parent2")); + client().prepareIndex("test", "parent", "1").setSource("{}").get(); + client().prepareIndex("test", "child", "1").setParent("1").setSource("{}").get(); + refresh(); + + try { + client().prepareSearch("test") + .setQuery(QueryBuilders.hasChildQuery("child", matchAllQuery())) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + } + + SearchResponse response = client().prepareSearch("test") + .setQuery(QueryBuilders.hasParentQuery("parent", matchAllQuery())) + .get(); + assertHitCount(response, 0); + + try { + client().prepareSearch("test") + .setQuery(QueryBuilders.constantScoreQuery(FilterBuilders.hasChildFilter("child", matchAllQuery()))) + .get(); + fail(); + } catch (SearchPhaseExecutionException e) { + } + + response = client().prepareSearch("test") + .setQuery(QueryBuilders.constantScoreQuery(FilterBuilders.hasParentFilter("parent", matchAllQuery()))) + .get(); + assertHitCount(response, 0); + } + + private static HasChildFilterBuilder hasChildFilter(String type, QueryBuilder queryBuilder) { + HasChildFilterBuilder hasChildFilterBuilder = FilterBuilders.hasChildFilter(type, queryBuilder); + hasChildFilterBuilder.setShortCircuitCutoff(randomInt(10)); + return hasChildFilterBuilder; + } + + private static HasChildFilterBuilder hasChildFilter(String type, FilterBuilder filterBuilder) { + HasChildFilterBuilder hasChildFilterBuilder = FilterBuilders.hasChildFilter(type, filterBuilder); + hasChildFilterBuilder.setShortCircuitCutoff(randomInt(10)); + return hasChildFilterBuilder; + } + + private static HasChildQueryBuilder hasChildQuery(String type, QueryBuilder queryBuilder) { + HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(type, queryBuilder); + hasChildQueryBuilder.setShortCircuitCutoff(randomInt(10)); + return hasChildQueryBuilder; + } + +} diff --git a/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchTests.java b/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchTests.java index e0ab41e6e867d..29202538ac086 100644 --- a/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchTests.java +++ b/src/test/java/org/elasticsearch/search/child/SimpleChildQuerySearchTests.java @@ -44,12 +44,7 @@ import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.FieldMapper.Loading; import org.elasticsearch.index.mapper.MergeMappingException; -import org.elasticsearch.index.query.FilterBuilder; -import org.elasticsearch.index.query.FilterBuilders; -import org.elasticsearch.index.query.HasChildFilterBuilder; -import org.elasticsearch.index.query.HasChildQueryBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.*; import org.elasticsearch.index.search.child.ScoreType; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.aggregations.AggregationBuilders; @@ -65,14 +60,7 @@ import org.junit.Test; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; +import java.util.*; import static com.google.common.collect.Maps.newHashMap; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; @@ -80,42 +68,12 @@ import static org.elasticsearch.common.settings.ImmutableSettings.builder; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.index.query.FilterBuilders.boolFilter; -import static org.elasticsearch.index.query.FilterBuilders.hasParentFilter; -import static org.elasticsearch.index.query.FilterBuilders.matchAllFilter; -import static org.elasticsearch.index.query.FilterBuilders.notFilter; -import static org.elasticsearch.index.query.FilterBuilders.queryFilter; -import static org.elasticsearch.index.query.FilterBuilders.termFilter; -import static org.elasticsearch.index.query.FilterBuilders.termsFilter; -import static org.elasticsearch.index.query.QueryBuilders.boolQuery; -import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; -import static org.elasticsearch.index.query.QueryBuilders.filteredQuery; -import static org.elasticsearch.index.query.QueryBuilders.hasParentQuery; -import static org.elasticsearch.index.query.QueryBuilders.idsQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; -import static org.elasticsearch.index.query.QueryBuilders.matchQuery; -import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; -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.topChildrenQuery; +import static org.elasticsearch.index.query.FilterBuilders.*; +import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.factorFunction; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; -import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.startsWith; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*; +import static org.hamcrest.Matchers.*; /** * @@ -135,8 +93,8 @@ protected Settings nodeSettings(int nodeOrdinal) { @Test public void multiLevelChild() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") - .addMapping("child", "_parent", "type=parent") + .addMapping("parent", "_parent", "parent=true") + .addMapping("child", "_parent", "type=parent,parent=true") .addMapping("grandchild", "_parent", "type=child")); ensureGreen(); @@ -190,7 +148,7 @@ public void multiLevelChild() throws Exception { // see #6722 public void test6722() throws ElasticsearchException, IOException { assertAcked(prepareCreate("test") - .addMapping("foo") + .addMapping("foo", "_parent", "parent=true") .addMapping("test", "_parent", "type=foo")); ensureGreen(); @@ -209,7 +167,7 @@ public void test6722() throws ElasticsearchException, IOException { // see #2744 public void test2744() throws ElasticsearchException, IOException { assertAcked(prepareCreate("test") - .addMapping("foo") + .addMapping("foo", "_parent", "parent=true") .addMapping("test", "_parent", "type=foo")); ensureGreen(); @@ -228,7 +186,7 @@ public void test2744() throws ElasticsearchException, IOException { @Test public void simpleChildQuery() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -266,23 +224,6 @@ public void simpleChildQuery() throws Exception { assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("c1"), equalTo("c2"))); assertThat(searchResponse.getHits().getAt(1).field("_parent").value().toString(), equalTo("p1")); - // TOP CHILDREN QUERY - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))).execute() - .actionGet(); - assertHitCount(searchResponse, 1l); - assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); - - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "blue"))) - .get(); - assertHitCount(searchResponse, 1l); - assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); - - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "red"))).execute() - .actionGet(); - assertHitCount(searchResponse, 2l); - assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); - assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); - // HAS CHILD searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")) .get(); @@ -317,6 +258,7 @@ public void simpleChildQuery() throws Exception { @Test @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch/issues/9270") + // TODO: this test only makes sense in the old p/c implementation public void testClearIdCacheBug() throws Exception { // enforce lazy loading to make sure that p/c stats are not counted as part of field data assertAcked(prepareCreate("test") @@ -400,7 +342,7 @@ public void testClearIdCacheBug() throws Exception { // See: https://github.com/elasticsearch/elasticsearch/issues/3290 public void testCachingBug_withFqueryFilter() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); List builders = new ArrayList<>(); @@ -427,10 +369,6 @@ public void testCachingBug_withFqueryFilter() throws Exception { for (int i = 1; i <= 10; i++) { logger.info("Round {}", i); SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(queryFilter(topChildrenQuery("child", matchAllQuery())).cache(true))).execute() - .actionGet(); - assertNoFailures(searchResponse); - searchResponse = client().prepareSearch("test") .setQuery(constantScoreQuery(queryFilter(hasChildQuery("child", matchAllQuery()).scoreType("max")).cache(true))) .get(); assertNoFailures(searchResponse); @@ -444,7 +382,7 @@ public void testCachingBug_withFqueryFilter() throws Exception { @Test public void testHasParentFilter() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); Map> parentToChildren = newHashMap(); @@ -494,7 +432,7 @@ public void testHasParentFilter() throws Exception { @Test public void simpleChildQueryWithFlush() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -513,31 +451,8 @@ public void simpleChildQueryWithFlush() throws Exception { client().admin().indices().prepareFlush().get(); refresh(); - // TOP CHILDREN QUERY - - SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))) - .get(); - assertNoFailures(searchResponse); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); - assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p1")); - - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "blue"))).execute() - .actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); - assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p2")); - - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "red"))).execute() - .actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); - assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p1"))); - assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p1"))); - // HAS CHILD QUERY - - searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"))).execute() + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"))).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); @@ -580,7 +495,7 @@ public void simpleChildQueryWithFlush() throws Exception { @Test public void testScopedFacet() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -596,7 +511,7 @@ public void testScopedFacet() throws Exception { SearchResponse searchResponse = client() .prepareSearch("test") - .setQuery(topChildrenQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")))) + .setQuery(hasChildQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")))) .addAggregation(AggregationBuilders.global("global").subAggregation( AggregationBuilders.filter("filter").filter(boolFilter().should(termFilter("c_field", "red")).should(termFilter("c_field", "yellow"))).subAggregation( AggregationBuilders.terms("facet1").field("c_field")))).get(); @@ -618,7 +533,7 @@ public void testScopedFacet() throws Exception { @Test public void testDeletedParent() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); // index simple data @@ -633,7 +548,7 @@ public void testDeletedParent() throws Exception { // TOP CHILDREN QUERY - SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))) + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"))) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); @@ -652,7 +567,7 @@ public void testDeletedParent() throws Exception { client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1_updated").get(); client().admin().indices().prepareRefresh().get(); - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "yellow"))).execute() + searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"))).execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); @@ -670,7 +585,7 @@ public void testDeletedParent() throws Exception { @Test public void testDfsSearchType() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -692,75 +607,12 @@ public void testDfsSearchType() throws Exception { .setQuery(boolQuery().mustNot(hasParentQuery("parent", boolQuery().should(queryStringQuery("p_field:*"))))).execute() .actionGet(); assertNoFailures(searchResponse); - - searchResponse = client().prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .setQuery(boolQuery().mustNot(topChildrenQuery("child", boolQuery().should(queryStringQuery("c_field:*"))))).execute() - .actionGet(); - assertNoFailures(searchResponse); - } - - @Test - public void testFixAOBEIfTopChildrenIsWrappedInMusNotClause() throws Exception { - assertAcked(prepareCreate("test") - .addMapping("parent") - .addMapping("child", "_parent", "type=parent")); - 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(); - - refresh(); - - SearchResponse searchResponse = client().prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) - .setQuery(boolQuery().mustNot(topChildrenQuery("child", boolQuery().should(queryStringQuery("c_field:*"))))).execute() - .actionGet(); - assertNoFailures(searchResponse); - } - - @Test - public void testTopChildrenReSearchBug() throws Exception { - assertAcked(prepareCreate("test") - .addMapping("parent") - .addMapping("child", "_parent", "type=parent")); - ensureGreen(); - int numberOfParents = 4; - int numberOfChildrenPerParent = 123; - for (int i = 1; i <= numberOfParents; i++) { - String parentId = String.format(Locale.ROOT, "p%d", i); - client().prepareIndex("test", "parent", parentId).setSource("p_field", String.format(Locale.ROOT, "p_value%d", i)).execute() - .actionGet(); - for (int j = 1; j <= numberOfChildrenPerParent; j++) { - client().prepareIndex("test", "child", String.format(Locale.ROOT, "%s_c%d", parentId, j)) - .setSource("c_field1", parentId, "c_field2", i % 2 == 0 ? "even" : "not_even").setParent(parentId).execute() - .actionGet(); - } - } - - refresh(); - - SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field1", "p3"))) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); - assertThat(searchResponse.getHits().getAt(0).id(), equalTo("p3")); - - searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field2", "even"))).execute() - .actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); - assertThat(searchResponse.getHits().getAt(0).id(), anyOf(equalTo("p2"), equalTo("p4"))); - assertThat(searchResponse.getHits().getAt(1).id(), anyOf(equalTo("p2"), equalTo("p4"))); } @Test public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrChildDocs() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -785,7 +637,7 @@ public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrCh @Test public void testCountApiUsage() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -794,11 +646,7 @@ public void testCountApiUsage() throws Exception { client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); refresh(); - CountResponse countResponse = client().prepareCount("test").setQuery(topChildrenQuery("child", termQuery("c_field", "1"))) - .get(); - assertHitCount(countResponse, 1l); - - countResponse = client().prepareCount("test").setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) + CountResponse countResponse = client().prepareCount("test").setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) .get(); assertHitCount(countResponse, 1l); @@ -818,7 +666,7 @@ public void testCountApiUsage() throws Exception { @Test public void testExplainUsage() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -828,31 +676,24 @@ public void testExplainUsage() throws Exception { refresh(); SearchResponse searchResponse = client().prepareSearch("test") - .setExplain(true) - .setQuery(topChildrenQuery("child", termQuery("c_field", "1"))) - .get(); - assertHitCount(searchResponse, 1l); - assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet...")); - - searchResponse = client().prepareSearch("test") .setExplain(true) .setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) .get(); assertHitCount(searchResponse, 1l); - assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet...")); + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("Score based on join value p1")); searchResponse = client().prepareSearch("test") .setExplain(true) .setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score")) .get(); assertHitCount(searchResponse, 1l); - assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("not implemented yet...")); + assertThat(searchResponse.getHits().getAt(0).explanation().getDescription(), equalTo("Score based on join value p1")); ExplainResponse explainResponse = client().prepareExplain("test", "parent", parentId) .setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max")) .get(); assertThat(explainResponse.isExists(), equalTo(true)); - assertThat(explainResponse.getExplanation().getDescription(), equalTo("not implemented yet...")); + assertThat(explainResponse.getExplanation().getDescription(), equalTo("Score based on join value p1")); } List createDocBuilders() { @@ -914,7 +755,7 @@ List createDocBuilders() { @Test public void testScoreForParentChildQueries_withFunctionScore() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent") .addMapping("child1", "_parent", "type=parent")); ensureGreen(); @@ -998,7 +839,7 @@ public void testScoreForParentChildQueries_withFunctionScore() throws Exception // https://github.com/elasticsearch/elasticsearch/issues/2536 public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1032,7 +873,7 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep @Test public void testHasChildAndHasParentFilter_withFilter() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1059,7 +900,7 @@ public void testHasChildAndHasParentFilter_withFilter() throws Exception { @Test public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1073,10 +914,6 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { .setQuery(filteredQuery(matchAllQuery(), queryFilter(hasChildQuery("child", matchQuery("c_field", 1))))).get(); assertSearchHit(searchResponse, 1, hasId("1")); - searchResponse = client().prepareSearch("test") - .setQuery(filteredQuery(matchAllQuery(), queryFilter(topChildrenQuery("child", matchQuery("c_field", 1))))).get(); - assertSearchHit(searchResponse, 1, hasId("1")); - searchResponse = client().prepareSearch("test") .setQuery(filteredQuery(matchAllQuery(), queryFilter(hasParentQuery("parent", matchQuery("p_field", 1))))).get(); assertSearchHit(searchResponse, 1, hasId("2")); @@ -1085,10 +922,6 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { .setQuery(filteredQuery(matchAllQuery(), queryFilter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1)))))).get(); assertSearchHit(searchResponse, 1, hasId("1")); - searchResponse = client().prepareSearch("test") - .setQuery(filteredQuery(matchAllQuery(), queryFilter(boolQuery().must(topChildrenQuery("child", matchQuery("c_field", 1)))))).get(); - assertSearchHit(searchResponse, 1, hasId("1")); - searchResponse = client().prepareSearch("test") .setQuery(filteredQuery(matchAllQuery(), queryFilter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1)))))).get(); assertSearchHit(searchResponse, 1, hasId("2")); @@ -1098,7 +931,7 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { public void testHasChildAndHasParentWrappedInAQueryFilterShouldNeverGetCached() throws Exception { assertAcked(prepareCreate("test") .setSettings(ImmutableSettings.builder().put("index.cache.filter.type", "weighted")) - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1136,7 +969,7 @@ public void testSimpleQueryRewrite() throws Exception { .setSettings(settingsBuilder() .put(indexSettings()) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, between(2, DEFAULT_MAX_NUM_SHARDS))) - .addMapping("parent", "p_field", "type=string") + .addMapping("parent", "_parent", "parent=true", "p_field", "type=string") .addMapping("child", "_parent", "type=parent", "c_field", "type=string")); ensureGreen(); @@ -1177,17 +1010,6 @@ public void testSimpleQueryRewrite() throws Exception { assertThat(searchResponse.getHits().hits()[2].id(), equalTo("c002")); assertThat(searchResponse.getHits().hits()[3].id(), equalTo("c003")); assertThat(searchResponse.getHits().hits()[4].id(), equalTo("c004")); - - searchResponse = client().prepareSearch("test").setSearchType(searchType) - .setQuery(topChildrenQuery("child", prefixQuery("c_field", "c")).factor(10)).addSort("p_field", SortOrder.ASC).setSize(5) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(10L)); - assertThat(searchResponse.getHits().hits()[0].id(), equalTo("p000")); - assertThat(searchResponse.getHits().hits()[1].id(), equalTo("p001")); - assertThat(searchResponse.getHits().hits()[2].id(), equalTo("p002")); - assertThat(searchResponse.getHits().hits()[3].id(), equalTo("p003")); - assertThat(searchResponse.getHits().hits()[4].id(), equalTo("p004")); } } @@ -1196,7 +1018,7 @@ public void testSimpleQueryRewrite() throws Exception { // https://github.com/elasticsearch/elasticsearch/issues/3144 public void testReIndexingParentAndChildDocuments() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1259,7 +1081,7 @@ public void testReIndexingParentAndChildDocuments() throws Exception { // https://github.com/elasticsearch/elasticsearch/issues/3203 public void testHasChildQueryWithMinimumScore() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1286,7 +1108,7 @@ public void testParentFieldFilter() throws Exception { assertAcked(prepareCreate("test") .setSettings(settingsBuilder().put(indexSettings()) .put("index.refresh_interval", -1)) - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent") .addMapping("child2", "_parent", "type=parent")); ensureGreen(); @@ -1351,7 +1173,7 @@ public void testParentFieldFilter() throws Exception { @Test public void testHasChildNotBeingCached() throws ElasticsearchException, IOException { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1393,7 +1215,7 @@ public void testDeleteByQuery_has_child() throws Exception { settingsBuilder().put(indexSettings()) .put("index.refresh_interval", "-1") ) - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1442,7 +1264,7 @@ public void testDeleteByQuery_has_child_SingleRefresh() throws Exception { .put(indexSettings()) .put("index.refresh_interval", "-1") ) - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1499,7 +1321,7 @@ public void testDeleteByQuery_has_parent() throws Exception { .put(indexSettings()) .put("index.refresh_interval", "-1") ) - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1550,8 +1372,8 @@ private QueryBuilder randomHasParent(String type, String field, String value) { // Relates to bug: https://github.com/elasticsearch/elasticsearch/issues/3818 public void testHasChildQueryOnlyReturnsSingleChildType() { assertAcked(prepareCreate("grandissue") - .addMapping("grandparent", "name", "type=string") - .addMapping("parent", "_parent", "type=grandparent") + .addMapping("grandparent", "name", "type=string", "_parent", "parent=true") + .addMapping("parent", "_parent", "type=grandparent,parent=true") .addMapping("child_type_one", "_parent", "type=parent") .addMapping("child_type_two", "_parent", "type=parent")); @@ -1649,63 +1471,10 @@ public void testAddingParentToExistingMapping() throws ElasticsearchException, I } } - @Test - // The SimpleIdReaderTypeCache#docById method used lget, which can't be used if a map is shared. - public void testTopChildrenBug_concurrencyIssue() throws Exception { - assertAcked(prepareCreate("test") - .addMapping("parent") - .addMapping("child", "_parent", "type=parent")); - 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(); - client().admin().indices().prepareRefresh("test").get(); - - int numThreads = 10; - final CountDownLatch latch = new CountDownLatch(numThreads); - final AtomicReference holder = new AtomicReference<>(); - Runnable r = new Runnable() { - @Override - public void run() { - try { - for (int i = 0; i < 100; i++) { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(topChildrenQuery("child", termQuery("c_field", "blue"))) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(1l)); - - searchResponse = client().prepareSearch("test") - .setQuery(topChildrenQuery("child", termQuery("c_field", "red"))) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().totalHits(), equalTo(2l)); - } - } catch (AssertionError error) { - holder.set(error); - } finally { - latch.countDown(); - } - } - }; - - for (int i = 0; i < 10; i++) { - new Thread(r).start(); - } - latch.await(); - if (holder.get() != null) { - throw holder.get(); - } - } - @Test public void testHasChildQueryWithNestedInnerObjects() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent", "objects", "type=nested") + .addMapping("parent", "_parent", "parent=true", "objects", "type=nested") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1747,7 +1516,7 @@ public void testHasChildQueryWithNestedInnerObjects() throws Exception { @Test public void testNamedFilters() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1756,13 +1525,7 @@ public void testNamedFilters() throws Exception { client().prepareIndex("test", "child", "c1").setSource("c_field", "1").setParent(parentId).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(topChildrenQuery("child", termQuery("c_field", "1")).queryName("test")) - .get(); - assertHitCount(searchResponse, 1l); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - - searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max").queryName("test")) + SearchResponse searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1")).scoreType("max").queryName("test")) .get(); assertHitCount(searchResponse, 1l); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); @@ -1826,15 +1589,6 @@ public void testParentChildQueriesNoParentType() throws Exception { assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); } - try { - client().prepareSearch("test") - .setQuery(topChildrenQuery("child", termQuery("c_field", "1")).score("max")) - .get(); - fail(); - } catch (SearchPhaseExecutionException e) { - assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); - } - try { client().prepareSearch("test") .setQuery(hasParentQuery("parent", termQuery("p_field", "1")).scoreType("score")) @@ -1859,7 +1613,9 @@ public void testAdd_ParentFieldAfterIndexingParentDocButBeforeIndexingChildDoc() assertAcked(prepareCreate("test") .setSettings(settingsBuilder() .put(indexSettings()) - .put("index.refresh_interval", -1))); + .put("index.refresh_interval", -1)) + .addMapping("parent", "_parent", "parent=true") + ); ensureGreen(); String parentId = "p1"; @@ -1892,12 +1648,6 @@ public void testAdd_ParentFieldAfterIndexingParentDocButBeforeIndexingChildDoc() assertHitCount(searchResponse, 1l); assertSearchHits(searchResponse, parentId); - searchResponse = client().prepareSearch("test") - .setQuery(topChildrenQuery("child", termQuery("c_field", "1")).score("max")) - .get(); - assertHitCount(searchResponse, 1l); - assertSearchHits(searchResponse, parentId); - searchResponse = client().prepareSearch("test") .setPostFilter(hasParentFilter("parent", termQuery("p_field", "1"))) .get(); @@ -1919,7 +1669,7 @@ public void testParentChildCaching() throws Exception { .put(indexSettings()) .put("index.refresh_interval", -1) ) - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -1965,7 +1715,7 @@ public void testParentChildCaching() throws Exception { @Test public void testParentChildQueriesViaScrollApi() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); for (int i = 0; i < 10; i++) { @@ -1979,8 +1729,7 @@ public void testParentChildQueriesViaScrollApi() throws Exception { hasChildQuery("child", matchAllQuery()), filteredQuery(matchAllQuery(), hasChildFilter("child", matchAllQuery())), hasParentQuery("parent", matchAllQuery()), - filteredQuery(matchAllQuery(), hasParentFilter("parent", matchAllQuery())), - topChildrenQuery("child", matchAllQuery()).factor(10) + filteredQuery(matchAllQuery(), hasParentFilter("parent", matchAllQuery())) }; for (QueryBuilder query : queries) { @@ -2013,6 +1762,7 @@ public void testValidateThatHasChildAndHasParentFilterAreNeverCached() throws Ex .setSettings(builder().put(indexSettings()) //we need 0 replicas here to make sure we always hit the very same shards .put(SETTING_NUMBER_OF_REPLICAS, 0)) + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -2108,7 +1858,7 @@ public void testValidateThatHasChildAndHasParentFilterAreNeverCached() throws Ex @Test public void testQueryBeforeChildType() throws Exception { assertAcked(prepareCreate("test") - .addMapping("features") + .addMapping("features", "_parent", "parent=true") .addMapping("posts", "_parent", "type=features") .addMapping("specials")); ensureGreen(); @@ -2133,7 +1883,7 @@ public void testQueryBeforeChildType() throws Exception { // https://github.com/elasticsearch/elasticsearch/issues/6256 public void testParentFieldInMultiMatchField() throws Exception { assertAcked(prepareCreate("test") - .addMapping("type1") + .addMapping("type1", "_parent", "parent=true") .addMapping("type2", "_parent", "type=type1") ); ensureGreen(); @@ -2152,7 +1902,7 @@ public void testParentFieldInMultiMatchField() throws Exception { @Test public void testTypeIsAppliedInHasParentInnerQuery() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent") + .addMapping("parent", "_parent", "parent=true") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -2247,7 +1997,7 @@ private SearchResponse minMaxFilter(int minChildren, int maxChildren, int cutoff @Test public void testMinMaxChildren() throws Exception { assertAcked(prepareCreate("test") - .addMapping("parent", "id", "type=long") + .addMapping("parent", "_parent", "parent=true", "id", "type=long") .addMapping("child", "_parent", "type=parent")); ensureGreen(); @@ -2660,7 +2410,7 @@ public void testMinMaxChildren() throws Exception { @Test @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch/issues/9461") public void testParentFieldToNonExistingType() { - assertAcked(prepareCreate("test").addMapping("parent").addMapping("child", "_parent", "type=parent2")); + assertAcked(prepareCreate("test").addMapping("parent", "_parent", "parent=true").addMapping("child", "_parent", "type=parent2")); client().prepareIndex("test", "parent", "1").setSource("{}").get(); client().prepareIndex("test", "child", "1").setParent("1").setSource("{}").get(); refresh();