Mapper: Dynamic Template Support, closes #397.
This commit is contained in:
parent
c657c7c6d0
commit
99fcfde307
|
@ -140,6 +140,7 @@
|
|||
<w>unboxed</w>
|
||||
<w>unicast</w>
|
||||
<w>unmanaged</w>
|
||||
<w>unmatch</w>
|
||||
<w>unregister</w>
|
||||
<w>uptime</w>
|
||||
<w>uuid</w>
|
||||
|
|
|
@ -29,6 +29,66 @@ import java.util.regex.Pattern;
|
|||
*/
|
||||
public class Regex {
|
||||
|
||||
/**
|
||||
* Match a String against the given pattern, supporting the following simple
|
||||
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
|
||||
* arbitrary number of pattern parts), as well as direct equality.
|
||||
*
|
||||
* @param pattern the pattern to match against
|
||||
* @param str the String to match
|
||||
* @return whether the String matches the given pattern
|
||||
*/
|
||||
public static boolean simpleMatch(String pattern, String str) {
|
||||
if (pattern == null || str == null) {
|
||||
return false;
|
||||
}
|
||||
int firstIndex = pattern.indexOf('*');
|
||||
if (firstIndex == -1) {
|
||||
return pattern.equals(str);
|
||||
}
|
||||
if (firstIndex == 0) {
|
||||
if (pattern.length() == 1) {
|
||||
return true;
|
||||
}
|
||||
int nextIndex = pattern.indexOf('*', firstIndex + 1);
|
||||
if (nextIndex == -1) {
|
||||
return str.endsWith(pattern.substring(1));
|
||||
}
|
||||
String part = pattern.substring(1, nextIndex);
|
||||
int partIndex = str.indexOf(part);
|
||||
while (partIndex != -1) {
|
||||
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
|
||||
return true;
|
||||
}
|
||||
partIndex = str.indexOf(part, partIndex + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return (str.length() >= firstIndex &&
|
||||
pattern.substring(0, firstIndex).equals(str.substring(0, firstIndex)) &&
|
||||
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a String against the given patterns, supporting the following simple
|
||||
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
|
||||
* arbitrary number of pattern parts), as well as direct equality.
|
||||
*
|
||||
* @param patterns the patterns to match against
|
||||
* @param str the String to match
|
||||
* @return whether the String matches any of the given patterns
|
||||
*/
|
||||
public static boolean simpleMatch(String[] patterns, String str) {
|
||||
if (patterns != null) {
|
||||
for (String pattern : patterns) {
|
||||
if (simpleMatch(pattern, str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Pattern compile(String regex, String flags) {
|
||||
int pFlags = flags == null ? 0 : flagsFromString(flags);
|
||||
return Pattern.compile(regex, pFlags);
|
||||
|
|
|
@ -33,6 +33,8 @@ public class ParseContext {
|
|||
|
||||
private final XContentDocumentMapper docMapper;
|
||||
|
||||
private final XContentDocumentMapperParser docMapperParser;
|
||||
|
||||
private final ContentPath path;
|
||||
|
||||
private XContentParser parser;
|
||||
|
@ -63,9 +65,10 @@ public class ParseContext {
|
|||
|
||||
private AllEntries allEntries = new AllEntries();
|
||||
|
||||
public ParseContext(String index, XContentDocumentMapper docMapper, ContentPath path) {
|
||||
public ParseContext(String index, XContentDocumentMapperParser docMapperParser, XContentDocumentMapper docMapper, ContentPath path) {
|
||||
this.index = index;
|
||||
this.docMapper = docMapper;
|
||||
this.docMapperParser = docMapperParser;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
|
@ -81,6 +84,10 @@ public class ParseContext {
|
|||
this.allEntries = new AllEntries();
|
||||
}
|
||||
|
||||
public XContentDocumentMapperParser docMapperParser() {
|
||||
return this.docMapperParser;
|
||||
}
|
||||
|
||||
public boolean mappersAdded() {
|
||||
return this.mappersAdded;
|
||||
}
|
||||
|
|
|
@ -133,9 +133,9 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
|
|||
return searchAnalyzer != null;
|
||||
}
|
||||
|
||||
public XContentDocumentMapper build() {
|
||||
public XContentDocumentMapper build(XContentDocumentMapperParser docMapperParser) {
|
||||
Preconditions.checkNotNull(rootObjectMapper, "Mapper builder must have the root object mapper set");
|
||||
return new XContentDocumentMapper(index, rootObjectMapper, attributes, uidFieldMapper, idFieldMapper, typeFieldMapper, indexFieldMapper,
|
||||
return new XContentDocumentMapper(index, docMapperParser, rootObjectMapper, attributes, uidFieldMapper, idFieldMapper, typeFieldMapper, indexFieldMapper,
|
||||
sourceFieldMapper, allFieldMapper, indexAnalyzer, searchAnalyzer, boostFieldMapper);
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
|
|||
|
||||
private ThreadLocal<ThreadLocals.CleanableValue<ParseContext>> cache = new ThreadLocal<ThreadLocals.CleanableValue<ParseContext>>() {
|
||||
@Override protected ThreadLocals.CleanableValue<ParseContext> initialValue() {
|
||||
return new ThreadLocals.CleanableValue<ParseContext>(new ParseContext(index, XContentDocumentMapper.this, new ContentPath(0)));
|
||||
return new ThreadLocals.CleanableValue<ParseContext>(new ParseContext(index, docMapperParser, XContentDocumentMapper.this, new ContentPath(0)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -151,6 +151,8 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
|
|||
|
||||
private final String type;
|
||||
|
||||
private final XContentDocumentMapperParser docMapperParser;
|
||||
|
||||
private volatile ImmutableMap<String, Object> attributes;
|
||||
|
||||
private volatile CompressedString mappingSource;
|
||||
|
@ -183,7 +185,8 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
|
|||
|
||||
private final Object mutex = new Object();
|
||||
|
||||
public XContentDocumentMapper(String index, XContentObjectMapper rootObjectMapper,
|
||||
public XContentDocumentMapper(String index, XContentDocumentMapperParser docMapperParser,
|
||||
XContentObjectMapper rootObjectMapper,
|
||||
ImmutableMap<String, Object> attributes,
|
||||
XContentUidFieldMapper uidFieldMapper,
|
||||
XContentIdFieldMapper idFieldMapper,
|
||||
|
@ -195,6 +198,7 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
|
|||
@Nullable XContentBoostFieldMapper boostFieldMapper) {
|
||||
this.index = index;
|
||||
this.type = rootObjectMapper.name();
|
||||
this.docMapperParser = docMapperParser;
|
||||
this.attributes = attributes;
|
||||
this.rootObjectMapper = rootObjectMapper;
|
||||
this.uidFieldMapper = uidFieldMapper;
|
||||
|
|
|
@ -90,6 +90,10 @@ public class XContentDocumentMapperParser extends AbstractIndexComponent impleme
|
|||
}
|
||||
}
|
||||
|
||||
public XContentTypeParser.ParserContext parserContext() {
|
||||
return new XContentTypeParser.ParserContext(analysisService, typeParsers);
|
||||
}
|
||||
|
||||
@Override public XContentDocumentMapper parse(String source) throws MapperParsingException {
|
||||
return parse(null, source);
|
||||
}
|
||||
|
@ -120,7 +124,7 @@ public class XContentDocumentMapperParser extends AbstractIndexComponent impleme
|
|||
}
|
||||
}
|
||||
|
||||
XContentTypeParser.ParserContext parserContext = new XContentTypeParser.ParserContext(mapping, analysisService, typeParsers);
|
||||
XContentTypeParser.ParserContext parserContext = new XContentTypeParser.ParserContext(analysisService, typeParsers);
|
||||
|
||||
XContentDocumentMapper.Builder docBuilder = doc(index.name(), (XContentObjectMapper.Builder) rootObjectTypeParser.parse(type, mapping, parserContext));
|
||||
|
||||
|
@ -165,7 +169,7 @@ public class XContentDocumentMapperParser extends AbstractIndexComponent impleme
|
|||
}
|
||||
docBuilder.attributes(attributes);
|
||||
|
||||
XContentDocumentMapper documentMapper = docBuilder.build();
|
||||
XContentDocumentMapper documentMapper = docBuilder.build(this);
|
||||
// update the source with the generated one
|
||||
documentMapper.refreshSource();
|
||||
return documentMapper;
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Licensed to Elastic Search and Shay Banon under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Elastic Search 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.index.mapper.xcontent;
|
||||
|
||||
import org.elasticsearch.ElasticSearchIllegalArgumentException;
|
||||
import org.elasticsearch.common.collect.Maps;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.index.mapper.MapperParsingException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class XContentDynamicTemplate {
|
||||
|
||||
public static enum MatchType {
|
||||
SIMPLE,
|
||||
REGEX;
|
||||
|
||||
public static MatchType fromString(String value) {
|
||||
if ("simple".equals(value)) {
|
||||
return SIMPLE;
|
||||
} else if ("regex".equals(value)) {
|
||||
return REGEX;
|
||||
}
|
||||
throw new ElasticSearchIllegalArgumentException("No matching pattern matched on [" + value + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private final String match;
|
||||
|
||||
private final String unmatch;
|
||||
|
||||
private final MatchType matchType;
|
||||
|
||||
private final String matchMappingType;
|
||||
|
||||
private final Map<String, Object> mapping;
|
||||
|
||||
public static XContentDynamicTemplate parse(Map<String, Object> conf) throws MapperParsingException {
|
||||
if (!conf.containsKey("match")) {
|
||||
throw new MapperParsingException("template must have match set");
|
||||
}
|
||||
String match = conf.get("match").toString();
|
||||
String unmatch = conf.containsKey("unmatch") ? conf.get("unmatch").toString() : null;
|
||||
String matchMappingType = conf.containsKey("match_mapping_type") ? conf.get("match_mapping_type").toString() : null;
|
||||
if (!conf.containsKey("mapping")) {
|
||||
throw new MapperParsingException("template must have mapping set");
|
||||
}
|
||||
Map<String, Object> mapping = (Map<String, Object>) conf.get("mapping");
|
||||
String matchType = conf.containsKey("match_pattern") ? conf.get("match_pattern").toString() : "simple";
|
||||
return new XContentDynamicTemplate(match, unmatch, matchMappingType, MatchType.fromString(matchType), mapping);
|
||||
}
|
||||
|
||||
public XContentDynamicTemplate(String match, String unmatch, String matchMappingType, MatchType matchType, Map<String, Object> mapping) {
|
||||
this.match = match;
|
||||
this.unmatch = unmatch;
|
||||
this.matchType = matchType;
|
||||
this.matchMappingType = matchMappingType;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
public boolean match(String name, String suggestedMappingType) {
|
||||
if (!patternMatch(match, name)) {
|
||||
return false;
|
||||
}
|
||||
if (patternMatch(unmatch, name)) {
|
||||
return false;
|
||||
}
|
||||
if (matchMappingType != null) {
|
||||
if (suggestedMappingType == null) {
|
||||
return false;
|
||||
}
|
||||
if (!patternMatch(matchMappingType, suggestedMappingType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String mappingType() {
|
||||
return mapping.containsKey("type") ? mapping.get("type").toString() : "object";
|
||||
}
|
||||
|
||||
private boolean patternMatch(String pattern, String str) {
|
||||
if (matchType == MatchType.SIMPLE) {
|
||||
return Regex.simpleMatch(pattern, str);
|
||||
}
|
||||
return str.matches(pattern);
|
||||
}
|
||||
|
||||
public Map<String, Object> mappingForName(String name) {
|
||||
return processMap(mapping, name);
|
||||
}
|
||||
|
||||
private Map<String, Object> processMap(Map<String, Object> map, String name) {
|
||||
Map<String, Object> processedMap = Maps.newHashMap();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey().replace("{name}", name);
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Map) {
|
||||
value = processMap((Map<String, Object>) value, name);
|
||||
} else if (value instanceof List) {
|
||||
value = processList((List) value, name);
|
||||
} else if (value instanceof String) {
|
||||
value = value.toString().replace("{name}", name);
|
||||
}
|
||||
processedMap.put(key, value);
|
||||
}
|
||||
return processedMap;
|
||||
}
|
||||
|
||||
private List processList(List list, String name) {
|
||||
List processedList = new ArrayList();
|
||||
for (Object value : list) {
|
||||
if (value instanceof Map) {
|
||||
value = processMap((Map<String, Object>) value, name);
|
||||
} else if (value instanceof List) {
|
||||
value = processList((List) value, name);
|
||||
} else if (value instanceof String) {
|
||||
value = value.toString().replace("{name}", name);
|
||||
}
|
||||
processedList.add(value);
|
||||
}
|
||||
return processedList;
|
||||
}
|
||||
}
|
|
@ -74,6 +74,8 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
|
||||
private final List<XContentMapper.Builder> mappersBuilders = newArrayList();
|
||||
|
||||
private final List<XContentDynamicTemplate> dynamicTemplates = newArrayList();
|
||||
|
||||
public Builder(String name) {
|
||||
super(name);
|
||||
this.builder = this;
|
||||
|
@ -121,6 +123,11 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder add(XContentDynamicTemplate dynamicTemplate) {
|
||||
this.dynamicTemplates.add(dynamicTemplate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder add(XContentMapper.Builder builder) {
|
||||
mappersBuilders.add(builder);
|
||||
return this;
|
||||
|
@ -144,7 +151,7 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
}
|
||||
XContentObjectMapper objectMapper = new XContentObjectMapper(name, enabled, dynamic, pathType,
|
||||
dateTimeFormatters.toArray(new FormatDateTimeFormatter[dateTimeFormatters.size()]),
|
||||
mappers);
|
||||
mappers, dynamicTemplates.toArray(new XContentDynamicTemplate[dynamicTemplates.size()]));
|
||||
|
||||
context.path().pathType(origPathType);
|
||||
context.path().remove();
|
||||
|
@ -171,6 +178,19 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
if (!type.equals("object")) {
|
||||
throw new MapperParsingException("Trying to parse an object but has a different type [" + type + "] for [" + name + "]");
|
||||
}
|
||||
} else if (fieldName.equals("dynamic_templates")) {
|
||||
// "dynamic_templates" : [
|
||||
// {
|
||||
// "match" : "*_test",
|
||||
// "match_mapping_type" : "string",
|
||||
// "mapping" : { "type" : "string", "store" : "yes" }
|
||||
// }
|
||||
// ]
|
||||
List tmplNodes = (List) fieldNode;
|
||||
for (Object tmplNode : tmplNodes) {
|
||||
Map<String, Object> tmpl = (Map<String, Object>) tmplNode;
|
||||
builder.add(XContentDynamicTemplate.parse(tmpl));
|
||||
}
|
||||
} else if (fieldName.equals("date_formats")) {
|
||||
List<FormatDateTimeFormatter> dateTimeFormatters = newArrayList();
|
||||
if (fieldNode instanceof List) {
|
||||
|
@ -243,6 +263,8 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
|
||||
private volatile ImmutableMap<String, XContentMapper> mappers = ImmutableMap.of();
|
||||
|
||||
private volatile XContentDynamicTemplate dynamicTemplates[];
|
||||
|
||||
private final Object mutex = new Object();
|
||||
|
||||
protected XContentObjectMapper(String name) {
|
||||
|
@ -255,16 +277,17 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
|
||||
protected XContentObjectMapper(String name, boolean enabled, boolean dynamic, ContentPath.Type pathType,
|
||||
FormatDateTimeFormatter[] dateTimeFormatters) {
|
||||
this(name, enabled, dynamic, pathType, dateTimeFormatters, null);
|
||||
this(name, enabled, dynamic, pathType, dateTimeFormatters, null, null);
|
||||
}
|
||||
|
||||
XContentObjectMapper(String name, boolean enabled, boolean dynamic, ContentPath.Type pathType,
|
||||
FormatDateTimeFormatter[] dateTimeFormatters, Map<String, XContentMapper> mappers) {
|
||||
FormatDateTimeFormatter[] dateTimeFormatters, Map<String, XContentMapper> mappers, XContentDynamicTemplate dynamicTemplates[]) {
|
||||
this.name = name;
|
||||
this.enabled = enabled;
|
||||
this.dynamic = dynamic;
|
||||
this.pathType = pathType;
|
||||
this.dateTimeFormatters = dateTimeFormatters;
|
||||
this.dynamicTemplates = dynamicTemplates == null ? new XContentDynamicTemplate[0] : dynamicTemplates;
|
||||
if (mappers != null) {
|
||||
this.mappers = copyOf(mappers);
|
||||
}
|
||||
|
@ -369,8 +392,12 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
objectMapper.parse(context);
|
||||
} else {
|
||||
BuilderContext builderContext = new BuilderContext(context.path());
|
||||
objectMapper = XContentMapperBuilders.object(currentFieldName).enabled(true)
|
||||
.dynamic(dynamic).pathType(pathType).dateTimeFormatter(dateTimeFormatters).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "object");
|
||||
if (builder == null) {
|
||||
builder = XContentMapperBuilders.object(currentFieldName).enabled(true)
|
||||
.dynamic(dynamic).pathType(pathType).dateTimeFormatter(dateTimeFormatters);
|
||||
}
|
||||
objectMapper = builder.build(builderContext);
|
||||
putMapper(objectMapper);
|
||||
objectMapper.parse(context);
|
||||
context.addedMapper();
|
||||
|
@ -408,7 +435,7 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
}
|
||||
}
|
||||
|
||||
private void serializeValue(ParseContext context, String currentFieldName, XContentParser.Token token) throws IOException {
|
||||
private void serializeValue(final ParseContext context, String currentFieldName, XContentParser.Token token) throws IOException {
|
||||
XContentMapper mapper = mappers.get(currentFieldName);
|
||||
if (mapper != null) {
|
||||
mapper.parse(context);
|
||||
|
@ -437,7 +464,11 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
for (FormatDateTimeFormatter dateTimeFormatter : dateTimeFormatters) {
|
||||
try {
|
||||
dateTimeFormatter.parser().parseMillis(text);
|
||||
mapper = dateField(currentFieldName).dateTimeFormatter(dateTimeFormatter).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "date");
|
||||
if (builder == null) {
|
||||
builder = dateField(currentFieldName).dateTimeFormatter(dateTimeFormatter);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
isDate = true;
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
|
@ -446,41 +477,100 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
|
|||
}
|
||||
}
|
||||
if (!isDate) {
|
||||
mapper = stringField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "string");
|
||||
if (builder == null) {
|
||||
builder = stringField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
XContentParser.NumberType numberType = context.parser().numberType();
|
||||
if (numberType == XContentParser.NumberType.INT) {
|
||||
if (context.parser().estimatedNumberType()) {
|
||||
mapper = longField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "long");
|
||||
if (builder == null) {
|
||||
builder = longField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
} else {
|
||||
mapper = integerField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "integer");
|
||||
if (builder == null) {
|
||||
builder = integerField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
}
|
||||
} else if (numberType == XContentParser.NumberType.LONG) {
|
||||
mapper = longField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "long");
|
||||
if (builder == null) {
|
||||
builder = longField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
} else if (numberType == XContentParser.NumberType.FLOAT) {
|
||||
if (context.parser().estimatedNumberType()) {
|
||||
mapper = doubleField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "double");
|
||||
if (builder == null) {
|
||||
builder = doubleField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
} else {
|
||||
mapper = floatField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "float");
|
||||
if (builder == null) {
|
||||
builder = floatField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
}
|
||||
} else if (numberType == XContentParser.NumberType.DOUBLE) {
|
||||
mapper = doubleField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "double");
|
||||
if (builder == null) {
|
||||
builder = doubleField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
mapper = booleanField(currentFieldName).build(builderContext);
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, "boolean");
|
||||
if (builder == null) {
|
||||
builder = booleanField(currentFieldName);
|
||||
}
|
||||
mapper = builder.build(builderContext);
|
||||
} else {
|
||||
// TODO how do we identify dynamically that its a binary value?
|
||||
throw new ElasticSearchIllegalStateException("Can't handle serializing a dynamic type with content token [" + token + "] and field name [" + currentFieldName + "]");
|
||||
XContentMapper.Builder builder = findTemplateBuilder(context, currentFieldName, null);
|
||||
if (builder != null) {
|
||||
mapper = builder.build(builderContext);
|
||||
} else {
|
||||
// TODO how do we identify dynamically that its a binary value?
|
||||
throw new ElasticSearchIllegalStateException("Can't handle serializing a dynamic type with content token [" + token + "] and field name [" + currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
putMapper(mapper);
|
||||
context.docMapper().addFieldMapper((FieldMapper) mapper);
|
||||
mapper.traverse(new FieldMapperListener() {
|
||||
@Override public void fieldMapper(FieldMapper fieldMapper) {
|
||||
context.docMapper().addFieldMapper(fieldMapper);
|
||||
}
|
||||
});
|
||||
|
||||
mapper.parse(context);
|
||||
context.addedMapper();
|
||||
}
|
||||
}
|
||||
|
||||
private XContentMapper.Builder findTemplateBuilder(ParseContext context, String name, String mappingType) {
|
||||
XContentDynamicTemplate dynamicTemplate = findTemplate(name, mappingType);
|
||||
if (dynamicTemplate == null) {
|
||||
return null;
|
||||
}
|
||||
XContentTypeParser.ParserContext parserContext = context.docMapperParser().parserContext();
|
||||
return parserContext.typeParser(dynamicTemplate.mappingType()).parse(name, dynamicTemplate.mappingForName(name), parserContext);
|
||||
}
|
||||
|
||||
private XContentDynamicTemplate findTemplate(String name, String mappingType) {
|
||||
for (XContentDynamicTemplate dynamicTemplate : dynamicTemplates) {
|
||||
if (dynamicTemplate.match(name, mappingType)) {
|
||||
return dynamicTemplate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public void merge(XContentMapper mergeWith, MergeContext mergeContext) throws MergeMappingException {
|
||||
if (!(mergeWith instanceof XContentObjectMapper)) {
|
||||
mergeContext.addConflict("Can't merge a non object mapping [" + mergeWith.name() + "] with an object mapping [" + name() + "]");
|
||||
|
|
|
@ -35,13 +35,10 @@ public interface XContentTypeParser {
|
|||
|
||||
private final AnalysisService analysisService;
|
||||
|
||||
private final Map<String, Object> rootNode;
|
||||
|
||||
private final ImmutableMap<String, XContentTypeParser> typeParsers;
|
||||
|
||||
public ParserContext(Map<String, Object> rootNode, AnalysisService analysisService, ImmutableMap<String, XContentTypeParser> typeParsers) {
|
||||
public ParserContext(AnalysisService analysisService, ImmutableMap<String, XContentTypeParser> typeParsers) {
|
||||
this.analysisService = analysisService;
|
||||
this.rootNode = rootNode;
|
||||
this.typeParsers = typeParsers;
|
||||
}
|
||||
|
||||
|
@ -49,10 +46,6 @@ public interface XContentTypeParser {
|
|||
return analysisService;
|
||||
}
|
||||
|
||||
public Map<String, Object> rootNode() {
|
||||
return this.rootNode;
|
||||
}
|
||||
|
||||
public XContentTypeParser typeParser(String type) {
|
||||
return typeParsers.get(Strings.toUnderscoreCase(type));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Licensed to Elastic Search and Shay Banon under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Elastic Search 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.index.mapper.xcontent.dynamictemplate.simple;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.elasticsearch.index.mapper.FieldMappers;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentDocumentMapper;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentMapperTests;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.elasticsearch.common.io.Streams.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class SimpleDynamicTemplatesTests {
|
||||
|
||||
@Test public void testSimple() throws Exception {
|
||||
String mapping = copyToStringFromClasspath("/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-mapping.json");
|
||||
XContentDocumentMapper docMapper = XContentMapperTests.newParser().parse(mapping);
|
||||
byte[] json = copyToBytesFromClasspath("/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-data.json");
|
||||
Document doc = docMapper.parse(json).doc();
|
||||
|
||||
Field f = doc.getField("name");
|
||||
assertThat(f.name(), equalTo("name"));
|
||||
assertThat(f.stringValue(), equalTo("some name"));
|
||||
assertThat(f.isIndexed(), equalTo(true));
|
||||
assertThat(f.isTokenized(), equalTo(false));
|
||||
|
||||
FieldMappers fieldMappers = docMapper.mappers().fullName("name");
|
||||
assertThat(fieldMappers.mappers().size(), equalTo(1));
|
||||
|
||||
f = doc.getField("multi1");
|
||||
assertThat(f.name(), equalTo("multi1"));
|
||||
assertThat(f.stringValue(), equalTo("multi 1"));
|
||||
assertThat(f.isIndexed(), equalTo(true));
|
||||
assertThat(f.isTokenized(), equalTo(true));
|
||||
|
||||
fieldMappers = docMapper.mappers().fullName("multi1");
|
||||
assertThat(fieldMappers.mappers().size(), equalTo(1));
|
||||
|
||||
f = doc.getField("multi1.org");
|
||||
assertThat(f.name(), equalTo("multi1.org"));
|
||||
assertThat(f.stringValue(), equalTo("multi 1"));
|
||||
assertThat(f.isIndexed(), equalTo(true));
|
||||
assertThat(f.isTokenized(), equalTo(false));
|
||||
|
||||
fieldMappers = docMapper.mappers().fullName("multi1.org");
|
||||
assertThat(fieldMappers.mappers().size(), equalTo(1));
|
||||
|
||||
f = doc.getField("multi2");
|
||||
assertThat(f.name(), equalTo("multi2"));
|
||||
assertThat(f.stringValue(), equalTo("multi 2"));
|
||||
assertThat(f.isIndexed(), equalTo(true));
|
||||
assertThat(f.isTokenized(), equalTo(true));
|
||||
|
||||
fieldMappers = docMapper.mappers().fullName("multi2");
|
||||
assertThat(fieldMappers.mappers().size(), equalTo(1));
|
||||
|
||||
f = doc.getField("multi2.org");
|
||||
assertThat(f.name(), equalTo("multi2.org"));
|
||||
assertThat(f.stringValue(), equalTo("multi 2"));
|
||||
assertThat(f.isIndexed(), equalTo(true));
|
||||
assertThat(f.isTokenized(), equalTo(false));
|
||||
|
||||
fieldMappers = docMapper.mappers().fullName("multi2.org");
|
||||
assertThat(fieldMappers.mappers().size(), equalTo(1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"_id" : "1",
|
||||
"name" : "some name",
|
||||
"age" : 1,
|
||||
"multi1" : "multi 1",
|
||||
"multi2" : "multi 2"
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"person" : {
|
||||
"dynamic_templates" : [
|
||||
{
|
||||
"match" : "multi*",
|
||||
"mapping" : {
|
||||
"type" : "multi_field",
|
||||
"fields" : {
|
||||
"{name}" : {"type": "string", "index" : "analyzed", "store" : "yes"},
|
||||
"org" : {"type": "string", "index" : "not_analyzed", "store" : "yes"}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match" : "*",
|
||||
"match_mapping_type" : "string",
|
||||
"mapping" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.index.mapper.xcontent.multifield;
|
|||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentDocumentMapper;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentDocumentMapperParser;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentMapperTests;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -69,18 +70,19 @@ public class XContentMultiFieldTests {
|
|||
}
|
||||
|
||||
@Test public void testBuildThenParse() throws Exception {
|
||||
XContentDocumentMapperParser mapperParser = XContentMapperTests.newParser();
|
||||
XContentDocumentMapper builderDocMapper = doc("test", object("person").add(
|
||||
multiField("name")
|
||||
.add(stringField("name").store(Field.Store.YES))
|
||||
.add(stringField("indexed").index(Field.Index.ANALYZED))
|
||||
.add(stringField("not_indexed").index(Field.Index.NO).store(Field.Store.YES))
|
||||
)).build();
|
||||
)).build(mapperParser);
|
||||
builderDocMapper.refreshSource();
|
||||
|
||||
String builtMapping = builderDocMapper.mappingSource().string();
|
||||
// System.out.println(builtMapping);
|
||||
// reparse it
|
||||
XContentDocumentMapper docMapper = XContentMapperTests.newParser().parse(builtMapping);
|
||||
XContentDocumentMapper docMapper = mapperParser.parse(builtMapping);
|
||||
|
||||
|
||||
byte[] json = copyToBytesFromClasspath("/org/elasticsearch/index/mapper/xcontent/multifield/test-data.json");
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.document.Document;
|
|||
import org.apache.lucene.document.Field;
|
||||
import org.elasticsearch.index.mapper.Uid;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentDocumentMapper;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentDocumentMapperParser;
|
||||
import org.elasticsearch.index.mapper.xcontent.XContentMapperTests;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -38,10 +39,11 @@ import static org.hamcrest.Matchers.*;
|
|||
public class SimpleXContentMapperTests {
|
||||
|
||||
@Test public void testSimpleMapper() throws Exception {
|
||||
XContentDocumentMapperParser mapperParser = XContentMapperTests.newParser();
|
||||
XContentDocumentMapper docMapper = doc("test",
|
||||
object("person")
|
||||
.add(object("name").add(stringField("first").store(YES).index(Field.Index.NO)))
|
||||
).sourceField(source()).build();
|
||||
).sourceField(source()).build(mapperParser);
|
||||
|
||||
byte[] json = copyToBytesFromClasspath("/org/elasticsearch/index/mapper/xcontent/simple/test1.json");
|
||||
Document doc = docMapper.parse("person", "1", json).doc();
|
||||
|
|
Loading…
Reference in New Issue