@Override public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException { ensureNotDeleteByQuery(NAME, parseContext); XContentParser parser = parseContext.parser(); boolean queryFound = false; float boost = 1.0f; String childType = null; ScoreType scoreType = ScoreType.NONE; int minChildren = 0; int maxChildren = 0; int shortCircuitParentDocSet = 8192; String queryName = null; Tuple<String, SubSearchContext> innerHits = null; String currentFieldName = null; XContentParser.Token token; XContentStructure.InnerQuery iq = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token == XContentParser.Token.START_OBJECT) { // Usually, the query would be parsed here, but the child // type may not have been extracted yet, so use the // XContentStructure.<type> facade to parse if available, // or delay parsing if not. if ("query".equals(currentFieldName)) { iq = new XContentStructure.InnerQuery( parseContext, childType == null ? null : new String[] {childType}); queryFound = true; } else if ("inner_hits".equals(currentFieldName)) { innerHits = innerHitsQueryParserHelper.parse(parseContext); } else { throw new QueryParsingException( parseContext.index(), "[has_child] query does not support [" + currentFieldName + "]"); } } else if (token.isValue()) { if ("type".equals(currentFieldName) || "child_type".equals(currentFieldName) || "childType".equals(currentFieldName)) { childType = parser.text(); } else if ("score_type".equals(currentFieldName) || "scoreType".equals(currentFieldName)) { scoreType = ScoreType.fromString(parser.text()); } else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) { scoreType = ScoreType.fromString(parser.text()); } else if ("boost".equals(currentFieldName)) { boost = parser.floatValue(); } else if ("min_children".equals(currentFieldName) || "minChildren".equals(currentFieldName)) { minChildren = parser.intValue(true); } else if ("max_children".equals(currentFieldName) || "maxChildren".equals(currentFieldName)) { maxChildren = parser.intValue(true); } else if ("short_circuit_cutoff".equals(currentFieldName)) { shortCircuitParentDocSet = parser.intValue(); } else if ("_name".equals(currentFieldName)) { queryName = parser.text(); } else { throw new QueryParsingException( parseContext.index(), "[has_child] query does not support [" + currentFieldName + "]"); } } } if (!queryFound) { throw new QueryParsingException(parseContext.index(), "[has_child] requires 'query' field"); } if (childType == null) { throw new QueryParsingException(parseContext.index(), "[has_child] requires 'type' field"); } Query innerQuery = iq.asQuery(childType); if (innerQuery == null) { return null; } innerQuery.setBoost(boost); DocumentMapper childDocMapper = parseContext.mapperService().documentMapper(childType); if (childDocMapper == null) { throw new QueryParsingException( parseContext.index(), "[has_child] No mapping for for type [" + childType + "]"); } if (!childDocMapper.parentFieldMapper().active()) { throw new QueryParsingException( parseContext.index(), "[has_child] Type [" + childType + "] does not have parent mapping"); } if (innerHits != null) { InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits( innerHits.v2(), innerQuery, null, childDocMapper); String name = innerHits.v1() != null ? innerHits.v1() : childType; parseContext.addInnerHits(name, parentChildInnerHits); } ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper(); if (!parentFieldMapper.active()) { throw new QueryParsingException( parseContext.index(), "[has_child] _parent field not configured"); } String parentType = parentFieldMapper.type(); DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType); if (parentDocMapper == null) { throw new QueryParsingException( parseContext.index(), "[has_child] Type [" + childType + "] points to a non existent parent type [" + parentType + "]"); } if (maxChildren > 0 && maxChildren < minChildren) { throw new QueryParsingException( parseContext.index(), "[has_child] 'max_children' is less than 'min_children'"); } BitDocIdSetFilter nonNestedDocsFilter = null; if (parentDocMapper.hasNestedObjects()) { nonNestedDocsFilter = parseContext.bitsetFilter(NonNestedDocsFilter.INSTANCE); } // 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); } else { query = new ChildrenConstantScoreQuery( parentChildIndexFieldData, innerQuery, parentType, childType, parentFilter, shortCircuitParentDocSet, nonNestedDocsFilter); } if (queryName != null) { parseContext.addNamedFilter(queryName, new CustomQueryWrappingFilter(query)); } query.setBoost(boost); return query; }
@Override public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException { XContentParser parser = parseContext.parser(); boolean queryFound = false; float boost = 1.0f; String parentType = null; boolean score = false; String queryName = null; String currentFieldName = null; XContentParser.Token token; XContentStructure.InnerQuery iq = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token == XContentParser.Token.START_OBJECT) { // Usually, the query would be parsed here, but the child // type may not have been extracted yet, so use the // XContentStructure.<type> facade to parse if available, // or delay parsing if not. if ("query".equals(currentFieldName)) { iq = new XContentStructure.InnerQuery( parseContext, parentType == null ? null : new String[] {parentType}); queryFound = true; } else { throw new QueryParsingException( parseContext.index(), "[has_parent] query does not support [" + currentFieldName + "]"); } } else if (token.isValue()) { if ("type".equals(currentFieldName) || "parent_type".equals(currentFieldName) || "parentType".equals(currentFieldName)) { parentType = parser.text(); } else if ("_scope".equals(currentFieldName)) { throw new QueryParsingException( parseContext.index(), "the [_scope] support in [has_parent] query has been removed, use a filter as a facet_filter in the relevant global facet"); } else if ("score_type".equals(currentFieldName) || "scoreType".equals(currentFieldName)) { String scoreTypeValue = parser.text(); if ("score".equals(scoreTypeValue)) { score = true; } else if ("none".equals(scoreTypeValue)) { score = false; } } else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) { String scoreModeValue = parser.text(); if ("score".equals(scoreModeValue)) { score = true; } else if ("none".equals(scoreModeValue)) { score = false; } } else if ("boost".equals(currentFieldName)) { boost = parser.floatValue(); } else if ("_name".equals(currentFieldName)) { queryName = parser.text(); } else { throw new QueryParsingException( parseContext.index(), "[has_parent] query does not support [" + currentFieldName + "]"); } } } if (!queryFound) { throw new QueryParsingException( parseContext.index(), "[has_parent] query requires 'query' field"); } if (parentType == null) { throw new QueryParsingException( parseContext.index(), "[has_parent] query requires 'parent_type' field"); } Query innerQuery = iq.asQuery(parentType); if (innerQuery == null) { return null; } 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"); } innerQuery.setBoost(boost); // wrap the query with type query innerQuery = new XFilteredQuery( innerQuery, parseContext.cacheFilter(parentDocMapper.typeFilter(), null)); ParentChildIndexFieldData parentChildIndexFieldData = null; Set<String> parentTypes = new HashSet<>(5); parentTypes.add(parentType); for (DocumentMapper documentMapper : parseContext.mapperService()) { ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper(); if (parentFieldMapper.active()) { parentChildIndexFieldData = parseContext.fieldData().getForField(parentFieldMapper); DocumentMapper parentTypeDocumentMapper = parseContext.mapperService().documentMapper(parentFieldMapper.type()); if (parentTypeDocumentMapper == null) { // Only add this, if this parentFieldMapper (also a parent) isn't a child of another // parent. parentTypes.add(parentFieldMapper.type()); } } } if (parentChildIndexFieldData == null) { throw new QueryParsingException( parseContext.index(), "[has_parent] no _parent field configured"); } Filter parentFilter; if (parentTypes.size() == 1) { DocumentMapper documentMapper = parseContext.mapperService().documentMapper(parentTypes.iterator().next()); parentFilter = parseContext.cacheFilter(documentMapper.typeFilter(), null); } else { XBooleanFilter parentsFilter = new XBooleanFilter(); for (String parentTypeStr : parentTypes) { DocumentMapper documentMapper = parseContext.mapperService().documentMapper(parentTypeStr); Filter filter = parseContext.cacheFilter(documentMapper.typeFilter(), null); parentsFilter.add(filter, BooleanClause.Occur.SHOULD); } parentFilter = parentsFilter; } Filter childrenFilter = parseContext.cacheFilter(new NotFilter(parentFilter), null); boolean deleteByQuery = "delete_by_query".equals(SearchContext.current().source()); Query query; if (!deleteByQuery && score) { query = new ParentQuery(parentChildIndexFieldData, innerQuery, parentType, childrenFilter); } else { query = new ParentConstantScoreQuery( parentChildIndexFieldData, innerQuery, parentType, childrenFilter); if (deleteByQuery) { query = new XConstantScoreQuery(new DeleteByQueryWrappingFilter(query)); } } query.setBoost(boost); if (queryName != null) { parseContext.addNamedFilter(queryName, new CustomQueryWrappingFilter(query)); } return query; }