add the ability to define meta _attributes for mapping, basically a place to store additional unstructured data on the mapping

This commit is contained in:
kimchy 2010-06-28 09:10:08 +03:00
parent 3770924300
commit cb9be9973b
7 changed files with 47 additions and 14 deletions
modules/elasticsearch/src
main/java/org/elasticsearch
test/java/org/elasticsearch/index/mapper/xcontent/simple

@ -226,7 +226,7 @@ public class MetaDataService extends AbstractComponent {
.initializeEmpty(newMetaData.index(index)); .initializeEmpty(newMetaData.index(index));
routingTableBuilder.add(indexRoutingBuilder); routingTableBuilder.add(indexRoutingBuilder);
logger.info("creating index [{}], cause [{}], shards [{}]/[{}], mappings {}", index, cause, indexMetaData.numberOfShards(), indexMetaData.numberOfReplicas(), fMappings.keySet()); logger.info("[{}] creating index, cause [{}], shards [{}]/[{}], mappings {}", index, cause, indexMetaData.numberOfShards(), indexMetaData.numberOfReplicas(), fMappings.keySet());
RoutingTable newRoutingTable = shardsRoutingStrategy.reroute(newClusterStateBuilder().state(currentState).routingTable(routingTableBuilder).metaData(newMetaData).build()); RoutingTable newRoutingTable = shardsRoutingStrategy.reroute(newClusterStateBuilder().state(currentState).routingTable(routingTableBuilder).metaData(newMetaData).build());
return newClusterStateBuilder().state(currentState).routingTable(newRoutingTable).metaData(newMetaData).build(); return newClusterStateBuilder().state(currentState).routingTable(newRoutingTable).metaData(newMetaData).build();
} }
@ -265,7 +265,7 @@ public class MetaDataService extends AbstractComponent {
throw new IndexMissingException(new Index(index)); throw new IndexMissingException(new Index(index));
} }
logger.info("deleting index [{}]", index); logger.info("[{}] deleting index", index);
final CountDownLatch latch = new CountDownLatch(clusterService.state().nodes().size()); final CountDownLatch latch = new CountDownLatch(clusterService.state().nodes().size());
NodeIndexDeletedAction.Listener listener = new NodeIndexDeletedAction.Listener() { NodeIndexDeletedAction.Listener listener = new NodeIndexDeletedAction.Listener() {
@ -320,9 +320,9 @@ public class MetaDataService extends AbstractComponent {
// build the updated mapping source // build the updated mapping source
final String updatedMappingSource = existingMapper.buildSource(); final String updatedMappingSource = existingMapper.buildSource();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("index [" + index + "]: Update mapping [" + type + "] (dynamic) with source [" + updatedMappingSource + "]"); logger.debug("[{}] update mapping [{}] (dynamic) with source [{}]", index, type, updatedMappingSource);
} else if (logger.isInfoEnabled()) { } else if (logger.isInfoEnabled()) {
logger.info("index [" + index + "]: Update mapping [" + type + "] (dynamic)"); logger.info("[{}] update mapping [{}] (dynamic)", index, type);
} }
// publish the new mapping // publish the new mapping
clusterService.submitStateUpdateTask("update-mapping [" + index + "][" + type + "]", new ClusterStateUpdateTask() { clusterService.submitStateUpdateTask("update-mapping [" + index + "][" + type + "]", new ClusterStateUpdateTask() {
@ -396,9 +396,9 @@ public class MetaDataService extends AbstractComponent {
} }
mappings.put(index, mapping); mappings.put(index, mapping);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("index [" + index + "]: Put mapping [" + mapping.v1() + "] with source [" + mapping.v2() + "]"); logger.debug("[{}] put_mapping [{}] with source [{}]", index, mapping.v1(), mapping.v2());
} else if (logger.isInfoEnabled()) { } else if (logger.isInfoEnabled()) {
logger.info("index [" + index + "]: Put mapping [" + mapping.v1() + "]"); logger.info("[{}] put_mapping [{}]", index, mapping.v1());
} }
} }

@ -21,6 +21,7 @@ package org.elasticsearch.index.mapper;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Fieldable; import org.apache.lucene.document.Fieldable;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.util.concurrent.ThreadSafe; import org.elasticsearch.common.util.concurrent.ThreadSafe;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -39,6 +40,11 @@ public interface DocumentMapper {
*/ */
String mappingSource(); String mappingSource();
/**
* Attributes of this type mappings.
*/
ImmutableMap<String, Object> attributes();
/** /**
* Generates the source of the mapper based on the current mappings. * Generates the source of the mapper based on the current mappings.
*/ */

@ -46,7 +46,7 @@ import java.net.URL;
import static org.elasticsearch.common.collect.MapBuilder.*; import static org.elasticsearch.common.collect.MapBuilder.*;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (shay.banon)
*/ */
@ThreadSafe @ThreadSafe
public class MapperService extends AbstractIndexComponent implements Iterable<DocumentMapper> { public class MapperService extends AbstractIndexComponent implements Iterable<DocumentMapper> {
@ -60,8 +60,6 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
private final URL dynamicMappingUrl; private final URL dynamicMappingUrl;
private final ClassLoader indexClassLoader;
private final String dynamicMappingSource; private final String dynamicMappingSource;
private volatile ImmutableMap<String, DocumentMapper> mappers = ImmutableMap.of(); private volatile ImmutableMap<String, DocumentMapper> mappers = ImmutableMap.of();
@ -87,7 +85,6 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
super(index, indexSettings); super(index, indexSettings);
this.documentParser = new XContentDocumentMapperParser(analysisService); this.documentParser = new XContentDocumentMapperParser(analysisService);
this.searchAnalyzer = new SmartIndexNameSearchAnalyzer(analysisService.defaultSearchAnalyzer()); this.searchAnalyzer = new SmartIndexNameSearchAnalyzer(analysisService.defaultSearchAnalyzer());
this.indexClassLoader = indexSettings.getClassLoader();
this.dynamic = componentSettings.getAsBoolean("dynamic", true); this.dynamic = componentSettings.getAsBoolean("dynamic", true);
String dynamicMappingLocation = componentSettings.get("dynamic_mapping_location"); String dynamicMappingLocation = componentSettings.get("dynamic_mapping_location");
@ -97,7 +94,7 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
dynamicMappingUrl = environment.resolveConfig("dynamic-mapping.json"); dynamicMappingUrl = environment.resolveConfig("dynamic-mapping.json");
} catch (FailedToResolveConfigException e) { } catch (FailedToResolveConfigException e) {
// not there, default to the built in one // not there, default to the built in one
dynamicMappingUrl = indexClassLoader.getResource("org/elasticsearch/index/mapper/xcontent/dynamic-mapping.json"); dynamicMappingUrl = indexSettings.getClassLoader().getResource("org/elasticsearch/index/mapper/xcontent/dynamic-mapping.json");
} }
} else { } else {
try { try {
@ -127,7 +124,7 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
} else { } else {
dynamicMappingSource = null; dynamicMappingSource = null;
} }
logger.debug("Using dynamic[{}] with location[{}] and source[{}]", new Object[]{dynamic, dynamicMappingLocation, dynamicMappingSource}); logger.debug("using dynamic[{}] with location[{}] and source[{}]", dynamic, dynamicMappingLocation, dynamicMappingSource);
} }
@Override public UnmodifiableIterator<DocumentMapper> iterator() { @Override public UnmodifiableIterator<DocumentMapper> iterator() {

@ -22,6 +22,7 @@ package org.elasticsearch.index.mapper.xcontent;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.elasticsearch.common.Preconditions; import org.elasticsearch.common.Preconditions;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.thread.ThreadLocals; import org.elasticsearch.common.thread.ThreadLocals;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
@ -64,12 +65,19 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
private String mappingSource; private String mappingSource;
private ImmutableMap<String, Object> attributes = ImmutableMap.of();
private XContentMapper.BuilderContext builderContext = new XContentMapper.BuilderContext(new ContentPath(1)); private XContentMapper.BuilderContext builderContext = new XContentMapper.BuilderContext(new ContentPath(1));
public Builder(XContentObjectMapper.Builder builder) { public Builder(XContentObjectMapper.Builder builder) {
this.rootObjectMapper = builder.build(builderContext); this.rootObjectMapper = builder.build(builderContext);
} }
public Builder attributes(ImmutableMap<String, Object> attributes) {
this.attributes = attributes;
return this;
}
public Builder sourceField(XContentSourceFieldMapper.Builder builder) { public Builder sourceField(XContentSourceFieldMapper.Builder builder) {
this.sourceFieldMapper = builder.build(builderContext); this.sourceFieldMapper = builder.build(builderContext);
return this; return this;
@ -125,7 +133,7 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
public XContentDocumentMapper build() { public XContentDocumentMapper build() {
Preconditions.checkNotNull(rootObjectMapper, "Mapper builder must have the root object mapper set"); Preconditions.checkNotNull(rootObjectMapper, "Mapper builder must have the root object mapper set");
return new XContentDocumentMapper(rootObjectMapper, uidFieldMapper, idFieldMapper, typeFieldMapper, return new XContentDocumentMapper(rootObjectMapper, attributes, uidFieldMapper, idFieldMapper, typeFieldMapper,
sourceFieldMapper, allFieldMapper, indexAnalyzer, searchAnalyzer, boostFieldMapper, mappingSource); sourceFieldMapper, allFieldMapper, indexAnalyzer, searchAnalyzer, boostFieldMapper, mappingSource);
} }
} }
@ -139,6 +147,8 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
private final String type; private final String type;
private volatile ImmutableMap<String, Object> attributes;
private volatile String mappingSource; private volatile String mappingSource;
private final XContentUidFieldMapper uidFieldMapper; private final XContentUidFieldMapper uidFieldMapper;
@ -166,6 +176,7 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
private final Object mutex = new Object(); private final Object mutex = new Object();
public XContentDocumentMapper(XContentObjectMapper rootObjectMapper, public XContentDocumentMapper(XContentObjectMapper rootObjectMapper,
ImmutableMap<String, Object> attributes,
XContentUidFieldMapper uidFieldMapper, XContentUidFieldMapper uidFieldMapper,
XContentIdFieldMapper idFieldMapper, XContentIdFieldMapper idFieldMapper,
XContentTypeFieldMapper typeFieldMapper, XContentTypeFieldMapper typeFieldMapper,
@ -175,6 +186,7 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
@Nullable XContentBoostFieldMapper boostFieldMapper, @Nullable XContentBoostFieldMapper boostFieldMapper,
@Nullable String mappingSource) { @Nullable String mappingSource) {
this.type = rootObjectMapper.name(); this.type = rootObjectMapper.name();
this.attributes = attributes;
this.mappingSource = mappingSource; this.mappingSource = mappingSource;
this.rootObjectMapper = rootObjectMapper; this.rootObjectMapper = rootObjectMapper;
this.uidFieldMapper = uidFieldMapper; this.uidFieldMapper = uidFieldMapper;
@ -220,6 +232,10 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
return this.type; return this.type;
} }
@Override public ImmutableMap<String, Object> attributes() {
return this.attributes;
}
@Override public String mappingSource() { @Override public String mappingSource() {
return this.mappingSource; return this.mappingSource;
} }
@ -367,7 +383,9 @@ public class XContentDocumentMapper implements DocumentMapper, ToXContent {
MergeContext mergeContext = new MergeContext(this, mergeFlags); MergeContext mergeContext = new MergeContext(this, mergeFlags);
rootObjectMapper.merge(xContentMergeWith.rootObjectMapper, mergeContext); rootObjectMapper.merge(xContentMergeWith.rootObjectMapper, mergeContext);
if (!mergeFlags.simulate()) { if (!mergeFlags.simulate()) {
// update the source to the merged one // let the merge with attributes to override the attributes
attributes = mergeWith.attributes();
// update the source of the merged one
mappingSource = buildSource(); mappingSource = buildSource();
} }
return new MergeResult(mergeContext.buildConflicts()); return new MergeResult(mergeContext.buildConflicts());

@ -149,6 +149,12 @@ public class XContentDocumentMapperParser implements DocumentMapperParser {
docBuilder.searchAnalyzer(analysisService.defaultSearchAnalyzer()); docBuilder.searchAnalyzer(analysisService.defaultSearchAnalyzer());
} }
ImmutableMap<String, Object> attributes = ImmutableMap.of();
if (rootObj.containsKey("_attributes")) {
attributes = ImmutableMap.copyOf((Map<String, Object>) rootObj.get("_attributes"));
}
docBuilder.attributes(attributes);
docBuilder.mappingSource(source); docBuilder.mappingSource(source);
XContentDocumentMapper documentMapper = docBuilder.build(); XContentDocumentMapper documentMapper = docBuilder.build();

@ -78,6 +78,9 @@ public class SimpleXContentMapperTests {
@Test public void testSimpleParser() throws Exception { @Test public void testSimpleParser() throws Exception {
String mapping = copyToStringFromClasspath("/org/elasticsearch/index/mapper/xcontent/simple/test-mapping.json"); String mapping = copyToStringFromClasspath("/org/elasticsearch/index/mapper/xcontent/simple/test-mapping.json");
XContentDocumentMapper docMapper = (XContentDocumentMapper) new XContentDocumentMapperParser(new AnalysisService(new Index("test"))).parse(mapping); XContentDocumentMapper docMapper = (XContentDocumentMapper) new XContentDocumentMapperParser(new AnalysisService(new Index("test"))).parse(mapping);
assertThat((String) docMapper.attributes().get("param1"), equalTo("value1"));
byte[] json = copyToBytesFromClasspath("/org/elasticsearch/index/mapper/xcontent/simple/test1.json"); byte[] json = copyToBytesFromClasspath("/org/elasticsearch/index/mapper/xcontent/simple/test1.json");
Document doc = docMapper.parse(json).doc(); Document doc = docMapper.parse(json).doc();
assertThat(doc.get(docMapper.uidMapper().names().indexName()), equalTo(Uid.createUid("person", "1"))); assertThat(doc.get(docMapper.uidMapper().names().indexName()), equalTo(Uid.createUid("person", "1")));

@ -1,5 +1,8 @@
{ {
person : { person : {
"_attributes" : {
"param1" : "value1"
},
date_formats : ["yyyy-MM-dd", "dd-MM-yyyy"], date_formats : ["yyyy-MM-dd", "dd-MM-yyyy"],
dynamic : false, dynamic : false,
enabled : true, enabled : true,