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();