Skip to content

Commit

Permalink
Parse has_child query/filter after child type has been parsed
Browse files Browse the repository at this point in the history
Fixes #5783
Fixes #5838

Conflicts:
	src/main/java/org/elasticsearch/index/query/HasChildFilterParser.java
	src/main/java/org/elasticsearch/index/query/HasChildQueryParser.java
	src/main/java/org/elasticsearch/index/query/HasParentFilterParser.java
	src/main/java/org/elasticsearch/index/query/HasParentQueryParser.java
	src/main/java/org/elasticsearch/index/query/TopChildrenQueryParser.java
  • Loading branch information
dakrone committed Apr 22, 2014
1 parent 627cd62 commit c280995
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 105 deletions.
Expand Up @@ -22,9 +22,9 @@
import org.apache.lucene.search.Query;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.support.XContentStructure;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.search.child.ChildrenConstantScoreQuery;
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;
Expand Down Expand Up @@ -54,38 +54,30 @@ public String[] names() {
public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();

Query query = null;
boolean queryFound = false;
boolean filterFound = false;
String childType = null;
int shortCircuitParentDocSet = 8192; // Tests show a cut of point between 8192 and 16384.

String filterName = null;
String currentFieldName = null;
XContentParser.Token token;
XContentStructure.InnerQuery innerQuery = null;
XContentStructure.InnerFilter innerFilter = 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)) {
// TODO we need to set the type, but, `query` can come before `type`...
// since we switch types, make sure we change the context
String[] origTypes = QueryParseContext.setTypesWithPrevious(childType == null ? null : new String[]{childType});
try {
query = parseContext.parseInnerQuery();
queryFound = true;
} finally {
QueryParseContext.setTypes(origTypes);
}
innerQuery = new XContentStructure.InnerQuery(parseContext, childType == null ? null : new String[] {childType});
queryFound = true;
} else if ("filter".equals(currentFieldName)) {
// TODO handle `filter` element before `type` element...
String[] origTypes = QueryParseContext.setTypesWithPrevious(childType == null ? null : new String[]{childType});
try {
Filter innerFilter = parseContext.parseInnerFilter();
query = new XConstantScoreQuery(innerFilter);
queryFound = true;
} finally {
QueryParseContext.setTypes(origTypes);
}
innerFilter = new XContentStructure.InnerFilter(parseContext, childType == null ? null : new String[] {childType});
filterFound = true;
} else {
throw new QueryParsingException(parseContext.index(), "[has_child] filter does not support [" + currentFieldName + "]");
}
Expand All @@ -107,16 +99,24 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
}
}
}
if (!queryFound) {
throw new QueryParsingException(parseContext.index(), "[has_child] filter requires 'query' field");
}
if (query == null) {
return null;
if (!queryFound && !filterFound) {
throw new QueryParsingException(parseContext.index(), "[has_child] filter requires 'query' or 'filter' field");
}
if (childType == null) {
throw new QueryParsingException(parseContext.index(), "[has_child] filter requires 'type' field");
}

Query query;
if (queryFound) {
query = innerQuery.asQuery(childType);
} else {
query = innerFilter.asFilter(childType);
}

if (query == null) {
return null;
}

DocumentMapper childDocMapper = parseContext.mapperService().documentMapper(childType);
if (childDocMapper == null) {
throw new QueryParsingException(parseContext.index(), "No mapping for for type [" + childType + "]");
Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.support.XContentStructure;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.search.child.*;
import org.elasticsearch.index.search.nested.NonNestedDocsFilter;
Expand Down Expand Up @@ -53,7 +54,6 @@ public String[] names() {
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();

Query innerQuery = null;
boolean queryFound = false;
float boost = 1.0f;
String childType = null;
Expand All @@ -63,20 +63,18 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars

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)) {
// TODO we need to set the type, but, `query` can come before `type`... (see HasChildFilterParser)
// since we switch types, make sure we change the context
String[] origTypes = QueryParseContext.setTypesWithPrevious(childType == null ? null : new String[]{childType});
try {
innerQuery = parseContext.parseInnerQuery();
queryFound = true;
} finally {
QueryParseContext.setTypes(origTypes);
}
iq = new XContentStructure.InnerQuery(parseContext, childType == null ? null : new String[] {childType});
queryFound = true;
} else {
throw new QueryParsingException(parseContext.index(), "[has_child] query does not support [" + currentFieldName + "]");
}
Expand Down Expand Up @@ -109,12 +107,15 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
if (!queryFound) {
throw new QueryParsingException(parseContext.index(), "[has_child] requires 'query' field");
}
if (innerQuery == null) {
return null;
}
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);
Expand Down
Expand Up @@ -25,10 +25,9 @@
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.NotFilter;
import org.elasticsearch.common.lucene.search.XBooleanFilter;
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
import org.elasticsearch.index.query.support.XContentStructure;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;
Expand Down Expand Up @@ -60,38 +59,29 @@ public String[] names() {
public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();

Query query = null;
boolean queryFound = false;
boolean filterFound = false;
String parentType = null;

boolean cache = false;
CacheKeyFilter.Key cacheKey = null;
String filterName = null;
String currentFieldName = null;
XContentParser.Token token;
XContentStructure.InnerQuery innerQuery = null;
XContentStructure.InnerFilter innerFilter = 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)) {
// TODO handle `query` element before `type` element...
String[] origTypes = QueryParseContext.setTypesWithPrevious(parentType == null ? null : new String[]{parentType});
try {
query = parseContext.parseInnerQuery();
queryFound = true;
} finally {
QueryParseContext.setTypes(origTypes);
}
innerQuery = new XContentStructure.InnerQuery(parseContext, parentType == null ? null : new String[] {parentType});
queryFound = true;
} else if ("filter".equals(currentFieldName)) {
// TODO handle `filter` element before `type` element...
String[] origTypes = QueryParseContext.setTypesWithPrevious(parentType == null ? null : new String[]{parentType});
try {
Filter innerFilter = parseContext.parseInnerFilter();
query = new XConstantScoreQuery(innerFilter);
queryFound = true;
} finally {
QueryParseContext.setTypes(origTypes);
}
innerFilter = new XContentStructure.InnerFilter(parseContext, parentType == null ? null : new String[] {parentType});
filterFound = true;
} else {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter does not support [" + currentFieldName + "]");
}
Expand All @@ -103,25 +93,32 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
} else if ("_name".equals(currentFieldName)) {
filterName = parser.text();
} else if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue();
// noop to be backwards compatible
} else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
cacheKey = new CacheKeyFilter.Key(parser.text());
// noop to be backwards compatible
} else {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter does not support [" + currentFieldName + "]");
}
}
}
if (!queryFound) {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter requires 'query' field");
if (!queryFound && !filterFound) {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter requires 'query' or 'filter' field");
}
if (query == null) {
return null;
}

if (parentType == null) {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter requires 'parent_type' field");
}

Query query;
if (queryFound) {
query = innerQuery.asQuery(parentType);
} else {
query = innerFilter.asFilter(parentType);
}

if (query == null) {
return null;
}

DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType);
if (parentDocMapper == null) {
throw new QueryParsingException(parseContext.index(), "[has_parent] filter configured 'parent_type' [" + parentType + "] is not a valid type");
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.support.XContentStructure;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;
Expand Down Expand Up @@ -57,7 +58,6 @@ public String[] names() {
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();

Query innerQuery = null;
boolean queryFound = false;
float boost = 1.0f;
String parentType = null;
Expand All @@ -66,19 +66,18 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars

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)) {
// TODO handle `query` element before `type` element...
String[] origTypes = QueryParseContext.setTypesWithPrevious(parentType == null ? null : new String[]{parentType});
try {
innerQuery = parseContext.parseInnerQuery();
queryFound = true;
} finally {
QueryParseContext.setTypes(origTypes);
}
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 + "]");
}
Expand Down Expand Up @@ -113,14 +112,16 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
if (!queryFound) {
throw new QueryParsingException(parseContext.index(), "[has_parent] query requires 'query' field");
}
if (innerQuery == null) {
return null;
}

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");
Expand Down
33 changes: 18 additions & 15 deletions src/main/java/org/elasticsearch/index/query/IndicesQueryParser.java
Expand Up @@ -26,6 +26,7 @@
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.support.XContentStructure;

import java.io.IOException;
import java.util.ArrayList;
Expand Down Expand Up @@ -54,33 +55,25 @@ public String[] names() {
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();

Query query = null;
Query noMatchQuery = Queries.newMatchAllQuery();
Query noMatchQuery = null;
boolean queryFound = false;
boolean indicesFound = false;
boolean currentIndexMatchesIndices = false;
String queryName = null;

String currentFieldName = null;
XContentParser.Token token;
XContentStructure.InnerQuery innerQuery = null;
XContentStructure.InnerQuery innerNoMatchQuery = 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) {
if ("query".equals(currentFieldName)) {
//TODO We are able to decide whether to parse the query or not only if indices in the query appears first
innerQuery = new XContentStructure.InnerQuery(parseContext, null);
queryFound = true;
if (indicesFound && !currentIndexMatchesIndices) {
parseContext.parser().skipChildren(); // skip the query object without parsing it
} else {
query = parseContext.parseInnerQuery();
}
} else if ("no_match_query".equals(currentFieldName)) {
if (indicesFound && currentIndexMatchesIndices) {
parseContext.parser().skipChildren(); // skip the query object without parsing it
} else {
noMatchQuery = parseContext.parseInnerQuery();
}
innerNoMatchQuery = new XContentStructure.InnerQuery(parseContext, null);
} else {
throw new QueryParsingException(parseContext.index(), "[indices] query does not support [" + currentFieldName + "]");
}
Expand Down Expand Up @@ -132,9 +125,19 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars

Query chosenQuery;
if (currentIndexMatchesIndices) {
chosenQuery = query;
chosenQuery = innerQuery.asQuery();
} else {
chosenQuery = noMatchQuery;
// If noMatchQuery is set, it means "no_match_query" was "all" or "none"
if (noMatchQuery != null) {
chosenQuery = noMatchQuery;
} else {
// There might be no "no_match_query" set, so default to the match_all if not set
if (innerNoMatchQuery == null) {
chosenQuery = Queries.newMatchAllQuery();
} else {
chosenQuery = innerNoMatchQuery.asQuery();
}
}
}
if (queryName != null) {
parseContext.addNamedQuery(queryName, chosenQuery);
Expand Down

0 comments on commit c280995

Please sign in to comment.