diff --git a/.idea/dictionaries/kimchy.xml b/.idea/dictionaries/kimchy.xml
index ead8627909b..2b538c07359 100644
--- a/.idea/dictionaries/kimchy.xml
+++ b/.idea/dictionaries/kimchy.xml
@@ -140,6 +140,7 @@
unboxed
unicast
unmanaged
+ unmatch
unregister
uptime
uuid
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java b/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java
index 4d9bb510144..3cd9b6463b1 100644
--- a/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java
@@ -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);
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/ParseContext.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/ParseContext.java
index 179878c9c5a..fed28c7a492 100644
--- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/ParseContext.java
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/ParseContext.java
@@ -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;
}
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapper.java
index 66d6c33cac8..91863290b95 100644
--- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapper.java
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapper.java
@@ -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> cache = new ThreadLocal>() {
@Override protected ThreadLocals.CleanableValue initialValue() {
- return new ThreadLocals.CleanableValue(new ParseContext(index, XContentDocumentMapper.this, new ContentPath(0)));
+ return new ThreadLocals.CleanableValue(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 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 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;
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapperParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapperParser.java
index 10dec129d69..a35d149617e 100644
--- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapperParser.java
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDocumentMapperParser.java
@@ -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;
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDynamicTemplate.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDynamicTemplate.java
new file mode 100644
index 00000000000..7ab24c05fd0
--- /dev/null
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentDynamicTemplate.java
@@ -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 mapping;
+
+ public static XContentDynamicTemplate parse(Map 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 mapping = (Map) 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 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 mappingForName(String name) {
+ return processMap(mapping, name);
+ }
+
+ private Map processMap(Map map, String name) {
+ Map processedMap = Maps.newHashMap();
+ for (Map.Entry entry : map.entrySet()) {
+ String key = entry.getKey().replace("{name}", name);
+ Object value = entry.getValue();
+ if (value instanceof Map) {
+ value = processMap((Map) 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) 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;
+ }
+}
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentObjectMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentObjectMapper.java
index 554fa6edba1..0a97cf7366f 100644
--- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentObjectMapper.java
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentObjectMapper.java
@@ -74,6 +74,8 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
private final List mappersBuilders = newArrayList();
+ private final List 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 tmpl = (Map) tmplNode;
+ builder.add(XContentDynamicTemplate.parse(tmpl));
+ }
} else if (fieldName.equals("date_formats")) {
List dateTimeFormatters = newArrayList();
if (fieldNode instanceof List) {
@@ -243,6 +263,8 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
private volatile ImmutableMap 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 mappers) {
+ FormatDateTimeFormatter[] dateTimeFormatters, Map 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() + "]");
diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentTypeParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentTypeParser.java
index efe89c696f4..db5bcdda1af 100644
--- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentTypeParser.java
+++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/xcontent/XContentTypeParser.java
@@ -35,13 +35,10 @@ public interface XContentTypeParser {
private final AnalysisService analysisService;
- private final Map rootNode;
-
private final ImmutableMap typeParsers;
- public ParserContext(Map rootNode, AnalysisService analysisService, ImmutableMap typeParsers) {
+ public ParserContext(AnalysisService analysisService, ImmutableMap typeParsers) {
this.analysisService = analysisService;
- this.rootNode = rootNode;
this.typeParsers = typeParsers;
}
@@ -49,10 +46,6 @@ public interface XContentTypeParser {
return analysisService;
}
- public Map rootNode() {
- return this.rootNode;
- }
-
public XContentTypeParser typeParser(String type) {
return typeParsers.get(Strings.toUnderscoreCase(type));
}
diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/SimpleDynamicTemplatesTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/SimpleDynamicTemplatesTests.java
new file mode 100644
index 00000000000..c0e4f49751d
--- /dev/null
+++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/SimpleDynamicTemplatesTests.java
@@ -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));
+ }
+}
diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-data.json b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-data.json
new file mode 100644
index 00000000000..5864c59cccf
--- /dev/null
+++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-data.json
@@ -0,0 +1,7 @@
+{
+ "_id" : "1",
+ "name" : "some name",
+ "age" : 1,
+ "multi1" : "multi 1",
+ "multi2" : "multi 2"
+}
\ No newline at end of file
diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-mapping.json b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-mapping.json
new file mode 100644
index 00000000000..d8a459e8452
--- /dev/null
+++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/dynamictemplate/simple/test-mapping.json
@@ -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"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/multifield/XContentMultiFieldTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/multifield/XContentMultiFieldTests.java
index 017e6210edc..4b046e02ee8 100644
--- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/multifield/XContentMultiFieldTests.java
+++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/multifield/XContentMultiFieldTests.java
@@ -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");
diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/simple/SimpleXContentMapperTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/simple/SimpleXContentMapperTests.java
index 054e7963e7a..9ecd8352dd4 100644
--- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/simple/SimpleXContentMapperTests.java
+++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/mapper/xcontent/simple/SimpleXContentMapperTests.java
@@ -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();