diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java b/modules/elasticsearch/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java index 6249bd54179..c30e7c4670a 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java @@ -35,7 +35,6 @@ import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.routing.GroupShardsIterator; import org.elasticsearch.cluster.routing.ShardIterator; -import org.elasticsearch.common.UUID; import org.elasticsearch.common.collect.Lists; import org.elasticsearch.common.collect.Maps; import org.elasticsearch.common.collect.Sets; @@ -139,14 +138,6 @@ public class TransportBulkAction extends BaseAction { for (ActionRequest request : bulkRequest.requests) { if (request instanceof IndexRequest) { IndexRequest indexRequest = (IndexRequest) request; - if (allowIdGeneration) { - if (indexRequest.id() == null) { - indexRequest.id(UUID.randomBase64UUID()); - // since we generate the id, change it to CREATE - indexRequest.opType(IndexRequest.OpType.CREATE); - } - } - String aliasOrIndex = indexRequest.index(); indexRequest.index(clusterState.metaData().concreteIndex(indexRequest.index())); @@ -154,7 +145,7 @@ public class TransportBulkAction extends BaseAction { if (metaData.hasIndex(indexRequest.index())) { mappingMd = metaData.index(indexRequest.index()).mapping(indexRequest.type()); } - indexRequest.process(metaData, aliasOrIndex, mappingMd); + indexRequest.process(metaData, aliasOrIndex, mappingMd, allowIdGeneration); } else if (request instanceof DeleteRequest) { DeleteRequest deleteRequest = (DeleteRequest) request; deleteRequest.index(clusterState.metaData().concreteIndex(deleteRequest.index())); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/IndexRequest.java b/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/IndexRequest.java index 91e712a7508..30bad4475d3 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/IndexRequest.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/IndexRequest.java @@ -34,6 +34,7 @@ import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Required; +import org.elasticsearch.common.UUID; import org.elasticsearch.common.Unicode; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -577,7 +578,7 @@ public class IndexRequest extends ShardReplicationOperationRequest { return this.percolate; } - public void process(MetaData metaData, String aliasOrIndex, @Nullable MappingMetaData mappingMd) throws ElasticSearchException { + public void process(MetaData metaData, String aliasOrIndex, @Nullable MappingMetaData mappingMd, boolean allowIdGeneration) throws ElasticSearchException { // resolve the routing if needed routing(metaData.resolveIndexRouting(routing, aliasOrIndex)); // resolve timestamp if provided externally @@ -587,13 +588,16 @@ public class IndexRequest extends ShardReplicationOperationRequest { } // extract values if needed if (mappingMd != null) { - MappingMetaData.ParseContext parseContext = mappingMd.createParseContext(routing, timestamp); + MappingMetaData.ParseContext parseContext = mappingMd.createParseContext(id, routing, timestamp); if (parseContext.shouldParse()) { XContentParser parser = null; try { parser = XContentFactory.xContent(source, sourceOffset, sourceLength).createParser(source, sourceOffset, sourceLength); mappingMd.parse(parser, parseContext); + if (parseContext.shouldParseId()) { + id = parseContext.id(); + } if (parseContext.shouldParseRouting()) { routing = parseContext.routing(); } @@ -609,11 +613,22 @@ public class IndexRequest extends ShardReplicationOperationRequest { } } } + // might as well check for routing here if (mappingMd.routing().required() && routing == null) { throw new RoutingMissingException(index, type, id); } } + + // generate id if not already provided and id generation is allowed + if (allowIdGeneration) { + if (id == null) { + id(UUID.randomBase64UUID()); + // since we generate the id, change it to CREATE + opType(IndexRequest.OpType.CREATE); + } + } + // generate timestamp if not provided, we always have one post this stage... if (timestamp == null) { timestamp = String.valueOf(System.currentTimeMillis()); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java b/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java index ecc2d81f6fa..731585d5b35 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java @@ -36,7 +36,6 @@ import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.UUID; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.engine.Engine; @@ -117,14 +116,6 @@ public class TransportIndexAction extends TransportShardReplicationOperationActi } private void innerExecute(final IndexRequest request, final ActionListener listener) { - if (allowIdGeneration) { - if (request.id() == null) { - request.id(UUID.randomBase64UUID()); - // since we generate the id, change it to CREATE - request.opType(IndexRequest.OpType.CREATE); - } - } - MetaData metaData = clusterService.state().metaData(); String aliasOrIndex = request.index(); request.index(metaData.concreteIndex(request.index())); @@ -132,7 +123,8 @@ public class TransportIndexAction extends TransportShardReplicationOperationActi if (metaData.hasIndex(request.index())) { mappingMd = metaData.index(request.index()).mapping(request.type()); } - request.process(metaData, aliasOrIndex, mappingMd); + request.process(metaData, aliasOrIndex, mappingMd, allowIdGeneration); + super.doExecute(request, listener); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MappingMetaData.java b/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MappingMetaData.java index ef5346528f4..3bf6a226fa0 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MappingMetaData.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/cluster/metadata/MappingMetaData.java @@ -42,6 +42,36 @@ import static org.elasticsearch.common.xcontent.support.XContentMapValues.*; */ public class MappingMetaData { + public static class Id { + + public static final Id EMPTY = new Id(null); + + private final String path; + + private final String[] pathElements; + + public Id(String path) { + this.path = path; + if (path == null) { + pathElements = Strings.EMPTY_ARRAY; + } else { + pathElements = Strings.delimitedListToStringArray(path, "."); + } + } + + public boolean hasPath() { + return path != null; + } + + public String path() { + return this.path; + } + + public String[] pathElements() { + return this.pathElements; + } + } + public static class Routing { public static final Routing EMPTY = new Routing(false, null); @@ -149,12 +179,14 @@ public class MappingMetaData { private final CompressedString source; + private final Id id; private final Routing routing; private final Timestamp timestamp; public MappingMetaData(DocumentMapper docMapper) { this.type = docMapper.type(); this.source = docMapper.mappingSource(); + this.id = new Id(docMapper.idFieldMapper().path()); this.routing = new Routing(docMapper.routingFieldMapper().required(), docMapper.routingFieldMapper().path()); this.timestamp = new Timestamp(docMapper.timestampFieldMapper().enabled(), docMapper.timestampFieldMapper().path(), docMapper.timestampFieldMapper().dateTimeFormatter().format()); } @@ -166,6 +198,20 @@ public class MappingMetaData { if (mapping.size() == 1 && mapping.containsKey(type)) { withoutType = (Map) mapping.get(type); } + if (withoutType.containsKey("_id")) { + String path = null; + Map routingNode = (Map) withoutType.get("_id"); + for (Map.Entry entry : routingNode.entrySet()) { + String fieldName = Strings.toUnderscoreCase(entry.getKey()); + Object fieldNode = entry.getValue(); + if (fieldName.equals("path")) { + path = fieldNode.toString(); + } + } + this.id = new Id(path); + } else { + this.id = Id.EMPTY; + } if (withoutType.containsKey("_routing")) { boolean required = false; String path = null; @@ -205,9 +251,10 @@ public class MappingMetaData { } } - MappingMetaData(String type, CompressedString source, Routing routing, Timestamp timestamp) { + MappingMetaData(String type, CompressedString source, Id id, Routing routing, Timestamp timestamp) { this.type = type; this.source = source; + this.id = id; this.routing = routing; this.timestamp = timestamp; } @@ -220,6 +267,10 @@ public class MappingMetaData { return this.source; } + public Id id() { + return this.id; + } + public Routing routing() { return this.routing; } @@ -228,8 +279,9 @@ public class MappingMetaData { return this.timestamp; } - public ParseContext createParseContext(@Nullable String routing, @Nullable String timestamp) { + public ParseContext createParseContext(@Nullable String id, @Nullable String routing, @Nullable String timestamp) { return new ParseContext( + id == null && id().hasPath(), routing == null && routing().hasPath(), timestamp == null && timestamp().hasPath() ); @@ -251,6 +303,7 @@ public class MappingMetaData { if (t == XContentParser.Token.START_OBJECT) { t = parser.nextToken(); } + String idPart = context.idParsingStillNeeded() ? id().pathElements()[context.locationId] : null; String routingPart = context.routingParsingStillNeeded() ? routing().pathElements()[context.locationRouting] : null; String timestampPart = context.timestampParsingStillNeeded() ? timestamp().pathElements()[context.locationTimestamp] : null; @@ -259,9 +312,17 @@ public class MappingMetaData { String fieldName = parser.currentName(); // And then the value... t = parser.nextToken(); - + boolean incLocationId = false; boolean incLocationRouting = false; boolean incLocationTimestamp = false; + if (context.idParsingStillNeeded() && fieldName.equals(idPart)) { + if (context.locationId + 1 == id.pathElements().length) { + context.id = parser.textOrNull(); + context.idResolved = true; + } else { + incLocationId = true; + } + } if (context.routingParsingStillNeeded() && fieldName.equals(routingPart)) { if (context.locationRouting + 1 == routing.pathElements().length) { context.routing = parser.textOrNull(); @@ -279,11 +340,13 @@ public class MappingMetaData { } } - if (incLocationRouting || incLocationTimestamp) { + if (incLocationId || incLocationRouting || incLocationTimestamp) { if (t == XContentParser.Token.START_OBJECT) { + context.locationId += incLocationId ? 1 : 0; context.locationRouting += incLocationRouting ? 1 : 0; context.locationTimestamp += incLocationTimestamp ? 1 : 0; innerParse(parser, context); + context.locationId -= incLocationId ? 1 : 0; context.locationRouting -= incLocationRouting ? 1 : 0; context.locationTimestamp -= incLocationTimestamp ? 1 : 0; } @@ -300,6 +363,13 @@ public class MappingMetaData { public static void writeTo(MappingMetaData mappingMd, StreamOutput out) throws IOException { out.writeUTF(mappingMd.type()); mappingMd.source().writeTo(out); + // id + if (mappingMd.id().hasPath()) { + out.writeBoolean(true); + out.writeUTF(mappingMd.id().path()); + } else { + out.writeBoolean(false); + } // routing out.writeBoolean(mappingMd.routing().required()); if (mappingMd.routing().hasPath()) { @@ -322,11 +392,13 @@ public class MappingMetaData { public static MappingMetaData readFrom(StreamInput in) throws IOException { String type = in.readUTF(); CompressedString source = CompressedString.readCompressedString(in); + // id + Id id = new Id(in.readBoolean() ? in.readUTF() : null); // routing Routing routing = new Routing(in.readBoolean(), in.readBoolean() ? in.readUTF() : null); // timestamp Timestamp timestamp = new Timestamp(in.readBoolean(), in.readBoolean() ? in.readUTF() : null, in.readUTF()); - return new MappingMetaData(type, source, routing, timestamp); + return new MappingMetaData(type, source, id, routing, timestamp); } public static class ParseResult { @@ -344,22 +416,54 @@ public class MappingMetaData { } public static class ParseContext { - + final boolean shouldParseId; final boolean shouldParseRouting; final boolean shouldParseTimestamp; + int locationId = 0; int locationRouting = 0; int locationTimestamp = 0; + boolean idResolved; boolean routingResolved; boolean timestampResolved; + String id; String routing; String timestamp; - public ParseContext(boolean shouldParseRouting, boolean shouldParseTimestamp) { + public ParseContext(boolean shouldParseId, boolean shouldParseRouting, boolean shouldParseTimestamp) { + this.shouldParseId = shouldParseId; this.shouldParseRouting = shouldParseRouting; this.shouldParseTimestamp = shouldParseTimestamp; } + /** + * The id value parsed, null if does not require parsing, or not resolved. + */ + public String id() { + return id; + } + + /** + * Does id parsing really needed at all? + */ + public boolean shouldParseId() { + return shouldParseId; + } + + /** + * Has id been resolved during the parsing phase. + */ + public boolean idResolved() { + return idResolved; + } + + /** + * Is id parsing still needed? + */ + public boolean idParsingStillNeeded() { + return shouldParseId && !idResolved; + } + /** * The routing value parsed, null if does not require parsing, or not resolved. */ @@ -420,14 +524,14 @@ public class MappingMetaData { * Do we really need parsing? */ public boolean shouldParse() { - return shouldParseRouting || shouldParseTimestamp; + return shouldParseId || shouldParseRouting || shouldParseTimestamp; } /** * Is parsing still needed? */ public boolean parsingStillNeeded() { - return routingParsingStillNeeded() || timestampParsingStillNeeded(); + return idParsingStillNeeded() || routingParsingStillNeeded() || timestampParsingStillNeeded(); } } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index 8b2e8f976c4..b7310991392 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -352,6 +352,10 @@ public class DocumentMapper implements ToXContent { return rootMapper(AllFieldMapper.class); } + public IdFieldMapper idFieldMapper() { + return rootMapper(IdFieldMapper.class); + } + public RoutingFieldMapper routingFieldMapper() { return rootMapper(RoutingFieldMapper.class); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java index 8ce748da436..1199aed7ccf 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/internal/IdFieldMapper.java @@ -22,6 +22,7 @@ package org.elasticsearch.index.mapper.internal; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.Fieldable; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; @@ -56,10 +57,13 @@ public class IdFieldMapper extends AbstractFieldMapper implements Intern public static final Field.Store STORE = Field.Store.NO; public static final boolean OMIT_NORMS = true; public static final boolean OMIT_TERM_FREQ_AND_POSITIONS = true; + public static final String PATH = null; } public static class Builder extends AbstractFieldMapper.Builder { + private String path = Defaults.PATH; + public Builder() { super(Defaults.NAME); indexName = Defaults.INDEX_NAME; @@ -69,8 +73,13 @@ public class IdFieldMapper extends AbstractFieldMapper implements Intern omitTermFreqAndPositions = Defaults.OMIT_TERM_FREQ_AND_POSITIONS; } + public Builder path(String path) { + this.path = path; + return builder; + } + @Override public IdFieldMapper build(BuilderContext context) { - return new IdFieldMapper(name, indexName, index, store, termVector, boost, omitNorms, omitTermFreqAndPositions); + return new IdFieldMapper(name, indexName, index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, path); } } @@ -78,10 +87,19 @@ public class IdFieldMapper extends AbstractFieldMapper implements Intern @Override public Mapper.Builder parse(String name, Map node, ParserContext parserContext) throws MapperParsingException { IdFieldMapper.Builder builder = id(); parseField(builder, builder.name, node, parserContext); + for (Map.Entry entry : node.entrySet()) { + String fieldName = Strings.toUnderscoreCase(entry.getKey()); + Object fieldNode = entry.getValue(); + if (fieldName.equals("path")) { + builder.path(fieldNode.toString()); + } + } return builder; } } + private final String path; + public IdFieldMapper() { this(Defaults.NAME, Defaults.INDEX_NAME, Defaults.INDEX); } @@ -92,13 +110,18 @@ public class IdFieldMapper extends AbstractFieldMapper implements Intern protected IdFieldMapper(String name, String indexName, Field.Index index) { this(name, indexName, index, Defaults.STORE, Defaults.TERM_VECTOR, Defaults.BOOST, - Defaults.OMIT_NORMS, Defaults.OMIT_TERM_FREQ_AND_POSITIONS); + Defaults.OMIT_NORMS, Defaults.OMIT_TERM_FREQ_AND_POSITIONS, Defaults.PATH); } protected IdFieldMapper(String name, String indexName, Field.Index index, Field.Store store, Field.TermVector termVector, - float boost, boolean omitNorms, boolean omitTermFreqAndPositions) { + float boost, boolean omitNorms, boolean omitTermFreqAndPositions, String path) { super(new Names(name, indexName, indexName, name), index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, Lucene.KEYWORD_ANALYZER, Lucene.KEYWORD_ANALYZER); + this.path = path; + } + + public String path() { + return this.path; } public String value(Document document) { diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/cluster/metadata/MappingMetaDataParserTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/cluster/metadata/MappingMetaDataParserTests.java index 097e189a2a8..f1028c57885 100644 --- a/modules/elasticsearch/src/test/java/org/elasticsearch/cluster/metadata/MappingMetaDataParserTests.java +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/cluster/metadata/MappingMetaDataParserTests.java @@ -30,14 +30,34 @@ import static org.hamcrest.Matchers.*; @Test public class MappingMetaDataParserTests { - @Test public void testParseRoutingAlone() throws Exception { + @Test public void testParseIdAlone() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("id"), new MappingMetaData.Routing(true, "routing"), new MappingMetaData.Timestamp(true, "timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") - .field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, "1"); + .field("id", "id").field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, "routing_value", "1"); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); + assertThat(parseContext.idResolved(), equalTo(true)); + assertThat(parseContext.routing(), nullValue()); + assertThat(parseContext.routingResolved(), equalTo(false)); + assertThat(parseContext.timestamp(), nullValue()); + assertThat(parseContext.timestampResolved(), equalTo(false)); + } + + @Test public void testParseRoutingAlone() throws Exception { + MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("id"), + new MappingMetaData.Routing(true, "routing"), + new MappingMetaData.Timestamp(true, "timestamp", "dateOptionalTime")); + byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") + .field("id", "id").field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); + MappingMetaData.ParseContext parseContext = md.createParseContext("id", null, "1"); + md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), nullValue()); + assertThat(parseContext.idResolved(), equalTo(false)); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.routingResolved(), equalTo(true)); assertThat(parseContext.timestamp(), nullValue()); @@ -46,54 +66,86 @@ public class MappingMetaDataParserTests { @Test public void testParseTimestampAlone() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("id"), new MappingMetaData.Routing(true, "routing"), new MappingMetaData.Timestamp(true, "timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") - .field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext("routing_value1", null); + .field("id", "id").field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); + MappingMetaData.ParseContext parseContext = md.createParseContext("id", "routing_value1", null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), nullValue()); + assertThat(parseContext.idResolved(), equalTo(false)); assertThat(parseContext.routing(), nullValue()); + assertThat(parseContext.routingResolved(), equalTo(false)); assertThat(parseContext.timestamp(), equalTo("1")); + assertThat(parseContext.timestampResolved(), equalTo(true)); } - @Test public void testParseRoutingAndTimestamp() throws Exception { + @Test public void testParseIdAndRoutingAndTimestamp() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("id"), new MappingMetaData.Routing(true, "routing"), new MappingMetaData.Timestamp(true, "timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") - .field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + .field("id", "id").field("routing", "routing_value").field("timestamp", "1").endObject().copiedBytes(); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.timestamp(), equalTo("1")); } - @Test public void testParseRoutingAndTimestampWithPath() throws Exception { + @Test public void testParseIdAndRoutingAndTimestampWithPath() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.id"), new MappingMetaData.Routing(true, "obj1.routing"), new MappingMetaData.Timestamp(true, "obj2.timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") .startObject("obj0").field("field1", "value1").field("field2", "value2").endObject() - .startObject("obj1").field("routing", "routing_value").endObject() + .startObject("obj1").field("id", "id").field("routing", "routing_value").endObject() .startObject("obj2").field("timestamp", "1").endObject() .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.timestamp(), equalTo("1")); } + @Test public void testParseIdWithPath() throws Exception { + MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.id"), + new MappingMetaData.Routing(true, "obj1.routing"), + new MappingMetaData.Timestamp(true, "obj2.timestamp", "dateOptionalTime")); + byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") + .startObject("obj0").field("field1", "value1").field("field2", "value2").endObject() + .startObject("obj1").field("id", "id").field("routing", "routing_value").endObject() + .startObject("obj2").field("timestamp", "1").endObject() + .endObject().copiedBytes(); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, "routing_value", "2"); + md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); + assertThat(parseContext.idResolved(), equalTo(true)); + assertThat(parseContext.routing(), nullValue()); + assertThat(parseContext.routingResolved(), equalTo(false)); + assertThat(parseContext.timestamp(), nullValue()); + assertThat(parseContext.timestampResolved(), equalTo(false)); + } + @Test public void testParseRoutingWithPath() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.id"), new MappingMetaData.Routing(true, "obj1.routing"), new MappingMetaData.Timestamp(true, "obj2.timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") .startObject("obj0").field("field1", "value1").field("field2", "value2").endObject() - .startObject("obj1").field("routing", "routing_value").endObject() + .startObject("obj1").field("id", "id").field("routing", "routing_value").endObject() .startObject("obj2").field("timestamp", "1").endObject() .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, "2"); + MappingMetaData.ParseContext parseContext = md.createParseContext("id", null, "2"); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), nullValue()); + assertThat(parseContext.idResolved(), equalTo(false)); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.routingResolved(), equalTo(true)); assertThat(parseContext.timestamp(), nullValue()); @@ -102,6 +154,7 @@ public class MappingMetaDataParserTests { @Test public void testParseTimestampWithPath() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.id"), new MappingMetaData.Routing(true, "obj1.routing"), new MappingMetaData.Timestamp(true, "obj2.timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") @@ -109,36 +162,44 @@ public class MappingMetaDataParserTests { .startObject("obj1").field("routing", "routing_value").endObject() .startObject("obj2").field("timestamp", "1").endObject() .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext("routing_value1", null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, "routing_value1", null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), nullValue()); + assertThat(parseContext.idResolved(), equalTo(false)); assertThat(parseContext.routing(), nullValue()); assertThat(parseContext.routingResolved(), equalTo(false)); assertThat(parseContext.timestamp(), equalTo("1")); assertThat(parseContext.timestampResolved(), equalTo(true)); } - @Test public void testParseRoutingAndTimestampWithinSamePath() throws Exception { + @Test public void testParseIdAndRoutingAndTimestampWithinSamePath() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.id"), new MappingMetaData.Routing(true, "obj1.routing"), new MappingMetaData.Timestamp(true, "obj1.timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") .startObject("obj0").field("field1", "value1").field("field2", "value2").endObject() - .startObject("obj1").field("routing", "routing_value").field("timestamp", "1").endObject() + .startObject("obj1").field("id", "id").field("routing", "routing_value").field("timestamp", "1").endObject() .startObject("obj2").field("field1", "value1").endObject() .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.timestamp(), equalTo("1")); } - @Test public void testParseRoutingAndTimestampWithinSamePathAndMoreLevels() throws Exception { + @Test public void testParseIdAndRoutingAndTimestampWithinSamePathAndMoreLevels() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.obj0.id"), new MappingMetaData.Routing(true, "obj1.obj2.routing"), new MappingMetaData.Timestamp(true, "obj1.obj3.timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") .startObject("obj0").field("field1", "value1").field("field2", "value2").endObject() .startObject("obj1") + .startObject("obj0") + .field("id", "id") + .endObject() .startObject("obj2") .field("routing", "routing_value") .endObject() @@ -148,31 +209,36 @@ public class MappingMetaDataParserTests { .endObject() .startObject("obj2").field("field1", "value1").endObject() .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.timestamp(), equalTo("1")); } - @Test public void testParseRoutingAndTimestampWithSameRepeatedObject() throws Exception { + @Test public void testParseIdAndRoutingAndTimestampWithSameRepeatedObject() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("obj1.id"), new MappingMetaData.Routing(true, "obj1.routing"), new MappingMetaData.Timestamp(true, "obj1.timestamp", "dateOptionalTime")); byte[] bytes = jsonBuilder().startObject().field("field1", "value1").field("field2", "value2") .startObject("obj0").field("field1", "value1").field("field2", "value2").endObject() + .startObject("obj1").field("id", "id").endObject() .startObject("obj1").field("routing", "routing_value").endObject() .startObject("obj1").field("timestamp", "1").endObject() .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("id")); assertThat(parseContext.routing(), equalTo("routing_value")); assertThat(parseContext.timestamp(), equalTo("1")); } // - @Test public void testParseRoutingTimestampWithRepeatedField() throws Exception { + @Test public void testParseIdRoutingTimestampWithRepeatedField() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("field1"), new MappingMetaData.Routing(true, "field1.field1"), new MappingMetaData.Timestamp(true, "field1", "dateOptionalTime")); @@ -185,14 +251,16 @@ public class MappingMetaDataParserTests { .field("zzz", "wr") .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), equalTo("foo")); assertThat(parseContext.routing(), nullValue()); assertThat(parseContext.timestamp(), equalTo("foo")); } - @Test public void testParseRoutingWithRepeatedFieldAndObject() throws Exception { + @Test public void testParseNoIdRoutingWithRepeatedFieldAndObject() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id("id"), new MappingMetaData.Routing(true, "field1.field1.field2"), new MappingMetaData.Timestamp(true, "field1", "dateOptionalTime")); @@ -205,14 +273,16 @@ public class MappingMetaDataParserTests { .field("zzz", "wr") .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), nullValue()); assertThat(parseContext.routing(), nullValue()); assertThat(parseContext.timestamp(), equalTo("foo")); } @Test public void testParseRoutingWithRepeatedFieldAndValidRouting() throws Exception { MappingMetaData md = new MappingMetaData("type1", new CompressedString(""), + new MappingMetaData.Id(null), new MappingMetaData.Routing(true, "field1.field2"), new MappingMetaData.Timestamp(true, "field1", "dateOptionalTime")); @@ -225,8 +295,9 @@ public class MappingMetaDataParserTests { .field("zzz", "wr") .endObject().copiedBytes(); - MappingMetaData.ParseContext parseContext = md.createParseContext(null, null); + MappingMetaData.ParseContext parseContext = md.createParseContext(null, null, null); md.parse(XContentFactory.xContent(bytes).createParser(bytes), parseContext); + assertThat(parseContext.id(), nullValue()); assertThat(parseContext.routing(), equalTo("bar")); assertThat(parseContext.timestamp(), equalTo("foo")); }