intial support for pluggable type mappers

This commit is contained in:
kimchy 2010-03-28 18:35:25 +03:00
parent b799b7a9d7
commit f560383b53
4 changed files with 515 additions and 381 deletions

View File

@ -19,27 +19,24 @@
package org.elasticsearch.index.mapper.json;
import org.apache.lucene.document.Field;
import com.google.common.collect.ImmutableMap;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.DocumentMapperParser;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.util.MapBuilder;
import org.elasticsearch.util.io.FastStringReader;
import org.elasticsearch.util.joda.FormatDateTimeFormatter;
import org.elasticsearch.util.joda.Joda;
import org.elasticsearch.util.json.Jackson;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static com.google.common.collect.Lists.*;
import static org.elasticsearch.index.mapper.json.JsonMapperBuilders.*;
import static org.elasticsearch.index.mapper.json.JsonTypeParsers.*;
import static org.elasticsearch.util.json.JacksonNodes.*;
/**
@ -51,8 +48,36 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
private final AnalysisService analysisService;
private final JsonObjectTypeParser rootObjectTypeParser = new JsonObjectTypeParser();
private final Object typeParsersMutex = new Object();
private volatile ImmutableMap<String, JsonTypeParser> typeParsers;
public JsonDocumentMapperParser(AnalysisService analysisService) {
this.analysisService = analysisService;
typeParsers = new MapBuilder<String, JsonTypeParser>()
.put(JsonShortFieldMapper.JSON_TYPE, new JsonShortTypeParser())
.put(JsonIntegerFieldMapper.JSON_TYPE, new JsonIntegerTypeParser())
.put(JsonLongFieldMapper.JSON_TYPE, new JsonLongTypeParser())
.put(JsonFloatFieldMapper.JSON_TYPE, new JsonFloatTypeParser())
.put(JsonDoubleFieldMapper.JSON_TYPE, new JsonDoubleTypeParser())
.put(JsonBooleanFieldMapper.JSON_TYPE, new JsonBooleanTypeParser())
.put(JsonBinaryFieldMapper.JSON_TYPE, new JsonBinaryTypeParser())
.put(JsonDateFieldMapper.JSON_TYPE, new JsonDateTypeParser())
.put(JsonStringFieldMapper.JSON_TYPE, new JsonStringTypeParser())
.put(JsonObjectMapper.JSON_TYPE, new JsonObjectTypeParser())
.put(JsonMultiFieldMapper.JSON_TYPE, new JsonMultiFieldTypeParser())
.immutableMap();
}
public void putTypeParser(String type, JsonTypeParser typeParser) {
synchronized (typeParsersMutex) {
typeParsers = new MapBuilder<String, JsonTypeParser>()
.putAll(typeParsers)
.put(type, typeParser)
.immutableMap();
}
}
@Override public DocumentMapper parse(String source) throws MapperParsingException {
@ -86,7 +111,9 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
}
}
JsonDocumentMapper.Builder docBuilder = JsonMapperBuilders.doc(parseObject(type, rootObj));
JsonTypeParser.ParserContext parserContext = new JsonTypeParser.ParserContext(rootObj, analysisService, typeParsers);
JsonDocumentMapper.Builder docBuilder = doc((JsonObjectMapper.Builder) rootObjectTypeParser.parse(type, rootObj, parserContext));
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = rootObj.getFields(); fieldsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = fieldsIt.next();
@ -94,17 +121,17 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
JsonNode fieldNode = entry.getValue();
if (JsonSourceFieldMapper.JSON_TYPE.equals(fieldName)) {
docBuilder.sourceField(parseSourceField((ObjectNode) fieldNode));
docBuilder.sourceField(parseSourceField((ObjectNode) fieldNode, parserContext));
} else if (JsonIdFieldMapper.JSON_TYPE.equals(fieldName)) {
docBuilder.idField(parseIdField((ObjectNode) fieldNode));
docBuilder.idField(parseIdField((ObjectNode) fieldNode, parserContext));
} else if (JsonTypeFieldMapper.JSON_TYPE.equals(fieldName)) {
docBuilder.typeField(parseTypeField((ObjectNode) fieldNode));
docBuilder.typeField(parseTypeField((ObjectNode) fieldNode, parserContext));
} else if (JsonUidFieldMapper.JSON_TYPE.equals(fieldName)) {
docBuilder.uidField(parseUidField((ObjectNode) fieldNode));
docBuilder.uidField(parseUidField((ObjectNode) fieldNode, parserContext));
} else if (JsonBoostFieldMapper.JSON_TYPE.equals(fieldName)) {
docBuilder.boostField(parseBoostField((ObjectNode) fieldNode));
docBuilder.boostField(parseBoostField((ObjectNode) fieldNode, parserContext));
} else if (JsonAllFieldMapper.JSON_TYPE.equals(fieldName)) {
docBuilder.allField(parseAllField((ObjectNode) fieldNode));
docBuilder.allField(parseAllField((ObjectNode) fieldNode, parserContext));
} else if ("indexAnalyzer".equals(fieldName)) {
docBuilder.indexAnalyzer(analysisService.analyzer(fieldNode.getTextValue()));
} else if ("searchAnalyzer".equals(fieldName)) {
@ -130,7 +157,7 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
return documentMapper;
}
private JsonUidFieldMapper.Builder parseUidField(ObjectNode uidNode) {
private JsonUidFieldMapper.Builder parseUidField(ObjectNode uidNode, JsonTypeParser.ParserContext parserContext) {
// String name = uidNode.get("name") == null ? JsonUidFieldMapper.Defaults.NAME : uidNode.get("name").getTextValue();
JsonUidFieldMapper.Builder builder = uid();
// for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = uidNode.getFields(); fieldsIt.hasNext();) {
@ -145,10 +172,10 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
return builder;
}
private JsonBoostFieldMapper.Builder parseBoostField(ObjectNode boostNode) {
private JsonBoostFieldMapper.Builder parseBoostField(ObjectNode boostNode, JsonTypeParser.ParserContext parserContext) {
String name = boostNode.get("name") == null ? JsonBoostFieldMapper.Defaults.NAME : boostNode.get("name").getTextValue();
JsonBoostFieldMapper.Builder builder = boost(name);
parseNumberField(builder, name, boostNode);
parseNumberField(builder, name, boostNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = boostNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
@ -160,25 +187,25 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
return builder;
}
private JsonTypeFieldMapper.Builder parseTypeField(ObjectNode typeNode) {
private JsonTypeFieldMapper.Builder parseTypeField(ObjectNode typeNode, JsonTypeParser.ParserContext parserContext) {
// String name = typeNode.get("name") == null ? JsonTypeFieldMapper.Defaults.NAME : typeNode.get("name").getTextValue();
JsonTypeFieldMapper.Builder builder = type();
parseJsonField(builder, builder.name, typeNode);
parseJsonField(builder, builder.name, typeNode, parserContext);
return builder;
}
private JsonIdFieldMapper.Builder parseIdField(ObjectNode idNode) {
private JsonIdFieldMapper.Builder parseIdField(ObjectNode idNode, JsonTypeParser.ParserContext parserContext) {
// String name = idNode.get("name") == null ? JsonIdFieldMapper.Defaults.NAME : idNode.get("name").getTextValue();
JsonIdFieldMapper.Builder builder = id();
parseJsonField(builder, builder.name, idNode);
parseJsonField(builder, builder.name, idNode, parserContext);
return builder;
}
private JsonAllFieldMapper.Builder parseAllField(ObjectNode allNode) {
private JsonAllFieldMapper.Builder parseAllField(ObjectNode allNode, JsonTypeParser.ParserContext parserContext) {
// String name = idNode.get("name") == null ? JsonIdFieldMapper.Defaults.NAME : idNode.get("name").getTextValue();
JsonAllFieldMapper.Builder builder = all();
parseJsonField(builder, builder.name, allNode);
parseJsonField(builder, builder.name, allNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = allNode.getFields(); fieldsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = fieldsIt.next();
String fieldName = entry.getKey();
@ -190,7 +217,7 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
return builder;
}
private JsonSourceFieldMapper.Builder parseSourceField(ObjectNode sourceNode) {
private JsonSourceFieldMapper.Builder parseSourceField(ObjectNode sourceNode, JsonTypeParser.ParserContext parserContext) {
// String name = sourceNode.get("name") == null ? JsonSourceFieldMapper.Defaults.NAME : sourceNode.get("name").getTextValue();
JsonSourceFieldMapper.Builder builder = source();
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = sourceNode.getFields(); fieldsIt.hasNext();) {
@ -217,360 +244,4 @@ public class JsonDocumentMapperParser implements DocumentMapperParser {
}
return builder;
}
private JsonObjectMapper.Builder parseObject(String name, ObjectNode node) {
JsonObjectMapper.Builder builder = object(name);
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = node.getFields(); fieldsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = fieldsIt.next();
String fieldName = entry.getKey();
JsonNode fieldNode = entry.getValue();
if (fieldName.equals("dynamic")) {
builder.dynamic(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("type")) {
String type = fieldNode.getTextValue();
if (!type.equals("object")) {
throw new MapperParsingException("Trying to parse an object but has a different type [" + type + "] for [" + name + "]");
}
} else if (fieldName.equals("dateFormats")) {
List<FormatDateTimeFormatter> dateTimeFormatters = newArrayList();
if (fieldNode.isArray()) {
for (JsonNode node1 : (ArrayNode) fieldNode) {
dateTimeFormatters.add(parseDateTimeFormatter(fieldName, node1));
}
} else if ("none".equals(fieldNode.getValueAsText())) {
dateTimeFormatters = null;
} else {
dateTimeFormatters.add(parseDateTimeFormatter(fieldName, fieldNode));
}
if (dateTimeFormatters == null) {
builder.noDateTimeFormatter();
} else {
builder.dateTimeFormatter(dateTimeFormatters);
}
} else if (fieldName.equals("enabled")) {
builder.enabled(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("pathType")) {
builder.pathType(parsePathType(name, fieldNode.getValueAsText()));
} else if (fieldName.equals("properties")) {
parseProperties(builder, (ObjectNode) fieldNode);
} else if (fieldName.equals("includeInAll")) {
builder.includeInAll(nodeBooleanValue(fieldNode));
}
}
return builder;
}
private JsonPath.Type parsePathType(String name, String path) throws MapperParsingException {
if ("justName".equals(path) || "just_name".equals(path)) {
return JsonPath.Type.JUST_NAME;
} else if ("full".equals(path)) {
return JsonPath.Type.FULL;
} else {
throw new MapperParsingException("Wrong value for pathType [" + path + "] for objet [" + name + "]");
}
}
private void parseProperties(JsonObjectMapper.Builder objBuilder, ObjectNode propsNode) {
for (Iterator<Map.Entry<String, JsonNode>> propsIt = propsNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
String type;
JsonNode typeNode = propNode.get("type");
if (typeNode != null) {
type = typeNode.getTextValue();
} else {
// lets see if we can derive this...
if (propNode.isObject() && propNode.get("properties") != null) {
type = JsonObjectMapper.JSON_TYPE;
} else if (propNode.isObject() && propNode.get("fields") != null) {
type = JsonMultiFieldMapper.JSON_TYPE;
} else {
throw new MapperParsingException("No type specified for property [" + propName + "]");
}
}
if (type.equals(JsonStringFieldMapper.JSON_TYPE)) {
objBuilder.add(parseString(propName, (ObjectNode) propNode));
} else if (type.equals(JsonDateFieldMapper.JSON_TYPE)) {
objBuilder.add(parseDate(propName, (ObjectNode) propNode));
} else if (type.equals(JsonShortFieldMapper.JSON_TYPE)) {
objBuilder.add(parseShort(propName, (ObjectNode) propNode));
} else if (type.equals(JsonIntegerFieldMapper.JSON_TYPE)) {
objBuilder.add(parseInteger(propName, (ObjectNode) propNode));
} else if (type.equals(JsonLongFieldMapper.JSON_TYPE)) {
objBuilder.add(parseLong(propName, (ObjectNode) propNode));
} else if (type.equals(JsonFloatFieldMapper.JSON_TYPE)) {
objBuilder.add(parseFloat(propName, (ObjectNode) propNode));
} else if (type.equals(JsonDoubleFieldMapper.JSON_TYPE)) {
objBuilder.add(parseDouble(propName, (ObjectNode) propNode));
} else if (type.equals(JsonBooleanFieldMapper.JSON_TYPE)) {
objBuilder.add(parseBoolean(propName, (ObjectNode) propNode));
} else if (type.equals(JsonObjectMapper.JSON_TYPE)) {
objBuilder.add(parseObject(propName, (ObjectNode) propNode));
} else if (type.equals(JsonMultiFieldMapper.JSON_TYPE)) {
objBuilder.add(parseMultiField(propName, (ObjectNode) propNode));
} else if (type.equals(JsonBinaryFieldMapper.JSON_TYPE)) {
objBuilder.add(parseBinary(propName, (ObjectNode) propNode));
}
}
}
private JsonMultiFieldMapper.Builder parseMultiField(String name, ObjectNode multiFieldNode) {
JsonMultiFieldMapper.Builder builder = multiField(name);
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = multiFieldNode.getFields(); fieldsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = fieldsIt.next();
String fieldName = entry.getKey();
JsonNode fieldNode = entry.getValue();
if (fieldName.equals("pathType")) {
builder.pathType(parsePathType(name, fieldNode.getValueAsText()));
} else if (fieldName.equals("fields")) {
ObjectNode fieldsNode = (ObjectNode) fieldNode;
for (Iterator<Map.Entry<String, JsonNode>> propsIt = fieldsNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry1 = propsIt.next();
String propName = entry1.getKey();
JsonNode propNode = entry1.getValue();
String type;
JsonNode typeNode = propNode.get("type");
if (typeNode != null) {
type = typeNode.getTextValue();
} else {
throw new MapperParsingException("No type specified for property [" + propName + "]");
}
if (type.equals(JsonStringFieldMapper.JSON_TYPE)) {
builder.add(parseString(propName, (ObjectNode) propNode));
} else if (type.equals(JsonDateFieldMapper.JSON_TYPE)) {
builder.add(parseDate(propName, (ObjectNode) propNode));
} else if (type.equals(JsonIntegerFieldMapper.JSON_TYPE)) {
builder.add(parseInteger(propName, (ObjectNode) propNode));
} else if (type.equals(JsonLongFieldMapper.JSON_TYPE)) {
builder.add(parseLong(propName, (ObjectNode) propNode));
} else if (type.equals(JsonFloatFieldMapper.JSON_TYPE)) {
builder.add(parseFloat(propName, (ObjectNode) propNode));
} else if (type.equals(JsonDoubleFieldMapper.JSON_TYPE)) {
builder.add(parseDouble(propName, (ObjectNode) propNode));
} else if (type.equals(JsonBooleanFieldMapper.JSON_TYPE)) {
builder.add(parseBoolean(propName, (ObjectNode) propNode));
} else if (type.equals(JsonBinaryFieldMapper.JSON_TYPE)) {
builder.add(parseBinary(propName, (ObjectNode) propNode));
}
}
}
}
return builder;
}
private JsonDateFieldMapper.Builder parseDate(String name, ObjectNode dateNode) {
JsonDateFieldMapper.Builder builder = dateField(name);
parseNumberField(builder, name, dateNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = dateNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(propNode.getValueAsText());
} else if (propName.equals("format")) {
builder.dateTimeFormatter(parseDateTimeFormatter(propName, propNode));
}
}
return builder;
}
private JsonShortFieldMapper.Builder parseShort(String name, ObjectNode integerNode) {
JsonShortFieldMapper.Builder builder = shortField(name);
parseNumberField(builder, name, integerNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = integerNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeShortValue(propNode));
}
}
return builder;
}
private JsonIntegerFieldMapper.Builder parseInteger(String name, ObjectNode integerNode) {
JsonIntegerFieldMapper.Builder builder = integerField(name);
parseNumberField(builder, name, integerNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = integerNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeIntegerValue(propNode));
}
}
return builder;
}
private JsonLongFieldMapper.Builder parseLong(String name, ObjectNode longNode) {
JsonLongFieldMapper.Builder builder = longField(name);
parseNumberField(builder, name, longNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = longNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeLongValue(propNode));
}
}
return builder;
}
private JsonFloatFieldMapper.Builder parseFloat(String name, ObjectNode floatNode) {
JsonFloatFieldMapper.Builder builder = floatField(name);
parseNumberField(builder, name, floatNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = floatNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeFloatValue(propNode));
}
}
return builder;
}
private JsonDoubleFieldMapper.Builder parseDouble(String name, ObjectNode doubleNode) {
JsonDoubleFieldMapper.Builder builder = doubleField(name);
parseNumberField(builder, name, doubleNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = doubleNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeDoubleValue(propNode));
}
}
return builder;
}
private JsonStringFieldMapper.Builder parseString(String name, ObjectNode stringNode) {
JsonStringFieldMapper.Builder builder = stringField(name);
parseJsonField(builder, name, stringNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = stringNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(propNode.getValueAsText());
}
}
return builder;
}
private JsonBinaryFieldMapper.Builder parseBinary(String name, ObjectNode binaryNode) {
JsonBinaryFieldMapper.Builder builder = binaryField(name);
parseJsonField(builder, name, binaryNode);
return builder;
}
private JsonBooleanFieldMapper.Builder parseBoolean(String name, ObjectNode booleanNode) {
JsonBooleanFieldMapper.Builder builder = booleanField(name);
parseJsonField(builder, name, booleanNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = booleanNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeBooleanValue(propNode));
}
}
return builder;
}
private void parseNumberField(JsonNumberFieldMapper.Builder builder, String name, ObjectNode numberNode) {
parseJsonField(builder, name, numberNode);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = numberNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("precisionStep")) {
builder.precisionStep(nodeIntegerValue(propNode));
}
}
}
private void parseJsonField(JsonFieldMapper.Builder builder, String name, ObjectNode fieldNode) {
for (Iterator<Map.Entry<String, JsonNode>> propsIt = fieldNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("indexName")) {
builder.indexName(propNode.getTextValue());
} else if (propName.equals("store")) {
builder.store(parseStore(name, propNode.getTextValue()));
} else if (propName.equals("index")) {
builder.index(parseIndex(name, propNode.getTextValue()));
} else if (propName.equals("termVector")) {
builder.termVector(parseTermVector(name, propNode.getTextValue()));
} else if (propName.equals("boost")) {
builder.boost(nodeFloatValue(propNode));
} else if (propName.equals("omitNorms")) {
builder.omitNorms(nodeBooleanValue(propNode));
} else if (propName.equals("omitTermFreqAndPositions")) {
builder.omitTermFreqAndPositions(nodeBooleanValue(propNode));
} else if (propName.equals("indexAnalyzer")) {
builder.indexAnalyzer(analysisService.analyzer(propNode.getTextValue()));
} else if (propName.equals("searchAnalyzer")) {
builder.searchAnalyzer(analysisService.analyzer(propNode.getTextValue()));
} else if (propName.equals("analyzer")) {
builder.indexAnalyzer(analysisService.analyzer(propNode.getTextValue()));
builder.searchAnalyzer(analysisService.analyzer(propNode.getTextValue()));
} else if (propName.equals("includeInAll")) {
builder.includeInAll(nodeBooleanValue(propNode));
}
}
}
private FormatDateTimeFormatter parseDateTimeFormatter(String fieldName, JsonNode node) {
if (node.isTextual()) {
return Joda.forPattern(node.getTextValue());
} else {
// TODO support more complex configuration...
throw new MapperParsingException("Wrong node to use to parse date formatters [" + fieldName + "]");
}
}
private Field.TermVector parseTermVector(String fieldName, String termVector) throws MapperParsingException {
if ("no".equals(termVector)) {
return Field.TermVector.NO;
} else if ("yes".equals(termVector)) {
return Field.TermVector.YES;
} else if ("with_offsets".equals(termVector)) {
return Field.TermVector.WITH_OFFSETS;
} else if ("with_positions".equals(termVector)) {
return Field.TermVector.WITH_POSITIONS;
} else if ("with_positions_offsets".equals(termVector)) {
return Field.TermVector.WITH_POSITIONS_OFFSETS;
} else {
throw new MapperParsingException("Wrong value for termVector [" + termVector + "] for field [" + fieldName + "]");
}
}
private Field.Index parseIndex(String fieldName, String index) throws MapperParsingException {
if ("no".equals(index)) {
return Field.Index.NO;
} else if ("not_analyzed".equals(index)) {
return Field.Index.NOT_ANALYZED;
} else if ("analyzed".equals(index)) {
return Field.Index.ANALYZED;
} else {
throw new MapperParsingException("Wrong value for index [" + index + "] for field [" + fieldName + "]");
}
}
private Field.Store parseStore(String fieldName, String store) throws MapperParsingException {
if ("no".equals(store)) {
return Field.Store.NO;
} else if ("yes".equals(store)) {
return Field.Store.YES;
} else {
throw new MapperParsingException("Wrong value for store [" + store + "] for field [" + fieldName + "]");
}
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.json;
import com.google.common.collect.ImmutableMap;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ObjectNode;
import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.mapper.MapperParsingException;
/**
* @author kimchy (shay.banon)
*/
public interface JsonTypeParser {
public static class ParserContext {
private final AnalysisService analysisService;
private final ObjectNode rootNode;
private final ImmutableMap<String, JsonTypeParser> typeParsers;
public ParserContext(ObjectNode rootNode, AnalysisService analysisService, ImmutableMap<String, JsonTypeParser> typeParsers) {
this.analysisService = analysisService;
this.rootNode = rootNode;
this.typeParsers = typeParsers;
}
public AnalysisService analysisService() {
return analysisService;
}
public ObjectNode rootNode() {
return this.rootNode;
}
public JsonTypeParser typeParser(String type) {
return typeParsers.get(type);
}
}
JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException;
}

View File

@ -0,0 +1,402 @@
/*
* 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.json;
import org.apache.lucene.document.Field;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.util.joda.FormatDateTimeFormatter;
import org.elasticsearch.util.joda.Joda;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static com.google.common.collect.Lists.*;
import static org.elasticsearch.index.mapper.json.JsonMapperBuilders.*;
import static org.elasticsearch.util.json.JacksonNodes.*;
/**
* @author kimchy (shay.banon)
*/
public class JsonTypeParsers {
public static class JsonObjectTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode objectNode = (ObjectNode) node;
JsonObjectMapper.Builder builder = object(name);
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = objectNode.getFields(); fieldsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = fieldsIt.next();
String fieldName = entry.getKey();
JsonNode fieldNode = entry.getValue();
if (fieldName.equals("dynamic")) {
builder.dynamic(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("type")) {
String type = fieldNode.getTextValue();
if (!type.equals("object")) {
throw new MapperParsingException("Trying to parse an object but has a different type [" + type + "] for [" + name + "]");
}
} else if (fieldName.equals("dateFormats")) {
List<FormatDateTimeFormatter> dateTimeFormatters = newArrayList();
if (fieldNode.isArray()) {
for (JsonNode node1 : (ArrayNode) fieldNode) {
dateTimeFormatters.add(parseDateTimeFormatter(fieldName, node1));
}
} else if ("none".equals(fieldNode.getValueAsText())) {
dateTimeFormatters = null;
} else {
dateTimeFormatters.add(parseDateTimeFormatter(fieldName, fieldNode));
}
if (dateTimeFormatters == null) {
builder.noDateTimeFormatter();
} else {
builder.dateTimeFormatter(dateTimeFormatters);
}
} else if (fieldName.equals("enabled")) {
builder.enabled(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("pathType")) {
builder.pathType(parsePathType(name, fieldNode.getValueAsText()));
} else if (fieldName.equals("properties")) {
parseProperties(builder, (ObjectNode) fieldNode, parserContext);
} else if (fieldName.equals("includeInAll")) {
builder.includeInAll(nodeBooleanValue(fieldNode));
}
}
return builder;
}
private void parseProperties(JsonObjectMapper.Builder objBuilder, ObjectNode propsNode, JsonTypeParser.ParserContext parserContext) {
for (Iterator<Map.Entry<String, JsonNode>> propsIt = propsNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
String type;
JsonNode typeNode = propNode.get("type");
if (typeNode != null) {
type = typeNode.getTextValue();
} else {
// lets see if we can derive this...
if (propNode.isObject() && propNode.get("properties") != null) {
type = JsonObjectMapper.JSON_TYPE;
} else if (propNode.isObject() && propNode.get("fields") != null) {
type = JsonMultiFieldMapper.JSON_TYPE;
} else {
throw new MapperParsingException("No type specified for property [" + propName + "]");
}
}
JsonTypeParser typeParser = parserContext.typeParser(type);
if (typeParser == null) {
throw new MapperParsingException("No handler for type [" + type + "] declared on field [" + propName + "]");
}
objBuilder.add(typeParser.parse(propName, propNode, parserContext));
}
}
}
public static class JsonMultiFieldTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode multiFieldNode = (ObjectNode) node;
JsonMultiFieldMapper.Builder builder = multiField(name);
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = multiFieldNode.getFields(); fieldsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = fieldsIt.next();
String fieldName = entry.getKey();
JsonNode fieldNode = entry.getValue();
if (fieldName.equals("pathType")) {
builder.pathType(parsePathType(name, fieldNode.getValueAsText()));
} else if (fieldName.equals("fields")) {
ObjectNode fieldsNode = (ObjectNode) fieldNode;
for (Iterator<Map.Entry<String, JsonNode>> propsIt = fieldsNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry1 = propsIt.next();
String propName = entry1.getKey();
JsonNode propNode = entry1.getValue();
String type;
JsonNode typeNode = propNode.get("type");
if (typeNode != null) {
type = typeNode.getTextValue();
} else {
throw new MapperParsingException("No type specified for property [" + propName + "]");
}
JsonTypeParser typeParser = parserContext.typeParser(type);
if (typeParser == null) {
throw new MapperParsingException("No handler for type [" + type + "] declared on field [" + fieldName + "]");
}
builder.add(typeParser.parse(propName, propNode, parserContext));
}
}
}
return builder;
}
}
public static class JsonDateTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode dateNode = (ObjectNode) node;
JsonDateFieldMapper.Builder builder = dateField(name);
parseNumberField(builder, name, dateNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = dateNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(propNode.getValueAsText());
} else if (propName.equals("format")) {
builder.dateTimeFormatter(parseDateTimeFormatter(propName, propNode));
}
}
return builder;
}
}
public static class JsonShortTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode shortNode = (ObjectNode) node;
JsonShortFieldMapper.Builder builder = shortField(name);
parseNumberField(builder, name, shortNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = shortNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeShortValue(propNode));
}
}
return builder;
}
}
public static class JsonIntegerTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode integerNode = (ObjectNode) node;
JsonIntegerFieldMapper.Builder builder = integerField(name);
parseNumberField(builder, name, integerNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = integerNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeIntegerValue(propNode));
}
}
return builder;
}
}
public static class JsonLongTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode longNode = (ObjectNode) node;
JsonLongFieldMapper.Builder builder = longField(name);
parseNumberField(builder, name, longNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = longNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeLongValue(propNode));
}
}
return builder;
}
}
public static class JsonFloatTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode floatNode = (ObjectNode) node;
JsonFloatFieldMapper.Builder builder = floatField(name);
parseNumberField(builder, name, floatNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = floatNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeFloatValue(propNode));
}
}
return builder;
}
}
public static class JsonDoubleTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode doubleNode = (ObjectNode) node;
JsonDoubleFieldMapper.Builder builder = doubleField(name);
parseNumberField(builder, name, doubleNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = doubleNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeDoubleValue(propNode));
}
}
return builder;
}
}
public static class JsonStringTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode stringNode = (ObjectNode) node;
JsonStringFieldMapper.Builder builder = stringField(name);
parseJsonField(builder, name, stringNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = stringNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(propNode.getValueAsText());
}
}
return builder;
}
}
public static class JsonBinaryTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode binaryNode = (ObjectNode) node;
JsonBinaryFieldMapper.Builder builder = binaryField(name);
parseJsonField(builder, name, binaryNode, parserContext);
return builder;
}
}
public static class JsonBooleanTypeParser implements JsonTypeParser {
@Override public JsonMapper.Builder parse(String name, JsonNode node, ParserContext parserContext) throws MapperParsingException {
ObjectNode booleanNode = (ObjectNode) node;
JsonBooleanFieldMapper.Builder builder = booleanField(name);
parseJsonField(builder, name, booleanNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = booleanNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("nullValue")) {
builder.nullValue(nodeBooleanValue(propNode));
}
}
return builder;
}
}
public static void parseNumberField(JsonNumberFieldMapper.Builder builder, String name, ObjectNode numberNode, JsonTypeParser.ParserContext parserContext) {
parseJsonField(builder, name, numberNode, parserContext);
for (Iterator<Map.Entry<String, JsonNode>> propsIt = numberNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("precisionStep")) {
builder.precisionStep(nodeIntegerValue(propNode));
}
}
}
public static void parseJsonField(JsonFieldMapper.Builder builder, String name, ObjectNode fieldNode, JsonTypeParser.ParserContext parserContext) {
for (Iterator<Map.Entry<String, JsonNode>> propsIt = fieldNode.getFields(); propsIt.hasNext();) {
Map.Entry<String, JsonNode> entry = propsIt.next();
String propName = entry.getKey();
JsonNode propNode = entry.getValue();
if (propName.equals("indexName")) {
builder.indexName(propNode.getTextValue());
} else if (propName.equals("store")) {
builder.store(parseStore(name, propNode.getTextValue()));
} else if (propName.equals("index")) {
builder.index(parseIndex(name, propNode.getTextValue()));
} else if (propName.equals("termVector")) {
builder.termVector(parseTermVector(name, propNode.getTextValue()));
} else if (propName.equals("boost")) {
builder.boost(nodeFloatValue(propNode));
} else if (propName.equals("omitNorms")) {
builder.omitNorms(nodeBooleanValue(propNode));
} else if (propName.equals("omitTermFreqAndPositions")) {
builder.omitTermFreqAndPositions(nodeBooleanValue(propNode));
} else if (propName.equals("indexAnalyzer")) {
builder.indexAnalyzer(parserContext.analysisService().analyzer(propNode.getTextValue()));
} else if (propName.equals("searchAnalyzer")) {
builder.searchAnalyzer(parserContext.analysisService().analyzer(propNode.getTextValue()));
} else if (propName.equals("analyzer")) {
builder.indexAnalyzer(parserContext.analysisService().analyzer(propNode.getTextValue()));
builder.searchAnalyzer(parserContext.analysisService().analyzer(propNode.getTextValue()));
} else if (propName.equals("includeInAll")) {
builder.includeInAll(nodeBooleanValue(propNode));
}
}
}
public static FormatDateTimeFormatter parseDateTimeFormatter(String fieldName, JsonNode node) {
if (node.isTextual()) {
return Joda.forPattern(node.getTextValue());
} else {
// TODO support more complex configuration...
throw new MapperParsingException("Wrong node to use to parse date formatters [" + fieldName + "]");
}
}
public static Field.TermVector parseTermVector(String fieldName, String termVector) throws MapperParsingException {
if ("no".equals(termVector)) {
return Field.TermVector.NO;
} else if ("yes".equals(termVector)) {
return Field.TermVector.YES;
} else if ("with_offsets".equals(termVector)) {
return Field.TermVector.WITH_OFFSETS;
} else if ("with_positions".equals(termVector)) {
return Field.TermVector.WITH_POSITIONS;
} else if ("with_positions_offsets".equals(termVector)) {
return Field.TermVector.WITH_POSITIONS_OFFSETS;
} else {
throw new MapperParsingException("Wrong value for termVector [" + termVector + "] for field [" + fieldName + "]");
}
}
public static Field.Index parseIndex(String fieldName, String index) throws MapperParsingException {
if ("no".equals(index)) {
return Field.Index.NO;
} else if ("not_analyzed".equals(index)) {
return Field.Index.NOT_ANALYZED;
} else if ("analyzed".equals(index)) {
return Field.Index.ANALYZED;
} else {
throw new MapperParsingException("Wrong value for index [" + index + "] for field [" + fieldName + "]");
}
}
public static Field.Store parseStore(String fieldName, String store) throws MapperParsingException {
if ("no".equals(store)) {
return Field.Store.NO;
} else if ("yes".equals(store)) {
return Field.Store.YES;
} else {
throw new MapperParsingException("Wrong value for store [" + store + "] for field [" + fieldName + "]");
}
}
public static JsonPath.Type parsePathType(String name, String path) throws MapperParsingException {
if ("justName".equals(path) || "just_name".equals(path)) {
return JsonPath.Type.JUST_NAME;
} else if ("full".equals(path)) {
return JsonPath.Type.FULL;
} else {
throw new MapperParsingException("Wrong value for pathType [" + path + "] for objet [" + name + "]");
}
}
}

View File

@ -12,9 +12,9 @@
"object1" : {
properties : {
"multi1" : {
type : "multi-field",
type : "multi_field",
"fields" : {
"number" : {type: "int"},
"number" : {type: "integer"},
"string" : {type: "string", index : "not_analyzed"}
}
}