The `has_child` query's inner query now is wrapped in a filtered query with the child type as filter, this prevents other children from being returned as hit.

Extended the specialized simplified mapping source method to support metadata mapping fields. These fields can just specified as normal fields, but will automatically be placed as top level mapping field.

Closes #3818
This commit is contained in:
Martijn van Groningen 2013-10-14 12:42:56 +02:00
parent c093e90d51
commit 7286a015db
3 changed files with 96 additions and 1 deletions

View File

@ -19,6 +19,7 @@
package org.elasticsearch.action.admin.indices.mapping.put; package org.elasticsearch.action.admin.indices.mapping.put;
import com.carrotsearch.hppc.ObjectOpenHashSet;
import org.elasticsearch.ElasticSearchGenerationException; import org.elasticsearch.ElasticSearchGenerationException;
import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
@ -52,6 +53,11 @@ import static org.elasticsearch.common.unit.TimeValue.readTimeValue;
*/ */
public class PutMappingRequest extends MasterNodeOperationRequest<PutMappingRequest> { public class PutMappingRequest extends MasterNodeOperationRequest<PutMappingRequest> {
private static ObjectOpenHashSet<String> RESERVED_FIELDS = ObjectOpenHashSet.from(
"_uid", "_id", "_type", "_source", "_all", "_analyzer", "_boost", "_parent", "_routing", "_index",
"_size", "_timestamp", "_ttl"
);
private String[] indices; private String[] indices;
private String type; private String type;
@ -125,6 +131,9 @@ public class PutMappingRequest extends MasterNodeOperationRequest<PutMappingRequ
/** /**
* A specialized simplified mapping source method, takes the form of simple properties definition: * A specialized simplified mapping source method, takes the form of simple properties definition:
* ("field1", "type=string,store=true"). * ("field1", "type=string,store=true").
*
* Also supports metadata mapping fields such as `_all` and `_parent` as property definition, these metadata
* mapping fields will automatically be put on the top level mapping object.
*/ */
public PutMappingRequest source(Object... source) { public PutMappingRequest source(Object... source) {
return source(buildFromSimplifiedDef(type, source)); return source(buildFromSimplifiedDef(type, source));
@ -137,9 +146,31 @@ public class PutMappingRequest extends MasterNodeOperationRequest<PutMappingRequ
if (type != null) { if (type != null) {
builder.startObject(type); builder.startObject(type);
} }
for (int i = 0; i < source.length; i++) {
String fieldName = source[i++].toString();
if (RESERVED_FIELDS.contains(fieldName)) {
builder.startObject(fieldName);
String[] s1 = Strings.splitStringByCommaToArray(source[i].toString());
for (String s : s1) {
String[] s2 = Strings.split(s, "=");
if (s2.length != 2) {
throw new ElasticSearchIllegalArgumentException("malformed " + s);
}
builder.field(s2[0], s2[1]);
}
builder.endObject();
}
}
builder.startObject("properties"); builder.startObject("properties");
for (int i = 0; i < source.length; i++) { for (int i = 0; i < source.length; i++) {
builder.startObject(source[i++].toString()); String fieldName = source[i++].toString();
if (RESERVED_FIELDS.contains(fieldName)) {
continue;
}
builder.startObject(fieldName);
String[] s1 = Strings.splitStringByCommaToArray(source[i].toString()); String[] s1 = Strings.splitStringByCommaToArray(source[i].toString());
for (String s : s1) { for (String s : s1) {
String[] s2 = Strings.split(s, "="); String[] s2 = Strings.split(s, "=");

View File

@ -24,6 +24,7 @@ import org.apache.lucene.search.Query;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.XConstantScoreQuery; import org.elasticsearch.common.lucene.search.XConstantScoreQuery;
import org.elasticsearch.common.lucene.search.XFilteredQuery;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.search.child.ChildrenConstantScoreQuery; import org.elasticsearch.index.search.child.ChildrenConstantScoreQuery;
@ -132,6 +133,9 @@ public class HasChildQueryParser implements QueryParser {
throw new QueryParsingException(parseContext.index(), "[has_child] Type [" + childType + "] points to a non existent parent type [" + parentType + "]"); throw new QueryParsingException(parseContext.index(), "[has_child] Type [" + childType + "] points to a non existent parent type [" + parentType + "]");
} }
// wrap the query with type query
innerQuery = new XFilteredQuery(innerQuery, parseContext.cacheFilter(childDocMapper.typeFilter(), null));
boolean deleteByQuery = "delete_by_query".equals(SearchContext.current().source()); boolean deleteByQuery = "delete_by_query".equals(SearchContext.current().source());
Query query; Query query;
Filter parentFilter = parseContext.cacheFilter(parentDocMapper.typeFilter(), null); Filter parentFilter = parseContext.cacheFilter(parentDocMapper.typeFilter(), null);

View File

@ -1772,4 +1772,64 @@ public class SimpleChildQuerySearchTests extends AbstractIntegrationTest {
} }
} }
@Test
// Relates to bug: https://github.com/elasticsearch/elasticsearch/issues/3818
public void testHasChildQueryOnlyReturnsSingleChildType() {
client().admin().indices().prepareCreate("grandissue")
.setSettings(
ImmutableSettings.settingsBuilder()
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 0)
)
.addMapping("grandparent", "name", "type=string")
.addMapping("parent", "_parent", "type=grandparent")
.addMapping("child_type_one", "_parent", "type=parent")
.addMapping("child_type_two", "_parent", "type=parent")
.execute().actionGet();
client().prepareIndex("grandissue", "grandparent", "1").setSource("name", "Grandpa").execute().actionGet();
client().prepareIndex("grandissue", "parent", "2").setParent("1").setSource("name", "Dana").execute().actionGet();
client().prepareIndex("grandissue", "child_type_one", "3").setParent("2").setRouting("1")
.setSource("name", "William")
.execute().actionGet();
client().prepareIndex("grandissue", "child_type_two", "4").setParent("2").setRouting("1")
.setSource("name", "Kate")
.execute().actionGet();
client().admin().indices().prepareRefresh("grandissue").execute().actionGet();
SearchResponse searchResponse = client().prepareSearch("grandissue").setQuery(
boolQuery().must(
hasChildQuery(
"parent",
boolQuery().must(
hasChildQuery(
"child_type_one",
boolQuery().must(
queryString("name:William*").analyzeWildcard(true)
)
)
)
)
)
).execute().actionGet();
assertHitCount(searchResponse, 1l);
searchResponse = client().prepareSearch("grandissue").setQuery(
boolQuery().must(
hasChildQuery(
"parent",
boolQuery().must(
hasChildQuery(
"child_type_two",
boolQuery().must(
queryString("name:William*").analyzeWildcard(true)
)
)
)
)
)
).execute().actionGet();
assertHitCount(searchResponse, 0l);
}
} }