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:
Adrien Grand 2014-10-17 12:35:20 +02:00
parent 5f4c6b04c8
commit 7c698146f5
26 changed files with 161 additions and 102 deletions

View File

@ -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" ]
}
}
]
}
}
---------------

View File

@ -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`

View File

@ -31,7 +31,7 @@
fields: [_routing]
- match: { _id: "1"}
- match: { fields._routing: "5"}
- match: { _routing: "5"}
- do:
catch: missing

View File

@ -38,6 +38,6 @@
fields: [_parent, _routing]
- match: { _id: "1"}
- match: { fields._parent: "5"}
- match: { fields._routing: "5"}
- match: { _parent: "5"}
- match: { _routing: "5"}

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -31,8 +31,8 @@ setup:
fields: [_parent, _routing]
- match: { _id: "1"}
- match: { fields._parent: 中文 }
- match: { fields._routing: 中文}
- match: { _parent: 中文 }
- match: { _routing: 中文}
---
"Parent omitted":

View File

@ -31,7 +31,7 @@
fields: [_routing]
- match: { _id: "1"}
- match: { fields._routing: "5"}
- match: { _routing: "5"}
- do:
catch: missing

View File

@ -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

View File

@ -30,8 +30,8 @@
routing: 5
fields: [_routing]
- match: { _id: "1"}
- match: { fields._routing: "5"}
- match: { _id: "1"}
- match: { _routing: "5"}
- do:
catch: missing

View File

@ -37,6 +37,6 @@
fields: [_parent, _routing]
- match: { _id: "1"}
- match: { fields._parent: "5"}
- match: { fields._routing: "5"}
- match: { _parent: "5"}
- match: { _routing: "5"}

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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" }

View File

@ -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" }

View File

@ -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" }

View File

@ -32,7 +32,7 @@
routing: 5
fields: _routing
- match: { fields._routing: "5"}
- match: { _routing: "5"}
- do:
catch: missing

View File

@ -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:

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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:

View File

@ -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();
}

View File

@ -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();
}