diff --git a/docs/reference/migration/migrate_2_0.asciidoc b/docs/reference/migration/migrate_2_0.asciidoc index 0e3c582ba2b..6a12ac3bbbc 100644 --- a/docs/reference/migration/migrate_2_0.asciidoc +++ b/docs/reference/migration/migrate_2_0.asciidoc @@ -647,3 +647,37 @@ created on or after clusters with version 2.0: * The `type` option on the `_parent` field can only point to a parent type that doesn't exist yet, so this means that an existing type/mapping can no longer become a parent type. * The `has_child` and `has_parent` queries can no longer be use in alias filters. + +=== Meta fields returned under the top-level json object + +When selecting meta fields such as `_routing` or `_timestamp`, the field values +are now directly put as a top-level property of the json objet, instead of being +put under `fields` like regular stored fields. + +[source,sh] +--------------- +curl -XGET 'localhost:9200/test/_search?fields=_timestamp,foo' +--------------- + +[source,json] +--------------- +{ + [...] + "hits": { + "total": 1, + "max_score": 1, + "hits": [ + { + "_index": "test", + "_type": "test", + "_id": "1", + "_score": 1, + "_timestamp": 10000000, + "fields": { + "foo" : [ "bar" ] + } + } + ] + } +} +--------------- diff --git a/rest-api-spec/test/README.asciidoc b/rest-api-spec/test/README.asciidoc index 2f2d50b9fe5..e19b1ebbee9 100644 --- a/rest-api-spec/test/README.asciidoc +++ b/rest-api-spec/test/README.asciidoc @@ -203,7 +203,7 @@ The specified key exists and has a true value (ie not `0`, `false`, `undefined`, or the empty string), eg: .... - - is_true: fields._parent # the _parent key exists in the fields hash and is "true" + - is_true: fields.foo # the foo key exists in the fields hash and is "true" .... === `is_false` @@ -238,7 +238,7 @@ Supports also regular expressions with flag X for more readability (accepts whit Compares two numeric values, eg: .... - - lt: { fields._ttl: 10000 } # the `_ttl` value is less than 10,000 + - lt: { _ttl: 10000 } # the `_ttl` value is less than 10,000 .... === `lte` and `gte` @@ -246,7 +246,7 @@ Compares two numeric values, eg: Compares two numeric values, eg: .... - - lte: { fields._ttl: 10000 } # the `_ttl` value is less than or equal to 10,000 + - lte: { _ttl: 10000 } # the `_ttl` value is less than or equal to 10,000 .... === `length` diff --git a/rest-api-spec/test/create/40_routing.yaml b/rest-api-spec/test/create/40_routing.yaml index e39cbd1287d..02f011d75b0 100644 --- a/rest-api-spec/test/create/40_routing.yaml +++ b/rest-api-spec/test/create/40_routing.yaml @@ -31,7 +31,7 @@ fields: [_routing] - match: { _id: "1"} - - match: { fields._routing: "5"} + - match: { _routing: "5"} - do: catch: missing diff --git a/rest-api-spec/test/create/50_parent.yaml b/rest-api-spec/test/create/50_parent.yaml index 6fe64b7bbed..44f1ed880f4 100644 --- a/rest-api-spec/test/create/50_parent.yaml +++ b/rest-api-spec/test/create/50_parent.yaml @@ -38,6 +38,6 @@ fields: [_parent, _routing] - match: { _id: "1"} - - match: { fields._parent: "5"} - - match: { fields._routing: "5"} + - match: { _parent: "5"} + - match: { _routing: "5"} diff --git a/rest-api-spec/test/create/55_parent_with_routing.yaml b/rest-api-spec/test/create/55_parent_with_routing.yaml index 8ce045a64b0..55b840526e5 100644 --- a/rest-api-spec/test/create/55_parent_with_routing.yaml +++ b/rest-api-spec/test/create/55_parent_with_routing.yaml @@ -35,8 +35,8 @@ fields: [_parent, _routing] - match: { _id: "1"} - - match: { fields._parent: "5"} - - match: { fields._routing: "4"} + - match: { _parent: "5"} + - match: { _routing: "4"} - do: catch: missing diff --git a/rest-api-spec/test/create/70_timestamp.yaml b/rest-api-spec/test/create/70_timestamp.yaml index b0cbc80bb84..83173817b6e 100644 --- a/rest-api-spec/test/create/70_timestamp.yaml +++ b/rest-api-spec/test/create/70_timestamp.yaml @@ -28,7 +28,7 @@ id: 1 fields: _timestamp - - is_true: fields._timestamp + - is_true: _timestamp # milliseconds since epoch @@ -52,7 +52,7 @@ id: 1 fields: _timestamp - - match: { fields._timestamp: 1372011280000 } + - match: { _timestamp: 1372011280000 } # date format @@ -76,5 +76,5 @@ id: 1 fields: _timestamp - - match: { fields._timestamp: 1372011280000 } + - match: { _timestamp: 1372011280000 } diff --git a/rest-api-spec/test/create/75_ttl.yaml b/rest-api-spec/test/create/75_ttl.yaml index 26561c22155..3f9b4763669 100644 --- a/rest-api-spec/test/create/75_ttl.yaml +++ b/rest-api-spec/test/create/75_ttl.yaml @@ -29,8 +29,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 10000} - - gt: { fields._ttl: 0} + - lte: { _ttl: 10000} + - gt: { _ttl: 0} # milliseconds @@ -53,8 +53,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 100000} - - gt: { fields._ttl: 10000} + - lte: { _ttl: 100000} + - gt: { _ttl: 10000} # duration @@ -78,8 +78,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 20000} - - gt: { fields._ttl: 10000} + - lte: { _ttl: 20000} + - gt: { _ttl: 10000} # with timestamp diff --git a/rest-api-spec/test/get/30_parent.yaml b/rest-api-spec/test/get/30_parent.yaml index f7f31d08a2c..b9a0f46f900 100644 --- a/rest-api-spec/test/get/30_parent.yaml +++ b/rest-api-spec/test/get/30_parent.yaml @@ -31,8 +31,8 @@ setup: fields: [_parent, _routing] - match: { _id: "1"} - - match: { fields._parent: 中文 } - - match: { fields._routing: 中文} + - match: { _parent: 中文 } + - match: { _routing: 中文} --- "Parent omitted": diff --git a/rest-api-spec/test/get/40_routing.yaml b/rest-api-spec/test/get/40_routing.yaml index f464e662c3a..11fe04884b3 100644 --- a/rest-api-spec/test/get/40_routing.yaml +++ b/rest-api-spec/test/get/40_routing.yaml @@ -31,7 +31,7 @@ fields: [_routing] - match: { _id: "1"} - - match: { fields._routing: "5"} + - match: { _routing: "5"} - do: catch: missing diff --git a/rest-api-spec/test/get/55_parent_with_routing.yaml b/rest-api-spec/test/get/55_parent_with_routing.yaml index c65a2b12198..a7ed2df0dd7 100644 --- a/rest-api-spec/test/get/55_parent_with_routing.yaml +++ b/rest-api-spec/test/get/55_parent_with_routing.yaml @@ -35,8 +35,8 @@ fields: [_parent, _routing] - match: { _id: "1"} - - match: { fields._parent: "5"} - - match: { fields._routing: "4"} + - match: { _parent: "5"} + - match: { _routing: "4"} - do: catch: missing diff --git a/rest-api-spec/test/index/40_routing.yaml b/rest-api-spec/test/index/40_routing.yaml index f464e662c3a..bb248a458ae 100644 --- a/rest-api-spec/test/index/40_routing.yaml +++ b/rest-api-spec/test/index/40_routing.yaml @@ -30,8 +30,8 @@ routing: 5 fields: [_routing] - - match: { _id: "1"} - - match: { fields._routing: "5"} + - match: { _id: "1"} + - match: { _routing: "5"} - do: catch: missing diff --git a/rest-api-spec/test/index/50_parent.yaml b/rest-api-spec/test/index/50_parent.yaml index 28ab61cb49b..59e166c99a2 100644 --- a/rest-api-spec/test/index/50_parent.yaml +++ b/rest-api-spec/test/index/50_parent.yaml @@ -37,6 +37,6 @@ fields: [_parent, _routing] - match: { _id: "1"} - - match: { fields._parent: "5"} - - match: { fields._routing: "5"} + - match: { _parent: "5"} + - match: { _routing: "5"} diff --git a/rest-api-spec/test/index/55_parent_with_routing.yaml b/rest-api-spec/test/index/55_parent_with_routing.yaml index c65a2b12198..a7ed2df0dd7 100644 --- a/rest-api-spec/test/index/55_parent_with_routing.yaml +++ b/rest-api-spec/test/index/55_parent_with_routing.yaml @@ -35,8 +35,8 @@ fields: [_parent, _routing] - match: { _id: "1"} - - match: { fields._parent: "5"} - - match: { fields._routing: "4"} + - match: { _parent: "5"} + - match: { _routing: "4"} - do: catch: missing diff --git a/rest-api-spec/test/index/70_timestamp.yaml b/rest-api-spec/test/index/70_timestamp.yaml index 9e94795b2c1..863df9ebfa8 100644 --- a/rest-api-spec/test/index/70_timestamp.yaml +++ b/rest-api-spec/test/index/70_timestamp.yaml @@ -29,7 +29,7 @@ id: 1 fields: _timestamp - - is_true: fields._timestamp + - is_true: _timestamp # milliseconds since epoch @@ -48,7 +48,7 @@ id: 1 fields: _timestamp - - match: { fields._timestamp: 1372011280000 } + - match: { _timestamp: 1372011280000 } # date format @@ -67,5 +67,5 @@ id: 1 fields: _timestamp - - match: { fields._timestamp: 1372011280000 } + - match: { _timestamp: 1372011280000 } diff --git a/rest-api-spec/test/index/75_ttl.yaml b/rest-api-spec/test/index/75_ttl.yaml index 6d55e307e19..2ec00eaf2f4 100644 --- a/rest-api-spec/test/index/75_ttl.yaml +++ b/rest-api-spec/test/index/75_ttl.yaml @@ -29,8 +29,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 10000} - - gt: { fields._ttl: 0} + - lte: { _ttl: 10000} + - gt: { _ttl: 0} # milliseconds @@ -48,8 +48,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 100000} - - gt: { fields._ttl: 10000} + - lte: { _ttl: 100000} + - gt: { _ttl: 10000} # duration @@ -68,8 +68,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 20000} - - gt: { fields._ttl: 10000} + - lte: { _ttl: 20000} + - gt: { _ttl: 10000} # with timestamp diff --git a/rest-api-spec/test/mget/30_parent.yaml b/rest-api-spec/test/mget/30_parent.yaml index 9f296274459..c9a8b68176f 100644 --- a/rest-api-spec/test/mget/30_parent.yaml +++ b/rest-api-spec/test/mget/30_parent.yaml @@ -45,15 +45,15 @@ - is_false: docs.1.found - is_true: docs.2.found - - match: { docs.2._index: test_1 } - - match: { docs.2._type: test } - - match: { docs.2._id: "1" } - - match: { docs.2.fields._parent: "4" } - - match: { docs.2.fields._routing: "4" } + - match: { docs.2._index: test_1 } + - match: { docs.2._type: test } + - match: { docs.2._id: "1" } + - match: { docs.2._parent: "4" } + - match: { docs.2._routing: "4" } - is_true: docs.3.found - - match: { docs.3._index: test_1 } - - match: { docs.3._type: test } - - match: { docs.3._id: "2" } - - match: { docs.3.fields._parent: "5" } - - match: { docs.3.fields._routing: "5" } + - match: { docs.3._index: test_1 } + - match: { docs.3._type: test } + - match: { docs.3._id: "2" } + - match: { docs.3._parent: "5" } + - match: { docs.3._routing: "5" } diff --git a/rest-api-spec/test/mget/40_routing.yaml b/rest-api-spec/test/mget/40_routing.yaml index 6c1884cf5d3..71ac0feabbe 100644 --- a/rest-api-spec/test/mget/40_routing.yaml +++ b/rest-api-spec/test/mget/40_routing.yaml @@ -37,7 +37,7 @@ - is_false: docs.1.found - is_true: docs.2.found - - match: { docs.2._index: test_1 } - - match: { docs.2._type: test } - - match: { docs.2._id: "1" } - - match: { docs.2.fields._routing: "5" } + - match: { docs.2._index: test_1 } + - match: { docs.2._type: test } + - match: { docs.2._id: "1" } + - match: { docs.2._routing: "5" } diff --git a/rest-api-spec/test/mget/55_parent_with_routing.yaml b/rest-api-spec/test/mget/55_parent_with_routing.yaml index 89e99d4c881..19b597675cf 100644 --- a/rest-api-spec/test/mget/55_parent_with_routing.yaml +++ b/rest-api-spec/test/mget/55_parent_with_routing.yaml @@ -40,8 +40,8 @@ - is_false: docs.1.found - is_true: docs.2.found - - match: { docs.2._index: test_1 } - - match: { docs.2._type: test } - - match: { docs.2._id: "1" } - - match: { docs.2.fields._parent: "4" } - - match: { docs.2.fields._routing: "5" } + - match: { docs.2._index: test_1 } + - match: { docs.2._type: test } + - match: { docs.2._id: "1" } + - match: { docs.2._parent: "4" } + - match: { docs.2._routing: "5" } diff --git a/rest-api-spec/test/update/40_routing.yaml b/rest-api-spec/test/update/40_routing.yaml index 4b03a538b92..e96c175489e 100644 --- a/rest-api-spec/test/update/40_routing.yaml +++ b/rest-api-spec/test/update/40_routing.yaml @@ -32,7 +32,7 @@ routing: 5 fields: _routing - - match: { fields._routing: "5"} + - match: { _routing: "5"} - do: catch: missing diff --git a/rest-api-spec/test/update/50_parent.yaml b/rest-api-spec/test/update/50_parent.yaml index bc64665e919..15adf26596c 100644 --- a/rest-api-spec/test/update/50_parent.yaml +++ b/rest-api-spec/test/update/50_parent.yaml @@ -42,8 +42,8 @@ setup: parent: 5 fields: [_parent, _routing] - - match: { fields._parent: "5"} - - match: { fields._routing: "5"} + - match: { _parent: "5"} + - match: { _routing: "5"} - do: update: diff --git a/rest-api-spec/test/update/55_parent_with_routing.yaml b/rest-api-spec/test/update/55_parent_with_routing.yaml index 51dd91af3ba..89dc83198c6 100644 --- a/rest-api-spec/test/update/55_parent_with_routing.yaml +++ b/rest-api-spec/test/update/55_parent_with_routing.yaml @@ -36,8 +36,8 @@ parent: 5 fields: [_parent, _routing] - - match: { fields._parent: "5"} - - match: { fields._routing: "4"} + - match: { _parent: "5"} + - match: { _routing: "4"} - do: catch: missing diff --git a/rest-api-spec/test/update/70_timestamp.yaml b/rest-api-spec/test/update/70_timestamp.yaml index 21cd297b4c4..864c219c448 100644 --- a/rest-api-spec/test/update/70_timestamp.yaml +++ b/rest-api-spec/test/update/70_timestamp.yaml @@ -30,7 +30,7 @@ id: 1 fields: _timestamp - - is_true: fields._timestamp + - is_true: _timestamp # milliseconds since epoch @@ -51,7 +51,7 @@ id: 1 fields: _timestamp - - match: { fields._timestamp: 1372011280000 } + - match: { _timestamp: 1372011280000 } # date format @@ -72,5 +72,5 @@ id: 1 fields: _timestamp - - match: { fields._timestamp: 1372011280000 } + - match: { _timestamp: 1372011280000 } diff --git a/rest-api-spec/test/update/75_ttl.yaml b/rest-api-spec/test/update/75_ttl.yaml index 92c4a5049a4..cdd5d7e1d0f 100644 --- a/rest-api-spec/test/update/75_ttl.yaml +++ b/rest-api-spec/test/update/75_ttl.yaml @@ -31,8 +31,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 10000} - - gt: { fields._ttl: 0} + - lte: { _ttl: 10000} + - gt: { _ttl: 0} # milliseconds @@ -53,8 +53,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 100000} - - gt: { fields._ttl: 10000} + - lte: { _ttl: 100000} + - gt: { _ttl: 10000} # duration @@ -75,8 +75,8 @@ id: 1 fields: _ttl - - lte: { fields._ttl: 20000} - - gt: { fields._ttl: 10000} + - lte: { _ttl: 20000} + - gt: { _ttl: 10000} # with timestamp diff --git a/rest-api-spec/test/update/85_fields_meta.yaml b/rest-api-spec/test/update/85_fields_meta.yaml index d10ac83f46b..985048ad44d 100644 --- a/rest-api-spec/test/update/85_fields_meta.yaml +++ b/rest-api-spec/test/update/85_fields_meta.yaml @@ -33,10 +33,10 @@ doc: { foo: baz } upsert: { foo: bar } - - match: { get.fields._parent: "5" } - - match: { get.fields._routing: "5" } - - is_true: get.fields._timestamp - - is_true: get.fields._ttl + - match: { get._parent: "5" } + - match: { get._routing: "5" } + - is_true: get._timestamp + - is_true: get._ttl - do: get: diff --git a/src/main/java/org/elasticsearch/index/get/GetResult.java b/src/main/java/org/elasticsearch/index/get/GetResult.java index ad3fc6ab646..cbfb485816e 100644 --- a/src/main/java/org/elasticsearch/index/get/GetResult.java +++ b/src/main/java/org/elasticsearch/index/get/GetResult.java @@ -20,6 +20,7 @@ package org.elasticsearch.index.get; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.compress.CompressorFactory; @@ -34,6 +35,7 @@ import org.elasticsearch.search.lookup.SourceLookup; import java.io.IOException; import java.util.Iterator; +import java.util.List; import java.util.Map; import static com.google.common.collect.Iterators.emptyIterator; @@ -206,28 +208,39 @@ public class GetResult implements Streamable, Iterable, ToXContent { } public XContentBuilder toXContentEmbedded(XContentBuilder builder, Params params) throws IOException { + List metaFields = Lists.newArrayList(); + List otherFields = Lists.newArrayList(); + if (fields != null && !fields.isEmpty()) { + for (GetField field : fields.values()) { + if (field.getValues().isEmpty()) { + continue; + } + if (field.isMetadataField()) { + metaFields.add(field); + } else { + otherFields.add(field); + } + } + } + + for (GetField field : metaFields) { + builder.field(field.getName(), field.getValue()); + } + builder.field(Fields.FOUND, exists); if (source != null) { XContentHelper.writeRawField("_source", source, builder, params); } - if (fields != null && !fields.isEmpty()) { + if (!otherFields.isEmpty()) { builder.startObject(Fields.FIELDS); - for (GetField field : fields.values()) { - if (field.getValues().isEmpty()) { - continue; - } - String fieldName = field.getName(); - if (field.isMetadataField()) { - builder.field(fieldName, field.getValue()); - } else { - builder.startArray(field.getName()); - for (Object value : field.getValues()) { - builder.value(value); - } - builder.endArray(); + for (GetField field : otherFields) { + builder.startArray(field.getName()); + for (Object value : field.getValues()) { + builder.value(value); } + builder.endArray(); } builder.endObject(); } diff --git a/src/main/java/org/elasticsearch/search/internal/InternalSearchHit.java b/src/main/java/org/elasticsearch/search/internal/InternalSearchHit.java index c09218f0e6b..3b475c5bd51 100644 --- a/src/main/java/org/elasticsearch/search/internal/InternalSearchHit.java +++ b/src/main/java/org/elasticsearch/search/internal/InternalSearchHit.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.internal; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import org.apache.lucene.search.Explanation; import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchParseException; @@ -48,6 +49,7 @@ import org.elasticsearch.search.lookup.SourceLookup; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import static org.elasticsearch.common.lucene.Lucene.readExplanation; @@ -430,6 +432,21 @@ public class InternalSearchHit implements SearchHit { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + List metaFields = Lists.newArrayList(); + List otherFields = Lists.newArrayList(); + if (fields != null && !fields.isEmpty()) { + for (SearchHitField field : fields.values()) { + if (field.values().isEmpty()) { + continue; + } + if (field.isMetadataField()) { + metaFields.add(field); + } else { + otherFields.add(field); + } + } + } + builder.startObject(); // For inner_hit hits shard is null and that is ok, because the parent search hit has all this information. // Even if this was included in the inner_hit hits this would be the same, so better leave it out. @@ -453,25 +470,20 @@ public class InternalSearchHit implements SearchHit { } else { builder.field(Fields._SCORE, score); } + for (SearchHitField field : metaFields) { + builder.field(field.name(), field.value()); + } if (source != null) { XContentHelper.writeRawField("_source", source, builder, params); } - if (fields != null && !fields.isEmpty()) { + if (!otherFields.isEmpty()) { builder.startObject(Fields.FIELDS); - for (SearchHitField field : fields.values()) { - if (field.values().isEmpty()) { - continue; - } - String fieldName = field.getName(); - if (field.isMetadataField()) { - builder.field(fieldName, field.value()); - } else { - builder.startArray(fieldName); - for (Object value : field.getValues()) { - builder.value(value); - } - builder.endArray(); + for (SearchHitField field : otherFields) { + builder.startArray(field.name()); + for (Object value : field.getValues()) { + builder.value(value); } + builder.endArray(); } builder.endObject(); }