Change `has_child`, `has_parent` queries and `childen` aggregation to work with the new join field type and
at the same time maintaining support for the `_parent` meta field type. Relates to #20257
This commit is contained in:
parent
a32d1b91fa
commit
2a71a7bffc
|
@ -30,6 +30,8 @@ import org.elasticsearch.index.mapper.DocumentMapper;
|
|||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.ParentFieldMapper;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.join.mapper.ParentIdFieldMapper;
|
||||
import org.elasticsearch.join.mapper.ParentJoinFieldMapper;
|
||||
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
|
||||
import org.elasticsearch.search.aggregations.AggregatorFactory;
|
||||
import org.elasticsearch.search.aggregations.support.FieldContext;
|
||||
|
@ -92,8 +94,30 @@ public class ChildrenAggregationBuilder
|
|||
@Override
|
||||
protected ValuesSourceConfig<WithOrdinals> resolveConfig(SearchContext context) {
|
||||
ValuesSourceConfig<WithOrdinals> config = new ValuesSourceConfig<>(ValuesSourceType.BYTES);
|
||||
DocumentMapper childDocMapper = context.mapperService().documentMapper(childType);
|
||||
if (context.mapperService().getIndexSettings().isSingleType()) {
|
||||
joinFieldResolveConfig(context, config);
|
||||
} else {
|
||||
parentFieldResolveConfig(context, config);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private void joinFieldResolveConfig(SearchContext context, ValuesSourceConfig<WithOrdinals> config) {
|
||||
ParentJoinFieldMapper parentJoinFieldMapper = ParentJoinFieldMapper.getMapper(context.mapperService());
|
||||
ParentIdFieldMapper parentIdFieldMapper = parentJoinFieldMapper.getParentIdFieldMapper(childType, false);
|
||||
if (parentIdFieldMapper != null) {
|
||||
parentFilter = parentIdFieldMapper.getParentFilter();
|
||||
childFilter = parentIdFieldMapper.getChildFilter(childType);
|
||||
MappedFieldType fieldType = parentIdFieldMapper.fieldType();
|
||||
final SortedSetDVOrdinalsIndexFieldData fieldData = context.fieldData().getForField(fieldType);
|
||||
config.fieldContext(new FieldContext(fieldType.name(), fieldData, fieldType));
|
||||
} else {
|
||||
config.unmapped(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void parentFieldResolveConfig(SearchContext context, ValuesSourceConfig<WithOrdinals> config) {
|
||||
DocumentMapper childDocMapper = context.mapperService().documentMapper(childType);
|
||||
if (childDocMapper != null) {
|
||||
ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
|
||||
if (!parentFieldMapper.active()) {
|
||||
|
@ -107,14 +131,13 @@ public class ChildrenAggregationBuilder
|
|||
MappedFieldType parentFieldType = parentDocMapper.parentFieldMapper().getParentJoinFieldType();
|
||||
final SortedSetDVOrdinalsIndexFieldData fieldData = context.fieldData().getForField(parentFieldType);
|
||||
config.fieldContext(new FieldContext(parentFieldType.name(), fieldData,
|
||||
parentFieldType));
|
||||
parentFieldType));
|
||||
} else {
|
||||
config.unmapped(true);
|
||||
}
|
||||
} else {
|
||||
config.unmapped(true);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -40,7 +40,7 @@ import java.util.List;
|
|||
* This class is also used to quickly retrieve the parent-join field defined in a mapping without
|
||||
* specifying the name of the field.
|
||||
*/
|
||||
class MetaJoinFieldMapper extends FieldMapper {
|
||||
public class MetaJoinFieldMapper extends FieldMapper {
|
||||
static final String NAME = "_parent_join";
|
||||
static final String CONTENT_TYPE = "parent_join";
|
||||
|
||||
|
@ -68,8 +68,9 @@ class MetaJoinFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
static final class MetaJoinFieldType extends StringFieldType {
|
||||
ParentJoinFieldMapper mapper;
|
||||
public static class MetaJoinFieldType extends StringFieldType {
|
||||
|
||||
private ParentJoinFieldMapper mapper;
|
||||
|
||||
MetaJoinFieldType() {}
|
||||
|
||||
|
@ -100,6 +101,10 @@ class MetaJoinFieldMapper extends FieldMapper {
|
|||
BytesRef binaryValue = (BytesRef) value;
|
||||
return binaryValue.utf8ToString();
|
||||
}
|
||||
|
||||
public ParentJoinFieldMapper getMapper() {
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
|
||||
MetaJoinFieldMapper(String name, MappedFieldType fieldType, Settings indexSettings) {
|
||||
|
|
|
@ -23,6 +23,12 @@ import org.apache.lucene.document.Field;
|
|||
import org.apache.lucene.document.SortedDocValuesField;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.common.lucene.Lucene;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -47,7 +53,7 @@ public final class ParentIdFieldMapper extends FieldMapper {
|
|||
static final String CONTENT_TYPE = "parent";
|
||||
|
||||
static class Defaults {
|
||||
public static final MappedFieldType FIELD_TYPE = new ParentIdFieldType();
|
||||
static final MappedFieldType FIELD_TYPE = new ParentIdFieldType();
|
||||
|
||||
static {
|
||||
FIELD_TYPE.setTokenized(false);
|
||||
|
@ -86,7 +92,7 @@ public final class ParentIdFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
public static final class ParentIdFieldType extends StringFieldType {
|
||||
public ParentIdFieldType() {
|
||||
ParentIdFieldType() {
|
||||
setIndexAnalyzer(Lucene.KEYWORD_ANALYZER);
|
||||
setSearchAnalyzer(Lucene.KEYWORD_ANALYZER);
|
||||
}
|
||||
|
@ -145,6 +151,9 @@ public final class ParentIdFieldMapper extends FieldMapper {
|
|||
return parentName;
|
||||
}
|
||||
|
||||
public Query getParentFilter() {
|
||||
return new TermQuery(new Term(name().substring(0, name().indexOf('#')), parentName));
|
||||
}
|
||||
/**
|
||||
* Returns the children names associated with this mapper.
|
||||
*/
|
||||
|
@ -152,6 +161,18 @@ public final class ParentIdFieldMapper extends FieldMapper {
|
|||
return children;
|
||||
}
|
||||
|
||||
public Query getChildFilter(String type) {
|
||||
return new TermQuery(new Term(name().substring(0, name().indexOf('#')), type));
|
||||
}
|
||||
|
||||
public Query getChildrenFilter() {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
for (String child : children) {
|
||||
builder.add(getChildFilter(child), BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
return new ConstantScoreQuery(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
|
||||
if (context.externalValueSet() == false) {
|
||||
|
|
|
@ -81,7 +81,7 @@ public final class ParentJoinFieldMapper extends FieldMapper {
|
|||
public static ParentJoinFieldMapper getMapper(MapperService service) {
|
||||
MetaJoinFieldMapper.MetaJoinFieldType fieldType =
|
||||
(MetaJoinFieldMapper.MetaJoinFieldType) service.fullName(MetaJoinFieldMapper.NAME);
|
||||
return fieldType == null ? null : fieldType.mapper;
|
||||
return fieldType == null ? null : fieldType.getMapper();
|
||||
}
|
||||
|
||||
private static String getParentIdFieldName(String joinFieldName, String parentName) {
|
||||
|
@ -121,11 +121,11 @@ public final class ParentJoinFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
static class Builder extends FieldMapper.Builder<Builder, ParentJoinFieldMapper> {
|
||||
public static class Builder extends FieldMapper.Builder<Builder, ParentJoinFieldMapper> {
|
||||
final List<ParentIdFieldMapper.Builder> parentIdFieldBuilders = new ArrayList<>();
|
||||
boolean eagerGlobalOrdinals = true;
|
||||
|
||||
Builder(String name) {
|
||||
public Builder(String name) {
|
||||
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
|
||||
builder = this;
|
||||
}
|
||||
|
@ -431,4 +431,5 @@ public final class ParentJoinFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ import org.elasticsearch.index.query.QueryParseContext;
|
|||
import org.elasticsearch.index.query.QueryRewriteContext;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.query.QueryShardException;
|
||||
import org.elasticsearch.join.mapper.ParentIdFieldMapper;
|
||||
import org.elasticsearch.join.mapper.ParentJoinFieldMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
@ -305,6 +307,34 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
|||
|
||||
@Override
|
||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||
if (context.getIndexSettings().isSingleType()) {
|
||||
return joinFieldDoToQuery(context);
|
||||
} else {
|
||||
return parentFieldDoToQuery(context);
|
||||
}
|
||||
}
|
||||
|
||||
private Query joinFieldDoToQuery(QueryShardContext context) throws IOException {
|
||||
ParentJoinFieldMapper joinFieldMapper = ParentJoinFieldMapper.getMapper(context.getMapperService());
|
||||
ParentIdFieldMapper parentIdFieldMapper = joinFieldMapper.getParentIdFieldMapper(type, false);
|
||||
if (parentIdFieldMapper != null) {
|
||||
Query parentFilter = parentIdFieldMapper.getParentFilter();
|
||||
Query childFilter = parentIdFieldMapper.getChildFilter(type);
|
||||
Query innerQuery = Queries.filtered(query.toQuery(context), childFilter);
|
||||
MappedFieldType fieldType = parentIdFieldMapper.fieldType();
|
||||
final SortedSetDVOrdinalsIndexFieldData fieldData = context.getForField(fieldType);
|
||||
return new LateParsingQuery(parentFilter, innerQuery, minChildren(), maxChildren(),
|
||||
fieldType.name(), scoreMode, fieldData, context.getSearchSimilarity());
|
||||
} else {
|
||||
if (ignoreUnmapped) {
|
||||
return new MatchNoDocsQuery();
|
||||
} else {
|
||||
throw new QueryShardException(context, "[" + NAME + "] join field has no parent type configured");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Query parentFieldDoToQuery(QueryShardContext context) throws IOException {
|
||||
Query innerQuery;
|
||||
final String[] previousTypes = context.getTypes();
|
||||
context.setTypes(type);
|
||||
|
@ -313,8 +343,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
|||
} finally {
|
||||
context.setTypes(previousTypes);
|
||||
}
|
||||
|
||||
DocumentMapper childDocMapper = context.documentMapper(type);
|
||||
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
|
||||
if (childDocMapper == null) {
|
||||
if (ignoreUnmapped) {
|
||||
return new MatchNoDocsQuery();
|
||||
|
@ -330,16 +359,17 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
|||
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
|
||||
if (parentDocMapper == null) {
|
||||
throw new QueryShardException(context,
|
||||
"[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]");
|
||||
"[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]");
|
||||
}
|
||||
|
||||
// wrap the query with type query
|
||||
innerQuery = Queries.filtered(innerQuery, childDocMapper.typeFilter(context));
|
||||
|
||||
String joinField = ParentFieldMapper.joinField(parentType);
|
||||
final MappedFieldType parentFieldType = parentDocMapper.parentFieldMapper().getParentJoinFieldType();
|
||||
final SortedSetDVOrdinalsIndexFieldData fieldData = context.getForField(parentFieldType);
|
||||
return new LateParsingQuery(parentDocMapper.typeFilter(context), innerQuery, minChildren(), maxChildren(),
|
||||
parentType, scoreMode, fieldData, context.getSearchSimilarity());
|
||||
joinField, scoreMode, fieldData, context.getSearchSimilarity());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,19 +388,19 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
|||
private final Query innerQuery;
|
||||
private final int minChildren;
|
||||
private final int maxChildren;
|
||||
private final String parentType;
|
||||
private final String joinField;
|
||||
private final ScoreMode scoreMode;
|
||||
private final SortedSetDVOrdinalsIndexFieldData fieldDataJoin;
|
||||
private final Similarity similarity;
|
||||
|
||||
LateParsingQuery(Query toQuery, Query innerQuery, int minChildren, int maxChildren,
|
||||
String parentType, ScoreMode scoreMode,
|
||||
String joinField, ScoreMode scoreMode,
|
||||
SortedSetDVOrdinalsIndexFieldData fieldData, Similarity similarity) {
|
||||
this.toQuery = toQuery;
|
||||
this.innerQuery = innerQuery;
|
||||
this.minChildren = minChildren;
|
||||
this.maxChildren = maxChildren;
|
||||
this.parentType = parentType;
|
||||
this.joinField = joinField;
|
||||
this.scoreMode = scoreMode;
|
||||
this.fieldDataJoin = fieldData;
|
||||
this.similarity = similarity;
|
||||
|
@ -383,7 +413,6 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
|||
return rewritten;
|
||||
}
|
||||
if (reader instanceof DirectoryReader) {
|
||||
String joinField = ParentFieldMapper.joinField(parentType);
|
||||
IndexSearcher indexSearcher = new IndexSearcher(reader);
|
||||
indexSearcher.setQueryCache(null);
|
||||
indexSearcher.setSimilarity(similarity);
|
||||
|
@ -414,18 +443,18 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
|||
if (maxChildren != that.maxChildren) return false;
|
||||
if (!toQuery.equals(that.toQuery)) return false;
|
||||
if (!innerQuery.equals(that.innerQuery)) return false;
|
||||
if (!parentType.equals(that.parentType)) return false;
|
||||
if (!joinField.equals(that.joinField)) return false;
|
||||
return scoreMode == that.scoreMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(classHash(), toQuery, innerQuery, minChildren, maxChildren, parentType, scoreMode);
|
||||
return Objects.hash(getClass(), toQuery, innerQuery, minChildren, maxChildren, joinField, scoreMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String s) {
|
||||
return "LateParsingQuery {parentType=" + parentType + "}";
|
||||
return "LateParsingQuery {joinField=" + joinField + "}";
|
||||
}
|
||||
|
||||
public int getMinChildren() {
|
||||
|
|
|
@ -55,6 +55,8 @@ import org.elasticsearch.index.query.QueryParseContext;
|
|||
import org.elasticsearch.index.query.QueryRewriteContext;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.query.QueryShardException;
|
||||
import org.elasticsearch.join.mapper.ParentIdFieldMapper;
|
||||
import org.elasticsearch.join.mapper.ParentJoinFieldMapper;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.SearchHitField;
|
||||
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
|
||||
|
@ -187,6 +189,35 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
|||
|
||||
@Override
|
||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||
if (context.getIndexSettings().isSingleType()) {
|
||||
return joinFieldDoToQuery(context);
|
||||
} else {
|
||||
return parentFieldDoToQuery(context);
|
||||
}
|
||||
}
|
||||
|
||||
private Query joinFieldDoToQuery(QueryShardContext context) throws IOException {
|
||||
ParentJoinFieldMapper joinFieldMapper = ParentJoinFieldMapper.getMapper(context.getMapperService());
|
||||
ParentIdFieldMapper parentIdFieldMapper = joinFieldMapper.getParentIdFieldMapper(type, true);
|
||||
if (parentIdFieldMapper != null) {
|
||||
Query parentFilter = parentIdFieldMapper.getParentFilter();
|
||||
Query innerQuery = Queries.filtered(query.toQuery(context), parentFilter);
|
||||
Query childFilter = parentIdFieldMapper.getChildrenFilter();
|
||||
MappedFieldType fieldType = parentIdFieldMapper.fieldType();
|
||||
final SortedSetDVOrdinalsIndexFieldData fieldData = context.getForField(fieldType);
|
||||
return new HasChildQueryBuilder.LateParsingQuery(childFilter, innerQuery,
|
||||
HasChildQueryBuilder.DEFAULT_MIN_CHILDREN, HasChildQueryBuilder.DEFAULT_MAX_CHILDREN,
|
||||
fieldType.name(), score ? ScoreMode.Max : ScoreMode.None, fieldData, context.getSearchSimilarity());
|
||||
} else {
|
||||
if (ignoreUnmapped) {
|
||||
return new MatchNoDocsQuery();
|
||||
} else {
|
||||
throw new QueryShardException(context, "[" + NAME + "] join field has no parent type configured");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Query parentFieldDoToQuery(QueryShardContext context) throws IOException {
|
||||
Query innerQuery;
|
||||
String[] previousTypes = context.getTypes();
|
||||
context.setTypes(type);
|
||||
|
@ -239,7 +270,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
|||
innerQuery,
|
||||
HasChildQueryBuilder.DEFAULT_MIN_CHILDREN,
|
||||
HasChildQueryBuilder.DEFAULT_MAX_CHILDREN,
|
||||
type,
|
||||
ParentFieldMapper.joinField(type),
|
||||
score ? ScoreMode.Max : ScoreMode.None,
|
||||
fieldData,
|
||||
context.getSearchSimilarity());
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.elasticsearch.action.update.UpdateResponse;
|
|||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.join.ParentJoinPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
|
@ -84,15 +83,58 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
return nodePlugins();
|
||||
}
|
||||
|
||||
protected boolean legacy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private IndexRequestBuilder createIndexRequest(String index, String type, String id, String parentId, Object... fields) {
|
||||
String name = type;
|
||||
if (legacy() == false) {
|
||||
type = "doc";
|
||||
}
|
||||
|
||||
IndexRequestBuilder indexRequestBuilder = client().prepareIndex(index, type, id);
|
||||
if (legacy()) {
|
||||
if (parentId != null) {
|
||||
indexRequestBuilder.setParent(parentId);
|
||||
}
|
||||
indexRequestBuilder.setSource(fields);
|
||||
} else {
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
for (int i = 0; i < fields.length; i += 2) {
|
||||
source.put((String) fields[i], fields[i + 1]);
|
||||
}
|
||||
Map<String, Object> joinField = new HashMap<>();
|
||||
if (parentId != null) {
|
||||
joinField.put("name", name);
|
||||
joinField.put("parent", parentId);
|
||||
indexRequestBuilder.setRouting(parentId);
|
||||
} else {
|
||||
joinField.put("name", name);
|
||||
}
|
||||
source.put("join_field", joinField);
|
||||
indexRequestBuilder.setSource(source);
|
||||
}
|
||||
indexRequestBuilder.setCreate(true);
|
||||
return indexRequestBuilder;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupCluster() throws Exception {
|
||||
categoryToControl.clear();
|
||||
assertAcked(
|
||||
if (legacy()) {
|
||||
assertAcked(
|
||||
prepareCreate("test")
|
||||
.setSettings("index.mapping.single_type", false)
|
||||
.addMapping("article", "category", "type=keyword")
|
||||
.addMapping("comment", "_parent", "type=article", "commenter", "type=keyword")
|
||||
);
|
||||
);
|
||||
} else {
|
||||
assertAcked(
|
||||
prepareCreate("test")
|
||||
.addMapping("doc", "category", "type=keyword", "join_field", "type=join,article=comment",
|
||||
"commenter", "type=keyword")
|
||||
);
|
||||
}
|
||||
|
||||
List<IndexRequestBuilder> requests = new ArrayList<>();
|
||||
String[] uniqueCategories = new String[randomIntBetween(1, 25)];
|
||||
|
@ -103,7 +145,7 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
|
||||
int numParentDocs = randomIntBetween(uniqueCategories.length, uniqueCategories.length * 5);
|
||||
for (int i = 0; i < numParentDocs; i++) {
|
||||
String id = Integer.toString(i);
|
||||
String id = "article-" + i;
|
||||
|
||||
// TODO: this array is always of length 1, and testChildrenAggs fails if this is changed
|
||||
String[] categories = new String[randomIntBetween(1,1)];
|
||||
|
@ -116,8 +158,7 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
control.articleIds.add(id);
|
||||
}
|
||||
|
||||
requests.add(client()
|
||||
.prepareIndex("test", "article", id).setCreate(true).setSource("category", categories, "randomized", true));
|
||||
requests.add(createIndexRequest("test", "article", id, null, "category", categories, "randomized", true));
|
||||
}
|
||||
|
||||
String[] commenters = new String[randomIntBetween(5, 50)];
|
||||
|
@ -131,31 +172,24 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
int numChildDocsPerParent = randomIntBetween(0, 5);
|
||||
for (int i = 0; i < numChildDocsPerParent; i++) {
|
||||
String commenter = commenters[id % commenters.length];
|
||||
String idValue = Integer.toString(id++);
|
||||
String idValue = "comment-" + id++;
|
||||
control.commentIds.add(idValue);
|
||||
Set<String> ids = control.commenterToCommentId.get(commenter);
|
||||
if (ids == null) {
|
||||
control.commenterToCommentId.put(commenter, ids = new HashSet<>());
|
||||
}
|
||||
ids.add(idValue);
|
||||
requests.add(client().prepareIndex("test", "comment", idValue)
|
||||
.setCreate(true).setParent(articleId).setSource("commenter", commenter));
|
||||
requests.add(createIndexRequest("test", "comment", idValue, articleId, "commenter", commenter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requests.add(client().prepareIndex("test", "article", "a")
|
||||
.setSource("category", new String[]{"a"}, "randomized", false));
|
||||
requests.add(client().prepareIndex("test", "article", "b")
|
||||
.setSource("category", new String[]{"a", "b"}, "randomized", false));
|
||||
requests.add(client().prepareIndex("test", "article", "c")
|
||||
.setSource("category", new String[]{"a", "b", "c"}, "randomized", false));
|
||||
requests.add(client().prepareIndex("test", "article", "d")
|
||||
.setSource("category", new String[]{"c"}, "randomized", false));
|
||||
requests.add(client().prepareIndex("test", "comment", "a")
|
||||
.setParent("a").setSource("{}", XContentType.JSON));
|
||||
requests.add(client().prepareIndex("test", "comment", "c")
|
||||
.setParent("c").setSource("{}", XContentType.JSON));
|
||||
requests.add(createIndexRequest("test", "article", "a", null, "category", new String[]{"a"}, "randomized", false));
|
||||
requests.add(createIndexRequest("test", "article", "b", null, "category", new String[]{"a", "b"}, "randomized", false));
|
||||
requests.add(createIndexRequest("test", "article", "c", null, "category", new String[]{"a", "b", "c"}, "randomized", false));
|
||||
requests.add(createIndexRequest("test", "article", "d", null, "category", new String[]{"c"}, "randomized", false));
|
||||
requests.add(createIndexRequest("test", "comment", "e", "a"));
|
||||
requests.add(createIndexRequest("test", "comment", "f", "c"));
|
||||
|
||||
indexRandom(true, requests);
|
||||
ensureSearchable("test");
|
||||
|
@ -184,11 +218,11 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
Children childrenBucket = categoryBucket.getAggregations().get("to_comment");
|
||||
assertThat(childrenBucket.getName(), equalTo("to_comment"));
|
||||
assertThat(childrenBucket.getDocCount(), equalTo((long) entry1.getValue().commentIds.size()));
|
||||
assertThat((long) ((InternalAggregation)childrenBucket).getProperty("_count"),
|
||||
assertThat(((InternalAggregation)childrenBucket).getProperty("_count"),
|
||||
equalTo((long) entry1.getValue().commentIds.size()));
|
||||
|
||||
Terms commentersTerms = childrenBucket.getAggregations().get("commenters");
|
||||
assertThat((Terms) ((InternalAggregation)childrenBucket).getProperty("commenters"), sameInstance(commentersTerms));
|
||||
assertThat(((InternalAggregation)childrenBucket).getProperty("commenters"), sameInstance(commentersTerms));
|
||||
assertThat(commentersTerms.getBuckets().size(), equalTo(entry1.getValue().commenterToCommentId.size()));
|
||||
for (Map.Entry<String, Set<String>> entry2 : entry1.getValue().commenterToCommentId.entrySet()) {
|
||||
Terms.Bucket commentBucket = commentersTerms.getBucketByKey(entry2.getKey());
|
||||
|
@ -235,10 +269,8 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
assertThat(childrenBucket.getDocCount(), equalTo(2L));
|
||||
TopHits topHits = childrenBucket.getAggregations().get("top_comments");
|
||||
assertThat(topHits.getHits().getTotalHits(), equalTo(2L));
|
||||
assertThat(topHits.getHits().getAt(0).getId(), equalTo("a"));
|
||||
assertThat(topHits.getHits().getAt(0).getType(), equalTo("comment"));
|
||||
assertThat(topHits.getHits().getAt(1).getId(), equalTo("c"));
|
||||
assertThat(topHits.getHits().getAt(1).getType(), equalTo("comment"));
|
||||
assertThat(topHits.getHits().getAt(0).getId(), equalTo("e"));
|
||||
assertThat(topHits.getHits().getAt(1).getId(), equalTo("f"));
|
||||
|
||||
categoryBucket = categoryTerms.getBucketByKey("b");
|
||||
assertThat(categoryBucket.getKeyAsString(), equalTo("b"));
|
||||
|
@ -249,8 +281,7 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
assertThat(childrenBucket.getDocCount(), equalTo(1L));
|
||||
topHits = childrenBucket.getAggregations().get("top_comments");
|
||||
assertThat(topHits.getHits().getTotalHits(), equalTo(1L));
|
||||
assertThat(topHits.getHits().getAt(0).getId(), equalTo("c"));
|
||||
assertThat(topHits.getHits().getAt(0).getType(), equalTo("comment"));
|
||||
assertThat(topHits.getHits().getAt(0).getId(), equalTo("f"));
|
||||
|
||||
categoryBucket = categoryTerms.getBucketByKey("c");
|
||||
assertThat(categoryBucket.getKeyAsString(), equalTo("c"));
|
||||
|
@ -261,25 +292,30 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
assertThat(childrenBucket.getDocCount(), equalTo(1L));
|
||||
topHits = childrenBucket.getAggregations().get("top_comments");
|
||||
assertThat(topHits.getHits().getTotalHits(), equalTo(1L));
|
||||
assertThat(topHits.getHits().getAt(0).getId(), equalTo("c"));
|
||||
assertThat(topHits.getHits().getAt(0).getType(), equalTo("comment"));
|
||||
assertThat(topHits.getHits().getAt(0).getId(), equalTo("f"));
|
||||
}
|
||||
|
||||
public void testWithDeletes() throws Exception {
|
||||
String indexName = "xyz";
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.setSettings("index.mapping.single_type", false)
|
||||
.addMapping("parent")
|
||||
.addMapping("child", "_parent", "type=parent", "count", "type=long")
|
||||
);
|
||||
if (legacy()) {
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.addMapping("parent")
|
||||
.addMapping("child", "_parent", "type=parent", "count", "type=long")
|
||||
);
|
||||
} else {
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.addMapping("doc", "join_field", "type=join,parent=child", "count", "type=long")
|
||||
);
|
||||
}
|
||||
|
||||
List<IndexRequestBuilder> requests = new ArrayList<>();
|
||||
requests.add(client().prepareIndex(indexName, "parent", "1").setSource("{}", XContentType.JSON));
|
||||
requests.add(client().prepareIndex(indexName, "child", "0").setParent("1").setSource("count", 1));
|
||||
requests.add(client().prepareIndex(indexName, "child", "1").setParent("1").setSource("count", 1));
|
||||
requests.add(client().prepareIndex(indexName, "child", "2").setParent("1").setSource("count", 1));
|
||||
requests.add(client().prepareIndex(indexName, "child", "3").setParent("1").setSource("count", 1));
|
||||
requests.add(createIndexRequest(indexName, "parent", "1", null));
|
||||
requests.add(createIndexRequest(indexName, "child", "2", "1", "count", 1));
|
||||
requests.add(createIndexRequest(indexName, "child", "3", "1", "count", 1));
|
||||
requests.add(createIndexRequest(indexName, "child", "4", "1", "count", 1));
|
||||
requests.add(createIndexRequest(indexName, "child", "5", "1", "count", 1));
|
||||
indexRandom(true, requests);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
|
@ -294,17 +330,26 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
Sum count = children.getAggregations().get("counts");
|
||||
assertThat(count.getValue(), equalTo(4.));
|
||||
|
||||
String idToUpdate = Integer.toString(randomInt(3));
|
||||
String idToUpdate = Integer.toString(2 + randomInt(3));
|
||||
/*
|
||||
* The whole point of this test is to test these things with deleted
|
||||
* docs in the index so we turn off detect_noop to make sure that
|
||||
* the updates cause that.
|
||||
*/
|
||||
UpdateResponse updateResponse = client().prepareUpdate(indexName, "child", idToUpdate)
|
||||
.setParent("1")
|
||||
.setDoc(Requests.INDEX_CONTENT_TYPE, "count", 1)
|
||||
.setDetectNoop(false)
|
||||
.get();
|
||||
UpdateResponse updateResponse;
|
||||
if (legacy()) {
|
||||
updateResponse = client().prepareUpdate(indexName, "child", idToUpdate)
|
||||
.setParent("1")
|
||||
.setDoc(Requests.INDEX_CONTENT_TYPE, "count", 1)
|
||||
.setDetectNoop(false)
|
||||
.get();
|
||||
} else {
|
||||
updateResponse = client().prepareUpdate(indexName, "doc", idToUpdate)
|
||||
.setRouting("1")
|
||||
.setDoc(Requests.INDEX_CONTENT_TYPE, "count", 1)
|
||||
.setDetectNoop(false)
|
||||
.get();
|
||||
}
|
||||
assertThat(updateResponse.getVersion(), greaterThan(1L));
|
||||
refresh();
|
||||
}
|
||||
|
@ -326,35 +371,47 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
String indexName = "prodcatalog";
|
||||
String masterType = "masterprod";
|
||||
String childType = "variantsku";
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.setSettings("index.mapping.single_type", false)
|
||||
.addMapping(masterType, "brand", "type=text", "name", "type=keyword", "material", "type=text")
|
||||
.addMapping(childType, "_parent", "type=masterprod", "color", "type=keyword", "size", "type=keyword")
|
||||
);
|
||||
if (legacy()) {
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.setSettings(Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
|
||||
.put("index.mapping.single_type", false))
|
||||
.addMapping(masterType, "brand", "type=text", "name", "type=keyword", "material", "type=text")
|
||||
.addMapping(childType, "_parent", "type=masterprod", "color", "type=keyword", "size", "type=keyword")
|
||||
);
|
||||
} else {
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.setSettings(Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0))
|
||||
.addMapping("doc", "join_field", "type=join," + masterType + "=" + childType, "brand", "type=text",
|
||||
"name", "type=keyword", "material", "type=text", "color", "type=keyword", "size", "type=keyword")
|
||||
);
|
||||
}
|
||||
|
||||
List<IndexRequestBuilder> requests = new ArrayList<>();
|
||||
requests.add(client().prepareIndex(indexName, masterType, "1")
|
||||
.setSource("brand", "Levis", "name", "Style 501", "material", "Denim"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "0").setParent("1").setSource("color", "blue", "size", "32"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "1").setParent("1").setSource("color", "blue", "size", "34"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "2").setParent("1").setSource("color", "blue", "size", "36"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "3").setParent("1").setSource("color", "black", "size", "38"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "4").setParent("1").setSource("color", "black", "size", "40"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "5").setParent("1").setSource("color", "gray", "size", "36"));
|
||||
requests.add(createIndexRequest(indexName, masterType, "1", null, "brand", "Levis", "name",
|
||||
"Style 501", "material", "Denim"));
|
||||
requests.add(createIndexRequest(indexName, childType, "3", "1", "color", "blue", "size", "32"));
|
||||
requests.add(createIndexRequest(indexName, childType, "4", "1", "color", "blue", "size", "34"));
|
||||
requests.add(createIndexRequest(indexName, childType, "5", "1", "color", "blue", "size", "36"));
|
||||
requests.add(createIndexRequest(indexName, childType, "6", "1", "color", "black", "size", "38"));
|
||||
requests.add(createIndexRequest(indexName, childType, "7", "1", "color", "black", "size", "40"));
|
||||
requests.add(createIndexRequest(indexName, childType, "8", "1", "color", "gray", "size", "36"));
|
||||
|
||||
requests.add(client().prepareIndex(indexName, masterType, "2")
|
||||
.setSource("brand", "Wrangler", "name", "Regular Cut", "material", "Leather"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "6").setParent("2").setSource("color", "blue", "size", "32"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "7").setParent("2").setSource("color", "blue", "size", "34"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "8").setParent("2").setSource("color", "black", "size", "36"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "9").setParent("2").setSource("color", "black", "size", "38"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "10").setParent("2").setSource("color", "black", "size", "40"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "11").setParent("2").setSource("color", "orange", "size", "36"));
|
||||
requests.add(client().prepareIndex(indexName, childType, "12").setParent("2").setSource("color", "green", "size", "44"));
|
||||
requests.add(createIndexRequest(indexName, masterType, "2", null, "brand", "Wrangler", "name",
|
||||
"Regular Cut", "material", "Leather"));
|
||||
requests.add(createIndexRequest(indexName, childType, "9", "2", "color", "blue", "size", "32"));
|
||||
requests.add(createIndexRequest(indexName, childType, "10", "2", "color", "blue", "size", "34"));
|
||||
requests.add(createIndexRequest(indexName, childType, "12", "2", "color", "black", "size", "36"));
|
||||
requests.add(createIndexRequest(indexName, childType, "13", "2", "color", "black", "size", "38"));
|
||||
requests.add(createIndexRequest(indexName, childType, "14", "2", "color", "black", "size", "40"));
|
||||
requests.add(createIndexRequest(indexName, childType, "15", "2", "color", "orange", "size", "36"));
|
||||
requests.add(createIndexRequest(indexName, childType, "16", "2", "color", "green", "size", "44"));
|
||||
indexRandom(true, requests);
|
||||
|
||||
SearchResponse response = client().prepareSearch(indexName).setTypes(masterType)
|
||||
SearchResponse response = client().prepareSearch(indexName)
|
||||
.setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None))
|
||||
.addAggregation(children("my-refinements", childType)
|
||||
.subAggregation(terms("my-colors").field("color"))
|
||||
|
@ -388,21 +445,27 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
String grandParentType = "continent";
|
||||
String parentType = "country";
|
||||
String childType = "city";
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.setSettings(Settings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
|
||||
)
|
||||
.setSettings("index.mapping.single_type", false)
|
||||
.addMapping(grandParentType, "name", "type=keyword")
|
||||
.addMapping(parentType, "_parent", "type=" + grandParentType)
|
||||
.addMapping(childType, "_parent", "type=" + parentType)
|
||||
);
|
||||
if (legacy()) {
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.setSettings(Settings.builder()
|
||||
.put("index.mapping.single_type", false)
|
||||
).addMapping(grandParentType, "name", "type=keyword")
|
||||
.addMapping(parentType, "_parent", "type=" + grandParentType)
|
||||
.addMapping(childType, "_parent", "type=" + parentType)
|
||||
|
||||
client().prepareIndex(indexName, grandParentType, "1").setSource("name", "europe").get();
|
||||
client().prepareIndex(indexName, parentType, "2").setParent("1").setSource("name", "belgium").get();
|
||||
client().prepareIndex(indexName, childType, "3").setParent("2").setRouting("1").setSource("name", "brussels").get();
|
||||
);
|
||||
} else {
|
||||
assertAcked(
|
||||
prepareCreate(indexName)
|
||||
.addMapping("doc", "join_field", "type=join," + grandParentType + "=" + parentType + "," +
|
||||
parentType + "=" + childType, "name", "type=keyword")
|
||||
);
|
||||
}
|
||||
|
||||
createIndexRequest(indexName, grandParentType, "1", null, "name", "europe").get();
|
||||
createIndexRequest(indexName, parentType, "2", "1", "name", "belgium").get();
|
||||
createIndexRequest(indexName, childType, "3", "2", "name", "brussels").setRouting("1").get();
|
||||
refresh();
|
||||
|
||||
SearchResponse response = client().prepareSearch(indexName)
|
||||
|
@ -435,32 +498,37 @@ public class ChildrenIT extends ESIntegTestCase {
|
|||
|
||||
// Before we only evaluated segments that yielded matches in 'towns' and 'parent_names' aggs, which caused
|
||||
// us to miss to evaluate child docs in segments we didn't have parent matches for.
|
||||
|
||||
assertAcked(
|
||||
prepareCreate("index")
|
||||
.setSettings("index.mapping.single_type", false)
|
||||
.addMapping("parentType", "name", "type=keyword", "town", "type=keyword")
|
||||
.addMapping("childType", "_parent", "type=parentType", "name", "type=keyword", "age", "type=integer")
|
||||
);
|
||||
if (legacy()) {
|
||||
assertAcked(
|
||||
prepareCreate("index")
|
||||
.addMapping("parentType", "name", "type=keyword", "town", "type=keyword")
|
||||
.addMapping("childType", "_parent", "type=parentType", "name", "type=keyword", "age", "type=integer")
|
||||
);
|
||||
} else {
|
||||
assertAcked(
|
||||
prepareCreate("index")
|
||||
.addMapping("doc", "join_field", "type=join,parentType=childType", "name", "type=keyword",
|
||||
"town", "type=keyword", "age", "type=integer")
|
||||
);
|
||||
}
|
||||
List<IndexRequestBuilder> requests = new ArrayList<>();
|
||||
requests.add(client().prepareIndex("index", "parentType", "1").setSource("name", "Bob", "town", "Memphis"));
|
||||
requests.add(client().prepareIndex("index", "parentType", "2").setSource("name", "Alice", "town", "Chicago"));
|
||||
requests.add(client().prepareIndex("index", "parentType", "3").setSource("name", "Bill", "town", "Chicago"));
|
||||
requests.add(client().prepareIndex("index", "childType", "1").setSource("name", "Jill", "age", 5).setParent("1"));
|
||||
requests.add(client().prepareIndex("index", "childType", "2").setSource("name", "Joey", "age", 3).setParent("1"));
|
||||
requests.add(client().prepareIndex("index", "childType", "3").setSource("name", "John", "age", 2).setParent("2"));
|
||||
requests.add(client().prepareIndex("index", "childType", "4").setSource("name", "Betty", "age", 6).setParent("3"));
|
||||
requests.add(client().prepareIndex("index", "childType", "5").setSource("name", "Dan", "age", 1).setParent("3"));
|
||||
requests.add(createIndexRequest("index", "parentType", "1", null, "name", "Bob", "town", "Memphis"));
|
||||
requests.add(createIndexRequest("index", "parentType", "2", null, "name", "Alice", "town", "Chicago"));
|
||||
requests.add(createIndexRequest("index", "parentType", "3", null, "name", "Bill", "town", "Chicago"));
|
||||
requests.add(createIndexRequest("index", "childType", "4", "1", "name", "Jill", "age", 5));
|
||||
requests.add(createIndexRequest("index", "childType", "5", "1", "name", "Joey", "age", 3));
|
||||
requests.add(createIndexRequest("index", "childType", "6", "2", "name", "John", "age", 2));
|
||||
requests.add(createIndexRequest("index", "childType", "7", "3", "name", "Betty", "age", 6));
|
||||
requests.add(createIndexRequest("index", "childType", "8", "3", "name", "Dan", "age", 1));
|
||||
indexRandom(true, requests);
|
||||
|
||||
SearchResponse response = client().prepareSearch("index")
|
||||
.setSize(0)
|
||||
.addAggregation(AggregationBuilders.terms("towns").field("town")
|
||||
.subAggregation(AggregationBuilders.terms("parent_names").field("name")
|
||||
.subAggregation(children("child_docs", "childType"))
|
||||
.subAggregation(children("child_docs", "childType"))
|
||||
)
|
||||
)
|
||||
.get();
|
||||
).get();
|
||||
|
||||
Terms towns = response.getAggregations().get("towns");
|
||||
assertThat(towns.getBuckets().size(), equalTo(2));
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.elasticsearch.join.aggregations;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
||||
import org.elasticsearch.test.ESIntegTestCase.Scope;
|
||||
|
||||
@ClusterScope(scope = Scope.SUITE)
|
||||
public class LegacyChildrenIT extends ChildrenIT {
|
||||
|
||||
@Override
|
||||
protected boolean legacy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings indexSettings() {
|
||||
Settings indexSettings = super.indexSettings();
|
||||
return Settings.builder()
|
||||
.put(indexSettings)
|
||||
.put("index.mapping.single_type", false)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -39,23 +39,22 @@ import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper;
|
||||
import org.elasticsearch.index.mapper.ParentFieldMapper;
|
||||
import org.elasticsearch.index.mapper.TypeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.Uid;
|
||||
import org.elasticsearch.index.mapper.UidFieldMapper;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.join.mapper.MetaJoinFieldMapper;
|
||||
import org.elasticsearch.join.mapper.ParentJoinFieldMapper;
|
||||
import org.elasticsearch.search.aggregations.AggregatorTestCase;
|
||||
import org.elasticsearch.search.aggregations.metrics.min.InternalMin;
|
||||
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -139,42 +138,41 @@ public class ParentToChildrenAggregatorTests extends AggregatorTestCase {
|
|||
}
|
||||
|
||||
private static List<Field> createParentDocument(String id) {
|
||||
return Arrays.asList(new StringField(TypeFieldMapper.NAME, PARENT_TYPE, Field.Store.NO),
|
||||
return Arrays.asList(
|
||||
new StringField(UidFieldMapper.NAME, Uid.createUid(PARENT_TYPE, id), Field.Store.NO),
|
||||
createJoinField(PARENT_TYPE, id));
|
||||
new StringField("join_field", PARENT_TYPE, Field.Store.NO),
|
||||
createJoinField(PARENT_TYPE, id)
|
||||
);
|
||||
}
|
||||
|
||||
private static List<Field> createChildDocument(String childId, String parentId, int value) {
|
||||
return Arrays.asList(new StringField(TypeFieldMapper.NAME, CHILD_TYPE, Field.Store.NO),
|
||||
return Arrays.asList(
|
||||
new StringField(UidFieldMapper.NAME, Uid.createUid(CHILD_TYPE, childId), Field.Store.NO),
|
||||
new SortedNumericDocValuesField("number", value),
|
||||
createJoinField(PARENT_TYPE, parentId));
|
||||
new StringField("join_field", CHILD_TYPE, Field.Store.NO),
|
||||
createJoinField(PARENT_TYPE, parentId),
|
||||
new SortedNumericDocValuesField("number", value)
|
||||
);
|
||||
}
|
||||
|
||||
private static SortedDocValuesField createJoinField(String parentType, String id) {
|
||||
return new SortedDocValuesField(ParentFieldMapper.joinField(parentType), new BytesRef(id));
|
||||
return new SortedDocValuesField("join_field#" + parentType, new BytesRef(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapperService mapperServiceMock() {
|
||||
ParentJoinFieldMapper joinFieldMapper = createJoinFieldMapper();
|
||||
MapperService mapperService = mock(MapperService.class);
|
||||
DocumentMapper childDocMapper = mock(DocumentMapper.class);
|
||||
DocumentMapper parentDocMapper = mock(DocumentMapper.class);
|
||||
ParentFieldMapper parentFieldMapper = createParentFieldMapper();
|
||||
when(childDocMapper.parentFieldMapper()).thenReturn(parentFieldMapper);
|
||||
when(parentDocMapper.parentFieldMapper()).thenReturn(parentFieldMapper);
|
||||
when(mapperService.documentMapper(CHILD_TYPE)).thenReturn(childDocMapper);
|
||||
when(mapperService.documentMapper(PARENT_TYPE)).thenReturn(parentDocMapper);
|
||||
when(mapperService.docMappers(false)).thenReturn(Arrays.asList(new DocumentMapper[] { childDocMapper, parentDocMapper }));
|
||||
when(parentDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(PARENT_TYPE)));
|
||||
when(childDocMapper.typeFilter(Mockito.any())).thenReturn(new TypeFieldMapper.TypesQuery(new BytesRef(CHILD_TYPE)));
|
||||
MetaJoinFieldMapper.MetaJoinFieldType metaJoinFieldType = mock(MetaJoinFieldMapper.MetaJoinFieldType.class);
|
||||
when(metaJoinFieldType.getMapper()).thenReturn(joinFieldMapper);
|
||||
when(mapperService.fullName("_parent_join")).thenReturn(metaJoinFieldType);
|
||||
return mapperService;
|
||||
}
|
||||
|
||||
private static ParentFieldMapper createParentFieldMapper() {
|
||||
private static ParentJoinFieldMapper createJoinFieldMapper() {
|
||||
Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
|
||||
return new ParentFieldMapper.Builder("parent_type")
|
||||
.type(PARENT_TYPE).build(new Mapper.BuilderContext(settings, new ContentPath(0)));
|
||||
return new ParentJoinFieldMapper.Builder("join_field")
|
||||
.addParent(PARENT_TYPE, Collections.singleton(CHILD_TYPE))
|
||||
.build(new Mapper.BuilderContext(settings, new ContentPath(0)));
|
||||
}
|
||||
|
||||
private void testCase(Query query, IndexSearcher indexSearcher, Consumer<InternalChildren> verify)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,12 +20,12 @@
|
|||
package org.elasticsearch.join.query;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||
import org.apache.lucene.search.TermInSetQuery;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermInSetQuery;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.join.ScoreMode;
|
||||
import org.apache.lucene.search.similarities.PerFieldSimilarityWrapper;
|
||||
|
@ -87,6 +87,8 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
|||
@Override
|
||||
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
|
||||
similarity = randomFrom("classic", "BM25");
|
||||
// TODO: use a single type when inner hits have been changed to work with join field,
|
||||
// this test randomly generates queries with inner hits
|
||||
mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE,
|
||||
STRING_FIELD_NAME, "type=text",
|
||||
STRING_FIELD_NAME_2, "type=keyword",
|
||||
|
|
|
@ -72,6 +72,8 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
|
|||
|
||||
@Override
|
||||
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
|
||||
// TODO: use a single type when inner hits have been changed to work with join field,
|
||||
// this test randomly generates queries with inner hits
|
||||
mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE,
|
||||
STRING_FIELD_NAME, "type=text",
|
||||
STRING_FIELD_NAME_2, "type=keyword",
|
||||
|
|
|
@ -547,7 +547,9 @@ public class InnerHitsIT extends ESIntegTestCase {
|
|||
.addMapping("parent_type", "nested_type", "type=nested")
|
||||
.addMapping("child_type", "_parent", "type=parent_type")
|
||||
);
|
||||
createIndex("index2");
|
||||
assertAcked(prepareCreate("index2")
|
||||
.setSettings("index.mapping.single_type", false)
|
||||
);
|
||||
client().prepareIndex("index1", "parent_type", "1").setSource("nested_type", Collections.singletonMap("key", "value")).get();
|
||||
client().prepareIndex("index1", "child_type", "2").setParent("1").setSource("{}", XContentType.JSON).get();
|
||||
client().prepareIndex("index2", "type", "3").setSource("key", "value").get();
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.elasticsearch.join.query;
|
||||
|
||||
import org.apache.lucene.search.join.ScoreMode;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
|
||||
import org.elasticsearch.action.bulk.BulkRequestBuilder;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
|
||||
import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery;
|
||||
import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class LegacyChildQuerySearchIT extends ChildQuerySearchIT {
|
||||
|
||||
@Override
|
||||
protected boolean legacy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings indexSettings() {
|
||||
Settings indexSettings = super.indexSettings();
|
||||
return Settings.builder()
|
||||
.put(indexSettings)
|
||||
.put("index.mapping.single_type", false)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void testIndexChildDocWithNoParentMapping() throws IOException {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("parent")
|
||||
.addMapping("child1"));
|
||||
ensureGreen();
|
||||
|
||||
client().prepareIndex("test", "parent", "p1").setSource("p_field", "p_value1").get();
|
||||
try {
|
||||
client().prepareIndex("test", "child1", "c1").setParent("p1").setSource("c_field", "blue").get();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.toString(), containsString("can't specify parent if no parent field has been configured"));
|
||||
}
|
||||
try {
|
||||
client().prepareIndex("test", "child2", "c2").setParent("p1").setSource("c_field", "blue").get();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.toString(), containsString("can't specify parent if no parent field has been configured"));
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void testAddingParentToExistingMapping() throws IOException {
|
||||
createIndex("test");
|
||||
ensureGreen();
|
||||
|
||||
PutMappingResponse putMappingResponse = client().admin().indices()
|
||||
.preparePutMapping("test").setType("child").setSource("number", "type=integer")
|
||||
.get();
|
||||
assertThat(putMappingResponse.isAcknowledged(), equalTo(true));
|
||||
|
||||
GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("test").get();
|
||||
Map<String, Object> mapping = getMappingsResponse.getMappings().get("test").get("child").getSourceAsMap();
|
||||
assertThat(mapping.size(), greaterThanOrEqualTo(1)); // there are potentially some meta fields configured randomly
|
||||
assertThat(mapping.get("properties"), notNullValue());
|
||||
|
||||
try {
|
||||
// Adding _parent metadata field to existing mapping is prohibited:
|
||||
client().admin().indices().preparePutMapping("test").setType("child").setSource(jsonBuilder().startObject().startObject("child")
|
||||
.startObject("_parent").field("type", "parent").endObject()
|
||||
.endObject().endObject()).get();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.toString(), containsString("The _parent field's type option can't be changed: [null]->[parent]"));
|
||||
}
|
||||
}
|
||||
|
||||
// Issue #5783
|
||||
public void testQueryBeforeChildType() throws Exception {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("features")
|
||||
.addMapping("posts", "_parent", "type=features")
|
||||
.addMapping("specials"));
|
||||
ensureGreen();
|
||||
|
||||
client().prepareIndex("test", "features", "1").setSource("field", "foo").get();
|
||||
client().prepareIndex("test", "posts", "1").setParent("1").setSource("field", "bar").get();
|
||||
refresh();
|
||||
|
||||
SearchResponse resp;
|
||||
resp = client().prepareSearch("test")
|
||||
.setSource(new SearchSourceBuilder().query(hasChildQuery("posts",
|
||||
QueryBuilders.matchQuery("field", "bar"), ScoreMode.None)))
|
||||
.get();
|
||||
assertHitCount(resp, 1L);
|
||||
}
|
||||
|
||||
// Issue #6256
|
||||
public void testParentFieldInMultiMatchField() throws Exception {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("type1")
|
||||
.addMapping("type2", "_parent", "type=type1")
|
||||
);
|
||||
ensureGreen();
|
||||
|
||||
client().prepareIndex("test", "type2", "1").setParent("1").setSource("field", "value").get();
|
||||
refresh();
|
||||
|
||||
SearchResponse response = client().prepareSearch("test")
|
||||
.setQuery(multiMatchQuery("1", "_parent#type1"))
|
||||
.get();
|
||||
|
||||
assertThat(response.getHits().getTotalHits(), equalTo(1L));
|
||||
assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
|
||||
}
|
||||
|
||||
public void testParentFieldToNonExistingType() {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("parent").addMapping("child", "_parent", "type=parent2"));
|
||||
client().prepareIndex("test", "parent", "1").setSource("{}", XContentType.JSON).get();
|
||||
client().prepareIndex("test", "child", "1").setParent("1").setSource("{}", XContentType.JSON).get();
|
||||
refresh();
|
||||
|
||||
try {
|
||||
client().prepareSearch("test")
|
||||
.setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None))
|
||||
.get();
|
||||
fail();
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test for https://github.com/elastic/elasticsearch/issues/3444
|
||||
*/
|
||||
public void testBulkUpdateDocAsUpsertWithParent() throws Exception {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("parent", "{\"parent\":{}}", XContentType.JSON)
|
||||
.addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON));
|
||||
ensureGreen();
|
||||
|
||||
BulkRequestBuilder builder = client().prepareBulk();
|
||||
|
||||
// It's important to use JSON parsing here and request objects: issue 3444 is related to incomplete option parsing
|
||||
byte[] addParent = new BytesArray(
|
||||
"{" +
|
||||
" \"index\" : {" +
|
||||
" \"_index\" : \"test\"," +
|
||||
" \"_type\" : \"parent\"," +
|
||||
" \"_id\" : \"parent1\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n" +
|
||||
"{" +
|
||||
" \"field1\" : \"value1\"" +
|
||||
"}" +
|
||||
"\n").array();
|
||||
|
||||
byte[] addChild = new BytesArray(
|
||||
"{" +
|
||||
" \"update\" : {" +
|
||||
" \"_index\" : \"test\"," +
|
||||
" \"_type\" : \"child\"," +
|
||||
" \"_id\" : \"child1\"," +
|
||||
" \"parent\" : \"parent1\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n" +
|
||||
"{" +
|
||||
" \"doc\" : {" +
|
||||
" \"field1\" : \"value1\"" +
|
||||
" }," +
|
||||
" \"doc_as_upsert\" : \"true\"" +
|
||||
"}" +
|
||||
"\n").array();
|
||||
|
||||
builder.add(addParent, 0, addParent.length, XContentType.JSON);
|
||||
builder.add(addChild, 0, addChild.length, XContentType.JSON);
|
||||
|
||||
BulkResponse bulkResponse = builder.get();
|
||||
assertThat(bulkResponse.getItems().length, equalTo(2));
|
||||
assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false));
|
||||
assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false));
|
||||
|
||||
client().admin().indices().prepareRefresh("test").get();
|
||||
|
||||
//we check that the _parent field was set on the child document by using the has parent query
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(hasParentQuery("parent", QueryBuilders.matchAllQuery(), false))
|
||||
.get();
|
||||
|
||||
assertNoFailures(searchResponse);
|
||||
assertSearchHits(searchResponse, "child1");
|
||||
}
|
||||
|
||||
/*
|
||||
Test for https://github.com/elastic/elasticsearch/issues/3444
|
||||
*/
|
||||
public void testBulkUpdateUpsertWithParent() throws Exception {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("parent", "{\"parent\":{}}", XContentType.JSON)
|
||||
.addMapping("child", "{\"child\": {\"_parent\": {\"type\": \"parent\"}}}", XContentType.JSON));
|
||||
ensureGreen();
|
||||
|
||||
BulkRequestBuilder builder = client().prepareBulk();
|
||||
|
||||
byte[] addParent = new BytesArray(
|
||||
"{" +
|
||||
" \"index\" : {" +
|
||||
" \"_index\" : \"test\"," +
|
||||
" \"_type\" : \"parent\"," +
|
||||
" \"_id\" : \"parent1\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n" +
|
||||
"{" +
|
||||
" \"field1\" : \"value1\"" +
|
||||
"}" +
|
||||
"\n").array();
|
||||
|
||||
byte[] addChild1 = new BytesArray(
|
||||
"{" +
|
||||
" \"update\" : {" +
|
||||
" \"_index\" : \"test\"," +
|
||||
" \"_type\" : \"child\"," +
|
||||
" \"_id\" : \"child1\"," +
|
||||
" \"parent\" : \"parent1\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n" +
|
||||
"{" +
|
||||
" \"script\" : {" +
|
||||
" \"inline\" : \"ctx._source.field2 = 'value2'\"" +
|
||||
" }," +
|
||||
" \"lang\" : \"" + InnerHitsIT.CustomScriptPlugin.NAME + "\"," +
|
||||
" \"upsert\" : {" +
|
||||
" \"field1\" : \"value1'\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n").array();
|
||||
|
||||
byte[] addChild2 = new BytesArray(
|
||||
"{" +
|
||||
" \"update\" : {" +
|
||||
" \"_index\" : \"test\"," +
|
||||
" \"_type\" : \"child\"," +
|
||||
" \"_id\" : \"child1\"," +
|
||||
" \"parent\" : \"parent1\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n" +
|
||||
"{" +
|
||||
" \"script\" : \"ctx._source.field2 = 'value2'\"," +
|
||||
" \"upsert\" : {" +
|
||||
" \"field1\" : \"value1'\"" +
|
||||
" }" +
|
||||
"}" +
|
||||
"\n").array();
|
||||
|
||||
builder.add(addParent, 0, addParent.length, XContentType.JSON);
|
||||
builder.add(addChild1, 0, addChild1.length, XContentType.JSON);
|
||||
builder.add(addChild2, 0, addChild2.length, XContentType.JSON);
|
||||
|
||||
BulkResponse bulkResponse = builder.get();
|
||||
assertThat(bulkResponse.getItems().length, equalTo(3));
|
||||
assertThat(bulkResponse.getItems()[0].isFailed(), equalTo(false));
|
||||
assertThat(bulkResponse.getItems()[1].isFailed(), equalTo(false));
|
||||
assertThat(bulkResponse.getItems()[2].isFailed(), equalTo(true));
|
||||
assertThat(bulkResponse.getItems()[2].getFailure().getCause().getCause().getMessage(),
|
||||
equalTo("script_lang not supported [painless]"));
|
||||
|
||||
client().admin().indices().prepareRefresh("test").get();
|
||||
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(hasParentQuery("parent", QueryBuilders.matchAllQuery(), false))
|
||||
.get();
|
||||
|
||||
assertSearchHits(searchResponse, "child1");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
setup:
|
||||
- do:
|
||||
indices.create:
|
||||
index: test
|
||||
body:
|
||||
mappings:
|
||||
doc:
|
||||
properties:
|
||||
join_field:
|
||||
type: join
|
||||
parent: child
|
||||
|
||||
---
|
||||
"Parent/child inner hits":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: mapping.single_type was added in 6.0
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: test
|
||||
type: doc
|
||||
id: 1
|
||||
body: {"foo": "bar", "join_field": {"name" : "parent"} }
|
||||
|
||||
- do:
|
||||
index:
|
||||
index: test
|
||||
type: doc
|
||||
id: 2
|
||||
routing: 1
|
||||
body: {"bar": "baz", "join_field": { "name" : "child", "parent": "1"} }
|
||||
|
||||
- do:
|
||||
indices.refresh: {}
|
||||
|
||||
# TODO: re-add inner hits here
|
||||
- do:
|
||||
search:
|
||||
body: { "query" : { "has_child" : { "type" : "child", "query" : { "match_all" : {} } } } }
|
||||
- match: { hits.total: 1 }
|
||||
- match: { hits.hits.0._index: "test" }
|
||||
- match: { hits.hits.0._type: "doc" }
|
||||
- match: { hits.hits.0._id: "1" }
|
Loading…
Reference in New Issue