add include_in_parent and include_in_root explicit flags for nested cases

This commit is contained in:
kimchy 2011-07-06 21:27:19 +03:00
parent 6f7b462f1d
commit 4d3b623923
5 changed files with 96 additions and 61 deletions

View File

@ -84,8 +84,6 @@ public class DocumentMapperParser extends AbstractIndexComponent {
.put(StringFieldMapper.CONTENT_TYPE, new StringFieldMapper.TypeParser())
.put(ObjectMapper.CONTENT_TYPE, new ObjectMapper.TypeParser())
.put(ObjectMapper.NESTED_CONTENT_TYPE, new ObjectMapper.TypeParser())
.put(ObjectMapper.OBJECT_AND_NESTED_CONTENT_TYPE, new ObjectMapper.TypeParser())
.put(ObjectMapper.ROOT_AND_NESTED_CONTENT_TYPE, new ObjectMapper.TypeParser())
.put(MultiFieldMapper.CONTENT_TYPE, new MultiFieldMapper.TypeParser())
.put(GeoPointFieldMapper.CONTENT_TYPE, new GeoPointFieldMapper.TypeParser())
.immutableMap();

View File

@ -155,6 +155,10 @@ public class ParseContext {
return this.listener;
}
public Document rootDoc() {
return documents.get(0);
}
public List<Document> docs() {
return this.documents;
}

View File

@ -60,8 +60,6 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
public static final String CONTENT_TYPE = "object";
public static final String NESTED_CONTENT_TYPE = "nested";
public static final String OBJECT_AND_NESTED_CONTENT_TYPE = "object_and_nested";
public static final String ROOT_AND_NESTED_CONTENT_TYPE = "root_and_nested";
public static class Defaults {
public static final boolean ENABLED = true;
@ -76,29 +74,37 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
STRICT
}
public static enum Nested {
NO {
@Override public boolean isNested() {
return false;
}
},
NESTED {
@Override public boolean isNested() {
return true;
}
},
OBJECT_AND_NESTED {
@Override public boolean isNested() {
return true;
}
},
ROOT_AND_NESTED {
@Override public boolean isNested() {
return true;
}
};
public static class Nested {
public abstract boolean isNested();
public static final Nested NO = new Nested(false, false, false);
public static Nested newNested(boolean includeInParent, boolean includeInRoot) {
return new Nested(true, includeInParent, includeInRoot);
}
private final boolean nested;
private final boolean includeInParent;
private final boolean includeInRoot;
private Nested(boolean nested, boolean includeInParent, boolean includeInRoot) {
this.nested = nested;
this.includeInParent = includeInParent;
this.includeInRoot = includeInRoot;
}
public boolean isNested() {
return nested;
}
public boolean isIncludeInParent() {
return includeInParent;
}
public boolean isIncludeInRoot() {
return includeInRoot;
}
}
public static class Builder<T extends Builder, Y extends ObjectMapper> extends Mapper.Builder<T, Y> {
@ -179,6 +185,9 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
Map<String, Object> objectNode = node;
ObjectMapper.Builder builder = createBuilder(name);
boolean nested = false;
boolean nestedIncludeInParent = false;
boolean nestedIncludeInRoot = false;
for (Map.Entry<String, Object> entry : objectNode.entrySet()) {
String fieldName = Strings.toUnderscoreCase(entry.getKey());
Object fieldNode = entry.getValue();
@ -195,14 +204,14 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
if (type.equals(CONTENT_TYPE)) {
builder.nested = Nested.NO;
} else if (type.equals(NESTED_CONTENT_TYPE)) {
builder.nested = Nested.NESTED;
} else if (type.equals(OBJECT_AND_NESTED_CONTENT_TYPE)) {
builder.nested = Nested.OBJECT_AND_NESTED;
} else if (type.equals(ROOT_AND_NESTED_CONTENT_TYPE)) {
builder.nested = Nested.ROOT_AND_NESTED;
nested = true;
} else {
throw new MapperParsingException("Trying to parse an object but has a different type [" + type + "] for [" + name + "]");
}
} else if (fieldName.equals("include_in_parent")) {
nestedIncludeInParent = nodeBooleanValue(fieldNode);
} else if (fieldName.equals("include_in_root")) {
nestedIncludeInRoot = nodeBooleanValue(fieldNode);
} else if (fieldName.equals("enabled")) {
builder.enabled(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("path")) {
@ -215,6 +224,11 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
processField(builder, fieldName, fieldNode);
}
}
if (nested) {
builder.nested = Nested.newNested(nestedIncludeInParent, nestedIncludeInRoot);
}
return builder;
}
@ -431,8 +445,7 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
context.path().pathType(origPathType);
if (nested.isNested()) {
Document nestedDoc = context.switchDoc(restoreDoc);
if (nested == Nested.OBJECT_AND_NESTED) {
// copy over all the fields to the parent doc...
if (nested.isIncludeInParent()) {
for (Fieldable field : nestedDoc.getFields()) {
if (field.name().equals(UidFieldMapper.NAME) || field.name().equals(TypeFieldMapper.NAME)) {
continue;
@ -440,13 +453,16 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
context.doc().add(field);
}
}
} else if (nested == Nested.ROOT_AND_NESTED) {
// copy over all the fields to the root doc...
for (Fieldable field : nestedDoc.getFields()) {
if (field.name().equals(UidFieldMapper.NAME) || field.name().equals(TypeFieldMapper.NAME)) {
continue;
} else {
context.docs().get(0).add(field);
}
if (nested.isIncludeInRoot()) {
// don't add it twice, if its included in parent, and we are handling the master doc...
if (!(nested.isIncludeInParent() && context.doc() == context.rootDoc())) {
for (Fieldable field : nestedDoc.getFields()) {
if (field.name().equals(UidFieldMapper.NAME) || field.name().equals(TypeFieldMapper.NAME)) {
continue;
} else {
context.rootDoc().add(field);
}
}
}
}
@ -756,12 +772,12 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll {
public void toXContent(XContentBuilder builder, Params params, ToXContent custom, Mapper... additionalMappers) throws IOException {
builder.startObject(name);
if (nested.isNested()) {
if (nested == Nested.NESTED) {
builder.field("type", NESTED_CONTENT_TYPE);
} else if (nested == Nested.OBJECT_AND_NESTED) {
builder.field("type", OBJECT_AND_NESTED_CONTENT_TYPE);
} else if (nested == Nested.ROOT_AND_NESTED) {
builder.field("type", ROOT_AND_NESTED_CONTENT_TYPE);
builder.field("type", NESTED_CONTENT_TYPE);
if (nested.isIncludeInParent()) {
builder.field("include_in_parent", true);
}
if (nested.isIncludeInRoot()) {
builder.field("include_in_root", true);
}
} else if (mappers.isEmpty()) { // only write the object content type if there are no properties, otherwise, it is automatically detected
builder.field("type", CONTENT_TYPE);

View File

@ -94,7 +94,7 @@ public class NestedQueryParser implements QueryParser {
}
}
}
if (query == null || filter == null) {
if (query == null && filter == null) {
throw new QueryParsingException(parseContext.index(), "[nested] requires either 'query' or 'filter' field");
}
if (path == null) {
@ -105,6 +105,8 @@ public class NestedQueryParser implements QueryParser {
query = new DeletionAwareConstantScoreQuery(filter);
}
query.setBoost(boost);
MapperService.SmartNameObjectMapper mapper = parseContext.mapperService().smartNameObjectMapper(path);
if (mapper == null) {
throw new QueryParsingException(parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
@ -136,7 +138,6 @@ public class NestedQueryParser implements QueryParser {
parentFilterContext.set(currentParentFilterContext);
BlockJoinQuery joinQuery = new BlockJoinQuery(query, parentFilter, scoreMode);
joinQuery.setBoost(boost);
return joinQuery;
}

View File

@ -42,7 +42,7 @@ public class NestedMappingTests {
assertThat(docMapper.hasNestedObjects(), equalTo(true));
ObjectMapper nested1Mapper = docMapper.objectMappers().get("nested1");
assertThat(nested1Mapper.nested(), equalTo(ObjectMapper.Nested.NESTED));
assertThat(nested1Mapper.nested().isNested(), equalTo(true));
ParsedDocument doc = docMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
@ -91,9 +91,13 @@ public class NestedMappingTests {
assertThat(docMapper.hasNestedObjects(), equalTo(true));
ObjectMapper nested1Mapper = docMapper.objectMappers().get("nested1");
assertThat(nested1Mapper.nested(), equalTo(ObjectMapper.Nested.NESTED));
assertThat(nested1Mapper.nested().isNested(), equalTo(true));
assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(false));
assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
ObjectMapper nested2Mapper = docMapper.objectMappers().get("nested1.nested2");
assertThat(nested2Mapper.nested(), equalTo(ObjectMapper.Nested.NESTED));
assertThat(nested2Mapper.nested().isNested(), equalTo(true));
assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(false));
assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(false));
ParsedDocument doc = docMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
@ -130,7 +134,7 @@ public class NestedMappingTests {
@Test public void multiObjectAndNested1() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("nested1").field("type", "nested").startObject("properties")
.startObject("nested2").field("type", "object_and_nested")
.startObject("nested2").field("type", "nested").field("include_in_parent", true)
.endObject().endObject()
.endObject().endObject().endObject().string();
@ -138,9 +142,13 @@ public class NestedMappingTests {
assertThat(docMapper.hasNestedObjects(), equalTo(true));
ObjectMapper nested1Mapper = docMapper.objectMappers().get("nested1");
assertThat(nested1Mapper.nested(), equalTo(ObjectMapper.Nested.NESTED));
assertThat(nested1Mapper.nested().isNested(), equalTo(true));
assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(false));
assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
ObjectMapper nested2Mapper = docMapper.objectMappers().get("nested1.nested2");
assertThat(nested2Mapper.nested(), equalTo(ObjectMapper.Nested.OBJECT_AND_NESTED));
assertThat(nested2Mapper.nested().isNested(), equalTo(true));
assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(true));
assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(false));
ParsedDocument doc = docMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
@ -176,8 +184,8 @@ public class NestedMappingTests {
@Test public void multiObjectAndNested2() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("nested1").field("type", "object_and_nested").startObject("properties")
.startObject("nested2").field("type", "object_and_nested")
.startObject("nested1").field("type", "nested").field("include_in_parent", true).startObject("properties")
.startObject("nested2").field("type", "nested").field("include_in_parent", true)
.endObject().endObject()
.endObject().endObject().endObject().string();
@ -185,9 +193,13 @@ public class NestedMappingTests {
assertThat(docMapper.hasNestedObjects(), equalTo(true));
ObjectMapper nested1Mapper = docMapper.objectMappers().get("nested1");
assertThat(nested1Mapper.nested(), equalTo(ObjectMapper.Nested.OBJECT_AND_NESTED));
assertThat(nested1Mapper.nested().isNested(), equalTo(true));
assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(true));
assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
ObjectMapper nested2Mapper = docMapper.objectMappers().get("nested1.nested2");
assertThat(nested2Mapper.nested(), equalTo(ObjectMapper.Nested.OBJECT_AND_NESTED));
assertThat(nested2Mapper.nested().isNested(), equalTo(true));
assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(true));
assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(false));
ParsedDocument doc = docMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()
@ -224,7 +236,7 @@ public class NestedMappingTests {
@Test public void multiRootAndNested1() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("nested1").field("type", "nested").startObject("properties")
.startObject("nested2").field("type", "root_and_nested")
.startObject("nested2").field("type", "nested").field("include_in_root", true)
.endObject().endObject()
.endObject().endObject().endObject().string();
@ -232,9 +244,13 @@ public class NestedMappingTests {
assertThat(docMapper.hasNestedObjects(), equalTo(true));
ObjectMapper nested1Mapper = docMapper.objectMappers().get("nested1");
assertThat(nested1Mapper.nested(), equalTo(ObjectMapper.Nested.NESTED));
assertThat(nested1Mapper.nested().isNested(), equalTo(true));
assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(false));
assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
ObjectMapper nested2Mapper = docMapper.objectMappers().get("nested1.nested2");
assertThat(nested2Mapper.nested(), equalTo(ObjectMapper.Nested.ROOT_AND_NESTED));
assertThat(nested2Mapper.nested().isNested(), equalTo(true));
assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(false));
assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(true));
ParsedDocument doc = docMapper.parse("type", "1", XContentFactory.jsonBuilder()
.startObject()