Rest: Add all meta fields to the top level json document.
Some of our meta fields (such as _id, _version, ...) are returned as top-level properties of the json document, while other properties (_timestamp, _routing, ...) are returned under `fields`. This commit makes all meta fields returned as top-level properties. So eg. `GET test/test/1?fields=_timestamp,foo` would now return ```json { "_index": "test", "_type": "test", "_id": "1", "_version": 1, "_timestamp": 10000000, "found": true, "fields": { "foo": [ "bar" ] } } ``` while it used to return ```json { "_index": "test", "_type": "test", "_id": "1", "_version": 1, "found": true, "fields": { "_timestamp": 10000000, "foo": [ "bar" ] } } ```
This commit is contained in:
parent
5f4c6b04c8
commit
7c698146f5
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
---------------
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
fields: [_routing]
|
||||
|
||||
- match: { _id: "1"}
|
||||
- match: { fields._routing: "5"}
|
||||
- match: { _routing: "5"}
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
|
|
|
@ -38,6 +38,6 @@
|
|||
fields: [_parent, _routing]
|
||||
|
||||
- match: { _id: "1"}
|
||||
- match: { fields._parent: "5"}
|
||||
- match: { fields._routing: "5"}
|
||||
- match: { _parent: "5"}
|
||||
- match: { _routing: "5"}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ setup:
|
|||
fields: [_parent, _routing]
|
||||
|
||||
- match: { _id: "1"}
|
||||
- match: { fields._parent: 中文 }
|
||||
- match: { fields._routing: 中文}
|
||||
- match: { _parent: 中文 }
|
||||
- match: { _routing: 中文}
|
||||
|
||||
---
|
||||
"Parent omitted":
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
fields: [_routing]
|
||||
|
||||
- match: { _id: "1"}
|
||||
- match: { fields._routing: "5"}
|
||||
- match: { _routing: "5"}
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
routing: 5
|
||||
fields: [_routing]
|
||||
|
||||
- match: { _id: "1"}
|
||||
- match: { fields._routing: "5"}
|
||||
- match: { _id: "1"}
|
||||
- match: { _routing: "5"}
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
|
|
|
@ -37,6 +37,6 @@
|
|||
fields: [_parent, _routing]
|
||||
|
||||
- match: { _id: "1"}
|
||||
- match: { fields._parent: "5"}
|
||||
- match: { fields._routing: "5"}
|
||||
- match: { _parent: "5"}
|
||||
- match: { _routing: "5"}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
routing: 5
|
||||
fields: _routing
|
||||
|
||||
- match: { fields._routing: "5"}
|
||||
- match: { _routing: "5"}
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<GetField>, ToXContent {
|
|||
}
|
||||
|
||||
public XContentBuilder toXContentEmbedded(XContentBuilder builder, Params params) throws IOException {
|
||||
List<GetField> metaFields = Lists.newArrayList();
|
||||
List<GetField> 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();
|
||||
}
|
||||
|
|
|
@ -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<SearchHitField> metaFields = Lists.newArrayList();
|
||||
List<SearchHitField> 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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue