The fields option should always return an array for json document fields and single valued field for metadata fields.

Also the `fields` option can only be used to fetch leaf fields, trying to do fetch object fields will return in a client error.

Closes #4542
This commit is contained in:
Martijn van Groningen 2014-01-02 23:38:08 +01:00
parent fdfc7d7460
commit f1bf585089
12 changed files with 314 additions and 46 deletions

View File

@ -115,6 +115,12 @@ For backward compatibility, if the requested fields are not stored, they will be
from the `_source` (parsed and extracted). This functionality has been replaced by the from the `_source` (parsed and extracted). This functionality has been replaced by the
<<get-source-filtering,source filtering>> parameter. <<get-source-filtering,source filtering>> parameter.
Field values fetched from the document it self are always returned as an array. Metadata fields like `_routing` and
`_parent` fields are never returned as an array.
Also only leaf fields can be returned via the `field` option. So object fields can't be returned and such requests
will fail.
[float] [float]
[[_source]] [[_source]]
=== Getting the _source directly === Getting the _source directly

View File

@ -34,9 +34,15 @@ For backwards compatibility, if the fields parameter specifies fields which are
`false`), it will load the `_source` and extract it from it. This functionality has been replaced by the `false`), it will load the `_source` and extract it from it. This functionality has been replaced by the
<<search-request-source-filtering,source filtering>> parameter. <<search-request-source-filtering,source filtering>> parameter.
Field values fetched from the document it self are always returned as an array. Metadata fields like `_routing` and
`_parent` fields are never returned as an array.
Also only leaf fields can be returned via the `field` option. So object fields can't be returned and such requests
will fail.
Script fields can also be automatically detected and used as fields, so Script fields can also be automatically detected and used as fields, so
things like `_source.obj1.obj2` can be used, though not recommended, as things like `_source.obj1.field1` can be used, though not recommended, as
`obj1.obj2` will work as well. `obj1.field1` will work as well.
[[partial]] [[partial]]
==== Partial ==== Partial

View File

@ -22,6 +22,7 @@ package org.elasticsearch.index.get;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.index.mapper.MapperService;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,11 +35,9 @@ import java.util.List;
public class GetField implements Streamable, Iterable<Object> { public class GetField implements Streamable, Iterable<Object> {
private String name; private String name;
private List<Object> values; private List<Object> values;
private GetField() { private GetField() {
} }
public GetField(String name, List<Object> values) { public GetField(String name, List<Object> values) {
@ -61,6 +60,10 @@ public class GetField implements Streamable, Iterable<Object> {
return values; return values;
} }
public boolean isMetadataField() {
return MapperService.isMetadataField(name);
}
@Override @Override
public Iterator<Object> iterator() { public Iterator<Object> iterator() {
return values.iterator(); return values.iterator();

View File

@ -219,11 +219,11 @@ public class GetResult implements Streamable, Iterable<GetField>, ToXContent {
if (field.getValues().isEmpty()) { if (field.getValues().isEmpty()) {
continue; continue;
} }
if (field.getValues().size() == 1) { String fieldName = field.getName();
builder.field(field.getName(), field.getValues().get(0)); if (field.isMetadataField()) {
builder.field(fieldName, field.getValue());
} else { } else {
builder.field(field.getName()); builder.startArray(field.getName());
builder.startArray();
for (Object value : field.getValues()) { for (Object value : field.getValues()) {
builder.value(value); builder.value(value);
} }

View File

@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.collect.Tuple;
@ -268,19 +269,18 @@ public class ShardGetService extends AbstractIndexShardComponent {
} }
FieldMapper<?> x = docMapper.mappers().smartNameFieldMapper(field); FieldMapper<?> x = docMapper.mappers().smartNameFieldMapper(field);
// only if the field is stored or source is enabled we should add it.. if (x == null) {
if (docMapper.sourceMapper().enabled() || x == null || x.fieldType().stored()) { if (docMapper.objectMappers().get(field) != null) {
value = searchLookup.source().extractValue(field); // Only fail if we know it is a object field, missing paths / fields shouldn't fail.
// normalize the data if needed (mainly for binary fields, to convert from base64 strings to bytes) throw new ElasticSearchIllegalArgumentException("field [" + field + "] isn't a leaf field");
if (value != null && x != null) { }
if (value instanceof List) { } else if (docMapper.sourceMapper().enabled() || x.fieldType().stored()) {
List list = (List) value; List<Object> values = searchLookup.source().extractRawValues(field);
for (int i = 0; i < list.size(); i++) { if (!values.isEmpty()) {
list.set(i, x.valueForSearch(list.get(i))); for (int i = 0; i < values.size(); i++) {
} values.set(i, x.valueForSearch(values.get(i)));
} else {
value = x.valueForSearch(value);
} }
value = values;
} }
} }
} }
@ -388,24 +388,25 @@ public class ShardGetService extends AbstractIndexShardComponent {
} }
} else { } else {
FieldMappers x = docMapper.mappers().smartName(field); FieldMappers x = docMapper.mappers().smartName(field);
if (x == null || !x.mapper().fieldType().stored()) { if (x == null) {
if (docMapper.objectMappers().get(field) != null) {
// Only fail if we know it is a object field, missing paths / fields shouldn't fail.
throw new ElasticSearchIllegalArgumentException("field [" + field + "] isn't a leaf field");
}
} else if (!x.mapper().fieldType().stored()) {
if (searchLookup == null) { if (searchLookup == null) {
searchLookup = new SearchLookup(mapperService, fieldDataService, new String[]{type}); searchLookup = new SearchLookup(mapperService, fieldDataService, new String[]{type});
searchLookup.setNextReader(docIdAndVersion.context); searchLookup.setNextReader(docIdAndVersion.context);
searchLookup.source().setNextSource(source); searchLookup.source().setNextSource(source);
searchLookup.setNextDocId(docIdAndVersion.docId); searchLookup.setNextDocId(docIdAndVersion.docId);
} }
value = searchLookup.source().extractValue(field);
// normalize the data if needed (mainly for binary fields, to convert from base64 strings to bytes) List<Object> values = searchLookup.source().extractRawValues(field);
if (value != null && x != null) { if (!values.isEmpty()) {
if (value instanceof List) { for (int i = 0; i < values.size(); i++) {
List list = (List) value; values.set(i, x.mapper().valueForSearch(values.get(i)));
for (int i = 0; i < list.size(); i++) {
list.set(i, x.mapper().valueForSearch(list.get(i)));
}
} else {
value = x.mapper().valueForSearch(value);
} }
value = values;
} }
} }
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.mapper; package org.elasticsearch.index.mapper;
import com.carrotsearch.hppc.ObjectOpenHashSet;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.*; import com.google.common.collect.*;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
@ -75,6 +76,10 @@ import static org.elasticsearch.index.mapper.DocumentMapper.MergeFlags.mergeFlag
public class MapperService extends AbstractIndexComponent implements Iterable<DocumentMapper> { public class MapperService extends AbstractIndexComponent implements Iterable<DocumentMapper> {
public static final String DEFAULT_MAPPING = "_default_"; public static final String DEFAULT_MAPPING = "_default_";
private static ObjectOpenHashSet<String> META_FIELDS = ObjectOpenHashSet.from(
"_uid", "_id", "_type", "_all", "_analyzer", "_boost", "_parent", "_routing", "_index",
"_size", "_timestamp", "_ttl"
);
private final AnalysisService analysisService; private final AnalysisService analysisService;
private final IndexFieldDataService fieldDataService; private final IndexFieldDataService fieldDataService;
@ -841,6 +846,13 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
return null; return null;
} }
/**
* @return Whether a field is a metadata field.
*/
public static boolean isMetadataField(String fieldName) {
return META_FIELDS.contains(fieldName);
}
public static class SmartNameObjectMapper { public static class SmartNameObjectMapper {
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final DocumentMapper docMapper; private final DocumentMapper docMapper;

View File

@ -59,4 +59,9 @@ public interface SearchHitField extends Streamable, Iterable<Object> {
* The field values. * The field values.
*/ */
List<Object> getValues(); List<Object> getValues();
/**
* @return The field is a metadata field
*/
boolean isMetadataField();
} }

View File

@ -22,6 +22,7 @@ package org.elasticsearch.search.fetch;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.index.ReaderUtil;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.text.StringAndBytesText; import org.elasticsearch.common.text.StringAndBytesText;
import org.elasticsearch.common.text.Text; import org.elasticsearch.common.text.Text;
@ -117,7 +118,12 @@ public class FetchPhase implements SearchPhase {
continue; continue;
} }
FieldMappers x = context.smartNameFieldMappers(fieldName); FieldMappers x = context.smartNameFieldMappers(fieldName);
if (x != null && x.mapper().fieldType().stored()) { if (x == null) {
// Only fail if we know it is a object field, missing paths / fields shouldn't fail.
if (context.smartNameObjectMapper(fieldName) != null) {
throw new ElasticSearchIllegalArgumentException("field [" + fieldName + "] isn't a leaf field");
}
} else if (x.mapper().fieldType().stored()) {
if (fieldNames == null) { if (fieldNames == null) {
fieldNames = new HashSet<String>(); fieldNames = new HashSet<String>();
} }
@ -180,8 +186,8 @@ public class FetchPhase implements SearchPhase {
} }
if (extractFieldNames != null) { if (extractFieldNames != null) {
for (String extractFieldName : extractFieldNames) { for (String extractFieldName : extractFieldNames) {
Object value = context.lookup().source().extractValue(extractFieldName); List<Object> values = context.lookup().source().extractRawValues(extractFieldName);
if (value != null) { if (!values.isEmpty()) {
if (searchHit.fieldsOrNull() == null) { if (searchHit.fieldsOrNull() == null) {
searchHit.fields(new HashMap<String, SearchHitField>(2)); searchHit.fields(new HashMap<String, SearchHitField>(2));
} }
@ -191,7 +197,9 @@ public class FetchPhase implements SearchPhase {
hitField = new InternalSearchHitField(extractFieldName, new ArrayList<Object>(2)); hitField = new InternalSearchHitField(extractFieldName, new ArrayList<Object>(2));
searchHit.fields().put(extractFieldName, hitField); searchHit.fields().put(extractFieldName, hitField);
} }
hitField.values().add(value); for (Object value : values) {
hitField.values().add(value);
}
} }
} }
} }

View File

@ -415,12 +415,12 @@ public class InternalSearchHit implements SearchHit {
if (field.values().isEmpty()) { if (field.values().isEmpty()) {
continue; continue;
} }
if (field.values().size() == 1) { String fieldName = field.getName();
builder.field(field.name(), field.values().get(0)); if (field.isMetadataField()) {
builder.field(fieldName, field.value());
} else { } else {
builder.field(field.name()); builder.startArray(fieldName);
builder.startArray(); for (Object value : field.getValues()) {
for (Object value : field.values()) {
builder.value(value); builder.value(value);
} }
builder.endArray(); builder.endArray();

View File

@ -21,6 +21,7 @@ package org.elasticsearch.search.internal;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.SearchHitField;
import java.io.IOException; import java.io.IOException;
@ -34,11 +35,9 @@ import java.util.List;
public class InternalSearchHitField implements SearchHitField { public class InternalSearchHitField implements SearchHitField {
private String name; private String name;
private List<Object> values; private List<Object> values;
private InternalSearchHitField() { private InternalSearchHitField() {
} }
public InternalSearchHitField(String name, List<Object> values) { public InternalSearchHitField(String name, List<Object> values) {
@ -77,6 +76,10 @@ public class InternalSearchHitField implements SearchHitField {
return values(); return values();
} }
@Override
public boolean isMetadataField() {
return MapperService.isMetadataField(name);
}
@Override @Override
public Iterator<Object> iterator() { public Iterator<Object> iterator() {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.get; package org.elasticsearch.get;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.delete.DeleteResponse;
@ -495,14 +496,14 @@ public class GetActionTests extends ElasticsearchIntegrationTest {
.endObject()) .endObject())
.execute().actionGet(); .execute().actionGet();
GetResponse responseBeforeFlush = client().prepareGet(index, type, "1").setFields("_source", "included", "excluded").execute().actionGet(); GetResponse responseBeforeFlush = client().prepareGet(index, type, "1").setFields("_source", "included.field", "excluded.field").execute().actionGet();
assertThat(responseBeforeFlush.isExists(), is(true)); assertThat(responseBeforeFlush.isExists(), is(true));
assertThat(responseBeforeFlush.getSourceAsMap(), not(hasKey("excluded"))); assertThat(responseBeforeFlush.getSourceAsMap(), not(hasKey("excluded")));
assertThat(responseBeforeFlush.getSourceAsMap(), not(hasKey("field"))); assertThat(responseBeforeFlush.getSourceAsMap(), not(hasKey("field")));
assertThat(responseBeforeFlush.getSourceAsMap(), hasKey("included")); assertThat(responseBeforeFlush.getSourceAsMap(), hasKey("included"));
// now tests that extra source filtering works as expected // now tests that extra source filtering works as expected
GetResponse responseBeforeFlushWithExtraFilters = client().prepareGet(index, type, "1").setFields("included", "excluded") GetResponse responseBeforeFlushWithExtraFilters = client().prepareGet(index, type, "1").setFields("included.field", "excluded.field")
.setFetchSource(new String[]{"field", "*.field"}, new String[]{"*.field2"}).get(); .setFetchSource(new String[]{"field", "*.field"}, new String[]{"*.field2"}).get();
assertThat(responseBeforeFlushWithExtraFilters.isExists(), is(true)); assertThat(responseBeforeFlushWithExtraFilters.isExists(), is(true));
assertThat(responseBeforeFlushWithExtraFilters.getSourceAsMap(), not(hasKey("excluded"))); assertThat(responseBeforeFlushWithExtraFilters.getSourceAsMap(), not(hasKey("excluded")));
@ -512,8 +513,8 @@ public class GetActionTests extends ElasticsearchIntegrationTest {
assertThat((Map<String, Object>) responseBeforeFlushWithExtraFilters.getSourceAsMap().get("included"), not(hasKey("field2"))); assertThat((Map<String, Object>) responseBeforeFlushWithExtraFilters.getSourceAsMap().get("included"), not(hasKey("field2")));
client().admin().indices().prepareFlush(index).execute().actionGet(); client().admin().indices().prepareFlush(index).execute().actionGet();
GetResponse responseAfterFlush = client().prepareGet(index, type, "1").setFields("_source", "included", "excluded").execute().actionGet(); GetResponse responseAfterFlush = client().prepareGet(index, type, "1").setFields("_source", "included.field", "excluded.field").execute().actionGet();
GetResponse responseAfterFlushWithExtraFilters = client().prepareGet(index, type, "1").setFields("included", "excluded") GetResponse responseAfterFlushWithExtraFilters = client().prepareGet(index, type, "1").setFields("included.field", "excluded.field")
.setFetchSource("*.field", "*.field2").get(); .setFetchSource("*.field", "*.field2").get();
assertThat(responseAfterFlush.isExists(), is(true)); assertThat(responseAfterFlush.isExists(), is(true));
@ -730,4 +731,135 @@ public class GetActionTests extends ElasticsearchIntegrationTest {
assertThat(response.getResponses()[2].getResponse().getSourceAsMap().get("field").toString(), equalTo("value2")); assertThat(response.getResponses()[2].getResponse().getSourceAsMap().get("field").toString(), equalTo("value2"));
} }
@Test
public void testGetFields_metaData() throws Exception {
client().admin().indices().prepareCreate("my-index")
.setSettings(ImmutableSettings.settingsBuilder().put("index.refresh_interval", -1))
.get();
client().prepareIndex("my-index", "my-type1", "1")
.setRouting("1")
.setSource(jsonBuilder().startObject().field("field1", "value").endObject())
.get();
GetResponse getResponse = client().prepareGet("my-index", "my-type1", "1")
.setRouting("1")
.setFields("field1", "_routing")
.get();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getField("field1").isMetadataField(), equalTo(false));
assertThat(getResponse.getField("field1").getValue().toString(), equalTo("value"));
assertThat(getResponse.getField("_routing").isMetadataField(), equalTo(true));
assertThat(getResponse.getField("_routing").getValue().toString(), equalTo("1"));
client().admin().indices().prepareFlush("my-index").get();
client().prepareGet("my-index", "my-type1", "1")
.setFields("field1", "_routing")
.setRouting("1")
.get();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getField("field1").isMetadataField(), equalTo(false));
assertThat(getResponse.getField("field1").getValue().toString(), equalTo("value"));
assertThat(getResponse.getField("_routing").isMetadataField(), equalTo(true));
assertThat(getResponse.getField("_routing").getValue().toString(), equalTo("1"));
}
@Test
public void testGetFields_nonLeafField() throws Exception {
client().admin().indices().prepareCreate("my-index")
.setSettings(ImmutableSettings.settingsBuilder().put("index.refresh_interval", -1))
.get();
client().prepareIndex("my-index", "my-type1", "1")
.setSource(jsonBuilder().startObject().startObject("field1").field("field2", "value1").endObject().endObject())
.get();
try {
client().prepareGet("my-index", "my-type1", "1").setFields("field1").get();
assert false;
} catch (ElasticSearchIllegalArgumentException e) {}
client().admin().indices().prepareFlush("my-index").get();
try {
client().prepareGet("my-index", "my-type1", "1").setFields("field1").get();
assert false;
} catch (ElasticSearchIllegalArgumentException e) {}
}
@Test
public void testGetFields_complexField() throws Exception {
client().admin().indices().prepareCreate("my-index")
.setSettings(ImmutableSettings.settingsBuilder().put("index.refresh_interval", -1))
.addMapping("my-type2", jsonBuilder().startObject().startObject("my-type2").startObject("properties")
.startObject("field1").field("type", "object")
.startObject("field2").field("type", "object")
.startObject("field3").field("type", "object")
.startObject("field4").field("type", "string").field("store", "yes")
.endObject()
.endObject()
.endObject()
.endObject().endObject().endObject())
.get();
BytesReference source = jsonBuilder().startObject()
.startArray("field1")
.startObject()
.startObject("field2")
.startArray("field3")
.startObject()
.field("field4", "value1")
.endObject()
.endArray()
.endObject()
.endObject()
.startObject()
.startObject("field2")
.startArray("field3")
.startObject()
.field("field4", "value2")
.endObject()
.endArray()
.endObject()
.endObject()
.endArray()
.endObject().bytes();
client().prepareIndex("my-index", "my-type1", "1").setSource(source).get();
client().prepareIndex("my-index", "my-type2", "1").setSource(source).get();
String field = "field1.field2.field3.field4";
GetResponse getResponse = client().prepareGet("my-index", "my-type1", "1").setFields(field).get();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getField(field).isMetadataField(), equalTo(false));
assertThat(getResponse.getField(field).getValues().size(), equalTo(2));
assertThat(getResponse.getField(field).getValues().get(0).toString(), equalTo("value1"));
assertThat(getResponse.getField(field).getValues().get(1).toString(), equalTo("value2"));
getResponse = client().prepareGet("my-index", "my-type2", "1").setFields(field).get();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getField(field).isMetadataField(), equalTo(false));
assertThat(getResponse.getField(field).getValues().size(), equalTo(2));
assertThat(getResponse.getField(field).getValues().get(0).toString(), equalTo("value1"));
assertThat(getResponse.getField(field).getValues().get(1).toString(), equalTo("value2"));
client().admin().indices().prepareFlush("my-index").get();
getResponse = client().prepareGet("my-index", "my-type1", "1").setFields(field).get();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getField(field).isMetadataField(), equalTo(false));
assertThat(getResponse.getField(field).getValues().size(), equalTo(2));
assertThat(getResponse.getField(field).getValues().get(0).toString(), equalTo("value1"));
assertThat(getResponse.getField(field).getValues().get(1).toString(), equalTo("value2"));
getResponse = client().prepareGet("my-index", "my-type2", "1").setFields(field).get();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getField(field).isMetadataField(), equalTo(false));
assertThat(getResponse.getField(field).getValues().size(), equalTo(2));
assertThat(getResponse.getField(field).getValues().get(0).toString(), equalTo("value1"));
assertThat(getResponse.getField(field).getValues().get(1).toString(), equalTo("value2"));
}
} }

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -331,4 +332,95 @@ public class SearchFieldsTests extends ElasticsearchIntegrationTest {
assertThat(((BytesReference) searchResponse.getHits().getAt(0).fields().get("binary_field").value()).toBytesArray(), equalTo((BytesReference) new BytesArray("testing text".getBytes("UTF8")))); assertThat(((BytesReference) searchResponse.getHits().getAt(0).fields().get("binary_field").value()).toBytesArray(), equalTo((BytesReference) new BytesArray("testing text".getBytes("UTF8"))));
} }
@Test
public void testSearchFields_metaData() throws Exception {
client().prepareIndex("my-index", "my-type1", "1")
.setRouting("1")
.setSource(jsonBuilder().startObject().field("field1", "value").endObject())
.setRefresh(true)
.get();
SearchResponse searchResponse = client().prepareSearch("my-index")
.setTypes("my-type1")
.addField("field1").addField("_routing")
.get();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
assertThat(searchResponse.getHits().getAt(0).field("field1").isMetadataField(), equalTo(false));
assertThat(searchResponse.getHits().getAt(0).field("field1").getValue().toString(), equalTo("value"));
assertThat(searchResponse.getHits().getAt(0).field("_routing").isMetadataField(), equalTo(true));
assertThat(searchResponse.getHits().getAt(0).field("_routing").getValue().toString(), equalTo("1"));
}
@Test
public void testSearchFields_nonLeafField() throws Exception {
client().prepareIndex("my-index", "my-type1", "1")
.setSource(jsonBuilder().startObject().startObject("field1").field("field2", "value1").endObject().endObject())
.setRefresh(true)
.get();
SearchResponse searchResponse = client().prepareSearch("my-index").setTypes("my-type1").addField("field1").get();
assertThat(searchResponse.getShardFailures().length, equalTo(1));
assertThat(searchResponse.getShardFailures()[0].status(), equalTo(RestStatus.BAD_REQUEST));
assertThat(searchResponse.getShardFailures()[0].reason(), containsString("field [field1] isn't a leaf field"));
}
@Test
public void testGetFields_complexField() throws Exception {
client().admin().indices().prepareCreate("my-index")
.setSettings(ImmutableSettings.settingsBuilder().put("index.refresh_interval", -1))
.addMapping("my-type2", jsonBuilder().startObject().startObject("my-type2").startObject("properties")
.startObject("field1").field("type", "object")
.startObject("field2").field("type", "object")
.startObject("field3").field("type", "object")
.startObject("field4").field("type", "string").field("store", "yes")
.endObject()
.endObject()
.endObject()
.endObject().endObject().endObject())
.get();
BytesReference source = jsonBuilder().startObject()
.startArray("field1")
.startObject()
.startObject("field2")
.startArray("field3")
.startObject()
.field("field4", "value1")
.endObject()
.endArray()
.endObject()
.endObject()
.startObject()
.startObject("field2")
.startArray("field3")
.startObject()
.field("field4", "value2")
.endObject()
.endArray()
.endObject()
.endObject()
.endArray()
.endObject().bytes();
client().prepareIndex("my-index", "my-type1", "1").setSource(source).get();
client().prepareIndex("my-index", "my-type2", "1").setRefresh(true).setSource(source).get();
String field = "field1.field2.field3.field4";
SearchResponse searchResponse = client().prepareSearch("my-index").setTypes("my-type1").addField(field).get();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
assertThat(searchResponse.getHits().getAt(0).field(field).isMetadataField(), equalTo(false));
assertThat(searchResponse.getHits().getAt(0).field(field).getValues().size(), equalTo(2));
assertThat(searchResponse.getHits().getAt(0).field(field).getValues().get(0).toString(), equalTo("value1"));
assertThat(searchResponse.getHits().getAt(0).field(field).getValues().get(1).toString(), equalTo("value2"));
searchResponse = client().prepareSearch("my-index").setTypes("my-type2").addField(field).get();
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
assertThat(searchResponse.getHits().getAt(0).field(field).isMetadataField(), equalTo(false));
assertThat(searchResponse.getHits().getAt(0).field(field).getValues().size(), equalTo(2));
assertThat(searchResponse.getHits().getAt(0).field(field).getValues().get(0).toString(), equalTo("value1"));
assertThat(searchResponse.getHits().getAt(0).field(field).getValues().get(1).toString(), equalTo("value2"));
}
} }